예제 #1
0
        public async Task <IActionResult> RequestAvatarDownloadUrl(
            [FromRoute(Name = "id")] long avatarId,
            [FromServices] IAvatarEntryRepository avatarEntryRepository,
            [FromServices] IStorageUrlBuilder urlBuilder,
            [FromServices] IContentDownloadAuthroizationValidator downloadAuthorizer)
        {
            if (avatarEntryRepository == null)
            {
                throw new ArgumentNullException(nameof(avatarEntryRepository));
            }

            //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(RequestAvatarDownloadUrl)} 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 avatarEntryRepository.ContainsAsync(avatarId).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, avatarId))
            {
                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.Avatar, (await avatarEntryRepository.RetrieveAsync(avatarId)).StorageGuid);

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

                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)));
        }
예제 #2
0
        public async Task <IActionResult> SetAvatarAsUploaded([FromRoute(Name = "id")] long worldId,
                                                              [FromServices] IAvatarEntryRepository avatarEntryRepository,
                                                              [FromServices] IContentResourceExistenceVerifier contentExistenceVerifier)
        {
            //At this point, the user is telling us they finished uploading the world.
            //They could be lying so we should check that the resource exists AND
            //we should also check that it's an asset bundle and gather some information from the header.

            //First we verify a world exists with this id
            if (!await avatarEntryRepository.ContainsAsync(worldId).ConfigureAwaitFalse())
            {
                //TODO: We should say something more specific
                return(BadRequest());
            }

            AvatarEntryModel model = await avatarEntryRepository.RetrieveAsync(worldId)
                                     .ConfigureAwaitFalse();

            //Check the model is associated with this account. Only 1 account can own a world resource
            if (model.AccountId != ClaimsReader.GetAccountIdInt(User))
            {
                return(Unauthorized());
            }

            //Now that we know the world is in the database and the account making this authorized requests owns it
            //we can now actually check that the resource exists on the storeage system
            //TODO: This relies on some outdated API/deprecated stuff.
            bool resourceExists = await contentExistenceVerifier.VerifyResourceExists(UserContentType.Avatar, model.StorageGuid)
                                  .ConfigureAwaitFalse(); //TODO: Don't hardcore bucket name

            //TODO: Be more descriptive
            if (!resourceExists)
            {
                return(NotFound());
            }

            //Ok, so the user IS the resource owner AND he did upload something, so let's validate the assetbundle header.
            //TODO: Refactor this into an object that does the validation and generates readable data
            //TODO: Actually implement asset bundle validation
            //We haven't implemented this yet, we should do asset bundle parsing and validation
            //This REALLY important to prevent invalid bundles from being uploaded
            //or content that isn't even an asset bundle being uploaded
            //See: https://github.com/HearthSim/UnityPack/wiki/Format-Documentation

            //For now, since it's unimplemented let's just set it validated
            await avatarEntryRepository.SetWorldValidated(model.AvatarId)
            .ConfigureAwaitFalseVoid();

            return(Ok());
        }