OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
SimianUserAccountServiceConnector.cs
Go to the documentation of this file.
1 /*
2  * Copyright (c) Contributors, http://opensimulator.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 OpenSimulator 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.Generic;
30 using System.Collections.Specialized;
31 using System.Reflection;
32 using OpenSim.Framework;
33 using OpenSim.Region.Framework.Interfaces;
34 using OpenSim.Region.Framework.Scenes;
35 using OpenSim.Services.Interfaces;
36 using log4net;
37 using Mono.Addins;
38 using Nini.Config;
39 using OpenMetaverse;
40 using OpenMetaverse.StructuredData;
41 
42 namespace OpenSim.Services.Connectors.SimianGrid
43 {
48  [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimianUserAccountServiceConnector")]
50  {
51  private const double CACHE_EXPIRATION_SECONDS = 120.0;
52 
53  private static readonly ILog m_log =
54  LogManager.GetLogger(
55  MethodBase.GetCurrentMethod().DeclaringType);
56 
57  private string m_serverUrl = String.Empty;
58  private ExpiringCache<UUID, UserAccount> m_accountCache = new ExpiringCache<UUID,UserAccount>();
59  private bool m_Enabled;
60 
61  #region ISharedRegionModule
62 
63  public Type ReplaceableInterface { get { return null; } }
64  public void RegionLoaded(Scene scene) { }
65  public void PostInitialise() { }
66  public void Close() { }
67 
69  public string Name { get { return "SimianUserAccountServiceConnector"; } }
70  public void AddRegion(Scene scene) { if (m_Enabled) { scene.RegisterModuleInterface<IUserAccountService>(this); } }
71  public void RemoveRegion(Scene scene) { if (m_Enabled) { scene.UnregisterModuleInterface<IUserAccountService>(this); } }
72 
73  #endregion ISharedRegionModule
74 
75  public SimianUserAccountServiceConnector(IConfigSource source)
76  {
77  CommonInit(source);
78  }
79 
80  public void Initialise(IConfigSource source)
81  {
82  IConfig moduleConfig = source.Configs["Modules"];
83  if (moduleConfig != null)
84  {
85  string name = moduleConfig.GetString("UserAccountServices", "");
86  if (name == Name)
87  CommonInit(source);
88  }
89  }
90 
91  private void CommonInit(IConfigSource source)
92  {
93  IConfig gridConfig = source.Configs["UserAccountService"];
94  if (gridConfig != null)
95  {
96  string serviceUrl = gridConfig.GetString("UserAccountServerURI");
97  if (!String.IsNullOrEmpty(serviceUrl))
98  {
99  if (!serviceUrl.EndsWith("/") && !serviceUrl.EndsWith("="))
100  serviceUrl = serviceUrl + '/';
101  m_serverUrl = serviceUrl;
102  m_Enabled = true;
103  }
104  }
105 
106  if (String.IsNullOrEmpty(m_serverUrl))
107  m_log.Info("[SIMIAN ACCOUNT CONNECTOR]: No UserAccountServerURI specified, disabling connector");
108  }
109 
110  public UserAccount GetUserAccount(UUID scopeID, string firstName, string lastName)
111  {
112  NameValueCollection requestArgs = new NameValueCollection
113  {
114  { "RequestMethod", "GetUser" },
115  { "Name", firstName + ' ' + lastName }
116  };
117 
118  return GetUser(requestArgs);
119  }
120 
121  public UserAccount GetUserAccount(UUID scopeID, string email)
122  {
123  NameValueCollection requestArgs = new NameValueCollection
124  {
125  { "RequestMethod", "GetUser" },
126  { "Email", email }
127  };
128 
129  return GetUser(requestArgs);
130  }
131 
132  public UserAccount GetUserAccount(UUID scopeID, UUID userID)
133  {
134  // Cache check
135  UserAccount account;
136  if (m_accountCache.TryGetValue(userID, out account))
137  return account;
138 
139  NameValueCollection requestArgs = new NameValueCollection
140  {
141  { "RequestMethod", "GetUser" },
142  { "UserID", userID.ToString() }
143  };
144 
145  account = GetUser(requestArgs);
146 
147  if (account == null)
148  {
149  // Store null responses too, to avoid repeated lookups for missing accounts
150  m_accountCache.AddOrUpdate(userID, null, CACHE_EXPIRATION_SECONDS);
151  }
152 
153  return account;
154  }
155 
156  public List<UserAccount> GetUserAccounts(UUID scopeID, string query)
157  {
158  List<UserAccount> accounts = new List<UserAccount>();
159 
160 // m_log.DebugFormat("[SIMIAN ACCOUNT CONNECTOR]: Searching for user accounts with name query " + query);
161 
162  NameValueCollection requestArgs = new NameValueCollection
163  {
164  { "RequestMethod", "GetUsers" },
165  { "NameQuery", query }
166  };
167 
168  OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs);
169  if (response["Success"].AsBoolean())
170  {
171  OSDArray array = response["Users"] as OSDArray;
172  if (array != null && array.Count > 0)
173  {
174  for (int i = 0; i < array.Count; i++)
175  {
176  UserAccount account = ResponseToUserAccount(array[i] as OSDMap);
177  if (account != null)
178  accounts.Add(account);
179  }
180  }
181  else
182  {
183  m_log.Warn("[SIMIAN ACCOUNT CONNECTOR]: Account search failed, response data was in an invalid format");
184  }
185  }
186  else
187  {
188  m_log.Warn("[SIMIAN ACCOUNT CONNECTOR]: Failed to search for account data by name " + query);
189  }
190 
191  return accounts;
192  }
193 
194  public void InvalidateCache(UUID userID)
195  {
196  m_accountCache.Remove(userID);
197  }
198 
199  public List<UserAccount> GetUserAccountsWhere(UUID scopeID, string query)
200  {
201  return null;
202  }
203 
204  public bool StoreUserAccount(UserAccount data)
205  {
206 // m_log.InfoFormat("[SIMIAN ACCOUNT CONNECTOR]: Storing user account for " + data.Name);
207 
208  NameValueCollection requestArgs = new NameValueCollection
209  {
210  { "RequestMethod", "AddUser" },
211  { "UserID", data.PrincipalID.ToString() },
212  { "Name", data.Name },
213  { "Email", data.Email },
214  { "AccessLevel", data.UserLevel.ToString() }
215  };
216 
217  OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs);
218 
219  if (response["Success"].AsBoolean())
220  {
221  m_log.InfoFormat("[SIMIAN ACCOUNT CONNECTOR]: Storing user account data for " + data.Name);
222 
223  requestArgs = new NameValueCollection
224  {
225  { "RequestMethod", "AddUserData" },
226  { "UserID", data.PrincipalID.ToString() },
227  { "CreationDate", data.Created.ToString() },
228  { "UserFlags", data.UserFlags.ToString() },
229  { "UserTitle", data.UserTitle }
230  };
231 
232  response = SimianGrid.PostToService(m_serverUrl, requestArgs);
233  bool success = response["Success"].AsBoolean();
234 
235  if (success)
236  {
237  // Cache the user account info
238  m_accountCache.AddOrUpdate(data.PrincipalID, data, CACHE_EXPIRATION_SECONDS);
239  }
240  else
241  {
242  m_log.Warn("[SIMIAN ACCOUNT CONNECTOR]: Failed to store user account data for " + data.Name + ": " + response["Message"].AsString());
243  }
244 
245  return success;
246  }
247  else
248  {
249  m_log.Warn("[SIMIAN ACCOUNT CONNECTOR]: Failed to store user account for " + data.Name + ": " + response["Message"].AsString());
250  }
251 
252  return false;
253  }
254 
260  private UserAccount GetUser(NameValueCollection requestArgs)
261  {
262  string lookupValue = (requestArgs.Count > 1) ? requestArgs[1] : "(Unknown)";
263 // m_log.DebugFormat("[SIMIAN ACCOUNT CONNECTOR]: Looking up user account with query: " + lookupValue);
264 
265  OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs);
266  if (response["Success"].AsBoolean())
267  {
268  OSDMap user = response["User"] as OSDMap;
269  if (user != null)
270  return ResponseToUserAccount(user);
271  else
272  m_log.Warn("[SIMIAN ACCOUNT CONNECTOR]: Account search failed, response data was in an invalid format");
273  }
274  else
275  {
276  m_log.Warn("[SIMIAN ACCOUNT CONNECTOR]: Failed to lookup user account with query: " + lookupValue);
277  }
278 
279  return null;
280  }
281 
287  private UserAccount ResponseToUserAccount(OSDMap response)
288  {
289  if (response == null)
290  return null;
291 
292  UserAccount account = new UserAccount();
293  account.PrincipalID = response["UserID"].AsUUID();
294  account.Created = response["CreationDate"].AsInteger();
295  account.Email = response["Email"].AsString();
296  account.ServiceURLs = new Dictionary<string, object>(0);
297  account.UserFlags = response["UserFlags"].AsInteger();
298  account.UserLevel = response["AccessLevel"].AsInteger();
299  account.UserTitle = response["UserTitle"].AsString();
300  account.LocalToGrid = true;
301  if (response.ContainsKey("LocalToGrid"))
302  account.LocalToGrid = (response["LocalToGrid"].AsString() == "true" ? true : false);
303 
304  GetFirstLastName(response["Name"].AsString(), out account.FirstName, out account.LastName);
305 
306  // Cache the user account info
307  m_accountCache.AddOrUpdate(account.PrincipalID, account, CACHE_EXPIRATION_SECONDS);
308 
309  return account;
310  }
311 
318  private static void GetFirstLastName(string name, out string firstName, out string lastName)
319  {
320  if (String.IsNullOrEmpty(name))
321  {
322  firstName = String.Empty;
323  lastName = String.Empty;
324  }
325  else
326  {
327  string[] names = name.Split(' ');
328 
329  if (names.Length == 2)
330  {
331  firstName = names[0];
332  lastName = names[1];
333  }
334  else
335  {
336  firstName = String.Empty;
337  lastName = name;
338  }
339  }
340  }
341  }
342 }
OpenMetaverse.StructuredData.OSDArray OSDArray
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
bool StoreUserAccount(UserAccount data)
Store the data given, wich replaces the stored data, therefore must be complete.
void PostInitialise()
This is called exactly once after all the shared region-modules have been instanciated and IRegionMod...
void Initialise(IConfigSource source)
This is called to initialize the region module. For shared modules, this is called exactly once...
OpenMetaverse.StructuredData.OSDMap OSDMap
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
Connects user account data (creating new users, looking up existing users) to the SimianGrid backend ...
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
List< UserAccount > GetUserAccounts(UUID scopeID, string query)
Returns the list of avatars that matches both the search criterion and the scope ID passed ...
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
UserAccount GetUserAccount(UUID scopeID, string firstName, string lastName)