diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Framework/Communications/LoginService.cs | 149 | ||||
-rw-r--r-- | OpenSim/Framework/Communications/UserManagerBase.cs | 17 | ||||
-rw-r--r-- | OpenSim/Framework/Data.DB4o/DB4oUserData.cs | 6 | ||||
-rw-r--r-- | OpenSim/Framework/Data.MSSQL/MSSQLUserData.cs | 13 | ||||
-rw-r--r-- | OpenSim/Framework/Data.MySQL/MySQLUserData.cs | 33 | ||||
-rw-r--r-- | OpenSim/Framework/Data.SQLite/SQLiteUserData.cs | 24 | ||||
-rw-r--r-- | OpenSim/Framework/IUserData.cs | 6 |
7 files changed, 242 insertions, 6 deletions
diff --git a/OpenSim/Framework/Communications/LoginService.cs b/OpenSim/Framework/Communications/LoginService.cs index 9cfac1c..f0a0a0b 100644 --- a/OpenSim/Framework/Communications/LoginService.cs +++ b/OpenSim/Framework/Communications/LoginService.cs | |||
@@ -29,6 +29,7 @@ | |||
29 | using System; | 29 | using System; |
30 | using System.Collections; | 30 | using System.Collections; |
31 | using System.Collections.Generic; | 31 | using System.Collections.Generic; |
32 | using System.IO; | ||
32 | using System.Threading; | 33 | using System.Threading; |
33 | using libsecondlife; | 34 | using libsecondlife; |
34 | using libsecondlife.StructuredData; | 35 | using libsecondlife.StructuredData; |
@@ -79,7 +80,7 @@ namespace OpenSim.Framework.UserManagement | |||
79 | Hashtable requestData = (Hashtable) request.Params[0]; | 80 | Hashtable requestData = (Hashtable) request.Params[0]; |
80 | 81 | ||
81 | bool GoodXML = (requestData.Contains("first") && requestData.Contains("last") && | 82 | bool GoodXML = (requestData.Contains("first") && requestData.Contains("last") && |
82 | requestData.Contains("passwd")); | 83 | (requestData.Contains("passwd") || requestData.Contains("web_login_key"))); |
83 | bool GoodLogin = false; | 84 | bool GoodLogin = false; |
84 | 85 | ||
85 | UserProfileData userProfile; | 86 | UserProfileData userProfile; |
@@ -89,7 +90,8 @@ namespace OpenSim.Framework.UserManagement | |||
89 | { | 90 | { |
90 | string firstname = (string) requestData["first"]; | 91 | string firstname = (string) requestData["first"]; |
91 | string lastname = (string) requestData["last"]; | 92 | string lastname = (string) requestData["last"]; |
92 | string passwd = (string) requestData["passwd"]; | 93 | |
94 | |||
93 | 95 | ||
94 | userProfile = GetTheUser(firstname, lastname); | 96 | userProfile = GetTheUser(firstname, lastname); |
95 | if (userProfile == null) | 97 | if (userProfile == null) |
@@ -100,8 +102,29 @@ namespace OpenSim.Framework.UserManagement | |||
100 | 102 | ||
101 | return logResponse.CreateLoginFailedResponse(); | 103 | return logResponse.CreateLoginFailedResponse(); |
102 | } | 104 | } |
105 | if (requestData.Contains("passwd")) | ||
106 | { | ||
107 | string passwd = (string)requestData["passwd"]; | ||
108 | GoodLogin = AuthenticateUser(userProfile, passwd); | ||
109 | } | ||
110 | else if (requestData.Contains("web_login_key")) | ||
111 | { | ||
112 | LLUUID webloginkey = null; | ||
113 | try | ||
114 | { | ||
115 | webloginkey = new LLUUID((string)requestData["web_login_key"]); | ||
116 | } | ||
117 | catch (System.Exception) | ||
118 | { | ||
119 | return logResponse.CreateFailedResponse(); | ||
120 | } | ||
121 | GoodLogin = AuthenticateUser(userProfile, webloginkey); | ||
103 | 122 | ||
104 | GoodLogin = AuthenticateUser(userProfile, passwd); | 123 | } |
124 | else | ||
125 | { | ||
126 | return logResponse.CreateFailedResponse(); | ||
127 | } | ||
105 | } | 128 | } |
106 | else | 129 | else |
107 | { | 130 | { |
@@ -334,6 +357,105 @@ namespace OpenSim.Framework.UserManagement | |||
334 | { | 357 | { |
335 | } | 358 | } |
336 | 359 | ||
360 | public Hashtable ProcessHTMLLogin(Hashtable keysvals) | ||
361 | { | ||
362 | Hashtable returnactions = new Hashtable(); | ||
363 | int statuscode = 200; | ||
364 | |||
365 | returnactions["int_response_code"] = statuscode; | ||
366 | returnactions["str_response_string"] = GetDefaultLoginForm(); | ||
367 | |||
368 | if (keysvals.ContainsKey("show_login_form")) | ||
369 | { | ||
370 | if ((string)keysvals["show_login_form"] == "TRUE") | ||
371 | { | ||
372 | |||
373 | } | ||
374 | else | ||
375 | { | ||
376 | |||
377 | |||
378 | } | ||
379 | |||
380 | } | ||
381 | return returnactions; | ||
382 | |||
383 | } | ||
384 | |||
385 | public string GetLoginForm() | ||
386 | { | ||
387 | string file = Path.Combine(Util.configDir(), "http_loginform.html"); | ||
388 | if (!File.Exists(file)) | ||
389 | return GetDefaultLoginForm(); | ||
390 | |||
391 | StreamReader sr = File.OpenText(file); | ||
392 | string result = sr.ReadToEnd(); | ||
393 | sr.Close(); | ||
394 | return result; | ||
395 | } | ||
396 | |||
397 | public string GetDefaultLoginForm() | ||
398 | { | ||
399 | string responseString = | ||
400 | "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"; | ||
401 | responseString = responseString + "<html xmlns=\"http://www.w3.org/1999/xhtml\">"; | ||
402 | responseString = responseString + "<head>"; | ||
403 | responseString = responseString + | ||
404 | "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"; | ||
405 | responseString = responseString + "<meta http-equiv=\"cache-control\" content=\"no-cache\">"; | ||
406 | responseString = responseString + "<meta http-equiv=\"Pragma\" content=\"no-cache\">"; | ||
407 | responseString = responseString + "<title>Second Life Login</title>"; | ||
408 | responseString = responseString + "<body>"; | ||
409 | responseString = responseString + "<div id=\"login_box\">"; | ||
410 | |||
411 | responseString = responseString + "<form action=\"/\" method=\"GET\" id=\"login-form\">"; | ||
412 | |||
413 | responseString = responseString + "<div id=\"message\">[$errors]</div>"; | ||
414 | responseString = responseString + "<fieldset id=\"firstname\">"; | ||
415 | responseString = responseString + "<legend>First Name:</legend>"; | ||
416 | responseString = responseString + "<input type=\"text\" id=\"firstname_input\" size=\"15\" maxlength=\"100\" name=\"username\" value=\"[$firstname]\" />"; | ||
417 | responseString = responseString + "</fieldset>"; | ||
418 | responseString = responseString + "<fieldset id=\"lastname\">"; | ||
419 | responseString = responseString + "<legend>Last Name:</legend>"; | ||
420 | responseString = responseString + "<input type=\"text\" size=\"15\" maxlength=\"100\" name=\"lastname\" value=\"[$lastname]\" />"; | ||
421 | responseString = responseString + "</fieldset>"; | ||
422 | responseString = responseString + "<fieldset id=\"password\">"; | ||
423 | responseString = responseString + "<legend>Password:</legend>"; | ||
424 | responseString = responseString + "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\">"; | ||
425 | responseString = responseString + "<tr>"; | ||
426 | responseString = responseString + "<td colspan=\"2\"><input type=\"password\" size=\"15\" maxlength=\"100\" name=\"password\" value=\"[$password]\" /></td>"; | ||
427 | responseString = responseString + "</tr>"; | ||
428 | responseString = responseString + "<tr>"; | ||
429 | responseString = responseString + "<td valign=\"middle\"><input type=\"checkbox\" name=\"remember_password\" id=\"remember_password\" [$remember_password] style=\"margin-left:0px;\"/></td>"; | ||
430 | responseString = responseString + "<td><label for=\"remember_password\">Remember password</label></td>"; | ||
431 | responseString = responseString + "</tr>"; | ||
432 | responseString = responseString + "</table>"; | ||
433 | responseString = responseString + "</fieldset>"; | ||
434 | responseString = responseString + "<input type=\"hidden\" name=\"show_login_form\" value=\"FALSE\" />"; | ||
435 | responseString = responseString + "<input type=\"hidden\" name=\"method\" value=\"login\" />"; | ||
436 | responseString = responseString + "<input type=\"hidden\" id=\"grid\" name=\"grid\" value=\"[$grid]\" />"; | ||
437 | responseString = responseString + "<div id=\"submitbtn\">"; | ||
438 | responseString = responseString + "<input class=\"input_over\" type=\"submit\" value=\"Connect\" />"; | ||
439 | responseString = responseString + "</div>"; | ||
440 | responseString = responseString + "<div id=\"connecting\" style=\"visibility:hidden\"> Connecting...</div>"; | ||
441 | |||
442 | responseString = responseString + "<div id=\"helplinks\">"; | ||
443 | responseString = responseString + "<a href=\"http://www.secondlife.com/join/index.php\" target=\"_blank\">Create new account</a> | "; | ||
444 | responseString = responseString + "<a href=\"http://www.secondlife.com/account/request.php\" target=\"_blank\">Forgot password?</a>"; | ||
445 | responseString = responseString + "</div>"; | ||
446 | |||
447 | responseString = responseString + "<div id=\"channelinfo\"> [$clientchannelinfo] | [$clientversion]=[$clientlanguage]</div>"; | ||
448 | responseString = responseString + "</form>"; | ||
449 | responseString = responseString + "<script language=\"JavaScript\">"; | ||
450 | responseString = responseString + "document.getElementById('firstname_input').focus();"; | ||
451 | responseString = responseString + "</script>"; | ||
452 | responseString = responseString + "</div>"; | ||
453 | responseString = responseString + "</div>"; | ||
454 | responseString = responseString + "</body>"; | ||
455 | responseString = responseString + "</html>"; | ||
456 | return responseString; | ||
457 | } | ||
458 | |||
337 | /// <summary> | 459 | /// <summary> |
338 | /// Saves a target agent to the database | 460 | /// Saves a target agent to the database |
339 | /// </summary> | 461 | /// </summary> |
@@ -353,14 +475,33 @@ namespace OpenSim.Framework.UserManagement | |||
353 | /// <returns>Authenticated?</returns> | 475 | /// <returns>Authenticated?</returns> |
354 | public virtual bool AuthenticateUser(UserProfileData profile, string password) | 476 | public virtual bool AuthenticateUser(UserProfileData profile, string password) |
355 | { | 477 | { |
478 | bool passwordSuccess = false; | ||
356 | MainLog.Instance.Verbose( | 479 | MainLog.Instance.Verbose( |
357 | "LOGIN", "Authenticating {0} {1} ({2})", profile.username, profile.surname, profile.UUID); | 480 | "LOGIN", "Authenticating {0} {1} ({2})", profile.username, profile.surname, profile.UUID); |
358 | 481 | ||
482 | // Web Login method seems to also occasionally send the hashed password itself | ||
483 | |||
484 | |||
359 | password = password.Remove(0, 3); //remove $1$ | 485 | password = password.Remove(0, 3); //remove $1$ |
360 | 486 | ||
361 | string s = Util.Md5Hash(password + ":" + profile.passwordSalt); | 487 | string s = Util.Md5Hash(password + ":" + profile.passwordSalt); |
362 | 488 | ||
363 | return profile.passwordHash.Equals(s.ToString(), StringComparison.InvariantCultureIgnoreCase); | 489 | passwordSuccess = (profile.passwordHash.Equals(s.ToString(), StringComparison.InvariantCultureIgnoreCase) |
490 | || profile.passwordHash.Equals(password.ToString(),StringComparison.InvariantCultureIgnoreCase)); | ||
491 | |||
492 | return passwordSuccess; | ||
493 | } | ||
494 | |||
495 | public virtual bool AuthenticateUser(UserProfileData profile, LLUUID webloginkey) | ||
496 | { | ||
497 | bool passwordSuccess = false; | ||
498 | MainLog.Instance.Verbose( | ||
499 | "LOGIN", "Authenticating {0} {1} ({2})", profile.username, profile.surname, profile.UUID); | ||
500 | |||
501 | // Match web login key unless it's the default weblogin key LLUUID.Zero | ||
502 | passwordSuccess = ((profile.webLoginKey==webloginkey) && profile.webLoginKey != LLUUID.Zero); | ||
503 | |||
504 | return passwordSuccess; | ||
364 | } | 505 | } |
365 | 506 | ||
366 | /// <summary> | 507 | /// <summary> |
diff --git a/OpenSim/Framework/Communications/UserManagerBase.cs b/OpenSim/Framework/Communications/UserManagerBase.cs index bea56ea..2d72629 100644 --- a/OpenSim/Framework/Communications/UserManagerBase.cs +++ b/OpenSim/Framework/Communications/UserManagerBase.cs | |||
@@ -222,6 +222,23 @@ namespace OpenSim.Framework.UserManagement | |||
222 | 222 | ||
223 | } | 223 | } |
224 | 224 | ||
225 | public void StoreWebLoginKey(LLUUID agentID, LLUUID webLoginKey) | ||
226 | { | ||
227 | |||
228 | foreach (KeyValuePair<string, IUserData> plugin in _plugins) | ||
229 | { | ||
230 | try | ||
231 | { | ||
232 | plugin.Value.StoreWebLoginKey(agentID, webLoginKey); | ||
233 | } | ||
234 | catch (Exception e) | ||
235 | { | ||
236 | MainLog.Instance.Verbose("USERSTORAGE", | ||
237 | "Unable to Store WebLoginKey via " + plugin.Key + "(" + e.ToString() + ")"); | ||
238 | } | ||
239 | } | ||
240 | } | ||
241 | |||
225 | public void AddNewUserFriend(LLUUID friendlistowner, LLUUID friend, uint perms) | 242 | public void AddNewUserFriend(LLUUID friendlistowner, LLUUID friend, uint perms) |
226 | { | 243 | { |
227 | foreach (KeyValuePair<string, IUserData> plugin in _plugins) | 244 | foreach (KeyValuePair<string, IUserData> plugin in _plugins) |
diff --git a/OpenSim/Framework/Data.DB4o/DB4oUserData.cs b/OpenSim/Framework/Data.DB4o/DB4oUserData.cs index c6ac526..47521e4 100644 --- a/OpenSim/Framework/Data.DB4o/DB4oUserData.cs +++ b/OpenSim/Framework/Data.DB4o/DB4oUserData.cs | |||
@@ -132,7 +132,13 @@ namespace OpenSim.Framework.Data.DB4o | |||
132 | return null; | 132 | return null; |
133 | } | 133 | } |
134 | } | 134 | } |
135 | public void StoreWebLoginKey(LLUUID AgentID, LLUUID WebLoginKey) | ||
136 | { | ||
137 | UserProfileData user = GetUserByUUID(AgentID); | ||
138 | user.webLoginKey = WebLoginKey; | ||
139 | UpdateUserProfile(user); | ||
135 | 140 | ||
141 | } | ||
136 | #region User Friends List Data | 142 | #region User Friends List Data |
137 | 143 | ||
138 | public void AddNewUserFriend(LLUUID friendlistowner, LLUUID friend, uint perms) | 144 | public void AddNewUserFriend(LLUUID friendlistowner, LLUUID friend, uint perms) |
diff --git a/OpenSim/Framework/Data.MSSQL/MSSQLUserData.cs b/OpenSim/Framework/Data.MSSQL/MSSQLUserData.cs index db3c1d6..429ed39 100644 --- a/OpenSim/Framework/Data.MSSQL/MSSQLUserData.cs +++ b/OpenSim/Framework/Data.MSSQL/MSSQLUserData.cs | |||
@@ -297,7 +297,13 @@ namespace OpenSim.Framework.Data.MSSQL | |||
297 | return null; | 297 | return null; |
298 | } | 298 | } |
299 | } | 299 | } |
300 | 300 | public void StoreWebLoginKey(LLUUID AgentID, LLUUID WebLoginKey) | |
301 | { | ||
302 | UserProfileData user = GetUserByUUID(AgentID); | ||
303 | user.webLoginKey = WebLoginKey; | ||
304 | UpdateUserProfile(user); | ||
305 | |||
306 | } | ||
301 | /// <summary> | 307 | /// <summary> |
302 | /// Creates a new users profile | 308 | /// Creates a new users profile |
303 | /// </summary> | 309 | /// </summary> |
@@ -358,7 +364,8 @@ namespace OpenSim.Framework.Data.MSSQL | |||
358 | "profileAboutText = @profileAboutText," + | 364 | "profileAboutText = @profileAboutText," + |
359 | "profileFirstText = @profileFirstText," + | 365 | "profileFirstText = @profileFirstText," + |
360 | "profileImage = @profileImage," + | 366 | "profileImage = @profileImage," + |
361 | "profileFirstImage = @profileFirstImage where " + | 367 | "profileFirstImage = @profileFirstImage, " + |
368 | "webLoginKey = @webLoginKey where " + | ||
362 | "UUID = @keyUUUID;", database.getConnection()); | 369 | "UUID = @keyUUUID;", database.getConnection()); |
363 | SqlParameter param1 = new SqlParameter("@uuid", user.UUID.ToString()); | 370 | SqlParameter param1 = new SqlParameter("@uuid", user.UUID.ToString()); |
364 | SqlParameter param2 = new SqlParameter("@username", user.username); | 371 | SqlParameter param2 = new SqlParameter("@username", user.username); |
@@ -383,6 +390,7 @@ namespace OpenSim.Framework.Data.MSSQL | |||
383 | SqlParameter param21 = new SqlParameter("@profileImage", LLUUID.Zero.ToString()); | 390 | SqlParameter param21 = new SqlParameter("@profileImage", LLUUID.Zero.ToString()); |
384 | SqlParameter param22 = new SqlParameter("@profileFirstImage", LLUUID.Zero.ToString()); | 391 | SqlParameter param22 = new SqlParameter("@profileFirstImage", LLUUID.Zero.ToString()); |
385 | SqlParameter param23 = new SqlParameter("@keyUUUID", user.UUID.ToString()); | 392 | SqlParameter param23 = new SqlParameter("@keyUUUID", user.UUID.ToString()); |
393 | SqlParameter param24 = new SqlParameter("@webLoginKey", user.webLoginKey.UUID.ToString()); | ||
386 | command.Parameters.Add(param1); | 394 | command.Parameters.Add(param1); |
387 | command.Parameters.Add(param2); | 395 | command.Parameters.Add(param2); |
388 | command.Parameters.Add(param3); | 396 | command.Parameters.Add(param3); |
@@ -406,6 +414,7 @@ namespace OpenSim.Framework.Data.MSSQL | |||
406 | command.Parameters.Add(param21); | 414 | command.Parameters.Add(param21); |
407 | command.Parameters.Add(param22); | 415 | command.Parameters.Add(param22); |
408 | command.Parameters.Add(param23); | 416 | command.Parameters.Add(param23); |
417 | command.Parameters.Add(param24); | ||
409 | try | 418 | try |
410 | { | 419 | { |
411 | int affected = command.ExecuteNonQuery(); | 420 | int affected = command.ExecuteNonQuery(); |
diff --git a/OpenSim/Framework/Data.MySQL/MySQLUserData.cs b/OpenSim/Framework/Data.MySQL/MySQLUserData.cs index e53ab18..76ad551 100644 --- a/OpenSim/Framework/Data.MySQL/MySQLUserData.cs +++ b/OpenSim/Framework/Data.MySQL/MySQLUserData.cs | |||
@@ -468,6 +468,39 @@ namespace OpenSim.Framework.Data.MySQL | |||
468 | return GetAgentByUUID(profile.UUID); | 468 | return GetAgentByUUID(profile.UUID); |
469 | } | 469 | } |
470 | 470 | ||
471 | public void StoreWebLoginKey(LLUUID AgentID, LLUUID WebLoginKey) | ||
472 | { | ||
473 | |||
474 | Dictionary<string, string> param = new Dictionary<string, string>(); | ||
475 | param["?UUID"] = AgentID.UUID.ToString(); | ||
476 | param["?webLoginKey"] = WebLoginKey.UUID.ToString(); | ||
477 | |||
478 | try | ||
479 | { | ||
480 | lock (database) | ||
481 | { | ||
482 | IDbCommand updater = | ||
483 | database.Query( | ||
484 | "update users " + | ||
485 | "SET webLoginKey = ?webLoginKey " + | ||
486 | "where UUID = ?UUID", | ||
487 | param); | ||
488 | updater.ExecuteNonQuery(); | ||
489 | |||
490 | } | ||
491 | } | ||
492 | catch (Exception e) | ||
493 | { | ||
494 | database.Reconnect(); | ||
495 | MainLog.Instance.Error(e.ToString()); | ||
496 | return; | ||
497 | } | ||
498 | |||
499 | |||
500 | |||
501 | |||
502 | } | ||
503 | |||
471 | /// <summary> | 504 | /// <summary> |
472 | /// Returns an agent session by account UUID | 505 | /// Returns an agent session by account UUID |
473 | /// </summary> | 506 | /// </summary> |
diff --git a/OpenSim/Framework/Data.SQLite/SQLiteUserData.cs b/OpenSim/Framework/Data.SQLite/SQLiteUserData.cs index cbbb349..f1f76c4 100644 --- a/OpenSim/Framework/Data.SQLite/SQLiteUserData.cs +++ b/OpenSim/Framework/Data.SQLite/SQLiteUserData.cs | |||
@@ -373,6 +373,29 @@ namespace OpenSim.Framework.Data.SQLite | |||
373 | } | 373 | } |
374 | } | 374 | } |
375 | 375 | ||
376 | |||
377 | public void StoreWebLoginKey(LLUUID AgentID, LLUUID WebLoginKey) | ||
378 | { | ||
379 | DataTable users = ds.Tables["users"]; | ||
380 | lock (ds) | ||
381 | { | ||
382 | DataRow row = users.Rows.Find(Util.ToRawUuidString(AgentID)); | ||
383 | if (row == null) | ||
384 | { | ||
385 | MainLog.Instance.Warn("WEBLOGIN", "Unable to store new web login key for non-existant user"); | ||
386 | } | ||
387 | else | ||
388 | { | ||
389 | UserProfileData user = GetUserByUUID(AgentID); | ||
390 | user.webLoginKey = WebLoginKey; | ||
391 | fillUserRow(row, user); | ||
392 | da.Update(ds, "users"); | ||
393 | |||
394 | } | ||
395 | } | ||
396 | |||
397 | } | ||
398 | |||
376 | /// <summary> | 399 | /// <summary> |
377 | /// Creates a new user profile | 400 | /// Creates a new user profile |
378 | /// </summary> | 401 | /// </summary> |
@@ -392,6 +415,7 @@ namespace OpenSim.Framework.Data.SQLite | |||
392 | else | 415 | else |
393 | { | 416 | { |
394 | fillUserRow(row, user); | 417 | fillUserRow(row, user); |
418 | |||
395 | } | 419 | } |
396 | // This is why we're getting the 'logins never log-off'.. because It isn't clearing the | 420 | // This is why we're getting the 'logins never log-off'.. because It isn't clearing the |
397 | // useragents table once the useragent is null | 421 | // useragents table once the useragent is null |
diff --git a/OpenSim/Framework/IUserData.cs b/OpenSim/Framework/IUserData.cs index eba2329..9fd6fa8 100644 --- a/OpenSim/Framework/IUserData.cs +++ b/OpenSim/Framework/IUserData.cs | |||
@@ -81,6 +81,12 @@ namespace OpenSim.Framework | |||
81 | UserAgentData GetAgentByName(string fname, string lname); | 81 | UserAgentData GetAgentByName(string fname, string lname); |
82 | 82 | ||
83 | /// <summary> | 83 | /// <summary> |
84 | /// Stores new web-login key for user during web page login | ||
85 | /// </summary> | ||
86 | /// <param name="webLoginKey"></param> | ||
87 | void StoreWebLoginKey(LLUUID agentID, LLUUID webLoginKey); | ||
88 | |||
89 | /// <summary> | ||
84 | /// Adds a new User profile to the database | 90 | /// Adds a new User profile to the database |
85 | /// </summary> | 91 | /// </summary> |
86 | /// <param name="user">UserProfile to add</param> | 92 | /// <param name="user">UserProfile to add</param> |