From ce7de3581cd678dd09227bdfde94fefb779f5a86 Mon Sep 17 00:00:00 2001 From: diva Date: Wed, 17 Jun 2009 03:52:39 +0000 Subject: Implementation of a simple authentication service + in connector in route to making HGInventory (client access) work in standalone again. This is the refactoring of what was/is there, but done in the new model. Not complete yet, but key authentication works. It should be enough to make HGInventory work again soon. --- .../AuthenticationService/AuthenticationService.cs | 181 ++++++++++++++++++++- .../Services/Interfaces/IAuthenticationService.cs | 30 ++-- 2 files changed, 191 insertions(+), 20 deletions(-) (limited to 'OpenSim/Services') diff --git a/OpenSim/Services/AuthenticationService/AuthenticationService.cs b/OpenSim/Services/AuthenticationService/AuthenticationService.cs index 3eaa03d..6eaf0b0 100644 --- a/OpenSim/Services/AuthenticationService/AuthenticationService.cs +++ b/OpenSim/Services/AuthenticationService/AuthenticationService.cs @@ -26,6 +26,7 @@ */ using System; +using System.Collections.Generic; using System.Reflection; using Nini.Config; using log4net; @@ -37,34 +38,196 @@ using OpenMetaverse; namespace OpenSim.Services.AuthenticationService { - public class AuthenticationService : ServiceBase, IAuthenticationService + /// + /// Simple authentication service implementation dealing only with users. + /// It uses the user DB directly to access user information. + /// It takes two config vars: + /// - Authenticate = {true|false} : to do or not to do authentication + /// - Authority = string like "osgrid.org" : this identity authority + /// that will be called back for identity verification + /// + public class HGAuthenticationService : ServiceBase, IAuthenticationService { - public AuthenticationService(IConfigSource config) : base(config) + private static readonly ILog m_log + = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected IUserDataPlugin m_Database; + protected string m_AuthorityURL; + protected bool m_PerformAuthentication; + protected Dictionary> m_UserKeys = new Dictionary>(); + + + public HGAuthenticationService(IConfigSource config) : base(config) { + string dllName = String.Empty; + string connString = String.Empty; + + // + // Try reading the [DatabaseService] section first, if it exists + // + IConfig dbConfig = config.Configs["DatabaseService"]; + if (dbConfig != null) + { + dllName = dbConfig.GetString("StorageProvider", String.Empty); + connString = dbConfig.GetString("ConnectionString", String.Empty); + } + + // + // Try reading the more specific [InventoryService] section, if it exists + // + IConfig authConfig = config.Configs["AuthenticationService"]; + if (authConfig != null) + { + dllName = authConfig.GetString("StorageProvider", dllName); + connString = authConfig.GetString("ConnectionString", connString); + + m_PerformAuthentication = authConfig.GetBoolean("Authenticate", true); + m_AuthorityURL = "http://" + authConfig.GetString("Authority", "localhost"); + } + + // + // We tried, but this doesn't exist. We can't proceed. + // + if (dllName.Equals(String.Empty)) + throw new Exception("No InventoryService configuration"); + + m_Database = LoadPlugin(dllName); + if (m_Database == null) + throw new Exception("Could not find a storage interface in the given module"); + + m_Database.Initialise(connString); + } + + /// + /// This implementation only authenticates users. + /// + /// + /// + /// + public bool Authenticate(UUID principalID, string password) + { + if (!m_PerformAuthentication) + return true; + + UserProfileData profile = m_Database.GetUserByUUID(principalID); + bool passwordSuccess = false; + m_log.InfoFormat("[AUTH]: Authenticating {0} {1} ({2})", profile.FirstName, profile.SurName, profile.ID); + + // we do this to get our hash in a form that the server password code can consume + // when the web-login-form submits the password in the clear (supposed to be over SSL!) + if (!password.StartsWith("$1$")) + password = "$1$" + Util.Md5Hash(password); + + password = password.Remove(0, 3); //remove $1$ + + string s = Util.Md5Hash(password + ":" + profile.PasswordSalt); + // Testing... + //m_log.Info("[LOGIN]: SubHash:" + s + " userprofile:" + profile.passwordHash); + //m_log.Info("[LOGIN]: userprofile:" + profile.passwordHash + " SubCT:" + password); + + passwordSuccess = (profile.PasswordHash.Equals(s.ToString(), StringComparison.InvariantCultureIgnoreCase) + || profile.PasswordHash.Equals(password, StringComparison.InvariantCultureIgnoreCase)); + + return passwordSuccess; } - public UUID AllocateUserSession(UUID userID) + /// + /// This generates authorization keys in the form + /// http://authority/uuid + /// after verifying that the caller is, indeed, authorized to request a key + /// + /// The principal ID requesting the new key + /// The original authorization token for that principal, obtained during login + /// + public string GetKey(UUID principalID, string authToken) { - return UUID.Zero; + UserProfileData profile = m_Database.GetUserByUUID(principalID); + string newKey = string.Empty; + + if (profile != null) + { + m_log.DebugFormat("[AUTH]: stored auth token is {0}. Given token is {1}", profile.WebLoginKey.ToString(), authToken); + // I'm overloading webloginkey for this, so that no changes are needed in the DB + // The uses of webloginkey are fairly mutually exclusive + if (profile.WebLoginKey.ToString().Equals(authToken)) + { + newKey = UUID.Random().ToString(); + List keys; + lock (m_UserKeys) + { + if (m_UserKeys.ContainsKey(principalID)) + { + keys = m_UserKeys[principalID]; + } + else + { + keys = new List(); + m_UserKeys.Add(principalID, keys); + } + keys.Add(newKey); + } + m_log.InfoFormat("[AUTH]: Successfully generated new auth key for {0}", principalID); + } + else + m_log.Warn("[AUTH]: Unauthorized key generation request. Denying new key."); + } + else + m_log.Warn("[AUTH]: Principal not found."); + + return m_AuthorityURL + newKey; } - public string GetUserKey(UUID userID, string authToken) + /// + /// This verifies the uuid portion of the key given out by GenerateKey + /// + /// + /// + /// + public bool VerifyKey(UUID userID, string key) { - return String.Empty; + lock (m_UserKeys) + { + if (m_UserKeys.ContainsKey(userID)) + { + List keys = m_UserKeys[userID]; + if (keys.Contains(key)) + { + // Keys are one-time only, so remove it + keys.Remove(key); + return true; + } + return false; + } + else + return false; + } } - public bool VerifyUserKey(UUID userID, string key) + public UUID AllocateUserSession(UUID userID) { - return false; + // Not implemented yet + return UUID.Zero; } - public bool VerifyUserSession(UUID userID, UUID session) + public bool VerifyUserSession(UUID userID, UUID sessionID) { + UserProfileData userProfile = m_Database.GetUserByUUID(userID); + + if (userProfile != null && userProfile.CurrentAgent != null) + { + m_log.DebugFormat("[AUTH]: Verifying session {0} for {1}; current session {2}", sessionID, userID, userProfile.CurrentAgent.SessionID); + if (userProfile.CurrentAgent.SessionID == sessionID) + { + return true; + } + } + return false; } public void DestroyUserSession(UUID userID) { + // Not implemented yet } } } diff --git a/OpenSim/Services/Interfaces/IAuthenticationService.cs b/OpenSim/Services/Interfaces/IAuthenticationService.cs index 35831c1..fa45cbc 100644 --- a/OpenSim/Services/Interfaces/IAuthenticationService.cs +++ b/OpenSim/Services/Interfaces/IAuthenticationService.cs @@ -30,31 +30,39 @@ using OpenMetaverse; namespace OpenSim.Services.Interfaces { + // Generic Authentication service used for identifying + // and authenticating principals. + // Principals may be clients acting on users' behalf, + // or any other components that need + // verifiable identification. + // public interface IAuthenticationService { - // Create a new user session. If one exists, it is cleared + // Check the pricipal's password // - UUID AllocateUserSession(UUID userID); + bool Authenticate(UUID principalID, string password); - // Get a user key from an authentication token. This must be - // done before the session allocated above is considered valid. - // Repeated calls to this method with the same auth token will - // create different keys and invalidate the previous ne. + // Get a service key given that principal's + // authentication token (master key). // - string GetUserKey(UUID userID, string authToken); + string GetKey(UUID principalID, string authToken); - // Verify that a user key is valid + // Verify that a principal key is valid // - bool VerifyUserKey(UUID userID, string key); + bool VerifyKey(UUID principalID, string key); + + // Create a new user session. If one exists, it is cleared + // + UUID AllocateUserSession(UUID userID); // Verify that a user session ID is valid. A session ID is // considered valid when a user has successfully authenticated // at least one time inside that session. // - bool VerifyUserSession(UUID userID, UUID session); + bool VerifyUserSession(UUID principalID, UUID session); // Remove a user session identifier and deauthenticate the user // - void DestroyUserSession(UUID userID); + void DestroyUserSession(UUID principalID); } } -- cgit v1.1