Exemple #1
0
        public IActionResult Modify([FromQuery] int id, [FromBody] PostModifyDto data)
        {
            if (string.IsNullOrEmpty(data.Content))
            {
                return(BadRequest());
            }

            var userId = ClaimsReader.GetUserId(Request);
            var post   = context.Posts.FirstOrDefault(a => a.Id == id && a.UserId == userId);

            if (post == null)
            {
                return(NotFound());
            }

            post.EditDate = DateTime.UtcNow;
            post.Content  = data.Content;

            try
            {
                context.SaveChanges();

                return(Ok(new { post.EditDate }));
            }
            catch (Exception)
            {
                return(BadRequest());
            }
        }
Exemple #2
0
        public async Task <IActionResult> RegisteredZoneCheckin([FromBody] ZoneServerCheckinRequestModel zoneCheckinRequest)
        {
            //This is ok, it means the zone was unregistered before the checkin message was recieved.
            //It doesn't alone indicate a failure.
            if (!await ZoneRepository.ContainsAsync(ClaimsReader.GetAccountIdInt(User)))
            {
                if (Logger.IsEnabled(LogLevel.Warning))
                {
                    Logger.LogWarning($"UserId: {ClaimsReader.GetPlayerAccountId(User)} attempted to checkin ZoneId: {ClaimsReader.GetAccountId(User)} but the zone was not registered.");
                }

                return(Ok());
            }

            ZoneInstanceEntryModel model = await ZoneRepository.RetrieveAsync(ClaimsReader.GetAccountIdInt(User));

            //We don't REMOVE if expired. This should only update checkin time
            //something else should be responsible for removal if expired at some point.
            model.UpdateCheckinTime();
            await ZoneRepository.UpdateAsync(model.ZoneId, model);

            //We don't really have anything to reply to, this shouldn't be called externally.
            //It's basically handling a proxied message queue message.
            return(Ok());
        }
        CreateCharacterAsync([FromBody] RPGCharacterCreationRequest <TRaceType, TClassType> request, CancellationToken token = default)
        {
            //TODO: Fix GetAccountId API
            int accountId = ClaimsReader.GetAccountId <int>(User);

            if (!ModelState.IsValid)
            {
                return(Failure <RPGCharacterCreationResult, CharacterCreationResponseCode>(CharacterCreationResponseCode.GeneralError));
            }

            //TODO: Add validation pipeline.
            if (String.IsNullOrWhiteSpace(request.Name))
            {
                return(Failure <RPGCharacterCreationResult, CharacterCreationResponseCode>(CharacterCreationResponseCode.InvalidName));
            }

            try
            {
                DBRPGCharacter character = await CharacterRepository.CreateCharacterAsync(accountId, request.Name, request.Race, request.ClassType, token);

                return(Success <RPGCharacterCreationResult, CharacterCreationResponseCode>(new RPGCharacterCreationResult(character.Id)));
            }
            catch (Exception e)
            {
                if (Logger.IsEnabled(LogLevel.Error))
                {
                    Logger.LogError($"Failed to create Character: {request} Reason: {e}");
                }

                return(Failure <RPGCharacterCreationResult, CharacterCreationResponseCode>(CharacterCreationResponseCode.GeneralError));
            }
        }
Exemple #4
0
        public IActionResult ResolveApplication([FromBody] GroupAccessAcceptanceDto data)
        {
            var userId = ClaimsReader.GetUserId(Request);

            if (!CheckAdminPriviliges(userId, data.GroupId))
            {
                return(BadRequest());
            }

            var targetId   = context.Users.SingleOrDefault(a => a.Id == data.UserId).Id;
            var group      = context.Groups.SingleOrDefault(a => a.Id == data.GroupId);
            var connection = context.UserGroups.SingleOrDefault(a => a.UserId == targetId && a.GroupId == data.GroupId);

            if (data.Accepted)
            {
                connection.Relation = GroupRelation.User;
            }
            else
            {
                context.UserGroups.Remove(connection);
            }

            try
            {
                context.SaveChanges();

                return(Ok());
            }
            catch (Exception)
            {
                return(BadRequest());
            }
        }
