public static UserBE CreateNewUser(UserBE newUser) { if(newUser == null) return null; //throw exception if licensing does not allow creation of another user LicenseBL.IsUserCreationAllowed(true); if(newUser.RoleId == 0) { RoleBE defaultRole = PermissionsBL.RetrieveDefaultRoleForNewAccounts(); if(defaultRole != null) newUser.RoleId = defaultRole.ID; } ValidateUser(newUser); newUser.CreateTimestamp = DateTime.UtcNow; uint userId = DbUtils.CurrentSession.Users_Insert(newUser); if(userId == 0) { return null; } newUser.ID = userId; PageBE userHomepage = null; try { // User homepages are created upon user creation (an attempt to create user homepages may also be done upon login) userHomepage = PageBL.CreateUserHomePage(newUser); } catch { } RecentChangeBL.AddUserCreatedRecentChange(DateTime.UtcNow, userHomepage, DekiContext.Current.User ?? newUser, String.Format(DekiResources.USER_ADDED, newUser.Name)); DekiContext.Current.Instance.EventSink.UserCreate(DreamContext.Current.StartTime, newUser); return newUser; }
public static void UpdateUser(UserBE user) { // Note (maxm): The user 'touched' timestamp is updated: // * at authentication time // * any user object changes (including group membership) // It's exposed in the user xml as 'date.lastlogin' user.Touched = DateTime.UtcNow; ValidateUser(user); DbUtils.CurrentSession.Users_Update(user); DekiContext.Current.Instance.EventSink.UserUpdate(DreamContext.Current.StartTime, user); }
public static void PerformBanCheck(UserBE user, string[] clientIps, out ulong banMask, out List<string> banReasons) { banMask = ulong.MinValue; banReasons = new List<string>(); ulong? cachedBanMask = GetBanMaskFromCache(user.ID, clientIps); if(cachedBanMask != null) { //TODO MaxM: Ban reasons isn't currently cached (or used) banMask = cachedBanMask.Value; } else { IList<BanBE> bans = DbUtils.CurrentSession.Bans_GetByRequest(user.ID, clientIps.ToList()); foreach(BanBE ban in bans) { if(!ban.Expires.HasValue || (ban.Expires.Value >= DateTime.UtcNow)) { banMask |= ban.RevokeMask; banReasons.Add(ban.Reason); } } CacheBanMask(user.ID, clientIps, banMask); } }
//--- Methods --- public static void SetRating(PageBE page, UserBE user, float? score) { ThrowOnInvalidLicense(); RatingBE currentRating = DbUtils.CurrentSession.Rating_GetUserResourceRating(user.ID, page.ID, ResourceBE.Type.PAGE); if(score == null) { if(currentRating == null) { // no rating exists currently: noop return; } // reset a user ratings for a page DbUtils.CurrentSession.Rating_ResetUserResourceRating(user.ID, page.ID, ResourceBE.Type.PAGE, DreamContext.Current.StartTime); } else { // set or update a page rating // Valid score is limited to 0 and 1. if(score != 0 && score != 1) { throw new Exceptions.InvalidRatingScoreException(); } if(currentRating != null && currentRating.Score == score) { // an equal score already exists: noop return; } RatingBE rating = new RatingBE(); rating.ResourceId = page.ID; rating.ResourceType = ResourceBE.Type.PAGE; rating.ResourceRevision = page.Revision; rating.Timestamp = DreamContext.Current.StartTime; rating.TimestampReset = null; rating.UserId = user.ID; rating.Score = score.Value; // Set a new rating DbUtils.CurrentSession.Rating_Insert(rating); } // Trigger a notification DekiContext.Current.Instance.EventSink.PageRated(DreamContext.Current.StartTime, page, user); }
//--- Methods --- public virtual UserBE Copy() { UserBE user = new UserBE(); user._NewPassword = _NewPassword; user._Password = _Password; user._Touched = _Touched; user.CreateTimestamp = CreateTimestamp; user.Email = Email; user.ExternalName = ExternalName; user.ID = ID; user.Language = Language; user.Name = Name; user.RealName = RealName; user.RoleId = RoleId; user.ServiceId = ServiceId; user.Timezone = Timezone; user.UserActive = UserActive; user.LicenseSeat = LicenseSeat; return(user); }
public static UserBE PostUserFromXml(XDoc userDoc, UserBE userToProcess, string accountpassword, string externalusername, string externalpassword) { List<GroupBE> externalGroups = null; uint? id; bool? active; string username, fullname, email, language, timezone; ServiceBE authService; RoleBE role; //Parse the standard user XML doc ParseUserXml(userDoc, out id, out username, out email, out fullname, out authService, out role, out active, out language, out timezone); //new user if(userToProcess == null && (id == null || id == 0)) { userToProcess = ReadUserXml(userDoc, username, email, fullname, authService, role, language, timezone); //External accounts should be confirmed, username normalized, groups retrieved if(authService != null && !ServiceBL.IsLocalAuthService(authService)) { if(!string.IsNullOrEmpty(accountpassword)) throw new DreamBadRequestException(DekiResources.CANNOT_SET_EXTERNAL_ACCOUNT_PASSWORD); //Only admins can create external accounts for others. Anyone can create their own external account if(externalusername != userToProcess.Name || externalusername == string.Empty) PermissionsBL.CheckUserAllowed(DekiContext.Current.User, Permissions.ADMIN); //username+password from request query params are used here userToProcess = ExternalServiceSA.BuildUserFromAuthService(authService, userToProcess, username, true, externalusername, externalpassword, out externalGroups); if(userToProcess == null) { throw new DreamAbortException(DreamMessage.NotFound(string.Format(DekiResources.EXTERNAL_USER_NOT_FOUND, username))); } //Does the external account already exist? UserBE matchingExternalAccount = DbUtils.CurrentSession.Users_GetByExternalName(userToProcess.ExternalName, userToProcess.ServiceId); if(matchingExternalAccount != null) { throw new DreamAbortException(DreamMessage.Conflict(string.Format(DekiResources.USER_EXISTS_WITH_EXTERNAL_NAME, matchingExternalAccount.Name, userToProcess.ExternalName, userToProcess.ServiceId))); } } else { //Creating local account //User creation requires admin rights unless the config flag allows it //Anonymous users are not allowed to set role if(!DekiContext.Current.Instance.AllowAnonymousLocalAccountCreation || role != null) { PermissionsBL.CheckUserAllowed(DekiContext.Current.User, Permissions.ADMIN); } } //Sanity check for already existing user UserBE existingUser = DbUtils.CurrentSession.Users_GetByName(userToProcess.Name); if(existingUser != null) { throw new DreamAbortException(DreamMessage.Conflict(string.Format(DekiResources.USER_EXISTS_WITH_ID, existingUser.Name, existingUser.ID))); } //if (UserDA.RetrieveUserRegistrations(userToProcess.Name)) { // throw new DreamAbortException(DreamMessage.Conflict(string.Format("User '{0}' has been reserved", userToProcess.Name))); //} userToProcess = UserBL.CreateOrUpdateUser(userToProcess, accountpassword); if(null != externalGroups) { UserBL.UpdateUsersGroups(userToProcess, externalGroups.ToArray()); } } //update existing user else { if(userToProcess == null) { //Modifying a user with POST if(id == null || id == 0) { throw new DreamAbortException(DreamMessage.BadRequest(DekiResources.USER_ID_ATTR_INVALID)); } userToProcess = UserBL.GetUserById(id.Value); if(userToProcess == null) { throw new DreamAbortException(DreamMessage.NotFound(string.Format(DekiResources.USER_ID_NOT_FOUND, id))); } } if(!string.IsNullOrEmpty(accountpassword)) throw new DreamBadRequestException(DekiResources.USE_PUT_TO_CHANGE_PASSWORDS); //Permission check if not modifying self if(userToProcess.ID != DekiContext.Current.User.ID) PermissionsBL.CheckUserAllowed(DekiContext.Current.User, Permissions.ADMIN); userToProcess = UpdateUserFromXml(userToProcess, userDoc, username, email, fullname, authService, role, active, externalusername, externalpassword, language, timezone, out externalGroups); userToProcess = CreateOrUpdateUser(userToProcess); if(null != externalGroups) { UserBL.UpdateUsersGroups(userToProcess, externalGroups.ToArray()); } if(UserBL.IsAnonymous(userToProcess) && DekiContext.Current.Instance.CacheAnonymousOutput) { DekiContext.Current.Deki.EmptyResponseCacheInternal(); } } return userToProcess; }
private static UserBE ReadUserXml(XDoc userDoc, string username, string email, string fullname, ServiceBE authService, RoleBE role, string language, string timezone) { UserBE user = new UserBE(); if(string.IsNullOrEmpty(username)) throw new DreamBadRequestException(DekiResources.USERNAME_PARAM_INVALID); //TODO (MaxM) Consider validation of fullname, email, username //Retrieve default auth service for new user if authservice not given if(authService == null) { authService = ServiceBL.RetrieveLocalAuthService(); } user.Name = username; //Default role will be applied if one is not given if(role != null) user.RoleId = role.ID; user.RealName = fullname ?? string.Empty; user.ServiceId = authService.Id; user.UserActive = true; user.Email = email ?? string.Empty; user.Language = language; user.Timezone = timezone; return user; }
public static XUri GetUriUiHomePage(UserBE user) { return XUri.TryParse(Utils.AsPublicUiUri(Title.FromDbPath(NS.USER, user.Name, null))); }
public static bool IsValidAuthenticationForLocalUser(UserBE user, string password, out bool altPassword) { bool isValid = false; altPassword = false; string encrypted = AuthBL.EncryptPassword(user, password); if(string.CompareOrdinal(encrypted, user.Password) == 0) { //On login if a user has a temp password but logs in with original password, clear out the temp password. if(!string.IsNullOrEmpty(user.NewPassword)) { user.NewPassword = string.Empty; DbUtils.CurrentSession.Users_Update(user); } isValid = true; } else if(!string.IsNullOrEmpty(user.NewPassword) && string.CompareOrdinal(encrypted, user.NewPassword) == 0) { isValid = true; altPassword = true; } return isValid; }
private static string CreateAuthTokenForUser(UserBE user, DateTime timestamp) { string ret = string.Empty; string tokenContent = string.Format("{0}_{1}", user.ID.ToString(), timestamp.ToUniversalTime().Ticks); //Include the users current password as part of validation to invalidate token upon pw change. string contentToValidate = string.Format("{0}.{1}.{2}", tokenContent, user.Password ?? string.Empty, DekiContext.Current.Instance.AuthTokenSalt); System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create(); string hash = new Guid(md5.ComputeHash(Encoding.Default.GetBytes(contentToValidate))).ToString("N"); ret = tokenContent + "_" + hash; return ret; }
public static string EncryptPassword(UserBE user, string pwd) { string md5Pwd = GetMD5(pwd); return GetMD5(user.ID + "-" + md5Pwd); }
public static UserBE CreateOrUpdateUser(UserBE user, string newPassword) { if(user.ID > 0) { UpdateUser(user); } else { //TODO consider logic here to confirm that the user does not yet exist. user = CreateNewUser(user); } if(!string.IsNullOrEmpty(newPassword) && ServiceBL.IsLocalAuthService(user.ServiceId)) { user = UserBL.SetPassword(user, newPassword, false); DekiContext.Current.Instance.EventSink.UserChangePassword(DreamContext.Current.StartTime, user); } return user; }
private static void ValidateGroupMemberList(ServiceBE groupService, UserBE[] potentialMembers) { //Groups belonging to built-in auth service are allowed to contain users from remote services if(!ServiceBL.IsLocalAuthService(groupService)) { foreach(UserBE u in potentialMembers) { if(u.ServiceId != groupService.Id) throw new GroupMembersRequireSameAuthInvalidOperationException(); } } }
public static XDoc GetUserXml(UserBE user, string relation, bool showPrivateInfo) { XDoc userXml = new XDoc(string.IsNullOrEmpty(relation) ? "user" : "user." + relation); userXml.Attr("id", user.ID); userXml.Attr("href", DekiContext.Current.ApiUri.At("users", user.ID.ToString())); userXml.Elem("nick", user.Name); userXml.Elem("username", user.Name); userXml.Elem("fullname", user.RealName ?? String.Empty); // check if we can add the email address if(showPrivateInfo) { userXml.Elem("email", user.Email); } else { userXml.Start("email").Attr("hidden", true).End(); } // add gravatar if(!IsAnonymous(user) && !string.IsNullOrEmpty(user.Email)) { DekiContext context = DekiContext.CurrentOrNull; XUri gravatar = new XUri("http://www.gravatar.com/avatar"); string hash = string.Empty; if(context != null) { DekiInstance deki = context.Instance; string secure = context.Instance.GravatarSalt ?? string.Empty; if(!secure.EqualsInvariantIgnoreCase("hidden")) { hash = StringUtil.ComputeHashString(secure + (user.Email ?? string.Empty).Trim().ToLowerInvariant(), System.Text.Encoding.UTF8); } // add size, if any string size = deki.GravatarSize; if(size != null) { gravatar = gravatar.With("s", size); } // add rating, if any string rating = deki.GravatarRating; if(rating != null) { gravatar = gravatar.With("r", rating); } // add default icon, if any string def = deki.GravatarDefault; if(def != null) { gravatar = gravatar.With("d", def); } } if(!string.IsNullOrEmpty(hash)) { userXml.Elem("hash.email", hash); userXml.Elem("uri.gravatar", gravatar.At(hash + ".png")); } else { userXml.Elem("hash.email", string.Empty); userXml.Elem("uri.gravatar", gravatar.At("no-email.png")); } } return userXml; }
/// <summary> /// Updates the given user in the db with current timestamp /// </summary> /// <param name="user"></param> public static UserBE UpdateUserTimestamp(UserBE user) { if(user == null) return null; //Update user's last logged time column to now if it's more than a minute old. if(user.Touched <= DateTime.UtcNow.AddMinutes(-1) && user.UserActive) { // Note (maxm): This does not call UserBL.UserUpdate to avoid frequent reindexing. user.Touched = DateTime.UtcNow; DbUtils.CurrentSession.Users_Update(user); } return user; }
public static void ValidateUser(UserBE user) { if(string.IsNullOrEmpty(user.Name) || user.Name.EndsWith(".", true, CultureInfo.InvariantCulture) || !Title.FromUIUsername(user.Name).IsValid) throw new DreamBadRequestException(string.Format(DekiResources.USER_VALIDATION_FAILED, user.Name)); }
public static void UpdateUsersGroups(UserBE user, GroupBE[] groups) { if(user == null || groups == null) return; IList<GroupBE> groupsWithIds = DbUtils.CurrentSession.Groups_GetByNames(groups.Select(e => e.Name).ToList()); DbUtils.CurrentSession.GroupMembers_UpdateGroupsForUser(user.ID, groupsWithIds.Select(e => e.Id).ToList()); UpdateUser(user); }
private static UserBE[] ReadUserListXml(XDoc usersList) { UserBE[] ret = null; List<uint> userIds = new List<uint>(); foreach(XDoc userXml in usersList["user/@id"]) { uint? id = userXml.AsUInt; if(id == null) throw new UserIdAttrInvalidArgumentException(); userIds.Add(id.Value); } if(userIds.Count > 0) { Dictionary<uint, UserBE> userHash = DbUtils.CurrentSession.Users_GetByIds(userIds).AsHash(e => e.ID); foreach(uint id in userIds) { if(!userHash.ContainsKey(id)) throw new GroupCouldNotFindUserInvalidArgumentException(id); } ret = new List<UserBE>(userHash.Values).ToArray(); } else { ret = new UserBE[] { }; } return ret; }
private static void ParseGroupXml(XDoc groupDoc, out uint? id, out string name, out ServiceBE authService, out RoleBE role, out UserBE[] userList) { name = groupDoc["groupname"].AsText ?? groupDoc["name"].AsText; string authserviceidstr = groupDoc["service.authentication/@id"].AsText; string rolestr = groupDoc["permissions.group/role"].AsText; authService = null; role = null; id = null; if(!groupDoc["@id"].IsEmpty) { uint id_temp; if(!uint.TryParse(groupDoc["@id"].Contents, out id_temp)) throw new GroupIdAttributeInvalidArgumentException(); id = id_temp; } if(!string.IsNullOrEmpty(authserviceidstr)) { uint serviceid; if(!uint.TryParse(authserviceidstr, out serviceid)) throw new ServiceAuthIdAttrInvalidArgumentException(); authService = ServiceBL.GetServiceById(serviceid); if(authService == null) throw new ServiceDoesNotExistInvalidArgumentException(serviceid); } if(!string.IsNullOrEmpty(rolestr)) { role = PermissionsBL.GetRoleByName(rolestr); if(role == null) throw new RoleDoesNotExistInvalidArgumentException(rolestr); } else { role = PermissionsBL.RetrieveDefaultRoleForNewAccounts(); } if(!groupDoc["users"].IsEmpty) { userList = ReadUserListXml(groupDoc["users"]); } else userList = new UserBE[] { }; }
public static XDoc GetUserXmlVerbose(UserBE user, string relationAttr, bool showPrivateInfo) { XDoc userXml = GetUserXml(user, relationAttr, showPrivateInfo); userXml.Elem("date.created", user.CreateTimestamp); PageBE homePage = GetHomePage(user); if(homePage != null && homePage.ID != 0) userXml.Add(PageBL.GetPageXml(homePage, "home")); userXml.Start("status").Value(user.UserActive ? "active" : "inactive").End(); userXml.Start("date.lastlogin").Value(user.Touched).End(); userXml.Start("language").Value(user.Language).End(); userXml.Start("timezone").Value(user.Timezone).End(); ServiceBE authService = ServiceBL.GetServiceById(user.ServiceId); if(authService != null) userXml.Add(ServiceBL.GetServiceXml(authService, "authentication")); //Permissions for the user from user role userXml.Add(PermissionsBL.GetRoleXml(PermissionsBL.GetRoleById(user.RoleId), "user")); ulong effectivePermissions = PermissionsBL.CalculateEffectiveUserRights(user); //Effective permissions for the user from the role + group roles. userXml.Add(PermissionsBL.GetPermissionXml(effectivePermissions, "effective")); userXml.Start("groups"); IList<GroupBE> groups = DbUtils.CurrentSession.Groups_GetByUser(user.ID); if(null != groups) { foreach(GroupBE g in groups) { userXml.Add(GroupBL.GetGroupXmlVerbose(g, null)); } } userXml.End(); //Retrieve properties for current user while providing an href for other users. if(DekiContext.Current.User.ID == user.ID) { IList<PropertyBE> props = PropertyBL.Instance.GetResources(user.ID, ResourceBE.Type.USER); userXml = PropertyBL.Instance.GetPropertyXml(props, GetUri(user), null, null, userXml); } else { userXml.Start("properties").Attr("href", GetUri(user).At("properties")).End(); } // TODO Max: get <subscriptions> (watchlist) not implemented return userXml; }
private static UserBE UpdateUserFromXml(UserBE userToUpdate, XDoc userDoc, string username, string email, string fullname, ServiceBE authservice, RoleBE role, bool? active, string externalusername, string externalpassword, string language, string timezone, out List<GroupBE> externalGroups) { externalGroups = null; if(userToUpdate.Name != username && !string.IsNullOrEmpty(username)) { if(UserBL.IsAnonymous(userToUpdate)) { throw new DreamBadRequestException(DekiResources.ANONYMOUS_USER_EDIT); } userToUpdate = RenameUser(userToUpdate, username); } //Modify a user's authentication service if(authservice != null && authservice.Id != userToUpdate.ServiceId) { if(UserBL.IsAnonymous(userToUpdate)) { throw new DreamBadRequestException(DekiResources.ANONYMOUS_USER_EDIT); } if(ServiceBL.IsLocalAuthService(authservice)) { //external to local userToUpdate.ExternalName = null; userToUpdate.ServiceId = authservice.Id; } else { //(local or external) to external userToUpdate = ExternalServiceSA.BuildUserFromAuthService(authservice, userToUpdate, userToUpdate.Name, true, externalusername, externalpassword, out externalGroups); if(userToUpdate == null) { throw new DreamInternalErrorException(DekiResources.USER_AUTHSERVICE_CHANGE_FAIL); } //Does the external account already exist? UserBE matchingExternalAccount = DbUtils.CurrentSession.Users_GetByExternalName(userToUpdate.ExternalName, userToUpdate.ServiceId); if(matchingExternalAccount != null) { throw new DreamAbortException(DreamMessage.Conflict(string.Format(DekiResources.USER_EXISTS_WITH_EXTERNAL_NAME, matchingExternalAccount.Name, matchingExternalAccount.ExternalName, matchingExternalAccount.ServiceId))); } } } if(email != null) { if(UserBL.IsAnonymous(userToUpdate) && email != userToUpdate.Email) { throw new DreamBadRequestException(DekiResources.ANONYMOUS_USER_EDIT); } userToUpdate.Email = email; } if(!string.IsNullOrEmpty(fullname)) userToUpdate.RealName = fullname; if(active != null) { if(UserBL.IsAnonymous(userToUpdate) && userToUpdate.UserActive && !active.Value) { throw new DreamBadRequestException(DekiResources.DEACTIVATE_ANONYMOUS_NOT_ALLOWED); } //throw exception if licensing does not allow activating a user if(!userToUpdate.UserActive && active.Value) { LicenseBL.IsUserCreationAllowed(true); } userToUpdate.UserActive = active.Value; } if(role != null && role.ID != userToUpdate.RoleId) { PermissionsBL.CheckUserAllowed(DekiContext.Current.User, Permissions.ADMIN); userToUpdate.RoleId = role.ID; } if(language != null) { userToUpdate.Language = language; } if(timezone != null) { userToUpdate.Timezone = timezone; } return userToUpdate; }
public static UserBE CreateOrUpdateUser(UserBE user) { return CreateOrUpdateUser(user, null); }
public static UserBE SetPassword(UserBE user, string password, bool altPassword) { string pwhash = Logic.AuthBL.EncryptPassword(user, password); if(altPassword) { //Set the alternate password only while keeping the main password untouched. user.NewPassword = pwhash; } else { //Set the main password and clear the alternate password. user.Password = pwhash; user.NewPassword = string.Empty; } user.Touched = DateTime.UtcNow; DbUtils.CurrentSession.Users_Update(user); return user; }
public static IList<CommentBE> RetrieveCommentsForUser(UserBE user) { IList<CommentBE> comments = DbUtils.CurrentSession.Comments_GetByUser(user.ID); comments = ApplyPermissionFilter(comments); return comments; }
private static UserBE RenameUser(UserBE user, string newUserName) { //Renaming requires admin rights. PermissionsBL.CheckUserAllowed(DekiContext.Current.User, Permissions.ADMIN); if(!ServiceBL.IsLocalAuthService(user.ServiceId)) { //TODO MaxM: allow renaming of external users throw new DreamAbortException(DreamMessage.NotImplemented(DekiResources.EXTERNAL_USER_RENAME_NOT_ALLOWED)); } //Check for already existing user with same name UserBE existingUser = DbUtils.CurrentSession.Users_GetByName(newUserName); if(existingUser != null) { throw new DreamAbortException(DreamMessage.Conflict(string.Format(DekiResources.USER_EXISTS_WITH_ID, existingUser.Name, existingUser.ID))); } PageBE existingTargetUserHomePage = PageBL.GetPageByTitle(Title.FromUIUsername(newUserName)); if(existingTargetUserHomePage != null && existingTargetUserHomePage.ID != 0) { throw new DreamAbortException(DreamMessage.Conflict(DekiResources.USER_RENAME_HOMEPAGE_CONFLICT)); } //Try to move the homepage. PageBE userHomePage = GetHomePage(user); if(userHomePage != null && userHomePage.ID != 0) { PageBL.MovePage(userHomePage, Title.FromUIUsername(newUserName)); } //Rename the user user.Name = newUserName; UserBL.UpdateUser(user); return user; }
public static string CreateAuthTokenForUser(UserBE user) { return CreateAuthTokenForUser(user, DateTime.Now); }
public static PageBE GetHomePage(UserBE user) { return PageBL.GetPageByTitle(Title.FromUIUsername(user.Name)); }
public static bool IsValidAuthenticationForLocalUser(UserBE user, string password) { bool altPassword; return IsValidAuthenticationForLocalUser(user, password, out altPassword); }
public static bool IsAnonymous(UserBE user) { return user.Name == DekiWikiService.ANON_USERNAME; }
public static UserBE BuildUserFromAuthService(ServiceBE serviceInfo, UserBE knownUser, string usernameToBuild, bool bypassAuthentication, string authusername, string password, out List<GroupBE> externalGroups) { externalGroups = null; if (serviceInfo == null || string.IsNullOrEmpty(usernameToBuild)) return null; //Dont perform external lookup for disabled users if (knownUser != null && !knownUser.UserActive) return knownUser; string errMsg = string.Format(DekiResources.UNABLE_TO_AUTH_WITH_SERVICE, serviceInfo.Type.ToString(), serviceInfo.SID, serviceInfo.Uri); if(knownUser != null && !string.IsNullOrEmpty(knownUser.ExternalName)) { usernameToBuild = knownUser.ExternalName; } UserBE ret = null; DreamMessage response = null; if (serviceInfo.Uri == null) throw new DreamAbortException(DreamMessage.InternalError(string.Format(DekiResources.SERVICE_NOT_STARTED, serviceInfo.Type.ToString(), serviceInfo.SID))); try { Plug dekiExternalAuthPlug; //bypassAuthentication is used when you only need user details but not to necessarily authenticate if (bypassAuthentication) { //An external auth service's GET: user/{username} does not necessarily require authentication to lookup users but it may. It's up to the service //to decide if anon requests are allowed. dekiExternalAuthPlug = Plug.New(serviceInfo.Uri).At(USER_INFO).At(XUri.Encode(usernameToBuild)); } else { //Credentials are always needed for GET: authenticate. The user details of the auth'd user is returned with same format as GET: user/{username} dekiExternalAuthPlug = Plug.New(serviceInfo.Uri).At(AUTHENTICATE_PATH); } //Always include credentials with the request if they're supplied if (!string.IsNullOrEmpty(authusername)) { dekiExternalAuthPlug = dekiExternalAuthPlug.WithCredentials(authusername, password ?? string.Empty); } response = dekiExternalAuthPlug.GetAsync().Wait(); } catch (Exception x) { throw new DreamResponseException(DreamMessage.InternalError(x), errMsg); } if (response.IsSuccessful) { XDoc userXml = response.ToDocument(); if (userXml == null || userXml.IsEmpty) { throw new DreamInternalErrorException(string.Format("Empty or not well-formed XML returned from remote auth service: " + userXml.ToPrettyString())); } string nameFromAuthProvider = userXml["@name"].Contents; if (!StringUtil.EqualsInvariantIgnoreCase(nameFromAuthProvider, usernameToBuild)) { throw new DreamInternalErrorException(string.Format(DekiResources.UNEXPECTED_EXTERNAL_USERNAME, userXml["@name"].AsText, usernameToBuild)); } if (knownUser != null) ret = knownUser; else ret = new UserBE(); ret.Email = string.IsNullOrEmpty(userXml["email"].AsText) ? (ret.Email ?? string.Empty) : userXml["email"].AsText; //Build the realname (exposed as 'fullname' in user xml) by saving it as '{firstname} {lastname}' string externalFirstName = userXml["firstname"].AsText ?? string.Empty; string externalLastName = userXml["lastname"].AsText ?? string.Empty; string separator = externalLastName.Length > 0 && externalFirstName.Length > 0 ? ", " : string.Empty; // NOTE (maxm): Fullname sync is disabled for now. Refer to bug 7855 #if !DISABLE_REAL_NAME_SYNCHRONIZATION ret.RealName = string.Format("{0}{1}{2}", externalLastName, separator, externalFirstName); #endif ret.ServiceId = serviceInfo.Id; ret.Touched = DateTime.UtcNow; ret.ExternalName = string.IsNullOrEmpty(ret.ExternalName) ? nameFromAuthProvider : ret.ExternalName; ret.Name = string.IsNullOrEmpty(ret.Name) ? nameFromAuthProvider : ret.Name; //For new users, the name must be normalized and unique if (ret.ID == 0) { string nameFromExternalName = string.Empty; //Allow using a displayname from an external provider only for new accounts if (!userXml["@displayname"].IsEmpty) { nameFromExternalName = userXml["@displayname"].AsText; } else { nameFromExternalName = ret.ExternalName; } ret.Name = UserBL.NormalizeExternalNameToWikiUsername(nameFromExternalName); } //Build group objects out of the user's group membership list externalGroups = new List<GroupBE>(); IList<GroupBE> userGroups = DbUtils.CurrentSession.Groups_GetByUser(ret.ID); //Preserve local groups for existing users if (ret.ID != 0 && userGroups != null) { foreach (GroupBE g in userGroups) { if (ServiceBL.IsLocalAuthService(g.ServiceId)) { externalGroups.Add(g); } } } foreach (XDoc group in userXml["groups/group"]) { GroupBE g = new GroupBE(); g.Name = group["@name"].AsText; g.ServiceId = serviceInfo.Id; if (!string.IsNullOrEmpty(g.Name)) externalGroups.Add(g); } } else { switch (response.Status) { case DreamStatus.Unauthorized: if (bypassAuthentication) { DekiContext.Current.Instance.Log.Warn(string.Format("Attempted to lookup user info on auth provider '{0}' but failed since it required credentials", serviceInfo.Id)); } throw new DreamAbortException(DreamMessage.AccessDenied(DekiWikiService.AUTHREALM, string.Format(DekiResources.AUTHENTICATION_FAILED_FOR, serviceInfo.Description))); case DreamStatus.InternalError: case DreamStatus.Forbidden: default: throw new DreamAbortException(response, errMsg); } } return ret; }
public static XUri GetUri(UserBE user) { return DekiContext.Current.ApiUri.At("users", user.ID.ToString()); }