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 UserAnonymousEditInvalidOperationException(); } userToUpdate = RenameUser(userToUpdate, username, fullname ?? userToUpdate.RealName); } //Modify a user's authentication service if (authservice != null && authservice.Id != userToUpdate.ServiceId) { if (UserBL.IsAnonymous(userToUpdate)) { throw new UserAnonymousEditInvalidOperationException(); } 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 UserAuthChangeFatalException(); } //Does the external account already exist? UserBE matchingExternalAccount = DbUtils.CurrentSession.Users_GetByExternalName(userToUpdate.ExternalName, userToUpdate.ServiceId); if (matchingExternalAccount != null) { throw new ExternalUserExistsConflictException(matchingExternalAccount.Name, matchingExternalAccount.ExternalName, matchingExternalAccount.ServiceId); } } } if (email != null) { if (UserBL.IsAnonymous(userToUpdate) && email != userToUpdate.Email) { throw new UserAnonymousEditInvalidOperationException(); } userToUpdate.Email = email; } if (!string.IsNullOrEmpty(fullname)) { userToUpdate.RealName = fullname; } if (active != null) { // disabling user if (userToUpdate.UserActive && !active.Value) { // cannot disable anonymous user if (UserBL.IsAnonymous(userToUpdate)) { throw new UserAnonymousDeactivationInvalidOperationException(); } // cannot disable owner if (DekiContext.Current.LicenseManager.GetSiteOwnerUserId().GetValueOrDefault(0) == userToUpdate.ID) { throw new UserOwnerDeactivationConflict(); } } //throw exception if licensing does not allow activating a user if (!userToUpdate.UserActive && active.Value) { DekiContext.Current.LicenseManager.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); }
//--- Class Methods --- public static UserBE Authenticate(DreamContext context, DreamMessage request, uint serviceId, bool autoCreateExternalUser, bool allowAnon, out bool altPassword) { UserBE user = null; altPassword = false; // Case 1: username/fullname, password, provider (login window) // 1. Validate & retrieve fullname using credentials // Failed -> return null // 2. Populate user object // A. Populates user info // B. Populates group info in user object // 3. Does fullname exist? // Yes -> Update user (email, fullname, ...) // No -> Create user // 4. Update Group information // 5. return user object // // Case 2: fullname, password (http, api, ...) // 1. Lookup full name, exist? // Yes -> return user // No -> return null // // Case 3: auth-token (header auth) // 0. Valid auth token? // No -> return null // 1. Lookup user by name // Found -> return user // Else -> return null string userName = null; string password = null; UserBE userFromToken = null; ServiceBE authService = null; // Extract authtoken and impersonation authtoken from request. // AllowAnon is false when in GET/POST: users/authenticate or when ?authenticate=true. // Standard user authtokens are ignored when AllowAnon=false but impersonation tokens are accepted. bool impersonationOnly = !allowAnon; userFromToken = UserFromAuthTokenInRequest(context, impersonationOnly); if (userFromToken == null) { HttpUtil.GetAuthentication(context.Uri.ToUri(), request.Headers, out userName, out password); } // check if we need to retrieve authentication service information if (serviceId > 0) { authService = ServiceBL.GetServiceById(serviceId); if (authService == null) { throw new AuthServiceIdInvalidArgumentException(serviceId); } if (authService.Type != ServiceType.AUTH) { throw new AuthNotAnAuthServiceInvalidArgumentException(serviceId); } } // check if a username was provided if (!string.IsNullOrEmpty(userName)) { //Case 2: Given username + password if (authService == null) { //Assuming local user or existing external account user = DbUtils.CurrentSession.Users_GetByName(userName); if (user != null) { serviceId = user.ServiceId; authService = ServiceBL.GetServiceById(serviceId); } else { LoginAccessDenied(context, request, userName, null, password); } } if (authService == null) { throw new AuthServiceIdInvalidArgumentException(serviceId); } if (authService.Type != ServiceType.AUTH) { throw new AuthNotAnAuthServiceInvalidArgumentException(serviceId); } if (user == null) { //Performing auth on local account if (ServiceBL.IsLocalAuthService(authService)) { user = DbUtils.CurrentSession.Users_GetByName(userName); } else { //Performing external auth. Lookup by external user name user = DbUtils.CurrentSession.Users_GetByExternalName(userName, authService.Id); } if (user != null && user.ServiceId != authService.Id) { ServiceBE currentUsersAuthService = ServiceBL.GetServiceById(user.ServiceId); if (currentUsersAuthService != null) { throw new AuthLoginExternalUserConflictException(currentUsersAuthService.Description); } throw new LoginExternalUserUnknownConflictException(); } } //Local account in the db. if (user != null && ServiceBL.IsLocalAuthService(authService)) { //Validate password for local account or validate the apikey if (!IsValidAuthenticationForLocalUser(user, password, out altPassword)) { // try impersonation using the ApiKey if (string.IsNullOrEmpty(password) && PermissionsBL.ValidateRequestApiKey()) { DekiContext.Current.Instance.Log.InfoFormat("user '{0}' authenticated via apikey impersonation", userName); } else { LoginAccessDenied(context, request, userName, user.ID, password); } } } // User was not found in the db and not being asked to create it. if (user == null && !autoCreateExternalUser) { LoginAccessDenied(context, request, userName, null, password); } // Creating local account if apikey checks out and our authservice is local if (user == null && string.IsNullOrEmpty(password) && PermissionsBL.ValidateRequestApiKey() && ServiceBL.IsLocalAuthService(authService)) { XDoc newUserDoc = new XDoc("user") .Elem("username", userName); DreamMessage newUserResponse = DekiContext.Current.ApiPlug.At("users") .With("apikey", DreamContext.Current.GetParam("apikey", string.Empty)) .Post(newUserDoc); user = UserBL.GetUserById(newUserResponse.ToDocument()["/user/@id"].AsUInt ?? 0); if (user != null && !string.IsNullOrEmpty(password)) { user = UserBL.SetPassword(user, password, false); } } // Got an external account // Passing in the user object from db if it was found. List <GroupBE> externalGroups = null; if (!ServiceBL.IsLocalAuthService(authService)) { bool bypassAuthentication = false; string externalName; if (user == null || string.IsNullOrEmpty(user.ExternalName)) { externalName = userName; } else { externalName = user.ExternalName; } // If apikey is valid, try to bypass auth with the external provider // and only lookup user/group details. if (string.IsNullOrEmpty(password) && PermissionsBL.ValidateRequestApiKey()) { DekiContext.Current.Instance.Log.InfoFormat("user '{0}' authenticating being bypassed via apikey impersonation", userName); bypassAuthentication = true; } user = ExternalServiceSA.BuildUserFromAuthService(authService, user, userName, bypassAuthentication, externalName, password, out externalGroups); } // User was not found or did not authenticate with external provider if (user == null) { LoginAccessDenied(context, request, userName, null, password); } else { //New user creation from external provider if (user.ID == 0) { if (!autoCreateExternalUser) { LoginAccessDenied(context, request, userName, null, password); } } else { //user exists // TODO (steveb): ??? } if (user.UserActive) { user = UserBL.CreateOrUpdateUser(user); if (externalGroups != null) { UserBL.UpdateUsersGroups(user, externalGroups.ToArray()); } } } } else if (userFromToken != null) { // valid token exists that resolved to a user user = userFromToken; } else if (allowAnon) { // Anonymous user user = DbUtils.CurrentSession.Users_GetByName(DekiWikiService.ANON_USERNAME); } if (user == null) { //No credentials. Or token not provided or is invalid. LoginAccessDenied(context, request, null, null, password); } else if (!user.UserActive && !PermissionsBL.ValidateRequestApiKey()) { //If a valid api key is provided, override the disabled account flag throw new AuthUserDisabledForbiddenException(user.Name); } 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)) { // check if a local-account password was provided if (!string.IsNullOrEmpty(accountpassword)) { throw new ExternalUserPasswordInvalidOperationException(); } // 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 ExternalUserNotFoundException(username); } // does the external account already exist? UserBE matchingExternalAccount = DbUtils.CurrentSession.Users_GetByExternalName(userToProcess.ExternalName, userToProcess.ServiceId); if (matchingExternalAccount != null) { throw new ExternalUserExistsConflictException(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 UserWithIdExistsConflictException(existingUser.Name, existingUser.ID); } //if (UserDA.RetrieveUserRegistrations(userToProcess.Name)) { // throw new DreamAbortException(DreamMessage.Conflict(string.Format("User '{0}' has been reserved", userToProcess.Name))); //} userToProcess = CreateOrUpdateUser(userToProcess, accountpassword); if (null != externalGroups) { UpdateUsersGroups(userToProcess, externalGroups.ToArray()); } } else { // update existing user if (userToProcess == null) { userToProcess = GetUserById(id.Value); if (userToProcess == null) { throw new UserIdNotFoundException(id.Value); } } // TODO (steveb): either this needs to go or the definition for PUT:user/{userid} should not mention the 'accountpassword' parameter if (!string.IsNullOrEmpty(accountpassword)) { throw new UserUsePutToChangePasswordInvalidOperationException(); } // 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) { UpdateUsersGroups(userToProcess, externalGroups.ToArray()); } if (IsAnonymous(userToProcess) && DekiContext.Current.Instance.CacheAnonymousOutput) { DekiContext.Current.Deki.EmptyResponseCacheInternal(); } try { if (!userToProcess.UserActive && userToProcess.LicenseSeat && DekiContext.Current.LicenseManager.IsSeatLicensingEnabled()) { DekiContext.Current.LicenseManager.RemoveSeatFromUser(userToProcess); } } catch (Exception x) { _log.WarnExceptionFormat(x, "Unable to remove license seat for disabled user. user id: {0}", userToProcess.ID); } } return(userToProcess); }