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);
        }
Esempio n. 2
0
        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);
        }
Esempio n. 3
0
        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();
                }
            }
        }
Esempio n. 4
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);
        }
Esempio n. 5
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);
        }
Esempio n. 6
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);
        }