Exemple #5
0
        public async Task <CharacterListResponse> GetCharacters()
        {
            int accountId = ClaimsReader.GetUserIdInt(User);

            //So to check characters we just need to query for the
            //characters with this account id
            int[] characterIds = await CharacterRepository.CharacterIdsForAccountId(accountId);

#warning This is just for the test build, we need to change this
            ProjectVersionStage.AssertInternalTesting();
            if (characterIds.Length == 0)
            {
                //We just create a new one for testing.
                bool result = await CharacterRepository.TryCreateAsync(new CharacterEntryModel(accountId, ClaimsReader.GetUserName(User)))
                              .ConfigureAwait(false);

                if (result)
                {
                    //Just return the get, a character should now exist.
                    return(await GetCharacters());
                }
                else
                {
                    return(new CharacterListResponse(CharacterListResponseCode.NoCharactersFoundError));
                }
            }

            /*if(characterIds.Length == 0)
             *      return new CharacterListResponse(CharacterListResponseCode.NoCharactersFoundError);*/

            //The reason we only provide the IDs is all other character data can be looked up
            //by the client when it needs it. Like name query, visible/character details/look stuff.
            //No reason to send all this data when they may only need names. Which can be queried through the known API
            return(new CharacterListResponse(characterIds));
        }
Exemple #6
0
        public IActionResult GetAdminGroupDetails([FromQuery] int id)
        {
            var userId = ClaimsReader.GetUserId(Request);

            if (!CheckAdminPriviliges(userId, id))
            {
                return(BadRequest());
            }

            var group = context.Groups
                        .Include(a => a.Users)
                        .ThenInclude(a => a.User)
                        .FirstOrDefault(a => a.Id == id);

            var result = new GroupAdminDetailsDto
            {
                Name        = group.Name,
                Description = group.Description,
                IsPrivate   = group.IsPrivate,
                Candidates  = group.Users
                              .Where(a => a.Relation == GroupRelation.Requesting)
                              .Select(a => new GroupCandidateDto
                {
                    UserId    = a.UserId,
                    UserLogin = a.User.Login
                }).ToList()
            };

            return(Ok(result));
        }
Exemple #7
0
        public IActionResult Modify([FromBody] GroupModifyDto data)
        {
            var userId = ClaimsReader.GetUserId(Request);

            if (!context.UserGroups.Any(a => a.GroupId == data.Id && a.UserId == userId && a.Relation == GroupRelation.Owner))
            {
                return(BadRequest());
            }

            var group = context.Groups.FirstOrDefault(a => a.Id == data.Id);

            group.Name        = data.Name;
            group.Description = data.Description;
            group.IsPrivate   = data.IsPrivate;

            try
            {
                context.SaveChanges();

                return(Ok());
            }
            catch (Exception)
            {
                return(BadRequest());
            }
        }
 public async Task <int> RetrieveAuthorizedCharacterAsync(CancellationToken token = default)
 {
     //Idea here is that a principal used for authorization may contain the claim for
     //the subaccount id. The subaccount id is to be treated as the character id
     //in our backend.
     return(ClaimsReader.GetCharacterId(User));
 }
Exemple #9
0
        public IActionResult AskForInvite([FromBody] GroupInviteDto data)
        {
            var userId = ClaimsReader.GetUserId(Request);
            var group  = context.Groups.SingleOrDefault(a => a.Id == data.Id);

            if (group == null)
            {
                return(NotFound());
            }

            var connection = new UserGroup
            {
                UserId   = userId,
                GroupId  = group.Id,
                Relation = group.IsPrivate ? GroupRelation.Requesting : GroupRelation.User
            };

            context.UserGroups.Add(connection);

            try
            {
                context.SaveChanges();

                return(Ok());
            }
            catch (Exception)
            {
                return(BadRequest());
            }
        }
