Exemplo n.º 1
0
        public async Task <IActionResult> RequestWorldDownloadUrl(
            [FromRoute(Name = "id")] long worldId,
            [FromServices] IWorldEntryRepository worldEntryRepository,
            [FromServices] IStorageUrlBuilder urlBuilder,
            [FromServices] IContentDownloadAuthroizationValidator downloadAuthorizer)
        {
            if (worldEntryRepository == null)
            {
                throw new ArgumentNullException(nameof(worldEntryRepository));
            }

            //TODO: We want to rate limit access to this API
            //TODO: We should use both app logging but also another logging service that always gets hit

            //TODO: Consolidate this shared logic between controllers
            if (Logger.IsEnabled(LogLevel.Information))
            {
                Logger.LogInformation($"Recieved {nameof(RequestWorldDownloadUrl)} request from {ClaimsReader.GetUserName(User)}:{ClaimsReader.GetUserId(User)}.");
            }

            //TODO: We should probably check the flags of world to see if it's private (IE hidden from user). Or if it's unlisted or removed.
            //It's possible a user is requesting a world that doesn't exist
            //Could be malicious or it could have been deleted for whatever reason
            if (!await worldEntryRepository.ContainsAsync(worldId).ConfigureAwait(false))
            {
                return(Json(new ContentDownloadURLResponse(ContentDownloadURLResponseCode.NoContentId)));
            }

            //TODO: Refactor this into a validation dependency
            //Now we need to do some validation to determine if they should even be downloading this world
            //we do not want people downloading a world they have no business of going to
            int userId = ClaimsReader.GetUserIdInt(User);

            if (!await downloadAuthorizer.CanUserAccessWorldContet(userId, worldId))
            {
                return(Json(new ContentDownloadURLResponse(ContentDownloadURLResponseCode.AuthorizationFailed)));
            }

            //We can get the URL from the urlbuilder if we provide the world storage GUID
            string downloadUrl = await urlBuilder.BuildRetrivalUrl(UserContentType.World, (await worldEntryRepository.RetrieveAsync(worldId)).StorageGuid);

            //TODO: Should we be validating S3 availability?
            if (String.IsNullOrEmpty(downloadUrl))
            {
                if (Logger.IsEnabled(LogLevel.Error))
                {
                    Logger.LogError($"Failed to create world upload URL for {ClaimsReader.GetUserName(User)}:{ClaimsReader.GetUserId(User)} with ID: {worldId}.");
                }

                return(Json(new ContentDownloadURLResponse(ContentDownloadURLResponseCode.ContentDownloadServiceUnavailable)));
            }

            if (Logger.IsEnabled(LogLevel.Information))
            {
                Logger.LogInformation($"Success. Sending {ClaimsReader.GetUserName(User)} URL: {downloadUrl}");
            }

            return(Json(new ContentDownloadURLResponse(downloadUrl)));
        }
Exemplo n.º 2
0
        public async Task <IActionResult> UpdateUploadedContent(
            [FromRoute(Name = "id")] long contentId,
            [FromServices] ICustomContentRepository <TContentType> contentEntryRepository,
            [FromServices] IStorageUrlBuilder urlBuilder)
        {
            //Content must exist.
            if (!await contentEntryRepository.ContainsAsync(contentId))
            {
                return(BuildFailedResponseModel(ContentUploadResponseCode.InvalidRequest));
            }

            //Unlike creation, we just load an existing one.
            TContentType content = await contentEntryRepository.RetrieveAsync(contentId)
                                   .ConfigureAwaitFalse();

            //WE MUST MAKE SURE THE AUTHORIZED USER OWNS THE CONTENT!!
            if (ClaimsReader.GetAccountIdInt(User) != content.AccountId)
            {
                return(BuildFailedResponseModel(ContentUploadResponseCode.AuthorizationFailed));
            }

            try
            {
                IActionResult response = await GenerateUploadTokenResponse(urlBuilder, content);

                //This is where we update the content versioning.
                content.Version += 1;
                await contentEntryRepository.UpdateAsync(content.ContentId, content)
                .ConfigureAwaitFalseVoid();

                return(response);
            }
            catch (Exception e)
            {
                if (Logger.IsEnabled(LogLevel.Error))
                {
                    Logger.LogError($"Failed to update content. Reason: {e.Message}");
                }

                return(BuildFailedResponseModel(ContentUploadResponseCode.InvalidRequest));
            }
        }
