예제 #1
0
        public async Task <ActionResult <ApiResult <SessionPreview> > > MfaDisable([FromBody] MfaDisableModel data, CancellationToken cancellationToken = default)
        {
            var user = this.RosettaUser;
            var pwd  = await this.UserRepository.GetUserPasswordAsync(user.Id, cancellationToken);

            if (pwd == null || !await this.Password.ValidatePasswordHashAsync(data.Password, pwd))
            {
                return(this.StatusCode(401, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Specified credentials were invalid."))));
            }

            var mfa = await this.MfaRepository.GetMfaSettingsAsync(user.Id, cancellationToken);

            if (mfa == null || !mfa.IsConfirmed)
            {
                return(this.StatusCode(401, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "MFA is not configured."))));
            }

            if (data.MfaCode.Length == 6 && !this.MfaValidator.ValidateCode(data.MfaCode, mfa) ||
                data.MfaCode.Length == 8 && !this.MfaValidator.ValidateRecoveryCode(data.MfaCode, mfa))
            {
                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Invalid MFA code provided."))));
            }

            await this.MfaRepository.RemoveMfaAsync(user.Id, cancellationToken);

            var ruser = this.UserPreviewRepository.GetUser(user);
            var token = this.Jwt.IssueToken(ruser);

            return(this.Ok(ApiResult.FromResult(this.UserPreviewRepository.GetSession(ruser, token.Token, token.ExpiresAt, user.RequiresMfa))));
        }
예제 #2
0
        public async Task <ActionResult <ApiResult <MfaSettingsModel> > > StartMfaEnable([FromBody] UserSudoModel data, CancellationToken cancellationToken = default)
        {
            var user = this.RosettaUser;
            var pwd  = await this.UserRepository.GetUserPasswordAsync(user.Id, cancellationToken);

            if (pwd == null || !await this.Password.ValidatePasswordHashAsync(data.Password, pwd))
            {
                return(this.StatusCode(401, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Specified credentials were invalid."))));
            }

            var mfa = await this.MfaRepository.GetMfaSettingsAsync(user.Id, cancellationToken);

            if (mfa != null && mfa.IsConfirmed)
            {
                return(this.StatusCode(400, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.AlreadyConfigured, "MFA is already configured."))));
            }

            var tkpair  = this.ActionTokenPairHandler.IssueTokenPair(TokenActionMFAConfigure);
            var stateId = await this.MfaStateRepository.GenerateStateAsync(this.HttpContext.Connection.RemoteIpAddress.ToString(), tkpair.Server, cancellationToken);

            var state = this.PackState(stateId, tkpair.Client);

            mfa = await this.MfaValidator.GenerateMfaAsync(this.MfaRepository, user.Id, false, cancellationToken);

            var rmfa = this.MfaValidator.GenerateClientData(mfa, user.Username, this.EventConfiguration.Name, state);

            return(this.Ok(ApiResult.FromResult(rmfa)));
        }
예제 #3
0
        public async Task <ActionResult <ApiResult <TeamPreview> > > Invite(long userId, CancellationToken cancellationToken = default)
        {
            var tuser = await this.UserRepository.GetUserAsync(userId, cancellationToken);

            if (tuser == null)
            {
                return(this.NotFound(ApiResult.FromError <TeamPreview>(new ApiError(ApiErrorCode.UserNotFound, "Specified user does not exist."))));
            }

            if (!tuser.IsAuthorized)
            {
                return(this.StatusCode(403, ApiResult.FromError <TeamPreview>(new ApiError(ApiErrorCode.MissingPermissions, "User is not allowed to participate."))));
            }

            if (tuser.Team != null)
            {
                return(this.StatusCode(403, ApiResult.FromError <TeamPreview>(new ApiError(ApiErrorCode.UserAlreadyOnTeam, "Specified user already has a team."))));
            }

            await this.UserRepository.CreateTeamInviteAsync(tuser.Id, this.RosettaUser.Team.Id, cancellationToken);

            var rteam = this.UserPreviewRepository.GetTeam(this.RosettaUser.Team);

            return(this.Ok(ApiResult.FromResult(rteam)));
        }
