コード例 #1
0
        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);
            }

            var errMsg = DekiResources.UNABLE_TO_AUTH_WITH_SERVICE(serviceInfo.Type, 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 ExternalServiceNotStartedFatalException(serviceInfo.Type, 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 ExternalServiceResponseException(errMsg, DreamMessage.InternalError(x));
            }

            if (response.IsSuccessful)
            {
                XDoc userXml = response.ToDocument();

                if (userXml == null || userXml.IsEmpty)
                {
                    throw new ExternalAuthResponseFatalException();
                }

                string nameFromAuthProvider = userXml["@name"].Contents;
                if (!nameFromAuthProvider.EqualsInvariantIgnoreCase(usernameToBuild))
                {
                    throw new ExternalServiceUnexpecteUsernameFatalException(userXml["@name"].AsText, usernameToBuild);
                }
                ret       = knownUser ?? 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 ExternalServiceAuthenticationDeniedException(DekiWikiService.AUTHREALM, serviceInfo.Description);

                default:
                    throw new ExternalServiceResponseException(errMsg, response);
                }
            }

            return(ret);
        }
コード例 #2
0
ファイル: UserBL.cs プロジェクト: Minocula/MindTouch_Core
        public static XDoc GetUserXmlVerbose(UserBE user, string relationAttr, bool showPrivateInfo, bool showGroups, bool showProperties)
        {
            XDoc userXml = GetUserXml(user, relationAttr, showPrivateInfo);

            userXml.Elem("date.created", user.CreateTimestamp);

            if (!IsAnonymous(user))
            {
                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"));

            // Set of permissions revoked from the user
            userXml.Add(PermissionsBL.GetPermissionsRevokedXml(user));

            // check if groups should be included
            if (showGroups)
            {
                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 (showProperties && (DekiContext.Current != null && DekiContext.Current.User != null && DekiContext.Current.User.ID == user.ID))
            {
                IList <ResourceBE> props = PropertyBL.Instance.GetUserProperties(user.ID);
                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);
        }
コード例 #3
0
ファイル: UserBL.cs プロジェクト: Minocula/MindTouch_Core
        private static void ParseUserXml(XDoc userDoc, out uint?id, out string username, out string email, out string fullname, out ServiceBE authService, out RoleBE role, out bool?active, out string language, out string timezone)
        {
            username = userDoc["username"].AsText;
            email    = userDoc["email"].AsText;
            fullname = userDoc["fullname"].AsText;
            language = userDoc["language"].AsText;
            timezone = userDoc["timezone"].AsText;
            string authserviceidstr = userDoc["service.authentication/@id"].AsText;
            string rolestr          = userDoc["permissions.user/role"].AsText;
            string statusStr        = userDoc["status"].AsText;

            authService = null;
            role        = null;

            id = null;

            if (!userDoc["@id"].IsEmpty)
            {
                uint id_temp;
                if (!uint.TryParse(userDoc["@id"].Contents, out id_temp))
                {
                    throw new UserIdAttrInvalidArgumentException();
                }
                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);
                }
            }

            if (!string.IsNullOrEmpty(statusStr))
            {
                switch (statusStr.ToLowerInvariant())
                {
                case "active":
                    active = true;
                    break;

                case "inactive":
                    active = false;
                    break;

                default:
                    throw new UserStatusAttrInvalidArgumentException();
                }
            }
            else
            {
                active = null;
            }

            if (!string.IsNullOrEmpty(timezone))
            {
                if (!timeZoneRegex.Match(timezone).Success)
                {
                    throw new UserTimezoneInvalidArgumentException();
                }
            }

            if (!string.IsNullOrEmpty(language))
            {
                string[] validLanguages = DekiContext.Current.Instance.Languages.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                string   tempLanguage   = language;
                if (!Array.Exists(validLanguages, delegate(string temp) { return(temp.EqualsInvariantIgnoreCase(tempLanguage)); }))
                {
                    throw new UserInvalidLanguageException();
                }
            }
        }
コード例 #4
0
ファイル: UserBL.cs プロジェクト: Minocula/MindTouch_Core
        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);
        }
コード例 #5
0
ファイル: UserBL.cs プロジェクト: Minocula/MindTouch_Core
        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);
        }
コード例 #6
0
ファイル: GroupBL.cs プロジェクト: Minocula/MindTouch_Core
        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);
        }