public string GetAuthLink(ulong userId, RolesPool pool)
        {
            const string authPath = "/Auth/Authenticate/";

            if (_cvutConfig.AppBaseUrl == null)
            {
                throw new InvalidOperationException("Invalid CVUT config");
            }

            return($"{_cvutConfig.AppBaseUrl}{authPath}{userId}/{pool.ToString().ToLowerInvariant()}");
        }
        public async Task <bool> RevokeRolesPoolAsync(ulong userId, RolesPool rolesPool)
        {
            bool         returnValue = true;
            DiscordGuild guild       = await _guildProvider.GetCurrentGuildAsync();

            IDictionary <string, ulong[]> rolesMapping = rolesPool switch
            {
                RolesPool.Auth => _roleConfig.AuthRoleMapping,
                RolesPool.Staff => _roleConfig.StaffRoleMapping,
                _ => throw new ArgumentOutOfRangeException(nameof(rolesPool), rolesPool, null)
            };

            List <DRole> roles = new();

            foreach ((string?key, ulong[] roleIds) in rolesMapping)
            {
                foreach (ulong value in roleIds)
                {
                    DRole?role = guild.GetRole(value);
                    if (role == null)
                    {
                        returnValue = false;
                        _logger.LogError("Revoking roles for user id {UserId} failed for role key:{Key} = value:{Value}",
                                         userId, key, value);
                    }
                    else
                    {
                        roles.Add(role);
                    }
                }
            }

            // TODO: job queue
            DiscordMember member = await guild.GetMemberAsync(userId);

            foreach (DRole role in roles)
            {
                if (member.Roles.Contains(role))
                {
                    await member.RevokeRoleAsync(role, "Auth");
                }
            }

            return(returnValue);
        }
        private bool GetRolesPool(string?value, out RolesPool rolesPool)
        {
            switch (value?.ToLowerInvariant())
            {
            case "auth":
                rolesPool = RolesPool.Auth;
                break;

            case "staff":
                rolesPool = RolesPool.Staff;
                break;

            default:
                rolesPool = RolesPool.Auth;
                return(false);
            }

            return(true);
        }
        public HashSet <DiscordRole> MapUsermapRoles(IReadOnlyCollection <string> kosRoles, RolesPool rolesPool)
        {
            HashSet <DiscordRole> discordRoles = new();

            IDictionary <string, ulong[]> roles = rolesPool switch
            {
                RolesPool.Auth => _roleConfig.AuthRoleMapping,
                RolesPool.Staff => _roleConfig.StaffRoleMapping,
                _ => throw new ArgumentOutOfRangeException(nameof(rolesPool), rolesPool, null)
            };

            IEnumerable <string> knowUserRolePrefixes = roles.Keys;

            foreach (string rolePrefix in knowUserRolePrefixes)
            {
                bool containsRole = kosRoles.Any(role => role.StartsWith(rolePrefix));

                if (containsRole)
                {
                    foreach (DiscordRole role in roles[rolePrefix].Select(roleId => new DiscordRole(roleId)))
                    {
                        discordRoles.Add(role);
                    }
                }
            }

            return(discordRoles);
        }
        public async Task <IAuthorizationService.AuthorizeResult> AuthorizeAsync(string accessToken, string username, ulong userId, RolesPool rolesPool)
        {
            bool discordIdPresent = await IsUserVerified(userId);

            UsermapPerson?person = await _usermapInfoService.GetUserInfoAsync(accessToken, username);

            if (person == null)
            {
                _logger.LogWarning("Couldn't fetch info from UserMap");
                return(IAuthorizationService.AuthorizeResult.UserMapError);
            }

            string authId      = _hashService.Hash(person.Username);
            bool   authPresent = await _dbContext.Verifications.AnyAsync(v => v.AuthId == authId);

            IReadOnlySet <DiscordRole> discordRoles = _roleManager.MapUsermapRoles(person.Roles, rolesPool);

            // discord and auth -> update roles
            if (discordIdPresent && authPresent)
            {
                bool verificationExists =
                    await _dbContext.Verifications.AnyAsync(v => v.UserId == userId && v.AuthId == authId);

                if (verificationExists)
                {
                    bool ungranted = await _roleManager.RevokeRolesPoolAsync(userId, rolesPool);

                    if (!ungranted)
                    {
                        _logger.LogWarning("Ungranting roles pool {2} for {0} (id {1}) failed.", username, userId, rolesPool);
                        return(IAuthorizationService.AuthorizeResult.Failed);
                    }
                    bool granted = await _roleManager.GrantRolesAsync(userId, discordRoles);

                    return(granted ? IAuthorizationService.AuthorizeResult.OK : IAuthorizationService.AuthorizeResult.Failed);
                }

                return(IAuthorizationService.AuthorizeResult.DifferentMember);
            }

            // discord xor auth -> user already verified, error
            if (discordIdPresent || authPresent)
            {
                return(IAuthorizationService.AuthorizeResult.DifferentMember);
            }

            // nothing -> create database entry, update roles
            {
                bool rolesGranted = await _roleManager.GrantRolesAsync(userId, discordRoles);

                if (rolesGranted)
                {
                    Verification verification = new() { AuthId = authId, UserId = userId };

                    await _dbContext.Verifications.AddAsync(verification);

                    await _dbContext.SaveChangesAsync();

                    await _roleManager.RevokeHostRolesAsync(userId);

                    return(IAuthorizationService.AuthorizeResult.OK);
                }

                return(IAuthorizationService.AuthorizeResult.Failed);
            }
        }