Exemple #10
0
        public IActionResult ResolveGroupInvite([FromBody] GroupInviteResolveDto data)
        {
            var userId     = ClaimsReader.GetUserId(Request);
            var connection = context.UserGroups.SingleOrDefault(a => a.UserId == userId && a.GroupId == data.Id);

            if (data.Value)
            {
                connection.Relation = GroupRelation.User;
            }
            else
            {
                context.UserGroups.Remove(connection);
            }

            try
            {
                context.SaveChanges();

                return(Ok());
            }
            catch (Exception)
            {
                return(BadRequest());
            }
        }
Exemple #11
0
        public IActionResult Get([FromQuery] GroupQueryDto query)
        {
            var userId = ClaimsReader.GetUserId(Request);

            if (!context.UserGroups.Any(a => a.GroupId == query.GroupId && a.UserId == userId && (a.Relation == GroupRelation.Owner || a.Relation == GroupRelation.User)))
            {
                return(BadRequest());
            }

            var result = context.Posts
                         .Where(a => a.GroupId == query.GroupId)
                         .OrderByDescending(a => a.DateAdded)
                         .ThenBy(a => a.DateAdded)
                         .Skip(query.PageSize * (query.Page - 1))
                         .Take(query.PageSize)
                         .Select(a => new PostDto
            {
                Id        = a.Id,
                DateAdded = a.DateAdded,
                Content   = a.Content,
                EditDate  = a.EditDate,
                IsOwner   = a.UserId == userId,
                Owner     = a.User.Login,
                Comments  = a.Comments.OrderBy(b => b.DateAdded).Select(b => new CommentDto
                {
                    Id        = b.Id,
                    DateAdded = b.DateAdded,
                    Content   = b.Content,
                    IsOwner   = b.UserId == userId,
                    Owner     = b.User.Login
                }).ToList()
            }).ToList();

            return(Ok(result));
        }
Exemple #12
0
        /// <inheritdoc />
        public override async Task OnConnectedAsync()
        {
            await base.OnConnectedAsync()
            .ConfigureAwaitFalseVoid();

            if (Logger.IsEnabled(LogLevel.Information))
            {
                Logger.LogInformation($"Account Connected: {ClaimsReader.GetAccountName(Context.User)}:{ClaimsReader.GetAccountId(Context.User)} with SignalR UserId: {Context.UserIdentifier}");
            }

            try
            {
                foreach (var listener in OnConnectionHubListeners)
                {
                    HubOnConnectionState connectionState = await listener.OnConnected(this).ConfigureAwaitFalse();

                    //if the listener indicated we need to abort for whatever reason we
                    //should believe it and just abort.
                    if (connectionState == HubOnConnectionState.Abort)
                    {
                        Context.Abort();
                        break;
                    }
                }
            }
            catch (Exception e)
            {
                if (Logger.IsEnabled(LogLevel.Error))
                {
                    Logger.LogError($"Account: {ClaimsReader.GetAccountName(Context.User)}:{ClaimsReader.GetAccountId(Context.User)} failed to properly connect to hub. Error: {e.ToString()}\n\nStack: {e.StackTrace}");
                }

                Context.Abort();
            }
        }
        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 <JsonResult> GetToken(bool regenerateToken = false)
        {
            var user = await this.userService.GetUserByEmailAsync(ClaimsReader.ReadEmail(this.User));

            var token = await this.tokenService.GetLongLivedUserToken(user, regenerateToken);

            return(Json(token));
        }