예제 #4
0
        /// <summary>
        /// Returns the object of a custom request with a provided endpoint.
        /// </summary>
        /// <typeparam name="T">The type that this request will be cast to.</typeparam>
        /// <param name="endpoint">The endpoint that will be appended to the base URL for this call.</param>
        /// <returns>An object of type T from the specified URL.</returns>
        protected async Task <T> GetAsync <T>(string endpoint)
        {
            HttpResponseMessage response = await HttpClient.GetAsync($"{DblApi.BaseUrl}{endpoint}");

            ApiResult <T> result;

            try
            {
                result = response.IsSuccessStatusCode
                                        ? ApiResult <T> .FromSuccess(JsonConvert.DeserializeObject <T>(await response.Content.ReadAsStringAsync()))
                                        : ApiResult <T> .FromHttpError(response.StatusCode);
            }
            catch (Exception ex)
            {
                result = ApiResult <T> .FromError(ex);
            }

            if (!result.IsSuccess)
            {
                throw new Exception(result.ErrorReason);
            }

            if (result.Value == null)
            {
                return(default);
예제 #5
0
        public async Task <ActionResult <ApiResult <IEnumerable <ScoreboardEntryPreview> > > > GetTeamSolves(long id, CancellationToken cancellationToken = default)
        {
            var team = await this.UserRepository.GetTeamAsync(id);

            if (team == null)
            {
                return(this.NotFound(ApiResult.FromError <IEnumerable <ScoreboardEntryPreview> >(new ApiError(ApiErrorCode.TeamNotFound, "Specified team does not exist."))));
            }

            var solves = await this.ChallengeRepository.GetSuccessfulSolvesAsync(id, cancellationToken);

            var challengeIds = solves.Select(x => x.Challenge.Id);

            var scores = await this.ChallengeCacheRepository.GetScoresAsync(challengeIds, cancellationToken);

            var rteam  = this.UserPreviewRepository.GetTeam(team);
            var rusers = team.Id == this.RosettaUser.Team.Id
                ? solves.Select(x => x.User)
                         .Distinct()
                         .ToDictionary(x => x.Id, x => this.UserPreviewRepository.GetUser(x))
                : null;

            var scoreboard = this.ChallengePreviewRepository.GetScoreboard(solves, scores, this.StartTime, rusers);

            return(this.Ok(ApiResult.FromResult(scoreboard)));
        }
예제 #6
0
        public async Task <ActionResult <ApiResult <SessionPreview> > > LoginMfa([FromBody] MfaLoginModel data, CancellationToken cancellationToken = default)
        {
            if (data.ActionToken == null || data.MfaCode == null)
            {
                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.Unauthorized, "MFA state validation failed."))));
            }

            var(stateId, clientTk) = this.UnpackState(data.ActionToken);
            var serverTk = await this.MfaStateRepository.ValidateStateAsync(this.HttpContext.Connection.RemoteIpAddress.ToString(), stateId, cancellationToken);

            var tokenPair = new ActionTokenPair(clientTk, serverTk);

            if (!this.ActionTokenPairHandler.ValidateTokenPair(tokenPair, TokenActionMFA))
            {
                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.Unauthorized, "MFA state validation failed."))));
            }

            var userId = BinaryPrimitives.ReadInt64BigEndian(clientTk.State);
            var mfa    = await this.MfaRepository.GetMfaSettingsAsync(userId, cancellationToken);

            if (mfa == null || !mfa.IsConfirmed)
            {
                return(this.StatusCode(400, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "MFA not configured."))));
            }

            if (data.MfaCode.Length == 6)
            {
                if (!this.MfaValidator.ValidateCode(data.MfaCode, mfa))
                {
                    return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Invalid MFA code provided."))));
                }
            }
            else if (data.MfaCode.Length == 8)
            {
                if (!this.MfaValidator.ValidateRecoveryCode(data.MfaCode, mfa))
                {
                    return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Invalid MFA code provided."))));
                }

                await this.MfaRepository.TripRecoveryCodeAsync(userId, cancellationToken);
            }
            else
            {
                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Invalid MFA code provided."))));
            }

            var user = await this.UserRepository.GetUserAsync(userId, cancellationToken);

            var ruser = this.UserPreviewRepository.GetUser(user);
            var token = this.Jwt.IssueToken(ruser);

            return(this.Ok(ApiResult.FromResult(this.UserPreviewRepository.GetSession(ruser, token.Token, token.ExpiresAt, user.RequiresMfa))));
        }
