public AuthCookie(string authToken, Uri identity) { AuthToken = authToken; Identity = identity; AuthedRealms = new List<string>(); }
public static bool SetAuthCookie(OSHttpRequest httpRequest, OSHttpResponse httpResponse, Uri identity, string consumer) { bool permissionGranted = false; HttpCookie cookie = (httpRequest.Cookies != null) ? httpRequest.Cookies["cb_auth"] : null; AuthCookie authCookie; string cookieKey; // Check for an existing cookie pointing to valid server-side cached info if (cookie != null && AuthCookies.TryGetValue(cookie.Value, out authCookie)) { cookieKey = cookie.Value; // TODO: Linear search could be eliminated with a HashSet<> if (authCookie.AuthedRealms.Contains(consumer)) permissionGranted = true; } else { // Create a new cookie cookieKey = UUID.Random().ToString(); authCookie = new AuthCookie(cookieKey, identity); } // Cookie will expire in five days DateTime cookieExpiration = DateTime.Now + TimeSpan.FromDays(5.0); // Set cookie information on the server side and in the client response AuthCookies.AddOrUpdate(cookieKey, authCookie, cookieExpiration); HttpCookie responseCookie = new HttpCookie("cb_auth", cookieKey); responseCookie.Expires = cookieExpiration; httpResponse.SetCookie(responseCookie); return permissionGranted; }
public OAuthRequest(Uri identity, UserAuthorizationRequest request, string[] capabilityNames) { Identity = identity; Request = request; CapabilityNames = capabilityNames; }
bool IsXmlRpcLogin(Uri requestUrl, out UUID sessionID) { for (int i = requestUrl.Segments.Length - 1; i >= 0; i--) { if (UUID.TryParse(requestUrl.Segments[i].Replace("/", String.Empty), out sessionID)) return true; } sessionID = UUID.Zero; return false; }
/// <summary> /// Called when an incoming request_capabilities message is received. Loops through all of the /// registered Cable Beach services and gives them a chance to create capabilities for each /// request in the dictionary /// </summary> /// <param name="requestUrl"></param> /// <param name="identity"></param> /// <param name="capabilities"></param> public static void CreateCapabilities(Uri identity, ref Dictionary<CapabilityIdentifier, Uri> capabilities) { UUID sessionID = UUID.Random(); lock (m_serviceCallbacks) { foreach (KeyValuePair<ServiceIdentifier, CreateCapabilitiesCallback> entry in m_serviceCallbacks) { try { entry.Value(sessionID, identity, ref capabilities); } catch (Exception ex) { Log.Error("[CABLE BEACH SERVER]: Service " + entry.Key + " threw an exception during capability creation: " + ex.Message, ex); } } } }
/// <summary> /// Handles all GET and POST requests for OpenID identifier pages and endpoint /// server communication /// </summary> public void Handle(string path, Stream request, Stream response, OSHttpRequest httpRequest, OSHttpResponse httpResponse) { // Try and lookup this avatar UserProfileData profile; if (CableBeachState.TryGetProfile(httpRequest.Url, out profile)) { if (httpRequest.Url.AbsolutePath.EndsWith(";xrd")) { m_log.Debug("[CABLE BEACH IDP]: Returning XRD document for " + profile.Name); Uri identity = new Uri(httpRequest.Url.ToString().Replace(";xrd", String.Empty)); // Create an XRD document from the identity URL and filesystem (inventory) service XrdDocument xrd = new XrdDocument(identity.ToString()); xrd.Links.Add(new XrdLink(new Uri("http://specs.openid.net/auth"), null, new XrdUri(identity))); xrd.Links.Add(new XrdLink(new Uri(CableBeachServices.FILESYSTEM), "application/json", new XrdUri(CableBeachState.LoginService.m_config.InventoryUrl))); byte[] data = System.Text.Encoding.UTF8.GetBytes(XrdParser.WriteXrd(xrd)); httpResponse.ContentLength = data.Length; httpResponse.ContentType = "application/xrd+xml"; httpResponse.OutputStream.Write(data, 0, data.Length); } else { m_log.Debug("[CABLE BEACH IDP]: Returning user identity page for " + profile.Name); Uri openidServerUrl = new Uri(httpRequest.Url, "/openid/server"); Uri xrdUrl = new Uri(httpRequest.Url, "/users/" + profile.FirstName + "." + profile.SurName + ";xrd"); CableBeachState.SendProviderUserTemplate(httpResponse, profile, openidServerUrl, xrdUrl); } } else { m_log.Warn("[CABLE BEACH IDP]: Couldn't find an account for identity page " + httpRequest.Url); // Couldn't parse an avatar name, or couldn't find the avatar in the user server httpResponse.StatusCode = (int)HttpStatusCode.NotFound; OpenAuthHelper.AddToBody(httpResponse, "OpenID identity not found"); } }
XmlRpcResponse LoginHandler(XmlRpcRequest request, Uri requestUrl) { XmlRpcResponse response = new XmlRpcResponse(); Hashtable requestData = (Hashtable)request.Params[0]; IPEndPoint remoteClient = null; if (request.Params.Count > 1) remoteClient = request.Params[1] as IPEndPoint; UserProfileData userProfile; LoginResponse logResponse = new LoginResponse(); UUID sessionID; IsXmlRpcLogin(requestUrl, out sessionID); m_log.Info("[CABLE BEACH XMLRPC]: XML-RPC Received login request message with sessionID " + sessionID); string startLocationRequest = "last"; if (requestData.Contains("start")) startLocationRequest = (requestData["start"] as string) ?? "last"; string clientVersion = "Unknown"; if (requestData.Contains("version")) clientVersion = (requestData["version"] as string) ?? "Unknown"; if (TryAuthenticateXmlRpcLogin(sessionID, out userProfile)) { try { UUID agentID = userProfile.ID; LoginService.InventoryData skeleton = null; try { skeleton = CableBeachState.LoginService.GetInventorySkeleton(agentID); } catch (Exception e) { m_log.ErrorFormat("[CABLE BEACH XMLRPC]: Error retrieving inventory skeleton of agent {0} - {1}", agentID, e); // Let's not panic if (!CableBeachState.LoginService.AllowLoginWithoutInventory()) return logResponse.CreateLoginInventoryFailedResponse(); } #region Inventory Skeletons if (skeleton != null) { ArrayList AgentInventoryArray = skeleton.InventoryArray; Hashtable InventoryRootHash = new Hashtable(); InventoryRootHash["folder_id"] = skeleton.RootFolderID.ToString(); ArrayList InventoryRoot = new ArrayList(); InventoryRoot.Add(InventoryRootHash); logResponse.InventoryRoot = InventoryRoot; logResponse.InventorySkeleton = AgentInventoryArray; } // Inventory Library Section Hashtable InventoryLibRootHash = new Hashtable(); InventoryLibRootHash["folder_id"] = "00000112-000f-0000-0000-000100bba000"; ArrayList InventoryLibRoot = new ArrayList(); InventoryLibRoot.Add(InventoryLibRootHash); logResponse.InventoryLibRoot = InventoryLibRoot; logResponse.InventoryLibraryOwner = CableBeachState.LoginService.GetLibraryOwner(); logResponse.InventoryLibrary = CableBeachState.LoginService.GetInventoryLibrary(); logResponse.CircuitCode = Util.RandomClass.Next(); logResponse.Lastname = userProfile.SurName; logResponse.Firstname = userProfile.FirstName; logResponse.AgentID = agentID; logResponse.SessionID = userProfile.CurrentAgent.SessionID; logResponse.SecureSessionID = userProfile.CurrentAgent.SecureSessionID; logResponse.Message = CableBeachState.LoginService.GetMessage(); logResponse.BuddList = CableBeachState.LoginService.ConvertFriendListItem(CableBeachState.LoginService.UserManager.GetUserFriendList(agentID)); logResponse.StartLocation = startLocationRequest; #endregion Inventory Skeletons if (CableBeachState.LoginService.CustomiseResponse(logResponse, userProfile, startLocationRequest, remoteClient)) { userProfile.LastLogin = userProfile.CurrentAgent.LoginTime; CableBeachState.LoginService.CommitAgent(ref userProfile); // If we reach this point, then the login has successfully logged onto the grid if (StatsManager.UserStats != null) StatsManager.UserStats.AddSuccessfulLogin(); m_log.DebugFormat("[CABLE BEACH XMLRPC]: Authentication of user {0} {1} successful. Sending response to client", userProfile.FirstName, userProfile.FirstName); return logResponse.ToXmlRpcResponse(); } else { m_log.ErrorFormat("[CABLE BEACH XMLRPC]: Informing user {0} {1} that login failed due to an unavailable region", userProfile.FirstName, userProfile.FirstName); return logResponse.CreateDeadRegionResponse(); } } catch (Exception e) { m_log.Error("[CABLE BEACH XMLRPC]: Login failed, returning a blank response. Error: " + e); return response; } } else { m_log.Warn("[CABLE BEACH XMLRPC]: Authentication failed using sessionID " + sessionID + ", there are " + CableBeachState.PendingLogins.Count + " valid pending logins"); return logResponse.CreateLoginFailedResponse(); } }
void OpenIDLoginGetHandler(OSHttpRequest httpRequest, OSHttpResponse httpResponse) { if (httpRequest.Url.AbsolutePath.EndsWith("openid_callback")) { #region OpenID Callback IAuthenticationResponse authResponse = CableBeachState.RelyingParty.GetResponse(OpenAuthHelper.GetRequestInfo(httpRequest)); if (authResponse != null) { if (authResponse.Status == AuthenticationStatus.Authenticated) { // OpenID authentication succeeded Uri identity = new Uri(authResponse.ClaimedIdentifier.ToString()); // Check if this identity is authorized for access. This check is done here for the second time // because the ClaimedIdentifier after authentication has finished is not necessarily the original // OpenID URL entered into the login form if (CableBeachState.IsIdentityAuthorized(identity)) { string firstName = null, lastName = null, email = null; // Get the Simple Registration attributes the IDP returned, if any ClaimsResponse sreg = authResponse.GetExtension<ClaimsResponse>(); if (sreg != null) { if (!String.IsNullOrEmpty(sreg.FullName)) { string[] firstLast = sreg.FullName.Split(' '); if (firstLast.Length == 2) { firstName = firstLast[0]; lastName = firstLast[1]; } } email = sreg.Email; } CableBeachState.StartLogin(httpRequest, httpResponse, identity, firstName, lastName, email, CableBeachAuthMethods.OPENID); } else { CableBeachState.SendLoginTemplate(httpResponse, null, identity + " is not authorized to access this world"); } } else { // Parse an error message out of authResponse string errorMsg = (authResponse.Exception != null) ? authResponse.Exception.Message : authResponse.Status.ToString(); CableBeachState.SendLoginTemplate(httpResponse, null, errorMsg); } } else { httpResponse.StatusCode = (int)HttpStatusCode.BadRequest; OpenAuthHelper.AddToBody(httpResponse, "Invalid or missing OpenID callback data"); } #endregion OpenID Callback } else if (httpRequest.Url.AbsolutePath.EndsWith("oauth_callback")) { #region OAuth Callback ServiceRequestsData stateData; string requestToken = OpenAuthHelper.GetQueryValue(httpRequest.Url.Query, "oauth_token"); if (!String.IsNullOrEmpty(requestToken) && CableBeachState.CurrentServiceRequests.TryGetValue(requestToken, out stateData)) { ServiceIdentifier serviceIdentifier = CableBeachState.GetCurrentService(stateData.ServiceRequirements); Service service; CapabilityRequirements capRequirements; if (serviceIdentifier != null) { if (stateData.Services.TryGetValue(serviceIdentifier, out service) && stateData.ServiceRequirements.TryGetValue(serviceIdentifier, out capRequirements)) { try { OAuthConsumer consumer = new OAuthConsumer(OpenAuthHelper.CreateServiceProviderDescription(service), CableBeachState.OAuthTokenManager); AuthorizedTokenResponse tokenResponse = consumer.ProcessUserAuthorization(OpenAuthHelper.GetRequestInfo(httpRequest)); // We actually don't need the access token at all since the capabilities should be in this response. // Parse the capabilities out of ExtraData CapabilityRequirements newCaps = new CapabilityRequirements(); foreach (KeyValuePair<string, string> capability in tokenResponse.ExtraData) { Uri capIdentifier, capUri; if (Uri.TryCreate(capability.Key, UriKind.Absolute, out capIdentifier) && Uri.TryCreate(capability.Value, UriKind.Absolute, out capUri)) { newCaps[capIdentifier] = capUri; } } m_log.Info("[CABLE BEACH LOGIN]: Fetched " + newCaps.Count + " capabilities through OAuth from " + service.OAuthGetAccessToken); // Update the capabilities for this service stateData.ServiceRequirements[serviceIdentifier] = newCaps; } catch (Exception ex) { m_log.Error("[CABLE BEACH LOGIN]: Failed to exchange request token for capabilities at " + service.OAuthGetAccessToken + ": " + ex.Message); CableBeachState.SendLoginTemplate(httpResponse, null, "OAuth request to " + service.OAuthGetAccessToken + " failed: " + ex.Message); return; } } else { m_log.Error("[CABLE BEACH LOGIN]: OAuth state data corrupted, could not find service or service requirements for " + serviceIdentifier); CableBeachState.SendLoginTemplate(httpResponse, null, "OAuth state data corrupted, please try again"); return; } } else { m_log.Warn("[CABLE BEACH LOGIN]: OAuth callback fired but there are no unfulfilled services. Could be a browser refresh"); } // Check if we need to continue the cap requesting process CableBeachState.GetCapabilitiesOrCompleteLogin(httpRequest, httpResponse, stateData, requestToken); } else { // A number of different things could lead here (incomplete login sequence, browser refresh of a completed sequence). // Safest thing to do would be to redirect back to the login screen httpResponse.StatusCode = (int)HttpStatusCode.Found; httpResponse.AddHeader("Location", new Uri(CableBeachState.UserServerUrl, "/login/").ToString()); } #endregion OAuth Callback } else { // Make sure we are starting from the correct URL if (httpRequest.Url.Authority != CableBeachState.UserServerUrl.Authority) { m_log.Debug("[CABLE BEACH LOGIN]: Redirecting from " + httpRequest.Url + " to " + CableBeachState.UserServerUrl); httpResponse.StatusCode = (int)HttpStatusCode.Redirect; httpResponse.RedirectLocation = new Uri(CableBeachState.UserServerUrl, "/login/").ToString(); } else if (httpRequest.Query.ContainsKey("openid_identifier")) { OpenIDLoginFormHandler(httpRequest, httpResponse, httpRequest.Query["openid_identifier"] as string); } else { // TODO: Check for a client cookie with an authenticated session CableBeachState.SendLoginTemplate(httpResponse, null, null); } } }
void SetCookie(OSHttpResponse httpResponse, Uri identity, UserProfileData profile) { string cookieKey = UUID.Random().ToString(); // Cookie will expire in five days DateTime cookieExpiration = DateTime.Now + TimeSpan.FromDays(5.0); // Cache the server-side data CableBeachState.AuthCookies.AddOrUpdate(cookieKey, new AuthCookie(cookieKey, identity, profile), cookieExpiration); // Create the cookie HttpCookie responseCookie = new HttpCookie("cb_openid_auth", cookieKey); responseCookie.Expires = cookieExpiration; httpResponse.SetCookie(responseCookie); }
bool DoAuthentication(OSHttpResponse httpResponse, DotNetOpenAuth.OpenId.Provider.IAuthenticationRequest authRequest, ClaimsRequest claimsRequest, string first, string last, string pass) { bool authSuccess = false; if (!String.IsNullOrEmpty(first) && !String.IsNullOrEmpty(last) && !String.IsNullOrEmpty(pass)) { UserProfileData profile; if (CableBeachState.TryGetProfile(first, last, out profile)) { // Set the claimed identifier to the URL of the given identity Uri identity = new Uri(CableBeachState.UserServerUrl, String.Format("/users/{0}.{1}", profile.FirstName, profile.SurName)); authRequest.ClaimedIdentifier = identity; authSuccess = CableBeachState.LoginService.AuthenticateUser(profile, pass); m_log.Info("[CABLE BEACH IDP]: Password match result for " + profile.Name + ": " + authRequest.IsAuthenticated); if (authSuccess) { // Mark the OpenID request as successfully authenticated authRequest.IsAuthenticated = true; // Cache this login SetCookie(httpResponse, identity, profile); if (claimsRequest != null) { // Fill in a few Simple Registration values if there was a request for SREG data ClaimsResponse claimsResponse = claimsRequest.CreateResponse(); claimsResponse.Email = profile.Email; claimsResponse.FullName = profile.Name; claimsResponse.BirthDate = Utils.UnixTimeToDateTime(profile.Created); authRequest.AddResponseExtension(claimsResponse); m_log.Debug("[CABLE BEACH IDP]: Appended SREG values to the positive assertion response"); } } } else { m_log.Warn("[CABLE BEACH IDP]: Profile for user " + first + " " + last + " not found"); } } else { // Valid POST but missing one or more fields m_log.Warn("[CABLE BEACH IDP]: POST is missing first, last, or pass field, sending directed login form"); } return authSuccess; }