Exemple #15
0
		/// <summary>
		/// Indicates if the provided character id is valid for the user in the message context.
		/// </summary>
		/// <param name="characterId">The id to check.</param>
		/// <param name="characterRepository">The character repository service.</param>
		/// <returns>True if the character id is valid/</returns>
		private async Task<bool> IsCharacterIdValidForUser(int characterId, ICharacterRepository characterRepository)
		{
			//We only support positive character ids so if they request a less than 0 it's invalid and likely spoofed
			//or if they request an id they don't own
			//or if it's an not a known character
			return characterId >= 0 &&
				await characterRepository.ContainsAsync(characterId) && 
				(await characterRepository.RetrieveAsync(characterId)).AccountId == ClaimsReader.GetAccountIdInt(User);
		}
        public async Task <CharacterSessionDataResponse> GetCharacterSessionData([FromRoute(Name = "id")] int characterId)
        {
            int accountId = ClaimsReader.GetUserIdInt(User);

            //TODO: Do we want to expose this to non-controlers?
            //First we should validate that the account that is authorized owns the character it is requesting session data from
            return(await RetrieveSessionDataIfAvailable(characterId, accountId)
                   .ConfigureAwait(false));
        }
        /// <inheritdoc />
        public async Task <HubOnConnectionState> OnConnected([JetBrains.Annotations.NotNull] Hub hubConnectedTo)
        {
            if (hubConnectedTo == null)
            {
                throw new ArgumentNullException(nameof(hubConnectedTo));
            }

            //We should never be here unless auth worked
            //so we can assume that and just try to request character session data
            //for the account.
            CharacterSessionDataResponse characterSessionDataResponse = await SocialToGameClient.GetCharacterSessionDataByAccount(ClaimsReader.GetUserIdInt(hubConnectedTo.Context.User))
                                                                        .ConfigureAwait(false);

            //TODO: To support website chat we shouldn't disconnect just because they don't have a zone session.
            //If the session data request fails we should just abort
            //and disconnect, the user shouldn't be connecting
            if (!characterSessionDataResponse.isSuccessful)
            {
                if (Logger.IsEnabled(LogLevel.Warning))
                {
                    Logger.LogWarning($"Failed to Query SessionData for AccountId: {ClaimsReader.GetUserId(hubConnectedTo.Context.User)} Reason: {characterSessionDataResponse.ResultCode}");
                }

                //TODO: Eventually we don't want to do this.
                return(HubOnConnectionState.Abort);
            }

            //This is ABSOLUTELY CRITICAL we need to validate that the character header they sent actually
            //is the character they have a session as
            //NOT CHECKING THIS IS EQUIVALENT TO LETTING USERS PRETEND THEY ARE ANYONE!
            if (hubConnectedTo.Context.UserIdentifier != characterSessionDataResponse.CharacterId.ToString())
            {
                //We can log account name and id here, because they were successfully authed.
                if (Logger.IsEnabled(LogLevel.Warning))
                {
                    Logger.LogWarning($"User with AccountId: {ClaimsReader.GetUserName(hubConnectedTo.Context.User)}:{ClaimsReader.GetUserId(hubConnectedTo.Context.User)} attempted to spoof as CharacterId: {hubConnectedTo.Context.UserIdentifier} but had session for CharacterID: {characterSessionDataResponse.CharacterId}.");
                }

                return(HubOnConnectionState.Abort);
            }

            if (Logger.IsEnabled(LogLevel.Information))
            {
                Logger.LogInformation($"Recieved SessionData: Id: {characterSessionDataResponse.CharacterId} ZoneId: {characterSessionDataResponse.ZoneId}");
            }

            //Registers for lookup so that we can tell where a connection is zone-wise.
            ZoneLookupService.Register(hubConnectedTo.Context.ConnectionId, characterSessionDataResponse.ZoneId);

            //TODO: We should have group name builders. Not hardcoded
            //Join the zoneserver's chat channel group
            await hubConnectedTo.Groups.AddToGroupAsync(hubConnectedTo.Context.ConnectionId, $"zone:{characterSessionDataResponse.ZoneId}", hubConnectedTo.Context.ConnectionAborted)
            .ConfigureAwait(false);

            return(HubOnConnectionState.Success);
        }
        /// <summary>
        /// Verifies that the provided <see cref="characterId"/>
        /// is owned by the current User claim.
        /// </summary>
        /// <param name="characterId"></param>
        /// <returns></returns>
        public async Task <bool> VerifyCharacterOwnedByAccount(int characterId)
        {
            int accountId = ClaimsReader.GetUserIdInt(User);

            //TODO: Do we want to expose this to non-controlers?
            //First we should validate that the account that is authorized owns the character it is requesting session data from

            return((await CharacterRepository.CharacterIdsForAccountId(accountId).ConfigureAwait(false))
                   .Contains(characterId));
        }
        public async Task <RPGCharacterData <TRaceType, TClassType>[]> RetrieveCharactersDataAsync(CancellationToken token = default)
        {
            //TODO: Fix GetAccountId API
            int accountId = ClaimsReader.GetAccountId <int>(User);

            return((await CharacterRepository
                    .RetrieveOwnedCharactersAsync(accountId, token))
                   .Select(ConvertDbToTransit)
                   .ToArray());
        }
        public async Task <int[]> RetrieveCharacterBasicListAsync(CancellationToken token = default)
        {
            //TODO: Properly handle failure and return correct response codes.
            int accountId = ClaimsReader.GetAccountId <int>(User);

            return((await CharacterRepository
                    .RetrieveOwnedCharactersAsync(accountId, token))
                   .Select(d => d.Character.Id)
                   .OrderBy(i => i)
                   .ToArray());
        }