예제 #7
0
        private async Task <ApiResult> TryRequest(ApiRequest request, Func <Task <HttpResponseMessage> > requestFunction)
        {
            try
            {
                var responseMessage = await requestFunction().ConfigureAwait(false);

                return(ApiResult.FromResponse(request, new ApiResponse(_serializer, responseMessage)));
            }
            catch (HttpRequestException ex)
            {
                return(await Task.FromResult(ApiResult.FromError(request, ex)).ConfigureAwait(false));
            }
        }
예제 #8
0
        public async Task <ActionResult <ApiResult <TeamPreview> > > Create([FromBody] TeamCreateModel teamCreate, CancellationToken cancellationToken = default)
        {
            var team = await this.UserRepository.CreateTeamAsync(teamCreate.Name, cancellationToken);

            if (team == null)
            {
                return(this.Conflict(ApiResult.FromError <TeamPreview>(new ApiError(ApiErrorCode.DuplicateTeamName, "A team with given name already exists."))));
            }

            await this.UserRepository.AssignTeamMembershipAsync(this.RosettaUser.Id, team.Id, cancellationToken);

            var rteam = this.UserPreviewRepository.GetTeam(team);

            return(this.StatusCode(201, ApiResult.FromResult(rteam)));
        }
예제 #9
0
        public async Task <ActionResult <ApiResult <bool> > > Register([FromBody] UserRegistrationModel data, CancellationToken cancellationToken = default)
        {
            var pwd = await this.Password.CreatePasswordHashAsync(data.Password);

            var user = await this.UserRepository.CreateUserAsync(data.Username, true, cancellationToken);

            if (user == null)
            {
                return(this.Conflict(ApiResult.FromError <bool>(new ApiError(ApiErrorCode.DuplicateUsername, "A user with given name already exists."))));
            }

            await this.UserRepository.UpdateUserPasswordAsync(user.Id, pwd, cancellationToken);

            return(this.Ok(ApiResult.FromResult(true)));
        }
예제 #10
0
        public async Task <ActionResult <ApiResult <SessionPreview> > > UpdateUserCountry([FromBody] UserCountryModel data, CancellationToken cancellationToken = default)
        {
            var user = this.RosettaUser;

            user = await this.UserRepository.UpdateUserCountryAsync(user.Id, data.Code, cancellationToken);

            if (user == null)
            {
                return(this.BadRequest(ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidName, "Invalid country name specified."))));
            }

            var ruser = this.UserPreviewRepository.GetUser(user);
            var token = this.Jwt.IssueToken(ruser);

            return(this.Ok(ApiResult.FromResult(this.UserPreviewRepository.GetSession(ruser, token.Token, token.ExpiresAt, user.RequiresMfa))));
        }
예제 #11
0
        public Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            var eventCfg = this.CtfConfigurationLoader.LoadEventData();
            var start    = eventCfg.EndTime - DateTimeOffset.UtcNow;

            if (start < TimeSpan.Zero)
            {
                context.Result = new ObjectResult(ApiResult.FromError <object>(new ApiError(ApiErrorCode.Unauthorized, "Event has concluded.")))
                {
                    StatusCode = 403
                }
            }
            ;

            return(Task.CompletedTask);
        }
    }