Exemplo n.º 3
0
        private async Task <IActionResult> GenerateUploadTokenResponse(IStorageUrlBuilder urlBuilder, TContentType content)
        {
            string uploadUrl = await urlBuilder.BuildUploadUrl(ContentType, content.StorageGuid);

            if (String.IsNullOrEmpty(uploadUrl))
            {
                if (Logger.IsEnabled(LogLevel.Error))
                {
                    Logger.LogError($"Failed to create content upload URL for {ClaimsReader.GetAccountName(User)}:{ClaimsReader.GetAccountId(User)} with GUID: {content.StorageGuid}.");
                }

                return(BuildFailedResponseModel(ContentUploadResponseCode.ServiceUnavailable));
            }

            if (Logger.IsEnabled(LogLevel.Information))
            {
                Logger.LogInformation($"Success. Sending {ClaimsReader.GetAccountName(User)} URL: {uploadUrl}");
            }

            return(BuildSuccessfulResponseModel(new ContentUploadToken(uploadUrl, content.ContentId, content.StorageGuid)));
        }
Exemplo n.º 4
0
        public async Task <IActionResult> RequestContentUploadUrl(
            [FromServices] ICustomContentRepository <TContentType> contentEntryRepository,
            [FromServices] IStorageUrlBuilder urlBuilder)
        {
            if (contentEntryRepository == null)
            {
                throw new ArgumentNullException(nameof(contentEntryRepository));
            }

            //TODO: We want to rate limit access to this API
            //TODO: We should use both app logging but also another logging service that always gets hit

            if (Logger.IsEnabled(LogLevel.Information))
            {
                Logger.LogInformation($"Recieved {nameof(RequestContentUploadUrl)} request from {ClaimsReader.GetAccountName(User)}:{ClaimsReader.GetAccountId(User)}.");
            }

            //TODO: Check if the result is valid? We should maybe return bool from this API (we do return bool from this API now)
            //The idea is to create an entry which will contain a GUID. From that GUID we can then generate the upload URL
            TContentType content = GenerateNewModel();
            bool         result  = await contentEntryRepository.TryCreateAsync(content);

            return(await GenerateUploadTokenResponse(urlBuilder, content));
        }