Exemple #21
0
        public IActionResult GetProfile()
        {
            var userId = ClaimsReader.GetUserId(HttpContext.Request);
            var user   = context.Users.SingleOrDefault(x => x.Id == userId);

            return(Ok(new UserProfileDto
            {
                Login = user.Login,
                Email = user.Email,
                ReceiveNotifications = user.ReceiveNotifications
            }));
        }
Exemple #22
0
        private async Task <bool> CheckZoneAuthorizedToModifyCharacterData(int characterId)
        {
            if (!await CharacterSessionRepository.CharacterHasActiveSession(characterId))
            {
                return(false);
            }

            CharacterSessionModel model = await CharacterSessionRepository.RetrieveAsync(characterId);

            //If they aren't in this zone we shouldn't be allowed to save it.
            return(ClaimsReader.GetAccountIdInt(User) == model.ZoneId);
        }
Exemple #23
0
        public async Task <IActionResult> CreateZoneServerAccount([FromBody][JetBrains.Annotations.NotNull] ZoneServerAccountRegistrationRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            //We want to log this out for information purposes whenever an auth request begins
            if (Logger.IsEnabled(LogLevel.Information))
            {
                Logger.LogInformation($"Zone Register Request by UserAccount: {ClaimsReader.GetAccountName(this.User)}:{ClaimsReader.GetAccountIdInt(this.User)} {HttpContext.Connection.RemoteIpAddress}:{HttpContext.Connection.RemotePort}");
            }

            //TODO: Create cryptographically secure random bytes for password and user.
            string zoneUserName = Guid.NewGuid().ToString();
            string zonePassword = Guid.NewGuid().ToString();

            ZoneServerApplicationUser user = new ZoneServerApplicationUser(ClaimsReader.GetAccountIdInt(this.User))
            {
                UserName = zoneUserName,
                Email    = "*****@*****.**",              //TODO: Real email???
            };

            IdentityResult identityResult = await UserManager.CreateAsync(user, zonePassword);

            if (identityResult.Succeeded)
            {
                //Adds the vrgid account owner claim.
                await UserManager.AddClaimAsync(user, new Claim(GladMMOAuthConstants.ACCOUNT_ID_OWNER_CLAIM_NAME, ClaimsReader.GetAccountId(this.User)));

                //Here we inherit each role the user account has to the zoneserver account
                foreach (var claim in User.Claims)
                {
                    if (claim.Type == "role")                                                               //TODO: Is there a constant for this??
                    {
                        if (claim.Value.ToLower() != GladMMOAuthConstants.PLAYERACCOUNT_AUTHORIZATION_ROLE) //DO NOT add the player role.
                        {
                            await UserManager.AddToRoleAsync(user, claim.Value);
                        }
                    }
                }

                //Also add the ZoneServer role
                await UserManager.AddToRoleAsync(user, GladMMOAuthConstants.ZONESERVER_AUTHORIZATION_ROLE);

                //At this point, the account has the PlayFab id claim so it's ready for use.
                return(Json(new ZoneServerAccountRegistrationResponse(user.Id, zoneUserName, zonePassword)));
            }
            else
            {
                return(BadRequest(identityResult.Errors.Aggregate("", (s, error) => $"{s} {error.Code}:{error.Description}")));
            }
        }