예제 #12
0
        public void FromError_ShouldInitializePropertiesCorrect()
        {
            // arrange
            var expectedRequest   = ApiRequest.To(string.Empty);
            var expectedException = new HttpRequestException();

            // act
            var result = ApiResult.FromError(expectedRequest, expectedException);

            // assert
            Assert.Equal(expectedRequest, result.Request);
            Assert.Equal(expectedException, result.Exception);
            Assert.Throws <InvalidOperationException>(() => result.Response);
            Assert.Equal(ResultState.RequestError, result.State);
            Assert.True(result.IsRequestError);
            Assert.False(result.IsSuccess);
        }
예제 #13
0
        public Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            var eventCfg = this.CtfConfigurationLoader.LoadEventData();
            var start    = DateTimeOffset.UtcNow - eventCfg.StartTime;

            if (start >= TimeSpan.Zero)
            {
                context.Result = new ObjectResult(ApiResult.FromError <object>(new ApiError(ApiErrorCode.EventStarted, "Event has already started. Team compositions are locked.")))
                {
                    StatusCode = 403
                }
            }
            ;

            return(Task.CompletedTask);
        }
    }
예제 #14
0
        public async Task <ActionResult <ApiResult <SessionPreview> > > MfaEnable([FromBody] MfaLoginModel data, CancellationToken cancellationToken = default)
        {
            var(stateId, clientTk) = this.UnpackState(data.ActionToken);
            var serverTk = await this.MfaStateRepository.ValidateStateAsync(this.HttpContext.Connection.RemoteIpAddress.ToString(), stateId, cancellationToken);

            var tokenPair = new ActionTokenPair(clientTk, serverTk);

            if (!this.ActionTokenPairHandler.ValidateTokenPair(tokenPair, TokenActionMFAConfigure))
            {
                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.Unauthorized, "MFA state validation failed."))));
            }

            var user = this.RosettaUser;
            var pwd  = await this.UserRepository.GetUserPasswordAsync(user.Id, cancellationToken);

            if (pwd == null)
            {
                return(this.StatusCode(401, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Specified credentials were invalid."))));
            }

            var mfa = await this.MfaRepository.GetMfaSettingsAsync(user.Id, cancellationToken);

            if (mfa == null)
            {
                return(this.StatusCode(401, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "MFA not configured."))));
            }

            if (mfa.IsConfirmed)
            {
                return(this.StatusCode(401, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.AlreadyConfigured, "MFA is already configured."))));
            }

            if (!this.MfaValidator.ValidateCode(data.MfaCode, mfa))
            {
                await this.MfaRepository.RemoveMfaAsync(user.Id, cancellationToken);

                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Invalid MFA code provided."))));
            }

            await this.MfaRepository.ConfirmMfaAsync(user.Id, cancellationToken);

            var ruser = this.UserPreviewRepository.GetUser(user);
            var token = this.Jwt.IssueToken(ruser);

            return(this.Ok(ApiResult.FromResult(this.UserPreviewRepository.GetSession(ruser, token.Token, token.ExpiresAt, user.RequiresMfa))));
        }
예제 #15
0
        /// <summary>
        /// Gets and parses objects
        /// </summary>
        /// <typeparam name="T">Type to parse to</typeparam>
        /// <param name="url">Url to get from</param>
        /// <returns>Object of type T</returns>
        protected async Task <T> GetAsync <T>(string url)
        {
            HttpResponseMessage t = await _httpClient.GetAsync(baseEndpoint + url);

            ApiResult <T> result;

            try
            {
                result = t.IsSuccessStatusCode ? ApiResult <T> .FromSuccess(JsonSerializer.Deserialize <T>(await t.Content.ReadAsStringAsync()))
                    : ApiResult <T> .FromHttpError(t.StatusCode);
            }
            catch (Exception ex)
            {
                result = ApiResult <T> .FromError(ex);
            }
            return(result.Value);
        }
