public override void DoLogin(RmUnifyUser user, DateTime maxSessionEnd, string returnUrl) { // Create our own cookie so we can insert DisplayName (because we want to use it on every page) // and make sure session length is appropriate. // If we didn't need this, we could just use FormsAuthentication.SetAuthCookie(user.Id, false) // Calculate session expiry as minimum of our app policy and that provided by RM Unify DateTime endTime = DateTime.Now.Add(FormsAuthentication.Timeout); if (endTime > maxSessionEnd) { endTime = maxSessionEnd; } FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2, user.Id, DateTime.Now, endTime, false, user.DisplayName, FormsAuthentication.FormsCookiePath); string encTicket = FormsAuthentication.Encrypt(ticket); HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket); cookie.HttpOnly = true; // If your website is entirely https, it is good practise to mark the cookie as secure // cookie.Secure = true; HttpContext.Current.Response.Cookies.Add(cookie); HttpContext.Current.Response.Redirect(returnUrl == null ? "/" : returnUrl); }
/// <summary> /// Create a new organization or update an existing one. /// This method will never be called if the organization.Id is null (for example, if the attribute /// has not been requested from RM Unify). /// If your app stores information about an organization, it should use organization.Id as a key to create a /// new organization record or update an existing one. /// </summary> /// <param name="organization">Organization profile</param> /// <param name="source">Source of update (sign on or provisioning)</param> public override void CreateOrUpdateUser(RmUnifyUser rmUser, Source source) { /// This override supports giving each user a login name that is suitable for display if (_school == null) { throw new Exception("CreateOrUpdateUser() called before CreateOrUpdateOrganization()"); } using (var context = new Context()) { Account account = (from a in context.Accounts where a.RmUnifyId == rmUser.Id select a).SingleOrDefault(); if (account == null) { account = new Account() { RmUnifyId = rmUser.Id, SchoolId = _school.Id, Password = Guid.NewGuid().ToString() // use random unguessable password }; context.Accounts.Add(account); } else { if (account.SchoolId != _school.Id) { // If you use rmUnifyUser.PersonId, you will need to support a user moving between schools // If you use rmUnifyUser.Id, a user moving between schools will be assigned a new Id throw new Exception("School moves not supported"); } } account.LoginName = DeDupeLoginName(rmUser.UnifyUserName, rmUser.Id); account.DisplayName = rmUser.DisplayName; account.RoleEnum = GetRole(rmUser); account.DeletedDate = null; // if previously deleted, restore if (source == Source.SingleSignOn) { account.LastLogin = DateTime.Now; } context.SaveChanges(); // Cache account for next method _account = account; } // Purge any old users from the system // In this example, we've chosen to implement this in the login process // If this is likely to be long running, we might want to implement it as a background task PurgeUsers(); }
/// <summary> /// Create or update a user in the application /// In this example, we want to store some custom data about the user (display name, last login and deleted date). /// Last login and deleted date allow us to soft delete users who haven't logged in for 3 months and then purge /// them after 1 year. /// We've implement the custom data by just extending the account model and going direct to the database to read /// and write the data; we could have equally created a custom membership user /// (http://msdn.microsoft.com/en-us/library/ms366730.aspx). /// </summary> /// <param name="rmUser">RM Unify user</param> /// <param name="source">Source of user information (always SingleSignOn at the moment)</param> public override void CreateOrUpdateUser(RmUnifyUser rmUser, Source source) { // Update the data associated with the user CreateOrUpdateUserData(rmUser); // Update the roles associated with the user CreateOrUpdateUserRoles(rmUser); // Purge any old users from the system // In this example, we've chosen to implement this in the login process // If this is likely to be long running, we might want to implement it as a background task PurgeUsers(); }
/// <summary> /// Log in as a pre-existing user who has been linked to an RM Unify user. /// This method is called when single sign on from RM Unify successfully completes. CreateOrUpdateOrganization() /// and CreateOrUpdateUser() will always be called first (in that order) provided the appropriate Ids are available. /// Your app should set any session cookies required to log the user in. /// Your app should then redirect the user to returnUrl (if set) or to the default URL for this user in your app. /// </summary> /// <param name="appUserId">User ID in your app (typically login name)</param> /// <param name="appEstablishmentKey">App establishment key (as provided by you to the organization)</param> /// <param name="user">User profile</param> /// <param name="maxSessionEnd">Maxiumum time after which reauthentication should be prompted</param> /// <param name="returnUrl">Return URL specified in login request (null if none)</param> public override void DoLoginForLinkedUser(string appUserId, string appEstablishmentKey, RmUnifyUser user, DateTime maxSessionEnd, string returnUrl) { if (_account == null) { throw new Exception("DoLoginForLinkedUser() called before UpdateLinkedUser()"); } if (_account.SchoolId != _school.Id) { throw new RmUnifySsoException(RmUnifySsoException.ERRORCODES_APPUSERIDNOTINESTABLISHMENT, "User " + appUserId + " is not in school with establishment key " + appEstablishmentKey); } // Can just call RmUnify.DoLogin() because the user to login as is already stored in _account DoLogin(user, maxSessionEnd, returnUrl); }
/// <summary> /// Update properties of a pre-existing user that has been linked to RM Unify. /// Only necessary if your app supports the RM Unify user account matching process /// (http://dev.rmunify.com/reference/supporting-user-account-matching/the-rm-unify-process.aspx). /// In this case, the organization has provisioned users into your app outside RM Unify and wishes to connect RM Unify /// to their existing users. Their "app establishment key" and the existing user id in your app (typically login name) /// is passed in to this method so that you can verify that the user is in the establishment and update them. /// Called instead of CreateOrUpdateUser() if the current user has been linked to a user in your app as part of the /// RM Unify user account matching process. /// </summary> /// <param name="appUserId">User ID in your app (typically login name)</param> /// <param name="appEstablishmentKey">App establishment key (as provided by you to the organization)</param> /// <param name="user">User profile</param> /// <param name="source">Source of update (sign on or provisioning)</param> public virtual void UpdateLinkedUser(string appUserId, string appEstablishmentKey, RmUnifyUser user, Source source) { throw new NotImplementedException(); }
/// <summary> /// Log in as a pre-existing user who has been linked to an RM Unify user. /// This method is called when single sign on from RM Unify successfully completes. CreateOrUpdateOrganization() /// and CreateOrUpdateUser() will always be called first (in that order) provided the appropriate Ids are available. /// Your app should set any session cookies required to log the user in. /// Your app should then redirect the user to returnUrl (if set) or to the default URL for this user in your app. /// </summary> /// <param name="appUserId">User ID in your app (typically login name)</param> /// <param name="appEstablishmentKey">App establishment key (as provided by you to the organization)</param> /// <param name="user">User profile</param> /// <param name="maxSessionEnd">Maxiumum time after which reauthentication should be prompted</param> /// <param name="returnUrl">Return URL specified in login request (null if none)</param> public virtual void DoLoginForLinkedUser(string appUserId, string appEstablishmentKey, RmUnifyUser user, DateTime maxSessionEnd, string returnUrl) { throw new NotImplementedException(); }
/// <summary> /// Log the current user into your app. /// This method is called when single sign on from RM Unify successfully completes. CreateOrUpdateOrganization() /// and CreateOrUpdateUser() will always be called first (in that order) provided the appropriate Ids are available. /// Your app should set any session cookies required to log the user in. /// Your app should then redirect the user to returnUrl (if set) or to the default URL for this user in your app. /// </summary> /// <param name="user">User profile</param> /// <param name="maxSessionEnd">Maxiumum time after which reauthentication should be prompted</param> /// <param name="returnUrl">Return URL specified in login request (null if none)</param> public abstract void DoLogin(RmUnifyUser user, DateTime maxSessionEnd, string returnUrl);
/// <summary> /// Create a new user or update an existing one. /// This method will never be called if the user.Id is null (for example, if neither IdentityGuid nor /// PersistentId has been requested from RM Unify). /// If your app stores information about a user, it should use user.Id as a key to create a new user record or /// update an existing one. /// Be aware that it is possible for a user to move organization (i.e. the same user.Id may appear in a different /// organization.Id). In this case, it is safe to delete the old user profile. /// </summary> /// <param name="user">User profile</param> /// <param name="source">Source of update (sign on or provisioning)</param> public abstract void CreateOrUpdateUser(RmUnifyUser user, Source source);
private void CreateOrUpdateUserRoles(RmUnifyUser rmUser) { string rmRole = null; switch (rmUser.UserRole) { case RmUnifyUser.Role.TeachingStaff: case RmUnifyUser.Role.NonTeachingStaff: rmRole = "staff"; break; case RmUnifyUser.Role.Student: rmRole = "student"; break; } if (rmUser.IsUnifyAdmin) { rmRole = "admin"; } if (rmRole == null) { throw new Exception("Sorry, this application does not support users with your role"); } var rolesProvider = (SimpleRoleProvider)Roles.Provider; var currentRoles = rolesProvider.GetRolesForUser(rmUser.Id); if (!currentRoles.Contains(rmRole)) { var allRoles = rolesProvider.GetAllRoles(); if (!allRoles.Contains("staff")) { rolesProvider.CreateRole("staff"); } if (!allRoles.Contains("student")) { rolesProvider.CreateRole("student"); } if (!allRoles.Contains("admin")) { rolesProvider.CreateRole("admin"); } rolesProvider.RemoveUsersFromRoles(new string[] { rmUser.Id }, currentRoles.Intersect(new string[] { "staff", "student", "admin" }).ToArray()); rolesProvider.AddUsersToRoles(new string[] { rmUser.Id }, new string[] { rmRole }); } }
private void CreateOrUpdateUserData(RmUnifyUser rmUser) { using (var context = new UsersContext()) { // Get the user (if they exist) UserProfile user = (from u in context.UserProfiles where u.UserName == rmUser.Id select u).SingleOrDefault(); if (user == null) { // User does not exist - create School school = (from s in context.Schools where s.RmUnifyOrganizationId == rmUser.Organization.Id select s).SingleOrDefault(); var userdata = new { DisplayName = rmUser.DisplayName, SchoolId = school.Id, LastLogin = DateTime.Now }; WebSecurity.CreateUserAndAccount(rmUser.Id, Guid.NewGuid().ToString(), userdata); } else { // User exists - update // We don't need to worry about the user moving school as we are using rmUser.Id // (which will change if the user moves school) rather than rmUser.PersonId (which may not) if (rmUser.DisplayName != user.DisplayName) { user.DisplayName = rmUser.DisplayName; } if (user.Deleted != null) { user.Deleted = null; } user.LastLogin = DateTime.Now; context.SaveChanges(); } } }
protected Role GetRole(RmUnifyUser rmUser) { if (rmUser.IsUnifyAdmin) { return Role.Admin; } switch (rmUser.UserRole) { case RmUnifyUser.Role.TeachingStaff: case RmUnifyUser.Role.NonTeachingStaff: return Role.Staff; case RmUnifyUser.Role.Parent: return Role.Parent; case RmUnifyUser.Role.Student: return Role.Student; } return Role.Guest; }
/// <summary> /// Log the current user into your app. /// This method is called when single sign on from RM Unify successfully completes. CreateOrUpdateOrganization() /// and CreateOrUpdateUser() will always be called first (in that order) provided the appropriate Ids are available. /// Your app should set any session cookies required to log the user in. /// Your app should then redirect the user to returnUrl (if set) or to the default URL for this user in your app. /// </summary> /// <param name="user">User profile</param> /// <param name="maxSessionEnd">Maxiumum time after which reauthentication should be prompted</param> /// <param name="returnUrl">Return URL specified in login request (null if none)</param> public override void DoLogin(RmUnifyUser user, DateTime maxSessionEnd, string returnUrl) { if (_account == null) { throw new Exception("DoLogin() called before CreateOrUpdateUser()"); } // Create our own cookie so we can make sure session length is appropriate. // Calculate session expiry as minimum of our app policy and that provided by RM Unify DateTime endTime = DateTime.Now.Add(FormsAuthentication.Timeout); if (endTime > maxSessionEnd) { endTime = maxSessionEnd; } FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2, _account.LoginName, DateTime.Now, endTime, false, "", FormsAuthentication.FormsCookiePath); string encTicket = FormsAuthentication.Encrypt(ticket); HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket); cookie.HttpOnly = true; // If your website is entirely https, it is good practise to mark the cookie as secure // cookie.Secure = true; HttpContext.Current.Response.Cookies.Add(cookie); HttpContext.Current.Response.Redirect(returnUrl == null ? "/" : returnUrl, true); }
/// <summary> /// Update properties of a pre-existing user that has been linked to RM Unify. /// Only necessary if your app supports the RM Unify user account matching process /// (http://dev.rmunify.com/reference/supporting-user-account-matching/the-rm-unify-process.aspx). /// In this case, the organization has provisioned users into your app outside RM Unify and wishes to connect RM Unify /// to their existing users. Their "app establishment key" and the existing user id in your app (typically login name) /// is passed in to this method so that you can verify that the user is in the establishment and update them. /// Called instead of CreateOrUpdateUser() if the current user has been linked to a user in your app as part of the /// RM Unify user account matching process. /// </summary> /// <param name="appUserId">User ID in your app (typically login name)</param> /// <param name="appEstablishmentKey">App establishment key (as provided by you to the organization)</param> /// <param name="user">User profile</param> /// <param name="source">Source of update (sign on or provisioning)</param> public override void UpdateLinkedUser(string appUserId, string appEstablishmentKey, RmUnifyUser rmUser, Source source) { if (_school == null) { throw new Exception("UpdateLinkedUser() called before UpdateLinkedOrganization()"); } using (var context = new Context()) { var account = (from a in context.Accounts where a.LoginName == appUserId select a).SingleOrDefault(); if (account == null) { throw new RmUnifySsoException(RmUnifySsoException.ERRORCODES_INVALIDAPPUSERID, "No such username: "******"User " + appUserId + " is not in school with establishment key " + appEstablishmentKey); } account.DisplayName = rmUser.DisplayName; account.RoleEnum = GetRole(rmUser); account.DeletedDate = null; // if previously deleted, restore if (source == Source.SingleSignOn) { account.LastLogin = DateTime.Now; } context.SaveChanges(); // Cache account for next method _account = account; } }