aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Grid/UserServer.Modules/OpenIdService.cs
diff options
context:
space:
mode:
authorDiva Canto2010-01-10 17:15:02 -0800
committerDiva Canto2010-01-10 17:15:02 -0800
commitb0bbe861cd0f3eb06de73a371ab961428c549c69 (patch)
tree67110bb96bf3dfd6b0236f75761db5327adc515e /OpenSim/Grid/UserServer.Modules/OpenIdService.cs
parentForgot to remove 'using' (diff)
downloadopensim-SC_OLD-b0bbe861cd0f3eb06de73a371ab961428c549c69.zip
opensim-SC_OLD-b0bbe861cd0f3eb06de73a371ab961428c549c69.tar.gz
opensim-SC_OLD-b0bbe861cd0f3eb06de73a371ab961428c549c69.tar.bz2
opensim-SC_OLD-b0bbe861cd0f3eb06de73a371ab961428c549c69.tar.xz
Moved OpenId authentication from user server to Server.Handlers.Authentication.
Diffstat (limited to 'OpenSim/Grid/UserServer.Modules/OpenIdService.cs')
-rw-r--r--OpenSim/Grid/UserServer.Modules/OpenIdService.cs338
1 files changed, 0 insertions, 338 deletions
diff --git a/OpenSim/Grid/UserServer.Modules/OpenIdService.cs b/OpenSim/Grid/UserServer.Modules/OpenIdService.cs
deleted file mode 100644
index 49dfd86..0000000
--- a/OpenSim/Grid/UserServer.Modules/OpenIdService.cs
+++ /dev/null
@@ -1,338 +0,0 @@
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
28using System;
29using System.Collections.Generic;
30using System.Collections.Specialized;
31using System.IO;
32using System.Net;
33using System.Web;
34using DotNetOpenId;
35using DotNetOpenId.Provider;
36using OpenSim.Framework;
37using OpenSim.Framework.Servers;
38using OpenSim.Framework.Servers.HttpServer;
39
40namespace OpenSim.Grid.UserServer.Modules
41{
42 /// <summary>
43 /// Temporary, in-memory store for OpenID associations
44 /// </summary>
45 public class ProviderMemoryStore : IAssociationStore<AssociationRelyingPartyType>
46 {
47 private class AssociationItem
48 {
49 public AssociationRelyingPartyType DistinguishingFactor;
50 public string Handle;
51 public DateTime Expires;
52 public byte[] PrivateData;
53 }
54
55 Dictionary<string, AssociationItem> m_store = new Dictionary<string, AssociationItem>();
56 SortedList<DateTime, AssociationItem> m_sortedStore = new SortedList<DateTime, AssociationItem>();
57 object m_syncRoot = new object();
58
59 #region IAssociationStore<AssociationRelyingPartyType> Members
60
61 public void StoreAssociation(AssociationRelyingPartyType distinguishingFactor, Association assoc)
62 {
63 AssociationItem item = new AssociationItem();
64 item.DistinguishingFactor = distinguishingFactor;
65 item.Handle = assoc.Handle;
66 item.Expires = assoc.Expires.ToLocalTime();
67 item.PrivateData = assoc.SerializePrivateData();
68
69 lock (m_syncRoot)
70 {
71 m_store[item.Handle] = item;
72 m_sortedStore[item.Expires] = item;
73 }
74 }
75
76 public Association GetAssociation(AssociationRelyingPartyType distinguishingFactor)
77 {
78 lock (m_syncRoot)
79 {
80 if (m_sortedStore.Count > 0)
81 {
82 AssociationItem item = m_sortedStore.Values[m_sortedStore.Count - 1];
83 return Association.Deserialize(item.Handle, item.Expires.ToUniversalTime(), item.PrivateData);
84 }
85 else
86 {
87 return null;
88 }
89 }
90 }
91
92 public Association GetAssociation(AssociationRelyingPartyType distinguishingFactor, string handle)
93 {
94 AssociationItem item;
95 bool success = false;
96 lock (m_syncRoot)
97 success = m_store.TryGetValue(handle, out item);
98
99 if (success)
100 return Association.Deserialize(item.Handle, item.Expires.ToUniversalTime(), item.PrivateData);
101 else
102 return null;
103 }
104
105 public bool RemoveAssociation(AssociationRelyingPartyType distinguishingFactor, string handle)
106 {
107 lock (m_syncRoot)
108 {
109 for (int i = 0; i < m_sortedStore.Values.Count; i++)
110 {
111 AssociationItem item = m_sortedStore.Values[i];
112 if (item.Handle == handle)
113 {
114 m_sortedStore.RemoveAt(i);
115 break;
116 }
117 }
118
119 return m_store.Remove(handle);
120 }
121 }
122
123 public void ClearExpiredAssociations()
124 {
125 lock (m_syncRoot)
126 {
127 List<AssociationItem> itemsCopy = new List<AssociationItem>(m_sortedStore.Values);
128 DateTime now = DateTime.Now;
129
130 for (int i = 0; i < itemsCopy.Count; i++)
131 {
132 AssociationItem item = itemsCopy[i];
133
134 if (item.Expires <= now)
135 {
136 m_sortedStore.RemoveAt(i);
137 m_store.Remove(item.Handle);
138 }
139 }
140 }
141 }
142
143 #endregion
144 }
145
146 public class OpenIdStreamHandler : IStreamHandler
147 {
148 #region HTML
149
150 /// <summary>Login form used to authenticate OpenID requests</summary>
151 const string LOGIN_PAGE =
152@"<html>
153<head><title>OpenSim OpenID Login</title></head>
154<body>
155<h3>OpenSim Login</h3>
156<form method=""post"">
157<label for=""first"">First Name:</label> <input readonly type=""text"" name=""first"" id=""first"" value=""{0}""/>
158<label for=""last"">Last Name:</label> <input readonly type=""text"" name=""last"" id=""last"" value=""{1}""/>
159<label for=""pass"">Password:</label> <input type=""password"" name=""pass"" id=""pass""/>
160<input type=""submit"" value=""Login"">
161</form>
162</body>
163</html>";
164
165 /// <summary>Page shown for a valid OpenID identity</summary>
166 const string OPENID_PAGE =
167@"<html>
168<head>
169<title>{2} {3}</title>
170<link rel=""openid2.provider openid.server"" href=""{0}://{1}/openid/server/""/>
171</head>
172<body>OpenID identifier for {2} {3}</body>
173</html>
174";
175
176 /// <summary>Page shown for an invalid OpenID identity</summary>
177 const string INVALID_OPENID_PAGE =
178@"<html><head><title>Identity not found</title></head>
179<body>Invalid OpenID identity</body></html>";
180
181 /// <summary>Page shown if the OpenID endpoint is requested directly</summary>
182 const string ENDPOINT_PAGE =
183@"<html><head><title>OpenID Endpoint</title></head><body>
184This is an OpenID server endpoint, not a human-readable resource.
185For more information, see <a href='http://openid.net/'>http://openid.net/</a>.
186</body></html>";
187
188 #endregion HTML
189
190 public string ContentType { get { return m_contentType; } }
191 public string HttpMethod { get { return m_httpMethod; } }
192 public string Path { get { return m_path; } }
193
194 string m_contentType;
195 string m_httpMethod;
196 string m_path;
197 UserLoginService m_loginService;
198 ProviderMemoryStore m_openidStore = new ProviderMemoryStore();
199
200 /// <summary>
201 /// Constructor
202 /// </summary>
203 public OpenIdStreamHandler(string httpMethod, string path, UserLoginService loginService)
204 {
205 m_loginService = loginService;
206 m_httpMethod = httpMethod;
207 m_path = path;
208
209 m_contentType = "text/html";
210 }
211
212 /// <summary>
213 /// Handles all GET and POST requests for OpenID identifier pages and endpoint
214 /// server communication
215 /// </summary>
216 public void Handle(string path, Stream request, Stream response, OSHttpRequest httpRequest, OSHttpResponse httpResponse)
217 {
218 Uri providerEndpoint = new Uri(String.Format("{0}://{1}{2}", httpRequest.Url.Scheme, httpRequest.Url.Authority, httpRequest.Url.AbsolutePath));
219
220 // Defult to returning HTML content
221 m_contentType = "text/html";
222
223 try
224 {
225 NameValueCollection postQuery = HttpUtility.ParseQueryString(new StreamReader(httpRequest.InputStream).ReadToEnd());
226 NameValueCollection getQuery = HttpUtility.ParseQueryString(httpRequest.Url.Query);
227 NameValueCollection openIdQuery = (postQuery.GetValues("openid.mode") != null ? postQuery : getQuery);
228
229 OpenIdProvider provider = new OpenIdProvider(m_openidStore, providerEndpoint, httpRequest.Url, openIdQuery);
230
231 if (provider.Request != null)
232 {
233 if (!provider.Request.IsResponseReady && provider.Request is IAuthenticationRequest)
234 {
235 IAuthenticationRequest authRequest = (IAuthenticationRequest)provider.Request;
236 string[] passwordValues = postQuery.GetValues("pass");
237
238 UserProfileData profile;
239 if (TryGetProfile(new Uri(authRequest.ClaimedIdentifier.ToString()), out profile))
240 {
241 // Check for form POST data
242 if (passwordValues != null && passwordValues.Length == 1)
243 {
244 if (profile != null && m_loginService.AuthenticateUser(profile, passwordValues[0]))
245 authRequest.IsAuthenticated = true;
246 else
247 authRequest.IsAuthenticated = false;
248 }
249 else
250 {
251 // Authentication was requested, send the client a login form
252 using (StreamWriter writer = new StreamWriter(response))
253 writer.Write(String.Format(LOGIN_PAGE, profile.FirstName, profile.SurName));
254 return;
255 }
256 }
257 else
258 {
259 // Cannot find an avatar matching the claimed identifier
260 authRequest.IsAuthenticated = false;
261 }
262 }
263
264 // Add OpenID headers to the response
265 foreach (string key in provider.Request.Response.Headers.Keys)
266 httpResponse.AddHeader(key, provider.Request.Response.Headers[key]);
267
268 string[] contentTypeValues = provider.Request.Response.Headers.GetValues("Content-Type");
269 if (contentTypeValues != null && contentTypeValues.Length == 1)
270 m_contentType = contentTypeValues[0];
271
272 // Set the response code and document body based on the OpenID result
273 httpResponse.StatusCode = (int)provider.Request.Response.Code;
274 response.Write(provider.Request.Response.Body, 0, provider.Request.Response.Body.Length);
275 response.Close();
276 }
277 else if (httpRequest.Url.AbsolutePath.Contains("/openid/server"))
278 {
279 // Standard HTTP GET was made on the OpenID endpoint, send the client the default error page
280 using (StreamWriter writer = new StreamWriter(response))
281 writer.Write(ENDPOINT_PAGE);
282 }
283 else
284 {
285 // Try and lookup this avatar
286 UserProfileData profile;
287 if (TryGetProfile(httpRequest.Url, out profile))
288 {
289 using (StreamWriter writer = new StreamWriter(response))
290 {
291 // TODO: Print out a full profile page for this avatar
292 writer.Write(String.Format(OPENID_PAGE, httpRequest.Url.Scheme,
293 httpRequest.Url.Authority, profile.FirstName, profile.SurName));
294 }
295 }
296 else
297 {
298 // Couldn't parse an avatar name, or couldn't find the avatar in the user server
299 using (StreamWriter writer = new StreamWriter(response))
300 writer.Write(INVALID_OPENID_PAGE);
301 }
302 }
303 }
304 catch (Exception ex)
305 {
306 httpResponse.StatusCode = (int)HttpStatusCode.InternalServerError;
307 using (StreamWriter writer = new StreamWriter(response))
308 writer.Write(ex.Message);
309 }
310 }
311
312 /// <summary>
313 /// Parse a URL with a relative path of the form /users/First_Last and try to
314 /// retrieve the profile matching that avatar name
315 /// </summary>
316 /// <param name="requestUrl">URL to parse for an avatar name</param>
317 /// <param name="profile">Profile data for the avatar</param>
318 /// <returns>True if the parse and lookup were successful, otherwise false</returns>
319 bool TryGetProfile(Uri requestUrl, out UserProfileData profile)
320 {
321 if (requestUrl.Segments.Length == 3 && requestUrl.Segments[1] == "users/")
322 {
323 // Parse the avatar name from the path
324 string username = requestUrl.Segments[requestUrl.Segments.Length - 1];
325 string[] name = username.Split('_');
326
327 if (name.Length == 2)
328 {
329 profile = m_loginService.GetTheUser(name[0], name[1]);
330 return (profile != null);
331 }
332 }
333
334 profile = null;
335 return false;
336 }
337 }
338}