예제 #16
0
        public async Task <ActionResult <ApiResult <string> > > Endpoint(string provider, CancellationToken cancellationToken = default)
        {
            var oauth = this.OAuthSelector.GetById(provider);

            if (oauth == null)
            {
                return(this.NotFound(ApiResult.FromError <string>(new ApiError(ApiErrorCode.InvalidProvider, "Specified provider does not exist."))));
            }

            var tkpair = this.ActionTokenPairHandler.IssueTokenPair(TokenActionOAuth);

            var stateId = await this.OAuthStateRepository.GenerateStateAsync(this.HttpContext.Connection.RemoteIpAddress.ToString(), tkpair.Server, cancellationToken);

            var clientToken = tkpair.Client;
            var state       = this.PackState(stateId, clientToken);

            return(this.Ok(ApiResult.FromResult(oauth.GetAuthenticationUrl(this.CreateContext(provider, state)))));
        }
예제 #17
0
        public async Task <ActionResult <ApiResult <TeamPreview> > > AcceptInvite(long teamId, CancellationToken cancellationToken = default)
        {
            var invite = await this.UserRepository.GetTeamInviteAsync(this.RosettaUser.Id, teamId, cancellationToken);

            if (invite == null)
            {
                return(this.NotFound(ApiResult.FromError <TeamPreview>(new ApiError(ApiErrorCode.Unauthorized, "You were not invited to that team."))));
            }

            await this.UserRepository.AssignTeamMembershipAsync(this.RosettaUser.Id, invite.Team.Id, cancellationToken);

            await this.UserRepository.ClearTeamInvitesAsync(this.RosettaUser.Id, cancellationToken);

            var team = await this.UserRepository.GetTeamAsync(invite.Team.Id);

            var rteam = this.UserPreviewRepository.GetTeam(team);

            return(this.Ok(ApiResult.FromResult(rteam)));
        }
예제 #18
0
        public Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            var eventCfg = this.CtfConfigurationLoader.LoadEventData();
            var elapsed  = DateTimeOffset.UtcNow - eventCfg.StartTime;

            if (elapsed < TimeSpan.Zero)
            {
                context.Result = new ObjectResult(ApiResult.FromError <object>(new ApiError(ApiErrorCode.Unauthorized, "Event has not yet started.")))
                {
                    StatusCode = 403
                }
            }
            ;

            context.HttpContext.Items["RosettaCTF:StartElapsed"] = elapsed;
            context.HttpContext.Items["RosettaCTF:StartTime"]    = eventCfg.StartTime;
            return(Task.CompletedTask);
        }
    }
예제 #19
0
        private async Task RunHandlerAsync(HttpContext ctx)
        {
            ctx.Response.ContentType = "application/json";

            ApiError error;
            var      exhpf = ctx.Features.Get <IExceptionHandlerPathFeature>();

            if (exhpf?.Error != null)
            {
                ctx.Response.StatusCode = 500;

                error = new ApiError(ApiErrorCode.GenericError, "Internal server error occured while processing the request.");
            }
            else
            {
                error = new ApiError(ApiErrorCode.GenericError, $"HTTP Error {ctx.Response.StatusCode}.");
            }

            await JsonSerializer.SerializeAsync(ctx.Response.Body, ApiResult.FromError <object>(error), AbstractionUtilities.DefaultJsonOptions);
        }
예제 #20
0
        public async Task <ActionResult <ApiResult <IEnumerable <string> > > > MfaGetBackups([FromBody] UserSudoModel data, CancellationToken cancellationToken = default)
        {
            var user = this.RosettaUser;
            var pwd  = await this.UserRepository.GetUserPasswordAsync(user.Id, cancellationToken);

            if (pwd == null || !await this.Password.ValidatePasswordHashAsync(data.Password, pwd))
            {
                return(this.StatusCode(401, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Specified credentials were invalid."))));
            }

            var mfa = await this.MfaRepository.GetMfaSettingsAsync(user.Id, cancellationToken);

            if (mfa == null)
            {
                return(this.StatusCode(401, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "MFA not configured."))));
            }

            var codes = this.MfaValidator.GenerateRecoveryCodes(mfa);

            return(this.Ok(ApiResult.FromResult(codes)));
        }
