aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--OpenSim/Grid/UserServer/Main.cs6
-rw-r--r--OpenSim/Grid/UserServer/OpenIdService.cs339
-rw-r--r--ThirdPartyLicenses/DotNetOpenid.txt10
-rw-r--r--bin/DotNetOpenId.dllbin0 -> 353280 bytes
-rw-r--r--prebuild.xml2
5 files changed, 357 insertions, 0 deletions
diff --git a/OpenSim/Grid/UserServer/Main.cs b/OpenSim/Grid/UserServer/Main.cs
index a001a6f..f688e4c 100644
--- a/OpenSim/Grid/UserServer/Main.cs
+++ b/OpenSim/Grid/UserServer/Main.cs
@@ -187,6 +187,12 @@ namespace OpenSim.Grid.UserServer
187 new RestStreamHandler("DELETE", "/usersessions/", m_userManager.RestDeleteUserSessionMethod)); 187 new RestStreamHandler("DELETE", "/usersessions/", m_userManager.RestDeleteUserSessionMethod));
188 188
189 m_httpServer.AddXmlRPCHandler("update_user_profile", m_userManager.XmlRpcResponseXmlRPCUpdateUserProfile); 189 m_httpServer.AddXmlRPCHandler("update_user_profile", m_userManager.XmlRpcResponseXmlRPCUpdateUserProfile);
190
191 // Handler for OpenID avatar identity pages
192 m_httpServer.AddStreamHandler(new OpenIdStreamHandler("GET", "/users/", m_loginService));
193 // Handlers for the OpenID endpoint server
194 m_httpServer.AddStreamHandler(new OpenIdStreamHandler("POST", "/openid/server/", m_loginService));
195 m_httpServer.AddStreamHandler(new OpenIdStreamHandler("GET", "/openid/server/", m_loginService));
190 } 196 }
191 197
192 public void do_create(string[] args) 198 public void do_create(string[] args)
diff --git a/OpenSim/Grid/UserServer/OpenIdService.cs b/OpenSim/Grid/UserServer/OpenIdService.cs
new file mode 100644
index 0000000..695968f
--- /dev/null
+++ b/OpenSim/Grid/UserServer/OpenIdService.cs
@@ -0,0 +1,339 @@
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 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
28using System;
29using System.Collections.Generic;
30using System.Collections.Specialized;
31using System.IO;
32using System.Net;
33using System.Web;
34using System.Text;
35using DotNetOpenId;
36using DotNetOpenId.Provider;
37using log4net;
38using OpenSim.Framework;
39using OpenSim.Framework.Servers;
40
41namespace OpenSim.Grid.UserServer
42{
43 /// <summary>
44 /// Temporary, in-memory store for OpenID associations
45 /// </summary>
46 public class ProviderMemoryStore : IAssociationStore<AssociationRelyingPartyType>
47 {
48 private class AssociationItem
49 {
50 public AssociationRelyingPartyType DistinguishingFactor;
51 public string Handle;
52 public DateTime Expires;
53 public byte[] PrivateData;
54 }
55
56 Dictionary<string, AssociationItem> m_store = new Dictionary<string, AssociationItem>();
57 SortedList<DateTime, AssociationItem> m_sortedStore = new SortedList<DateTime, AssociationItem>();
58 object m_syncRoot = new object();
59
60 #region IAssociationStore<AssociationRelyingPartyType> Members
61
62 public void StoreAssociation(AssociationRelyingPartyType distinguishingFactor, Association assoc)
63 {
64 AssociationItem item = new AssociationItem();
65 item.DistinguishingFactor = distinguishingFactor;
66 item.Handle = assoc.Handle;
67 item.Expires = assoc.Expires.ToLocalTime();
68 item.PrivateData = assoc.SerializePrivateData();
69
70 lock (m_syncRoot)
71 {
72 m_store[item.Handle] = item;
73 m_sortedStore[item.Expires] = item;
74 }
75 }
76
77 public Association GetAssociation(AssociationRelyingPartyType distinguishingFactor)
78 {
79 lock (m_syncRoot)
80 {
81 if (m_sortedStore.Count > 0)
82 {
83 AssociationItem item = m_sortedStore.Values[m_sortedStore.Count - 1];
84 return Association.Deserialize(item.Handle, item.Expires.ToUniversalTime(), item.PrivateData);
85 }
86 else
87 {
88 return null;
89 }
90 }
91 }
92
93 public Association GetAssociation(AssociationRelyingPartyType distinguishingFactor, string handle)
94 {
95 AssociationItem item;
96 bool success = false;
97 lock (m_syncRoot)
98 success = m_store.TryGetValue(handle, out item);
99
100 if (success)
101 return Association.Deserialize(item.Handle, item.Expires.ToUniversalTime(), item.PrivateData);
102 else
103 return null;
104 }
105
106 public bool RemoveAssociation(AssociationRelyingPartyType distinguishingFactor, string handle)
107 {
108 lock (m_syncRoot)
109 {
110 for (int i = 0; i < m_sortedStore.Values.Count; i++)
111 {
112 AssociationItem item = m_sortedStore.Values[i];
113 if (item.Handle == handle)
114 {
115 m_sortedStore.RemoveAt(i);
116 break;
117 }
118 }
119
120 return m_store.Remove(handle);
121 }
122 }
123
124 public void ClearExpiredAssociations()
125 {
126 lock (m_syncRoot)
127 {
128 List<AssociationItem> itemsCopy = new List<AssociationItem>(m_sortedStore.Values);
129 DateTime now = DateTime.Now;
130
131 for (int i = 0; i < itemsCopy.Count; i++)
132 {
133 AssociationItem item = itemsCopy[i];
134
135 if (item.Expires <= now)
136 {
137 m_sortedStore.RemoveAt(i);
138 m_store.Remove(item.Handle);
139 }
140 }
141 }
142 }
143
144 #endregion
145 }
146
147 public class OpenIdStreamHandler : IStreamHandler
148 {
149 #region HTML
150
151 /// <summary>Login form used to authenticate OpenID requests</summary>
152 const string LOGIN_PAGE =
153@"<html>
154<head><title>OpenSim OpenID Login</title></head>
155<body>
156<h3>OpenSim Login</h3>
157<form method=""post"">
158<label for=""first"">First Name:</label> <input readonly type=""text"" name=""first"" id=""first"" value=""{0}""/>
159<label for=""last"">Last Name:</label> <input readonly type=""text"" name=""last"" id=""last"" value=""{1}""/>
160<label for=""pass"">Password:</label> <input type=""password"" name=""pass"" id=""pass""/>
161<input type=""submit"" value=""Login"">
162</form>
163</body>
164</html>";
165
166 /// <summary>Page shown for a valid OpenID identity</summary>
167 const string OPENID_PAGE =
168@"<html>
169<head>
170<title>{2} {3}</title>
171<link rel=""openid2.provider openid.server"" href=""{0}://{1}/openid/server/""/>
172</head>
173<body>OpenID identifier for {2} {3}</body>
174</html>
175";
176
177 /// <summary>Page shown for an invalid OpenID identity</summary>
178 const string INVALID_OPENID_PAGE =
179@"<html><head><title>Identity not found</title></head>
180<body>Invalid OpenID identity</body></html>";
181
182 /// <summary>Page shown if the OpenID endpoint is requested directly</summary>
183 const string ENDPOINT_PAGE =
184@"<html><head><title>OpenID Endpoint</title></head><body>
185This is an OpenID server endpoint, not a human-readable resource.
186For more information, see <a href='http://openid.net/'>http://openid.net/</a>.
187</body></html>";
188
189 #endregion HTML
190
191 public string ContentType { get { return m_contentType; } }
192 public string HttpMethod { get { return m_httpMethod; } }
193 public string Path { get { return m_path; } }
194
195 string m_contentType;
196 string m_httpMethod;
197 string m_path;
198 UserLoginService m_loginService;
199 ProviderMemoryStore m_openidStore = new ProviderMemoryStore();
200
201 /// <summary>
202 /// Constructor
203 /// </summary>
204 public OpenIdStreamHandler(string httpMethod, string path, UserLoginService loginService)
205 {
206 m_loginService = loginService;
207 m_httpMethod = httpMethod;
208 m_path = path;
209
210 m_contentType = "text/html";
211 }
212
213 /// <summary>
214 /// Handles all GET and POST requests for OpenID identifier pages and endpoint
215 /// server communication
216 /// </summary>
217 public void Handle(string path, Stream request, Stream response, OSHttpRequest httpRequest, OSHttpResponse httpResponse)
218 {
219 Uri providerEndpoint = new Uri(String.Format("{0}://{1}{2}", httpRequest.Url.Scheme, httpRequest.Url.Authority, httpRequest.Url.AbsolutePath));
220
221 // Defult to returning HTML content
222 m_contentType = "text/html";
223
224 try
225 {
226 NameValueCollection postQuery = HttpUtility.ParseQueryString(new StreamReader(httpRequest.InputStream).ReadToEnd());
227 NameValueCollection getQuery = HttpUtility.ParseQueryString(httpRequest.Url.Query);
228 NameValueCollection openIdQuery = (postQuery.GetValues("openid.mode") != null ? postQuery : getQuery);
229
230 OpenIdProvider provider = new OpenIdProvider(m_openidStore, providerEndpoint, httpRequest.Url, openIdQuery);
231
232 if (provider.Request != null)
233 {
234 if (!provider.Request.IsResponseReady && provider.Request is IAuthenticationRequest)
235 {
236 IAuthenticationRequest authRequest = (IAuthenticationRequest)provider.Request;
237 string[] passwordValues = postQuery.GetValues("pass");
238
239 UserProfileData profile;
240 if (TryGetProfile(new Uri(authRequest.ClaimedIdentifier.ToString()), out profile))
241 {
242 // Check for form POST data
243 if (passwordValues != null && passwordValues.Length == 1)
244 {
245 if (profile != null && m_loginService.AuthenticateUser(profile, passwordValues[0]))
246 authRequest.IsAuthenticated = true;
247 else
248 authRequest.IsAuthenticated = false;
249 }
250 else
251 {
252 // Authentication was requested, send the client a login form
253 using (StreamWriter writer = new StreamWriter(response))
254 writer.Write(String.Format(LOGIN_PAGE, profile.FirstName, profile.SurName));
255 return;
256 }
257 }
258 else
259 {
260 // Cannot find an avatar matching the claimed identifier
261 authRequest.IsAuthenticated = false;
262 }
263 }
264
265 // Add OpenID headers to the response
266 foreach (string key in provider.Request.Response.Headers.Keys)
267 httpResponse.AddHeader(key, provider.Request.Response.Headers[key]);
268
269 string[] contentTypeValues = provider.Request.Response.Headers.GetValues("Content-Type");
270 if (contentTypeValues != null && contentTypeValues.Length == 1)
271 m_contentType = contentTypeValues[0];
272
273 // Set the response code and document body based on the OpenID result
274 httpResponse.StatusCode = (int)provider.Request.Response.Code;
275 response.Write(provider.Request.Response.Body, 0, provider.Request.Response.Body.Length);
276 response.Close();
277 }
278 else if (httpRequest.Url.AbsolutePath.Contains("/openid/server"))
279 {
280 // Standard HTTP GET was made on the OpenID endpoint, send the client the default error page
281 using (StreamWriter writer = new StreamWriter(response))
282 writer.Write(ENDPOINT_PAGE);
283 }
284 else
285 {
286 // Try and lookup this avatar
287 UserProfileData profile;
288 if (TryGetProfile(httpRequest.Url, out profile))
289 {
290 using (StreamWriter writer = new StreamWriter(response))
291 {
292 // TODO: Print out a full profile page for this avatar
293 writer.Write(String.Format(OPENID_PAGE, httpRequest.Url.Scheme,
294 httpRequest.Url.Authority, profile.FirstName, profile.SurName));
295 }
296 }
297 else
298 {
299 // Couldn't parse an avatar name, or couldn't find the avatar in the user server
300 using (StreamWriter writer = new StreamWriter(response))
301 writer.Write(INVALID_OPENID_PAGE);
302 }
303 }
304 }
305 catch (Exception ex)
306 {
307 httpResponse.StatusCode = (int)HttpStatusCode.InternalServerError;
308 using (StreamWriter writer = new StreamWriter(response))
309 writer.Write(ex.Message);
310 }
311 }
312
313 /// <summary>
314 /// Parse a URL with a relative path of the form /users/First_Last and try to
315 /// retrieve the profile matching that avatar name
316 /// </summary>
317 /// <param name="requestUrl">URL to parse for an avatar name</param>
318 /// <param name="profile">Profile data for the avatar</param>
319 /// <returns>True if the parse and lookup were successful, otherwise false</returns>
320 bool TryGetProfile(Uri requestUrl, out UserProfileData profile)
321 {
322 if (requestUrl.Segments.Length == 3 && requestUrl.Segments[1] == "users/")
323 {
324 // Parse the avatar name from the path
325 string username = requestUrl.Segments[requestUrl.Segments.Length - 1];
326 string[] name = username.Split('_');
327
328 if (name.Length == 2)
329 {
330 profile = m_loginService.GetTheUser(name[0], name[1]);
331 return (profile != null);
332 }
333 }
334
335 profile = null;
336 return false;
337 }
338 }
339}
diff --git a/ThirdPartyLicenses/DotNetOpenid.txt b/ThirdPartyLicenses/DotNetOpenid.txt
new file mode 100644
index 0000000..1b6f678
--- /dev/null
+++ b/ThirdPartyLicenses/DotNetOpenid.txt
@@ -0,0 +1,10 @@
1Copyright (c) 2008, Andrew Arnott, Scott Hanselman, Jason Alexander, et. al
2All rights reserved.
3
4Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5
6 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8 * Neither the name of the DotNetOpenId nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
9
10THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file
diff --git a/bin/DotNetOpenId.dll b/bin/DotNetOpenId.dll
new file mode 100644
index 0000000..e4b0f26
--- /dev/null
+++ b/bin/DotNetOpenId.dll
Binary files differ
diff --git a/prebuild.xml b/prebuild.xml
index 006a063..91d3215 100644
--- a/prebuild.xml
+++ b/prebuild.xml
@@ -845,6 +845,7 @@
845 <Reference name="System" localCopy="false"/> 845 <Reference name="System" localCopy="false"/>
846 <Reference name="System.Data" localCopy="false"/> 846 <Reference name="System.Data" localCopy="false"/>
847 <Reference name="System.Xml" localCopy="false"/> 847 <Reference name="System.Xml" localCopy="false"/>
848 <Reference name="System.Web" localCopy="false"/>
848 <Reference name="OpenSim.Framework"/> 849 <Reference name="OpenSim.Framework"/>
849 <Reference name="OpenSim.Framework.Console"/> 850 <Reference name="OpenSim.Framework.Console"/>
850 <Reference name="OpenSim.Framework.Communications"/> 851 <Reference name="OpenSim.Framework.Communications"/>
@@ -856,6 +857,7 @@
856 <Reference name="OpenMetaverse.StructuredData.dll"/> 857 <Reference name="OpenMetaverse.StructuredData.dll"/>
857 <Reference name="XMLRPC.dll"/> 858 <Reference name="XMLRPC.dll"/>
858 <Reference name="log4net.dll"/> 859 <Reference name="log4net.dll"/>
860 <Reference name="DotNetOpenId"/>
859 861
860 <Files> 862 <Files>
861 <Match pattern="*.cs" recurse="true"/> 863 <Match pattern="*.cs" recurse="true"/>