예제 #1
0
        //--- 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);
        }
예제 #2
0
        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);
        }
예제 #3
0
        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);
        }
예제 #4
0
        public static GroupBE PostGroupFromXml(XDoc groupDoc, GroupBE groupToProcess, string externalusername, string externalpassword)
        {
            GroupBE   group        = null;
            string    groupName    = string.Empty;
            ServiceBE groupService = null;
            RoleBE    groupRole    = null;

            UserBE[] groupMembers = null;
            uint?    groupId      = null;

            ParseGroupXml(groupDoc, out groupId, out groupName, out groupService, out groupRole, out groupMembers);

            //Create new group
            if (groupToProcess == null && (groupId == null || groupId == 0))
            {
                if (groupService == null)
                {
                    groupService = ServiceBL.RetrieveLocalAuthService();
                }

                //External groups should be confirmed with the auth provider
                if (groupService != null && !ServiceBL.IsLocalAuthService(groupService))
                {
                    //username+password from request query params are used here
                    group = ExternalServiceSA.BuildGroupFromAuthService(groupService, groupToProcess, groupName, externalusername, externalpassword);

                    if (group == null)
                    {
                        throw new ExternalGroupNotFoundException(groupName);
                    }
                }

                //Does this group already exist?
                GroupBE tempGroup = GetGroupByName(groupName);
                if (tempGroup != null)
                {
                    throw new GroupExistsWithServiceConflictException(groupName, tempGroup.ServiceId);
                }

                ValidateGroupMemberList(groupService, groupMembers);

                // Insert the group
                GroupBE newGroup = new GroupBE();
                newGroup.Name          = groupName;
                newGroup.RoleId        = groupRole.ID;
                newGroup.ServiceId     = groupService.Id;
                newGroup.CreatorUserId = DekiContext.Current.User.ID;
                newGroup.TimeStamp     = DateTime.UtcNow;
                uint newGroupId = DbUtils.CurrentSession.Groups_Insert(newGroup);
                if (newGroupId == 0)
                {
                    group = null;
                }
                else
                {
                    DbUtils.CurrentSession.GroupMembers_UpdateUsersInGroup(newGroupId, groupMembers.Select(e => e.ID).ToList(), newGroup.TimeStamp);

                    // reload the group to ensure group members are set
                    group = GetGroupById(newGroupId);
                }
            }
            //Edit existing group
            else
            {
                if (groupId != null)
                {
                    groupToProcess = GetGroupById(groupId.Value);
                }

                if (groupToProcess == null)
                {
                    throw new GroupIdNotFoundException(groupId);
                }

                group = groupToProcess;

                //Change the role?
                if (group.RoleId != groupRole.ID)
                {
                    group.RoleId = groupRole.ID;
                }

                //Rename the group?
                if (group.Name != groupName && !string.IsNullOrEmpty(groupName))
                {
                    GroupBE tempGroup = GetGroupByName(groupName);

                    if (tempGroup != null)
                    {
                        throw new GroupExistsWithServiceConflictException(groupName, tempGroup.ServiceId);
                    }

                    if (!ServiceBL.IsLocalAuthService(group.ServiceId))
                    {
                        //TODO MaxM: allow renaming of external groups
                        throw new ExternalGroupRenameNotImplementedException();
                    }

                    //Set the new name of the group.
                    group.Name = groupName;
                }

                DbUtils.CurrentSession.Groups_Update(group);
                //TODO (MaxM): Update group list as well?
                group = GetGroupById(group.Id);
            }

            if (group == null)
            {
                throw new GroupCreateUpdateFatalException();
            }

            return(group);
        }