예제 #21
0
        public async Task <ActionResult <ApiResult <TeamPreview> > > Kick(long userId, CancellationToken cancellationToken = default)
        {
            if (DateTimeOffset.UtcNow >= this.EventConfiguration.StartTime)
            {
                return(this.StatusCode(403, ApiResult.FromError <TeamPreview>(new ApiError(ApiErrorCode.EventStarted, "Cannot modify team composition after the event has started."))));
            }

            var tuser = await this.UserRepository.GetUserAsync(userId, cancellationToken);

            if (tuser == null)
            {
                return(this.NotFound(ApiResult.FromError <TeamPreview>(new ApiError(ApiErrorCode.UserNotFound, "Specified user does not exist."))));
            }

            if (tuser.Team == null || tuser.Team.Id != this.RosettaUser.Team.Id)
            {
                return(this.NotFound(ApiResult.FromError <TeamPreview>(new ApiError(ApiErrorCode.UserNotFound, "Specified user is not part of this team."))));
            }

            var teamId = tuser.Team.Id;

            await this.UserRepository.AssignTeamMembershipAsync(tuser.Id, null, cancellationToken);

            var team = await this.UserRepository.GetTeamAsync(teamId);

            if (!team.Members.Any())
            {
                await this.UserRepository.DeleteTeamAsync(teamId);
            }

            if (this.RosettaUser.Id != tuser.Id)
            {
                var rteam = this.UserPreviewRepository.GetTeam(team);
                return(this.Ok(ApiResult.FromResult(rteam)));
            }
            else
            {
                return(this.Ok(ApiResult.FromResult <TeamPreview>(null)));
            }
        }
예제 #22
0
        public async Task <ActionResult <ApiResult <TeamPreview> > > Get(long?id = null, CancellationToken cancellationToken = default)
        {
            ITeam team;

            if (id == null)
            {
                team = this.RosettaUser?.Team;
            }
            else
            {
                team = await this.UserRepository.GetTeamAsync(id.Value);
            }

            if (team == null)
            {
                return(this.NotFound(ApiResult.FromError <TeamPreview>(new ApiError(ApiErrorCode.TeamNotFound, "Specified team does not exist."))));
            }

            var rteam = this.UserPreviewRepository.GetTeam(team);

            return(this.Ok(ApiResult.FromResult(rteam)));
        }