Exemple #24
0
        public IActionResult Create([FromBody] CommentCreateDto data)
        {
            if (string.IsNullOrEmpty(data.Content))
            {
                return(BadRequest());
            }

            var userId = ClaimsReader.GetUserId(Request);
            var post   = context.Posts.FirstOrDefault(a => a.Id == data.PostId);

            if (post == null)
            {
                return(NotFound());
            }

            if (!context.UserGroups.Any(a => a.GroupId == post.GroupId && a.UserId == userId && (a.Relation == GroupRelation.Owner || a.Relation == GroupRelation.User)))
            {
                return(BadRequest());
            }

            var comment = new Comment
            {
                DateAdded = DateTime.UtcNow,
                Content   = data.Content,
                PostId    = data.PostId,
                UserId    = userId
            };

            context.Comments.Add(comment);

            try
            {
                context.SaveChanges();

                var user = context.Users.FirstOrDefault(a => a.Id == userId);

                var result = new CommentDto
                {
                    Id        = comment.Id,
                    Content   = comment.Content,
                    DateAdded = comment.DateAdded,
                    Owner     = user.Login,
                    IsOwner   = true
                };

                return(Ok(result));
            }
            catch (Exception)
            {
                return(BadRequest());
            }
        }
Exemple #25
0
        public async Task <IActionResult> SetWorldAsUploaded(
            [FromRoute(Name = "id")] long worldId,
            [FromServices] IWorldEntryRepository worldEntryRepository,
            [FromServices] IContentResourceExistenceVerifier contentResourceExistenceVerifier)
        {
            //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 worldEntryRepository.ContainsAsync(worldId).ConfigureAwaitFalse())
            {
                //TODO: We should say something more specific
                return(BadRequest());
            }

            WorldEntryModel model = await worldEntryRepository.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 contentResourceExistenceVerifier.VerifyResourceExists(UserContentType.World, 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 worldEntryRepository.SetWorldValidated(model.WorldId)
            .ConfigureAwaitFalseVoid();

            return(Ok());
        }
Exemple #26
0
        public async Task <CharacterDataQueryResponseCode> CreateCharacterAppearanceAsync([FromRoute(Name = "id")] int characterId, [FromBody] RPGCharacterCustomizationData <TCustomizableSlotType, TColorStructureType, TProportionSlotType, TProportionStructureType> data, CancellationToken token = default)
        {
            if (!ModelState.IsValid)
            {
                return(CharacterDataQueryResponseCode.GeneralError);
            }

            int accountId = ClaimsReader.GetAccountId <int>(User);

            try
            {
                if (!await CharacterRepository.ContainsAsync(characterId, token))
                {
                    return(CharacterDataQueryResponseCode.CharacterDoesNotExist);
                }

                //Only allow users who own the character to create its appearance
                if (!await CharacterRepository.AccountOwnsCharacterAsync(accountId, characterId, token))
                {
                    return(CharacterDataQueryResponseCode.NotAuthorized);
                }

                //We scope the appearance persistence in a transaction because we don't want a HALF customized character.
                await using IDbContextTransaction transaction = await AppearanceRepository.CreateTransactionAsync(token);

                if (data.ProportionData.Count > 0)
                {
                    await AppearanceRepository.CreateSlotsAsync(data.ProportionData.Select(p => new DBRPGCharacterProportionSlot <TProportionSlotType, TProportionStructureType>(characterId, p.Key, p.Value)).ToArray(), token);
                }

                if (data.SlotData.Count > 0)
                {
                    await AppearanceRepository.CreateSlotsAsync(ConvertToCustomizableSlotData(characterId, data), token);
                }

                await transaction.CommitAsync(token);

                return(CharacterDataQueryResponseCode.Success);
            }
            catch (Exception e)
            {
                if (Logger.IsEnabled(LogLevel.Error))
                {
                    Logger.LogError($"Failed to create character appearance for Character: {characterId} Account: {accountId}. Reason: {e}");
                }

                return(CharacterDataQueryResponseCode.GeneralError);
            }
        }
        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)));
        }
        /// <inheritdoc />
        public override async Task OnConnectedAsync()
        {
            await base.OnConnectedAsync()
            .ConfigureAwait(false);

            if (Logger.IsEnabled(LogLevel.Information))
            {
                Logger.LogInformation($"Account Connected: {ClaimsReader.GetUserName(Context.User)}:{ClaimsReader.GetUserId(Context.User)}");
            }

            NetworkEntityGuid guid = new NetworkEntityGuidBuilder()
                                     .WithId(int.Parse(Context.UserIdentifier))
                                     .WithType(EntityType.Player)
                                     .Build();

            //Register interest and then lock
            //We need to lock on the entity so only 1 connection for the entity can go through this process at a time.
            await EntityLockService.RegisterEntityInterestAsync(guid)
            .ConfigureAwait(false);

            using (await EntityLockService.AquireEntityLockAsync(guid).ConfigureAwait(false))
            {
                try
                {
                    foreach (var listener in OnConnectionHubListeners)
                    {
                        HubOnConnectionState connectionState = await listener.OnConnected(this).ConfigureAwait(false);

                        //if the listener indicated we need to abort for whatever reason we
                        //should believe it and just abort.
                        if (connectionState == HubOnConnectionState.Abort)
                        {
                            Context.Abort();
                            break;
                        }
                    }
                }
                catch (Exception e)
                {
                    if (Logger.IsEnabled(LogLevel.Error))
                    {
                        Logger.LogInformation($"Account: {ClaimsReader.GetUserName(Context.User)}:{ClaimsReader.GetUserId(Context.User)} failed to properly connect to hub. Error: {e.Message}\n\nStack: {e.StackTrace}");
                    }

                    Context.Abort();
                }
            }
        }
Exemple #29
0
        public IActionResult Details([FromQuery] int id)
        {
            var userId = ClaimsReader.GetUserId(Request);
            var group  = context.Groups.SingleOrDefault(a => a.Id == id);

            var result = new GroupDetailsDto
            {
                Id          = group.Id,
                Name        = group.Name,
                Description = group.Description,
                IsPrivate   = group.IsPrivate,
                IsOwner     = CheckAdminPriviliges(userId, id)
            };

            return(Ok(result));
        }
Exemple #30
0
        public async Task <IActionResult> PostPointsToDevice(string id, [FromBody] TrackingPoint[] points)
        {
            var subject  = ClaimsReader.ReadSubject(this.User);
            var audience = ClaimsReader.ReadAudience(this.User);

            if (audience == JwtAuthConstants.DeviceAudience && id != subject)
            {
                return(Forbid());
            }

            points.ForEach((point) => point.TrackingDeviceId = id);
            var addedPoints = await this.pointService.AddAsync(points);

            await this.geoFenceService.HandlePoints(addedPoints.First().AssetId, addedPoints.ToArray());

            return(Ok());
        }