Exemplo n.º 5
0
        public async Task <IActionResult> RequestWorldUploadUrl([FromServices] IWorldEntryRepository worldEntryRepository, [FromServices] IStorageUrlBuilder urlBuilder)
        {
            if (worldEntryRepository == null)
            {
                throw new ArgumentNullException(nameof(worldEntryRepository));
            }

            //TODO: We want to rate limit access to this API
            //TODO: We should use both app logging but also another logging service that always gets hit

            if (Logger.IsEnabled(LogLevel.Information))
            {
                Logger.LogInformation($"Recieved {nameof(RequestWorldUploadUrl)} request from {ClaimsReader.GetUserName(User)}:{ClaimsReader.GetUserId(User)}.");
            }

            int userId = ClaimsReader.GetUserIdInt(User);

            //TODO: We should send this if we can't get a user id
            //return new JsonResult(RequestedUrlResponseModel.CreateFailure("Failed to authorize action.", RequestedUrlResponseCode.AuthorizationFailed));
            //TODO: Abstract this behind an issuer
            Guid worldGuid = Guid.NewGuid();

            //TODO: Check if the result is valid? We should maybe return bool from this API (we do return bool from this API now)
            //The idea is to create an entry which will contain a GUID. From that GUID we can then generate the upload URL
            WorldEntryModel world  = new WorldEntryModel(userId, this.HttpContext.Connection.RemoteIpAddress.ToString(), worldGuid);
            bool            result = await worldEntryRepository.TryCreateAsync(world);  //TODO: Ok to just provide a guid right?

            //TODO: Check world's worldid has been set

            string uploadUrl = await urlBuilder.BuildUploadUrl(UserContentType.World, worldGuid);

            if (String.IsNullOrEmpty(uploadUrl))
            {
                if (Logger.IsEnabled(LogLevel.Error))
                {
                    Logger.LogError($"Failed to create world upload URL for {ClaimsReader.GetUserName(User)}:{ClaimsReader.GetUserId(User)} with GUID: {worldGuid}.");
                }

                return(new JsonResult(RequestedUrlResponseModel.CreateFailure("Upload service unavailable.", RequestedUrlResponseCode.ServiceUnavailable)));
            }

            if (Logger.IsEnabled(LogLevel.Information))
            {
                Logger.LogInformation($"Success. Sending {ClaimsReader.GetUserName(User)} URL: {uploadUrl}");
            }

            return(new JsonResult(RequestedUrlResponseModel.CreateSuccess(uploadUrl, world.WorldId)));
        }
        public async Task <IActionResult> RequestWorldDownloadUrl([FromServices] IReadOnlyWorldEntryRepository worldEntryRepository, [FromServices] IStorageUrlBuilder urlBuilder,
                                                                  [FromBody] WorldDownloadURLRequest downloadRequest)
        {
            if (worldEntryRepository == null)
            {
                throw new ArgumentNullException(nameof(worldEntryRepository));
            }

            //TODO: We want to rate limit access to this API
            //TODO: We should use both app logging but also another logging service that always gets hit

            //TODO: Consolidate this shared logic between controllers
            if (Logger.IsEnabled(LogLevel.Information))
            {
                Logger.LogInformation($"Recieved {nameof(RequestWorldDownloadUrl)} request from {HaloLiveUserManager.GetUserName(User)}:{HaloLiveUserManager.GetUserId(User)}.");
            }

            int userId;

            if (!int.TryParse(HaloLiveUserManager.GetUserId(User), out userId))
            {
                if (Logger.IsEnabled(LogLevel.Error))
                {
                    Logger.LogError($"Error: Encountered authorized user with unparsable UserId from User: {HaloLiveUserManager.GetUserName(User)}.");
                }

                return(Json(new WorldDownloadURLResponse(WorldDownloadURLResponseCode.AuthorizationFailed)));
            }

            //TODO: We should probably check the flags of world to see if it's private (IE hidden from user). Or if it's unlisted or removed.
            //It's possible a user is requesting a world that doesn't exist
            //Could be malicious or it could have been deleted for whatever reason
            if (!await worldEntryRepository.HasEntry(downloadRequest.WorldId))
            {
                return(Json(new WorldDownloadURLResponse(WorldDownloadURLResponseCode.NoWorld)));
            }

            //We can get the URL from the urlbuilder if we provide the world storage GUID
            string downloadUrl = await urlBuilder.BuildRetrivalUrl(UserContentType.World, (await worldEntryRepository.GetWorldEntry(downloadRequest.WorldId)).StorageGuid);

            //TODO: Should we both validating S3 availability?
            if (String.IsNullOrEmpty(downloadUrl))
            {
                if (Logger.IsEnabled(LogLevel.Error))
                {
                    Logger.LogError($"Failed to create world upload URL for {HaloLiveUserManager.GetUserName(User)}:{HaloLiveUserManager.GetUserId(User)} with ID: {downloadRequest.WorldId}.");
                }

                return(Json(new WorldDownloadURLResponse(WorldDownloadURLResponseCode.WorldDownloadServiceUnavailable)));
            }

            if (Logger.IsEnabled(LogLevel.Information))
            {
                Logger.LogInformation($"Success. Sending {HaloLiveUserManager.GetUserName(User)} URL: {downloadUrl}");
            }

            return(Json(new WorldDownloadURLResponse(downloadUrl)));
        }