예제 #23
0
        public async Task <ActionResult <ApiResult <SessionPreview> > > Login([FromBody] UserLoginModel data, CancellationToken cancellationToken = default)
        {
            var user = await this.UserRepository.GetUserAsync(data.Username, cancellationToken);

            if (user == null)
            {
                return(this.StatusCode(401, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Specified credentials were invalid."))));
            }

            var pwd = await this.UserRepository.GetUserPasswordAsync(user.Id, cancellationToken);

            if (pwd == null)
            {
                return(this.StatusCode(401, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Specified credentials were invalid."))));
            }

            if (!await this.Password.ValidatePasswordHashAsync(data.Password, pwd))
            {
                return(this.StatusCode(401, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Specified credentials were invalid."))));
            }

            if (user.RequiresMfa)
            {
                var mfaState = new byte[8];
                BinaryPrimitives.WriteInt64BigEndian(mfaState, user.Id);
                var tokenPair = this.ActionTokenPairHandler.IssueTokenPair(TokenActionMFA, mfaState);

                var stateId = await this.MfaStateRepository.GenerateStateAsync(this.HttpContext.Connection.RemoteIpAddress.ToString(), tokenPair.Server, cancellationToken);

                var continuation = this.PackState(stateId, tokenPair.Client);

                return(this.Ok(ApiResult.FromResult(this.UserPreviewRepository.GetSession(continuation))));
            }

            var ruser = this.UserPreviewRepository.GetUser(user);
            var token = this.Jwt.IssueToken(ruser);

            return(this.Ok(ApiResult.FromResult(this.UserPreviewRepository.GetSession(ruser, token.Token, token.ExpiresAt, user.RequiresMfa))));
        }
예제 #24
0
        public async Task <ActionResult <ApiResult <SessionPreview> > > RemovePassword([FromBody] UserPasswordRemoveModel data, CancellationToken cancellationToken = default)
        {
            var user = this.RosettaUser;

            if (!user.ConnectedAccounts.Any())
            {
                return(this.StatusCode(401, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.GenericError, "To disable a password, at least one external account must be connected to this account."))));
            }

            var pwd = await this.UserRepository.GetUserPasswordAsync(user.Id, cancellationToken);

            if (pwd == null || !await this.Password.ValidatePasswordHashAsync(data.Password, pwd))
            {
                return(this.StatusCode(401, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Specified credentials were invalid."))));
            }

            await this.UserRepository.UpdateUserPasswordAsync(user.Id, null, cancellationToken);

            var ruser = this.UserPreviewRepository.GetUser(user);
            var token = this.Jwt.IssueToken(ruser);

            return(this.Ok(ApiResult.FromResult(this.UserPreviewRepository.GetSession(ruser, token.Token, token.ExpiresAt, user.RequiresMfa))));
        }
예제 #25
0
        public async Task <ActionResult <ApiResult <bool> > > SubmitFlag([FromRoute] string id, [FromBody] ChallengeFlagModel challengeFlag, CancellationToken cancellationToken = default)
        {
            var challenge = await this.ChallengeRepository.GetChallengeAsync(id, cancellationToken);

            var flag  = challengeFlag.Flag;
            var valid = flag == challenge.Flag;
            int?score = null;

            if (valid && this.EventConfiguration.Scoring != CtfScoringMode.Static)
            {
                var scoreInfo = await this.ScoreCalculator.ComputeCurrentScoreAsync(challenge, cancellationToken);

                if (this.EventConfiguration.Scoring == CtfScoringMode.Freezer)
                {
                    score = scoreInfo.Current;
                }
            }

            var solve = await this.ChallengeRepository.SubmitSolveAsync(flag, valid, challenge.Id, this.RosettaUser.Id, this.RosettaUser.Team.Id, score, cancellationToken);

            if (solve == null)
            {
                return(this.Conflict(ApiResult.FromError <bool>(new ApiError(ApiErrorCode.AlreadySolved, "Your team already solved this challenge."))));
            }

            if (valid && challenge.BaseScore == 1)
            {
                await this.ChallengeCacheRepository.IncrementBaselineSolveCountAsync(cancellationToken);

                if (this.EventConfiguration.Scoring != CtfScoringMode.Static)
                {
                    await this.ScoreCalculator.UpdateAllScoresAsync(this.EventConfiguration.Scoring == CtfScoringMode.Freezer, false, cancellationToken);
                }
            }

            return(this.Ok(ApiResult.FromResult(valid)));
        }
예제 #26
0
        public async Task <ActionResult <ApiResult <SessionPreview> > > ChangePassword([FromBody] UserPasswordChangeModel data, CancellationToken cancellationToken = default)
        {
            var user = this.RosettaUser;
            var pwd  = await this.UserRepository.GetUserPasswordAsync(user.Id, cancellationToken);

            if (pwd != null && data.OldPassword == null)
            {
                return(this.StatusCode(401, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Specified credentials were invalid."))));
            }

            if (pwd != null && !await this.Password.ValidatePasswordHashAsync(data.OldPassword, pwd))
            {
                return(this.StatusCode(401, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidCredentials, "Specified credentials were invalid."))));
            }

            var npwd = await this.Password.CreatePasswordHashAsync(data.NewPassword);

            await this.UserRepository.UpdateUserPasswordAsync(user.Id, npwd, cancellationToken);

            var ruser = this.UserPreviewRepository.GetUser(user);
            var token = this.Jwt.IssueToken(ruser);

            return(this.Ok(ApiResult.FromResult(this.UserPreviewRepository.GetSession(ruser, token.Token, token.ExpiresAt, user.RequiresMfa))));
        }
예제 #27
0
 public void FromError_ThrowsException_WhenArgumentsInvalid()
 {
     Assert.Throws <ArgumentNullException>(() => ApiResult.FromError(null, null));
     Assert.Throws <ArgumentNullException>(() => ApiResult.FromError(null, new Exception()));
     Assert.Throws <ArgumentNullException>(() => ApiResult.FromError(ApiRequest.To(string.Empty), null));
 }
예제 #28
0
        public async Task <ActionResult <ApiResult <SessionPreview> > > Login([FromBody] OAuthAuthenticationModel data, CancellationToken cancellationToken = default)
        {
            if (data.State == null)
            {
                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.Unauthorized, "OAuth state validation failed."))));
            }

            var(stateId, clientTk) = this.UnpackState(data.State);
            var serverTk = await this.OAuthStateRepository.ValidateStateAsync(this.HttpContext.Connection.RemoteIpAddress.ToString(), stateId, cancellationToken);

            var tokenPair = new ActionTokenPair(clientTk, serverTk);

            if (!this.ActionTokenPairHandler.ValidateTokenPair(tokenPair, TokenActionOAuth))
            {
                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.Unauthorized, "OAuth state validation failed."))));
            }

            var provider = this.OAuthSelector.IdFromReferrer(new Uri(data.Referrer));
            var oauth    = this.OAuthSelector.GetById(provider);

            if (oauth == null)
            {
                return(this.NotFound(ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidProvider, "Specified provider does not exist."))));
            }

            var ctx    = this.CreateContext(provider, data.State);
            var tokens = await oauth.CompleteLoginAsync(ctx, data.Code, cancellationToken);

            if (tokens == null)
            {
                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.ExternalAuthenticationError, "Failed to authenticate with the OAuth provider."))));
            }

            var expires = DateTimeOffset.UtcNow.AddSeconds(tokens.ExpiresIn - 20);
            var ouser   = await oauth.GetUserAsync(ctx, tokens.AccessToken, cancellationToken);

            if (ouser == null || !ouser.IsAuthorized)
            {
                return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.Unauthorized, "You are not authorized to participate."))));
            }

            var oid   = ouser.Id;
            var euser = await this.UserRepository.GetExternalAccountAsync(oid, provider, cancellationToken);

            var user = euser?.User;

            if (euser == null)
            {
                if (!AbstractionUtilities.NameRegex.IsMatch(ouser.Username))
                {
                    return(this.StatusCode(403, ApiResult.FromError <SessionPreview>(new ApiError(ApiErrorCode.InvalidName, "Specified username contained invalid characters."))));
                }

                var uid = this.GetUserId();
                if (uid == null)
                {
                    user = await this.UserRepository.CreateUserAsync(ouser.Username, ouser.IsAuthorized, cancellationToken);
                }
                else
                {
                    user = await this.UserRepository.GetUserAsync(uid.Value, cancellationToken);
                }

                euser = await this.UserRepository.ConnectExternalAccountAsync(user.Id, ouser.Id, ouser.Username, provider, cancellationToken);
            }

            await this.UserRepository.UpdateTokensAsync(user.Id, euser.ProviderId, tokens.AccessToken, tokens.RefreshToken, expires, cancellationToken);

            var ruser = this.UserPreviewRepository.GetUser(user);
            var token = this.Jwt.IssueToken(ruser);

            return(this.Ok(ApiResult.FromResult(this.UserPreviewRepository.GetSession(ruser, token.Token, token.ExpiresAt, user.RequiresMfa))));
        }