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))); }
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)); } }
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))); }
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)); }
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))); }