UserManagerBase.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. /*
  2. * Copyright (c) Contributors, http://www.openmetaverse.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSim Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. *
  27. */
  28. using System;
  29. using System.Collections;
  30. using System.Collections.Generic;
  31. using System.Reflection;
  32. using System.Security.Cryptography;
  33. using libsecondlife;
  34. using Nwc.XmlRpc;
  35. using OpenSim.Framework.Console;
  36. using OpenSim.Framework.Data;
  37. using OpenSim.Framework.Interfaces;
  38. using OpenSim.Framework.Inventory;
  39. using OpenSim.Framework.Utilities;
  40. namespace OpenSim.Framework.UserManagement
  41. {
  42. public abstract class UserManagerBase
  43. {
  44. public UserConfig _config;
  45. Dictionary<string, IUserData> _plugins = new Dictionary<string, IUserData>();
  46. /// <summary>
  47. /// Adds a new user server plugin - user servers will be requested in the order they were loaded.
  48. /// </summary>
  49. /// <param name="FileName">The filename to the user server plugin DLL</param>
  50. public void AddPlugin(string FileName)
  51. {
  52. MainLog.Instance.Verbose( "Userstorage: Attempting to load " + FileName);
  53. Assembly pluginAssembly = Assembly.LoadFrom(FileName);
  54. MainLog.Instance.Verbose( "Userstorage: Found " + pluginAssembly.GetTypes().Length + " interfaces.");
  55. foreach (Type pluginType in pluginAssembly.GetTypes())
  56. {
  57. if (!pluginType.IsAbstract)
  58. {
  59. Type typeInterface = pluginType.GetInterface("IUserData", true);
  60. if (typeInterface != null)
  61. {
  62. IUserData plug = (IUserData)Activator.CreateInstance(pluginAssembly.GetType(pluginType.ToString()));
  63. plug.Initialise();
  64. this._plugins.Add(plug.getName(), plug);
  65. MainLog.Instance.Verbose( "Userstorage: Added IUserData Interface");
  66. }
  67. typeInterface = null;
  68. }
  69. }
  70. pluginAssembly = null;
  71. }
  72. #region Get UserProfile
  73. /// <summary>
  74. /// Loads a user profile from a database by UUID
  75. /// </summary>
  76. /// <param name="uuid">The target UUID</param>
  77. /// <returns>A user profile</returns>
  78. public UserProfileData getUserProfile(LLUUID uuid)
  79. {
  80. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  81. {
  82. try
  83. {
  84. UserProfileData profile = plugin.Value.getUserByUUID(uuid);
  85. profile.currentAgent = getUserAgent(profile.UUID);
  86. return profile;
  87. }
  88. catch (Exception e)
  89. {
  90. MainLog.Instance.Verbose( "Unable to find user via " + plugin.Key + "(" + e.ToString() + ")");
  91. }
  92. }
  93. return null;
  94. }
  95. /// <summary>
  96. /// Loads a user profile by name
  97. /// </summary>
  98. /// <param name="name">The target name</param>
  99. /// <returns>A user profile</returns>
  100. public UserProfileData getUserProfile(string name)
  101. {
  102. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  103. {
  104. try
  105. {
  106. UserProfileData profile = plugin.Value.getUserByName(name);
  107. profile.currentAgent = getUserAgent(profile.UUID);
  108. return profile;
  109. }
  110. catch (Exception e)
  111. {
  112. MainLog.Instance.Verbose( "Unable to find user via " + plugin.Key + "(" + e.ToString() + ")");
  113. }
  114. }
  115. return null;
  116. }
  117. /// <summary>
  118. /// Loads a user profile by name
  119. /// </summary>
  120. /// <param name="fname">First name</param>
  121. /// <param name="lname">Last name</param>
  122. /// <returns>A user profile</returns>
  123. public UserProfileData getUserProfile(string fname, string lname)
  124. {
  125. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  126. {
  127. try
  128. {
  129. UserProfileData profile = plugin.Value.getUserByName(fname,lname);
  130. profile.currentAgent = getUserAgent(profile.UUID);
  131. return profile;
  132. }
  133. catch (Exception e)
  134. {
  135. MainLog.Instance.Verbose( "Unable to find user via " + plugin.Key + "(" + e.ToString() + ")");
  136. }
  137. }
  138. return null;
  139. }
  140. #endregion
  141. #region Get UserAgent
  142. /// <summary>
  143. /// Loads a user agent by uuid (not called directly)
  144. /// </summary>
  145. /// <param name="uuid">The agents UUID</param>
  146. /// <returns>Agent profiles</returns>
  147. public UserAgentData getUserAgent(LLUUID uuid)
  148. {
  149. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  150. {
  151. try
  152. {
  153. return plugin.Value.getAgentByUUID(uuid);
  154. }
  155. catch (Exception e)
  156. {
  157. MainLog.Instance.Verbose( "Unable to find user via " + plugin.Key + "(" + e.ToString() + ")");
  158. }
  159. }
  160. return null;
  161. }
  162. /// <summary>
  163. /// Loads a user agent by name (not called directly)
  164. /// </summary>
  165. /// <param name="name">The agents name</param>
  166. /// <returns>A user agent</returns>
  167. public UserAgentData getUserAgent(string name)
  168. {
  169. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  170. {
  171. try
  172. {
  173. return plugin.Value.getAgentByName(name);
  174. }
  175. catch (Exception e)
  176. {
  177. MainLog.Instance.Verbose( "Unable to find user via " + plugin.Key + "(" + e.ToString() + ")");
  178. }
  179. }
  180. return null;
  181. }
  182. /// <summary>
  183. /// Loads a user agent by name (not called directly)
  184. /// </summary>
  185. /// <param name="fname">The agents firstname</param>
  186. /// <param name="lname">The agents lastname</param>
  187. /// <returns>A user agent</returns>
  188. public UserAgentData getUserAgent(string fname, string lname)
  189. {
  190. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  191. {
  192. try
  193. {
  194. return plugin.Value.getAgentByName(fname,lname);
  195. }
  196. catch (Exception e)
  197. {
  198. MainLog.Instance.Verbose( "Unable to find user via " + plugin.Key + "(" + e.ToString() + ")");
  199. }
  200. }
  201. return null;
  202. }
  203. #endregion
  204. #region CreateAgent
  205. /// <summary>
  206. /// Creates and initialises a new user agent - make sure to use CommitAgent when done to submit to the DB
  207. /// </summary>
  208. /// <param name="profile">The users profile</param>
  209. /// <param name="request">The users loginrequest</param>
  210. public void CreateAgent(UserProfileData profile, XmlRpcRequest request)
  211. {
  212. Hashtable requestData = (Hashtable)request.Params[0];
  213. UserAgentData agent = new UserAgentData();
  214. // User connection
  215. agent.agentOnline = true;
  216. // Generate sessions
  217. RNGCryptoServiceProvider rand = new RNGCryptoServiceProvider();
  218. byte[] randDataS = new byte[16];
  219. byte[] randDataSS = new byte[16];
  220. rand.GetBytes(randDataS);
  221. rand.GetBytes(randDataSS);
  222. agent.secureSessionID = new LLUUID(randDataSS, 0);
  223. agent.sessionID = new LLUUID(randDataS, 0);
  224. // Profile UUID
  225. agent.UUID = profile.UUID;
  226. // Current position (from Home)
  227. agent.currentHandle = profile.homeRegion;
  228. agent.currentPos = profile.homeLocation;
  229. // If user specified additional start, use that
  230. if (requestData.ContainsKey("start"))
  231. {
  232. string startLoc = ((string)requestData["start"]).Trim();
  233. if (!(startLoc == "last" || startLoc == "home"))
  234. {
  235. // Format: uri:Ahern&162&213&34
  236. try
  237. {
  238. string[] parts = startLoc.Remove(0, 4).Split('&');
  239. string region = parts[0];
  240. ////////////////////////////////////////////////////
  241. //SimProfile SimInfo = new SimProfile();
  242. //SimInfo = SimInfo.LoadFromGrid(theUser.currentAgent.currentHandle, _config.GridServerURL, _config.GridSendKey, _config.GridRecvKey);
  243. }
  244. catch (Exception)
  245. {
  246. }
  247. }
  248. }
  249. // What time did the user login?
  250. agent.loginTime = Util.UnixTimeSinceEpoch();
  251. agent.logoutTime = 0;
  252. // Current location
  253. agent.regionID = new LLUUID(); // Fill in later
  254. agent.currentRegion = new LLUUID(); // Fill in later
  255. profile.currentAgent = agent;
  256. }
  257. /// <summary>
  258. /// Saves a target agent to the database
  259. /// </summary>
  260. /// <param name="profile">The users profile</param>
  261. /// <returns>Successful?</returns>
  262. public bool CommitAgent(ref UserProfileData profile)
  263. {
  264. // Saves the agent to database
  265. return true;
  266. }
  267. #endregion
  268. /// <summary>
  269. /// Checks a user against it's password hash
  270. /// </summary>
  271. /// <param name="profile">The users profile</param>
  272. /// <param name="password">The supplied password</param>
  273. /// <returns>Authenticated?</returns>
  274. public virtual bool AuthenticateUser(UserProfileData profile, string password)
  275. {
  276. MainLog.Instance.Verbose(
  277. "Authenticating " + profile.username + " " + profile.surname);
  278. password = password.Remove(0, 3); //remove $1$
  279. string s = Util.Md5Hash(password + ":" + profile.passwordSalt);
  280. return profile.passwordHash.Equals(s.ToString(), StringComparison.InvariantCultureIgnoreCase);
  281. }
  282. #region Xml Response
  283. /// <summary>
  284. ///
  285. /// </summary>
  286. /// <param name="firstname"></param>
  287. /// <param name="lastname"></param>
  288. /// <returns></returns>
  289. public virtual UserProfileData GetTheUser(string firstname, string lastname)
  290. {
  291. return getUserProfile(firstname, lastname);
  292. }
  293. /// <summary>
  294. ///
  295. /// </summary>
  296. /// <returns></returns>
  297. public virtual string GetMessage()
  298. {
  299. return _config.DefaultStartupMsg;
  300. }
  301. /// <summary>
  302. /// Customises the login response and fills in missing values.
  303. /// </summary>
  304. /// <param name="response">The existing response</param>
  305. /// <param name="theUser">The user profile</param>
  306. public abstract void CustomiseResponse( LoginResponse response, UserProfileData theUser);
  307. /// <summary>
  308. /// Main user login function
  309. /// </summary>
  310. /// <param name="request">The XMLRPC request</param>
  311. /// <returns>The response to send</returns>
  312. public XmlRpcResponse XmlRpcLoginMethod(XmlRpcRequest request)
  313. {
  314. System.Console.WriteLine("Attempting login now...");
  315. XmlRpcResponse response = new XmlRpcResponse();
  316. Hashtable requestData = (Hashtable)request.Params[0];
  317. bool GoodXML = (requestData.Contains("first") && requestData.Contains("last") && requestData.Contains("passwd"));
  318. bool GoodLogin = false;
  319. string firstname = "";
  320. string lastname = "";
  321. string passwd = "";
  322. UserProfileData userProfile;
  323. LoginResponse logResponse = new LoginResponse();
  324. if (GoodXML)
  325. {
  326. firstname = (string)requestData["first"];
  327. lastname = (string)requestData["last"];
  328. passwd = (string)requestData["passwd"];
  329. userProfile = GetTheUser(firstname, lastname);
  330. if (userProfile == null)
  331. return logResponse.CreateLoginFailedResponse();
  332. GoodLogin = AuthenticateUser(userProfile, passwd);
  333. }
  334. else
  335. {
  336. return logResponse.CreateGridErrorResponse();
  337. }
  338. if (!GoodLogin)
  339. {
  340. return logResponse.CreateLoginFailedResponse();
  341. }
  342. else
  343. {
  344. // If we already have a session...
  345. if (userProfile.currentAgent != null && userProfile.currentAgent.agentOnline)
  346. {
  347. // Reject the login
  348. return logResponse.CreateAlreadyLoggedInResponse();
  349. }
  350. // Otherwise...
  351. // Create a new agent session
  352. CreateAgent( userProfile, request);
  353. try
  354. {
  355. LLUUID AgentID = userProfile.UUID;
  356. // Inventory Library Section
  357. ArrayList AgentInventoryArray = new ArrayList();
  358. Hashtable TempHash;
  359. AgentInventory Library = new AgentInventory();
  360. Library.CreateRootFolder(AgentID, true);
  361. foreach (InventoryFolder InvFolder in Library.InventoryFolders.Values)
  362. {
  363. TempHash = new Hashtable();
  364. TempHash["name"] = InvFolder.FolderName;
  365. TempHash["parent_id"] = InvFolder.ParentID.ToStringHyphenated();
  366. TempHash["version"] = (Int32)InvFolder.Version;
  367. TempHash["type_default"] = (Int32)InvFolder.DefaultType;
  368. TempHash["folder_id"] = InvFolder.FolderID.ToStringHyphenated();
  369. AgentInventoryArray.Add(TempHash);
  370. }
  371. Hashtable InventoryRootHash = new Hashtable();
  372. InventoryRootHash["folder_id"] = Library.InventoryRoot.FolderID.ToStringHyphenated();
  373. ArrayList InventoryRoot = new ArrayList();
  374. InventoryRoot.Add(InventoryRootHash);
  375. // Circuit Code
  376. uint circode = (uint)(Util.RandomClass.Next());
  377. logResponse.Lastname = userProfile.surname;
  378. logResponse.Firstname = userProfile.username;
  379. logResponse.AgentID = AgentID.ToStringHyphenated();
  380. logResponse.SessionID = userProfile.currentAgent.sessionID.ToStringHyphenated();
  381. logResponse.SecureSessionID = userProfile.currentAgent.secureSessionID.ToStringHyphenated();
  382. logResponse.InventoryRoot = InventoryRoot;
  383. logResponse.InventorySkeleton = AgentInventoryArray;
  384. logResponse.CircuitCode = (Int32)circode;
  385. //logResponse.RegionX = 0; //overwritten
  386. //logResponse.RegionY = 0; //overwritten
  387. logResponse.Home = "!!null temporary value {home}!!"; // Overwritten
  388. //logResponse.LookAt = "\n[r" + TheUser.homeLookAt.X.ToString() + ",r" + TheUser.homeLookAt.Y.ToString() + ",r" + TheUser.homeLookAt.Z.ToString() + "]\n";
  389. //logResponse.SimAddress = "127.0.0.1"; //overwritten
  390. //logResponse.SimPort = 0; //overwritten
  391. logResponse.Message = this.GetMessage();
  392. try
  393. {
  394. this.CustomiseResponse( logResponse, userProfile);
  395. }
  396. catch (Exception e)
  397. {
  398. System.Console.WriteLine(e.ToString());
  399. return logResponse.CreateDeadRegionResponse();
  400. //return logResponse.ToXmlRpcResponse();
  401. }
  402. CommitAgent(ref userProfile);
  403. return logResponse.ToXmlRpcResponse();
  404. }
  405. catch (Exception E)
  406. {
  407. System.Console.WriteLine(E.ToString());
  408. }
  409. //}
  410. }
  411. return response;
  412. }
  413. #endregion
  414. /// <summary>
  415. /// Deletes an active agent session
  416. /// </summary>
  417. /// <param name="request">The request</param>
  418. /// <param name="path">The path (eg /bork/narf/test)</param>
  419. /// <param name="param">Parameters sent</param>
  420. /// <returns>Success "OK" else error</returns>
  421. public string RestDeleteUserSessionMethod(string request, string path, string param)
  422. {
  423. // TODO! Important!
  424. return "OK";
  425. }
  426. /// <summary>
  427. ///
  428. /// </summary>
  429. /// <param name="user"></param>
  430. public void AddUserProfile(string firstName, string lastName, string pass, uint regX, uint regY)
  431. {
  432. UserProfileData user = new UserProfileData();
  433. user.homeLocation = new LLVector3(128, 128, 100);
  434. user.UUID = LLUUID.Random();
  435. user.username = firstName;
  436. user.surname = lastName;
  437. user.passwordHash = pass;
  438. user.passwordSalt = "";
  439. user.created = Util.UnixTimeSinceEpoch();
  440. user.homeLookAt = new LLVector3(100, 100, 100);
  441. user.homeRegion = Util.UIntsToLong((regX * 256), (regY * 256));
  442. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  443. {
  444. try
  445. {
  446. plugin.Value.addNewUserProfile(user);
  447. }
  448. catch (Exception e)
  449. {
  450. MainLog.Instance.Verbose("Unable to add user via " + plugin.Key + "(" + e.ToString() + ")");
  451. }
  452. }
  453. }
  454. /// <summary>
  455. /// Returns an error message that the user could not be found in the database
  456. /// </summary>
  457. /// <returns>XML string consisting of a error element containing individual error(s)</returns>
  458. public XmlRpcResponse CreateUnknownUserErrorResponse()
  459. {
  460. XmlRpcResponse response = new XmlRpcResponse();
  461. Hashtable responseData = new Hashtable();
  462. responseData["error_type"] = "unknown_user";
  463. responseData["error_desc"] = "The user requested is not in the database";
  464. response.Value = responseData;
  465. return response;
  466. }
  467. /// <summary>
  468. /// Converts a user profile to an XML element which can be returned
  469. /// </summary>
  470. /// <param name="profile">The user profile</param>
  471. /// <returns>A string containing an XML Document of the user profile</returns>
  472. public XmlRpcResponse ProfileToXmlRPCResponse(UserProfileData profile)
  473. {
  474. XmlRpcResponse response = new XmlRpcResponse();
  475. Hashtable responseData = new Hashtable();
  476. // Account information
  477. responseData["firstname"] = profile.username;
  478. responseData["lastname"] = profile.surname;
  479. responseData["uuid"] = profile.UUID.ToStringHyphenated();
  480. // Server Information
  481. responseData["server_inventory"] = profile.userInventoryURI;
  482. responseData["server_asset"] = profile.userAssetURI;
  483. // Profile Information
  484. responseData["profile_about"] = profile.profileAboutText;
  485. responseData["profile_firstlife_about"] = profile.profileFirstText;
  486. responseData["profile_firstlife_image"] = profile.profileFirstImage.ToStringHyphenated();
  487. responseData["profile_can_do"] = profile.profileCanDoMask.ToString();
  488. responseData["profile_want_do"] = profile.profileWantDoMask.ToString();
  489. responseData["profile_image"] = profile.profileImage.ToStringHyphenated();
  490. responseData["profile_created"] = profile.created.ToString();
  491. responseData["profile_lastlogin"] = profile.lastLogin.ToString();
  492. // Home region information
  493. responseData["home_coordinates_x"] = profile.homeLocation.X.ToString();
  494. responseData["home_coordinates_y"] = profile.homeLocation.Y.ToString();
  495. responseData["home_coordinates_z"] = profile.homeLocation.Z.ToString();
  496. responseData["home_region"] = profile.homeRegion.ToString();
  497. responseData["home_look_x"] = profile.homeLookAt.X.ToString();
  498. responseData["home_look_y"] = profile.homeLookAt.Y.ToString();
  499. responseData["home_look_z"] = profile.homeLookAt.Z.ToString();
  500. response.Value = responseData;
  501. return response;
  502. }
  503. #region XMLRPC User Methods
  504. //should most likely move out of here and into the grid's userserver sub class
  505. public XmlRpcResponse XmlRPCGetUserMethodName(XmlRpcRequest request)
  506. {
  507. XmlRpcResponse response = new XmlRpcResponse();
  508. Hashtable requestData = (Hashtable)request.Params[0];
  509. UserProfileData userProfile;
  510. if (requestData.Contains("avatar_name"))
  511. {
  512. userProfile = getUserProfile((string)requestData["avatar_name"]);
  513. if (userProfile == null)
  514. {
  515. return CreateUnknownUserErrorResponse();
  516. }
  517. }
  518. else
  519. {
  520. return CreateUnknownUserErrorResponse();
  521. }
  522. return ProfileToXmlRPCResponse(userProfile);
  523. }
  524. public XmlRpcResponse XmlRPCGetUserMethodUUID(XmlRpcRequest request)
  525. {
  526. XmlRpcResponse response = new XmlRpcResponse();
  527. Hashtable requestData = (Hashtable)request.Params[0];
  528. UserProfileData userProfile;
  529. if (requestData.Contains("avatar_uuid"))
  530. {
  531. userProfile = getUserProfile((LLUUID)requestData["avatar_uuid"]);
  532. if (userProfile == null)
  533. {
  534. return CreateUnknownUserErrorResponse();
  535. }
  536. }
  537. else
  538. {
  539. return CreateUnknownUserErrorResponse();
  540. }
  541. return ProfileToXmlRPCResponse(userProfile);
  542. }
  543. #endregion
  544. }
  545. }