UserManagerBase.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  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. System.Console.WriteLine("EEK!");
  113. MainLog.Instance.Verbose( "Unable to find user via " + plugin.Key + "(" + e.ToString() + ")");
  114. }
  115. }
  116. return null;
  117. }
  118. /// <summary>
  119. /// Loads a user profile by name
  120. /// </summary>
  121. /// <param name="fname">First name</param>
  122. /// <param name="lname">Last name</param>
  123. /// <returns>A user profile</returns>
  124. public UserProfileData getUserProfile(string fname, string lname)
  125. {
  126. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  127. {
  128. try
  129. {
  130. UserProfileData profile = plugin.Value.getUserByName(fname,lname);
  131. profile.currentAgent = getUserAgent(profile.UUID);
  132. return profile;
  133. }
  134. catch (Exception e)
  135. {
  136. MainLog.Instance.Verbose( "Unable to find user via " + plugin.Key + "(" + e.ToString() + ")");
  137. }
  138. }
  139. return null;
  140. }
  141. #endregion
  142. #region Get UserAgent
  143. /// <summary>
  144. /// Loads a user agent by uuid (not called directly)
  145. /// </summary>
  146. /// <param name="uuid">The agents UUID</param>
  147. /// <returns>Agent profiles</returns>
  148. public UserAgentData getUserAgent(LLUUID uuid)
  149. {
  150. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  151. {
  152. try
  153. {
  154. return plugin.Value.getAgentByUUID(uuid);
  155. }
  156. catch (Exception e)
  157. {
  158. MainLog.Instance.Verbose( "Unable to find user via " + plugin.Key + "(" + e.ToString() + ")");
  159. }
  160. }
  161. return null;
  162. }
  163. /// <summary>
  164. /// Loads a user agent by name (not called directly)
  165. /// </summary>
  166. /// <param name="name">The agents name</param>
  167. /// <returns>A user agent</returns>
  168. public UserAgentData getUserAgent(string name)
  169. {
  170. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  171. {
  172. try
  173. {
  174. return plugin.Value.getAgentByName(name);
  175. }
  176. catch (Exception e)
  177. {
  178. MainLog.Instance.Verbose( "Unable to find user via " + plugin.Key + "(" + e.ToString() + ")");
  179. }
  180. }
  181. return null;
  182. }
  183. /// <summary>
  184. /// Loads a user agent by name (not called directly)
  185. /// </summary>
  186. /// <param name="fname">The agents firstname</param>
  187. /// <param name="lname">The agents lastname</param>
  188. /// <returns>A user agent</returns>
  189. public UserAgentData getUserAgent(string fname, string lname)
  190. {
  191. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  192. {
  193. try
  194. {
  195. return plugin.Value.getAgentByName(fname,lname);
  196. }
  197. catch (Exception e)
  198. {
  199. MainLog.Instance.Verbose( "Unable to find user via " + plugin.Key + "(" + e.ToString() + ")");
  200. }
  201. }
  202. return null;
  203. }
  204. #endregion
  205. #region CreateAgent
  206. /// <summary>
  207. /// Creates and initialises a new user agent - make sure to use CommitAgent when done to submit to the DB
  208. /// </summary>
  209. /// <param name="profile">The users profile</param>
  210. /// <param name="request">The users loginrequest</param>
  211. public void CreateAgent(UserProfileData profile, XmlRpcRequest request)
  212. {
  213. Hashtable requestData = (Hashtable)request.Params[0];
  214. UserAgentData agent = new UserAgentData();
  215. // User connection
  216. agent.agentOnline = true;
  217. // Generate sessions
  218. RNGCryptoServiceProvider rand = new RNGCryptoServiceProvider();
  219. byte[] randDataS = new byte[16];
  220. byte[] randDataSS = new byte[16];
  221. rand.GetBytes(randDataS);
  222. rand.GetBytes(randDataSS);
  223. agent.secureSessionID = new LLUUID(randDataSS, 0);
  224. agent.sessionID = new LLUUID(randDataS, 0);
  225. // Profile UUID
  226. agent.UUID = profile.UUID;
  227. // Current position (from Home)
  228. agent.currentHandle = profile.homeRegion;
  229. agent.currentPos = profile.homeLocation;
  230. // If user specified additional start, use that
  231. if (requestData.ContainsKey("start"))
  232. {
  233. string startLoc = ((string)requestData["start"]).Trim();
  234. if (!(startLoc == "last" || startLoc == "home"))
  235. {
  236. // Format: uri:Ahern&162&213&34
  237. try
  238. {
  239. string[] parts = startLoc.Remove(0, 4).Split('&');
  240. string region = parts[0];
  241. ////////////////////////////////////////////////////
  242. //SimProfile SimInfo = new SimProfile();
  243. //SimInfo = SimInfo.LoadFromGrid(theUser.currentAgent.currentHandle, _config.GridServerURL, _config.GridSendKey, _config.GridRecvKey);
  244. }
  245. catch (Exception)
  246. {
  247. }
  248. }
  249. }
  250. // What time did the user login?
  251. agent.loginTime = Util.UnixTimeSinceEpoch();
  252. agent.logoutTime = 0;
  253. // Current location
  254. agent.regionID = new LLUUID(); // Fill in later
  255. agent.currentRegion = new LLUUID(); // Fill in later
  256. profile.currentAgent = agent;
  257. }
  258. /// <summary>
  259. /// Saves a target agent to the database
  260. /// </summary>
  261. /// <param name="profile">The users profile</param>
  262. /// <returns>Successful?</returns>
  263. public bool CommitAgent(ref UserProfileData profile)
  264. {
  265. // Saves the agent to database
  266. return true;
  267. }
  268. #endregion
  269. /// <summary>
  270. /// Checks a user against it's password hash
  271. /// </summary>
  272. /// <param name="profile">The users profile</param>
  273. /// <param name="password">The supplied password</param>
  274. /// <returns>Authenticated?</returns>
  275. public virtual bool AuthenticateUser(UserProfileData profile, string password)
  276. {
  277. MainLog.Instance.Verbose(
  278. "Authenticating " + profile.username + " " + profile.surname);
  279. password = password.Remove(0, 3); //remove $1$
  280. string s = Util.Md5Hash(password + ":" + profile.passwordSalt);
  281. return profile.passwordHash.Equals(s.ToString(), StringComparison.InvariantCultureIgnoreCase);
  282. }
  283. #region Xml Response
  284. /// <summary>
  285. ///
  286. /// </summary>
  287. /// <param name="firstname"></param>
  288. /// <param name="lastname"></param>
  289. /// <returns></returns>
  290. public virtual UserProfileData GetTheUser(string firstname, string lastname)
  291. {
  292. return getUserProfile(firstname, lastname);
  293. }
  294. /// <summary>
  295. ///
  296. /// </summary>
  297. /// <returns></returns>
  298. public virtual string GetMessage()
  299. {
  300. return _config.DefaultStartupMsg;
  301. }
  302. /// <summary>
  303. /// Customises the login response and fills in missing values.
  304. /// </summary>
  305. /// <param name="response">The existing response</param>
  306. /// <param name="theUser">The user profile</param>
  307. public abstract void CustomiseResponse( LoginResponse response, UserProfileData theUser);
  308. /// <summary>
  309. /// Main user login function
  310. /// </summary>
  311. /// <param name="request">The XMLRPC request</param>
  312. /// <returns>The response to send</returns>
  313. public XmlRpcResponse XmlRpcLoginMethod(XmlRpcRequest request)
  314. {
  315. System.Console.WriteLine("Attempting login now...");
  316. XmlRpcResponse response = new XmlRpcResponse();
  317. Hashtable requestData = (Hashtable)request.Params[0];
  318. bool GoodXML = (requestData.Contains("first") && requestData.Contains("last") && requestData.Contains("passwd"));
  319. bool GoodLogin = false;
  320. string firstname = "";
  321. string lastname = "";
  322. string passwd = "";
  323. UserProfileData userProfile;
  324. LoginResponse logResponse = new LoginResponse();
  325. if (GoodXML)
  326. {
  327. firstname = (string)requestData["first"];
  328. lastname = (string)requestData["last"];
  329. passwd = (string)requestData["passwd"];
  330. userProfile = GetTheUser(firstname, lastname);
  331. if (userProfile == null)
  332. return logResponse.CreateLoginFailedResponse();
  333. GoodLogin = AuthenticateUser(userProfile, passwd);
  334. }
  335. else
  336. {
  337. return logResponse.CreateGridErrorResponse();
  338. }
  339. if (!GoodLogin)
  340. {
  341. return logResponse.CreateLoginFailedResponse();
  342. }
  343. else
  344. {
  345. // If we already have a session...
  346. if (userProfile.currentAgent != null && userProfile.currentAgent.agentOnline)
  347. {
  348. // Reject the login
  349. return logResponse.CreateAlreadyLoggedInResponse();
  350. }
  351. // Otherwise...
  352. // Create a new agent session
  353. CreateAgent( userProfile, request);
  354. try
  355. {
  356. LLUUID AgentID = userProfile.UUID;
  357. // Inventory Library Section
  358. ArrayList AgentInventoryArray = new ArrayList();
  359. Hashtable TempHash;
  360. AgentInventory Library = new AgentInventory();
  361. Library.CreateRootFolder(AgentID, true);
  362. foreach (InventoryFolder InvFolder in Library.InventoryFolders.Values)
  363. {
  364. TempHash = new Hashtable();
  365. TempHash["name"] = InvFolder.FolderName;
  366. TempHash["parent_id"] = InvFolder.ParentID.ToStringHyphenated();
  367. TempHash["version"] = (Int32)InvFolder.Version;
  368. TempHash["type_default"] = (Int32)InvFolder.DefaultType;
  369. TempHash["folder_id"] = InvFolder.FolderID.ToStringHyphenated();
  370. AgentInventoryArray.Add(TempHash);
  371. }
  372. Hashtable InventoryRootHash = new Hashtable();
  373. InventoryRootHash["folder_id"] = Library.InventoryRoot.FolderID.ToStringHyphenated();
  374. ArrayList InventoryRoot = new ArrayList();
  375. InventoryRoot.Add(InventoryRootHash);
  376. // Circuit Code
  377. uint circode = (uint)(Util.RandomClass.Next());
  378. logResponse.Lastname = userProfile.surname;
  379. logResponse.Firstname = userProfile.username;
  380. logResponse.AgentID = AgentID.ToStringHyphenated();
  381. logResponse.SessionID = userProfile.currentAgent.sessionID.ToStringHyphenated();
  382. logResponse.SecureSessionID = userProfile.currentAgent.secureSessionID.ToStringHyphenated();
  383. logResponse.InventoryRoot = InventoryRoot;
  384. logResponse.InventorySkeleton = AgentInventoryArray;
  385. logResponse.CircuitCode = (Int32)circode;
  386. //logResponse.RegionX = 0; //overwritten
  387. //logResponse.RegionY = 0; //overwritten
  388. logResponse.Home = "!!null temporary value {home}!!"; // Overwritten
  389. //logResponse.LookAt = "\n[r" + TheUser.homeLookAt.X.ToString() + ",r" + TheUser.homeLookAt.Y.ToString() + ",r" + TheUser.homeLookAt.Z.ToString() + "]\n";
  390. //logResponse.SimAddress = "127.0.0.1"; //overwritten
  391. //logResponse.SimPort = 0; //overwritten
  392. logResponse.Message = this.GetMessage();
  393. try
  394. {
  395. this.CustomiseResponse( logResponse, userProfile);
  396. }
  397. catch (Exception e)
  398. {
  399. System.Console.WriteLine(e.ToString());
  400. return logResponse.CreateDeadRegionResponse();
  401. //return logResponse.ToXmlRpcResponse();
  402. }
  403. CommitAgent(ref userProfile);
  404. return logResponse.ToXmlRpcResponse();
  405. }
  406. catch (Exception E)
  407. {
  408. System.Console.WriteLine(E.ToString());
  409. }
  410. //}
  411. }
  412. return response;
  413. }
  414. #endregion
  415. /// <summary>
  416. /// Deletes an active agent session
  417. /// </summary>
  418. /// <param name="request">The request</param>
  419. /// <param name="path">The path (eg /bork/narf/test)</param>
  420. /// <param name="param">Parameters sent</param>
  421. /// <returns>Success "OK" else error</returns>
  422. public string RestDeleteUserSessionMethod(string request, string path, string param)
  423. {
  424. // TODO! Important!
  425. return "OK";
  426. }
  427. /// <summary>
  428. ///
  429. /// </summary>
  430. /// <param name="user"></param>
  431. public void AddUserProfile(string firstName, string lastName, string pass, uint regX, uint regY)
  432. {
  433. UserProfileData user = new UserProfileData();
  434. user.homeLocation = new LLVector3(128, 128, 100);
  435. user.UUID = LLUUID.Random();
  436. user.username = firstName;
  437. user.surname = lastName;
  438. user.passwordHash = pass;
  439. user.passwordSalt = "";
  440. user.created = Util.UnixTimeSinceEpoch();
  441. user.homeLookAt = new LLVector3(100, 100, 100);
  442. user.homeRegion = Util.UIntsToLong((regX * 256), (regY * 256));
  443. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  444. {
  445. try
  446. {
  447. plugin.Value.addNewUserProfile(user);
  448. }
  449. catch (Exception e)
  450. {
  451. MainLog.Instance.Verbose("Unable to add user via " + plugin.Key + "(" + e.ToString() + ")");
  452. }
  453. }
  454. }
  455. /// <summary>
  456. /// Returns an error message that the user could not be found in the database
  457. /// </summary>
  458. /// <returns>XML string consisting of a error element containing individual error(s)</returns>
  459. public XmlRpcResponse CreateUnknownUserErrorResponse()
  460. {
  461. XmlRpcResponse response = new XmlRpcResponse();
  462. Hashtable responseData = new Hashtable();
  463. responseData["error_type"] = "unknown_user";
  464. responseData["error_desc"] = "The user requested is not in the database";
  465. response.Value = responseData;
  466. return response;
  467. }
  468. /// <summary>
  469. /// Converts a user profile to an XML element which can be returned
  470. /// </summary>
  471. /// <param name="profile">The user profile</param>
  472. /// <returns>A string containing an XML Document of the user profile</returns>
  473. public XmlRpcResponse ProfileToXmlRPCResponse(UserProfileData profile)
  474. {
  475. XmlRpcResponse response = new XmlRpcResponse();
  476. Hashtable responseData = new Hashtable();
  477. // Account information
  478. responseData["firstname"] = profile.username;
  479. responseData["lastname"] = profile.surname;
  480. responseData["uuid"] = profile.UUID.ToStringHyphenated();
  481. // Server Information
  482. responseData["server_inventory"] = profile.userInventoryURI;
  483. responseData["server_asset"] = profile.userAssetURI;
  484. // Profile Information
  485. responseData["profile_about"] = profile.profileAboutText;
  486. responseData["profile_firstlife_about"] = profile.profileFirstText;
  487. responseData["profile_firstlife_image"] = profile.profileFirstImage.ToStringHyphenated();
  488. responseData["profile_can_do"] = profile.profileCanDoMask.ToString();
  489. responseData["profile_want_do"] = profile.profileWantDoMask.ToString();
  490. responseData["profile_image"] = profile.profileImage.ToStringHyphenated();
  491. responseData["profile_created"] = profile.created.ToString();
  492. responseData["profile_lastlogin"] = profile.lastLogin.ToString();
  493. // Home region information
  494. responseData["home_coordinates_x"] = profile.homeLocation.X.ToString();
  495. responseData["home_coordinates_y"] = profile.homeLocation.Y.ToString();
  496. responseData["home_coordinates_z"] = profile.homeLocation.Z.ToString();
  497. responseData["home_region"] = profile.homeRegion.ToString();
  498. responseData["home_look_x"] = profile.homeLookAt.X.ToString();
  499. responseData["home_look_y"] = profile.homeLookAt.Y.ToString();
  500. responseData["home_look_z"] = profile.homeLookAt.Z.ToString();
  501. response.Value = responseData;
  502. return response;
  503. }
  504. #region XMLRPC User Methods
  505. //should most likely move out of here and into the grid's userserver sub class
  506. public XmlRpcResponse XmlRPCGetUserMethodName(XmlRpcRequest request)
  507. {
  508. XmlRpcResponse response = new XmlRpcResponse();
  509. Hashtable requestData = (Hashtable)request.Params[0];
  510. UserProfileData userProfile;
  511. if (requestData.Contains("avatar_name"))
  512. {
  513. userProfile = getUserProfile((string)requestData["avatar_name"]);
  514. if (userProfile == null)
  515. {
  516. return CreateUnknownUserErrorResponse();
  517. }
  518. }
  519. else
  520. {
  521. return CreateUnknownUserErrorResponse();
  522. }
  523. return ProfileToXmlRPCResponse(userProfile);
  524. }
  525. public XmlRpcResponse XmlRPCGetUserMethodUUID(XmlRpcRequest request)
  526. {
  527. XmlRpcResponse response = new XmlRpcResponse();
  528. Hashtable requestData = (Hashtable)request.Params[0];
  529. UserProfileData userProfile;
  530. System.Console.WriteLine("METHOD BY UUID CALLED");
  531. if (requestData.Contains("avatar_uuid"))
  532. {
  533. userProfile = getUserProfile((LLUUID)requestData["avatar_uuid"]);
  534. if (userProfile == null)
  535. {
  536. return CreateUnknownUserErrorResponse();
  537. }
  538. }
  539. else
  540. {
  541. return CreateUnknownUserErrorResponse();
  542. }
  543. return ProfileToXmlRPCResponse(userProfile);
  544. }
  545. #endregion
  546. }
  547. }