public async Task <IActionResult> InitializeModelAsync(Puzzle puzzle, Team team, SortOrder?sort)
        {
            if (puzzle == null && team == null)
            {
                return(NotFound());
            }

            IQueryable <PuzzleStatePerTeam> statesQ = PuzzleStateHelper.GetFullReadOnlyQuery(
                _context,
                Event,
                puzzle,
                team,
                EventRole == EventRole.admin ? null : LoggedInUser);

            Sort = sort;

            switch (sort ?? DefaultSort)
            {
            case SortOrder.PuzzleAscending:
                statesQ = statesQ.OrderBy(state => state.Puzzle.Name);
                break;

            case SortOrder.PuzzleDescending:
                statesQ = statesQ.OrderByDescending(state => state.Puzzle.Name);
                break;

            case SortOrder.TeamAscending:
                statesQ = statesQ.OrderBy(state => state.Team.Name);
                break;

            case SortOrder.TeamDescending:
                statesQ = statesQ.OrderByDescending(state => state.Team.Name);
                break;

            case SortOrder.UnlockAscending:
                statesQ = statesQ.OrderBy(state => state.UnlockedTime ?? DateTime.MaxValue);
                break;

            case SortOrder.UnlockDescending:
                statesQ = statesQ.OrderByDescending(state => state.UnlockedTime ?? DateTime.MaxValue);
                break;

            case SortOrder.SolveAscending:
                statesQ = statesQ.OrderBy(state => state.SolvedTime ?? DateTime.MaxValue);
                break;

            case SortOrder.SolveDescending:
                statesQ = statesQ.OrderByDescending(state => state.SolvedTime ?? DateTime.MaxValue);
                break;

            default:
                throw new ArgumentException($"unknown sort: {sort}");
            }

            PuzzleStatePerTeam = await statesQ.Include(pspt => pspt.Team).Include(pspt => pspt.Puzzle).ToListAsync();

            return(Page());
        }
Пример #2
0
        private async Task SetupContext(int puzzleId)
        {
            Team = await UserEventHelper.GetTeamForPlayer(_context, Event, LoggedInUser);

            Puzzle = await _context.Puzzles.Where(
                (p) => p.ID == puzzleId).FirstOrDefaultAsync();

            PuzzleState = await(PuzzleStateHelper
                                .GetFullReadOnlyQuery(
                                    _context,
                                    Event,
                                    Puzzle,
                                    Team))
                          .FirstAsync();

            SubmissionViews = await(from submission in _context.Submissions
                                    join user in _context.PuzzleUsers on submission.Submitter equals user
                                    join r in _context.Responses on submission.Response equals r into responses
                                    from response in responses.DefaultIfEmpty()
                                    where submission.Team == Team &&
                                    submission.Puzzle == Puzzle
                                    orderby submission.TimeSubmitted
                                    select new SubmissionView()
            {
                Submission      = submission,
                Response        = response,
                SubmitterName   = user.Name,
                FreeformReponse = submission.FreeformResponse,
                IsFreeform      = Puzzle.IsFreeform
            }).ToListAsync();

            Submissions = new List <Submission>(SubmissionViews.Count);
            foreach (SubmissionView submissionView in SubmissionViews)
            {
                Submissions.Add(submissionView.Submission);
            }

            PuzzlesCausingGlobalLockout = await PuzzleStateHelper.PuzzlesCausingGlobalLockout(_context, Event, Team).ToListAsync();

            if (PuzzleState.SolvedTime != null)
            {
                if (!Puzzle.IsFreeform && Submissions?.Count > 0)
                {
                    AnswerToken = Submissions.Last().SubmissionText;
                }
                else
                {
                    AnswerToken = "(marked as solved by admin or author)";
                }
            }
        }
Пример #3
0
        public async Task <IActionResult> OnGet(int puzzleId)
        {
            PrereqPuzzleId = puzzleId;

            Puzzle puzzle = await(from puzz in _context.Puzzles
                                  where puzz.ID == puzzleId
                                  select puzz).FirstOrDefaultAsync();

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

            // Restrict this page to whistle stop non-puzzles
            if (puzzle.MinutesOfEventLockout == 0 || puzzle.IsPuzzle)
            {
                return(NotFound());
            }

            Team team = await UserEventHelper.GetTeamForPlayer(_context, Event, LoggedInUser);

            var puzzleStateQuery     = PuzzleStateHelper.GetFullReadOnlyQuery(_context, Event, null, team);
            PuzzleStatePerTeam state = await(from pspt in puzzleStateQuery
                                             where pspt.PuzzleID == puzzle.ID
                                             select pspt).FirstOrDefaultAsync();

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

            // Only move forward if the puzzle is open and unsolved
            if (state.UnlockedTime == null || state.SolvedTime != null)
            {
                return(NotFound());
            }

            CurrentPuzzle = puzzle;

            // Treat all locked puzzles where this is a prerequisite as puzzles that can be unlocked
            PuzzleOptions = await(from pspt in puzzleStateQuery
                                  join prereq in _context.Prerequisites on pspt.PuzzleID equals prereq.PuzzleID
                                  where prereq.PrerequisiteID == puzzle.ID &&
                                  pspt.SolvedTime == null && pspt.UnlockedTime == null
                                  select prereq.Puzzle).ToListAsync();

            return(Page());
        }
        public async Task InitializeModelAsync(Puzzle puzzle, Team team, SortOrder?sort)
        {
            IQueryable <PuzzleStatePerTeam> statesQ = PuzzleStateHelper.GetFullReadOnlyQuery(this.Context, this.Event, puzzle, team);

            this.Sort = sort;

            switch (sort ?? this.DefaultSort)
            {
            case SortOrder.PuzzleAscending:
                statesQ = statesQ.OrderBy(state => state.Puzzle.Name);
                break;

            case SortOrder.PuzzleDescending:
                statesQ = statesQ.OrderByDescending(state => state.Puzzle.Name);
                break;

            case SortOrder.TeamAscending:
                statesQ = statesQ.OrderBy(state => state.Team.Name);
                break;

            case SortOrder.TeamDescending:
                statesQ = statesQ.OrderByDescending(state => state.Team.Name);
                break;

            case SortOrder.UnlockAscending:
                statesQ = statesQ.OrderBy(state => state.UnlockedTime ?? DateTime.MaxValue);
                break;

            case SortOrder.UnlockDescending:
                statesQ = statesQ.OrderByDescending(state => state.UnlockedTime ?? DateTime.MaxValue);
                break;

            case SortOrder.SolveAscending:
                statesQ = statesQ.OrderBy(state => state.SolvedTime ?? DateTime.MaxValue);
                break;

            case SortOrder.SolveDescending:
                statesQ = statesQ.OrderByDescending(state => state.SolvedTime ?? DateTime.MaxValue);
                break;

            default:
                throw new ArgumentException($"unknown sort: {sort}");
            }

            PuzzleStatePerTeam = await statesQ.ToListAsync();
        }
Пример #5
0
        public async Task <IActionResult> OnGet(int puzzleId)
        {
            Puzzle puzzle = await(from puzz in _context.Puzzles
                                  where puzz.ID == puzzleId
                                  select puzz).FirstOrDefaultAsync();

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

            // Restrict to the Backstage metapuzzle
            if (puzzle.Group != "Backstage")
            {
                return(NotFound());
            }
            if (puzzle.SolveValue != 50)
            {
                return(NotFound());
            }

            Team team = await UserEventHelper.GetTeamForPlayer(_context, Event, LoggedInUser);

            var puzzleQuery            = PuzzleStateHelper.GetFullReadOnlyQuery(_context, Event, null, team, null);
            var solvedBackstagePuzzles = await(from PuzzleStatePerTeam pspt in puzzleQuery
                                               where pspt.SolvedTime != null && pspt.UnlockedTime != null && pspt.Puzzle.IsPuzzle && pspt.Puzzle.Group == "Backstage"
                                               select new BackstageSolveStatus()
            {
                PuzzleName = pspt.Puzzle.Name, SolveTime = (int)(pspt.SolvedTime.Value - pspt.UnlockedTime.Value).TotalSeconds
            }).ToListAsync();

            string json        = JsonConvert.SerializeObject(solvedBackstagePuzzles);
            string escapedJson = Uri.EscapeDataString(json);

            byte[]   macKey = Encoding.ASCII.GetBytes(puzzle.Description);
            HMACSHA1 hasher = new HMACSHA1(macKey);

            byte[] jsonBytes = Encoding.ASCII.GetBytes(escapedJson);
            byte[] mac       = hasher.ComputeHash(jsonBytes);
            string macString = Convert.ToBase64String(mac).Replace('/', '_').Replace('+', '.');

            return(Redirect($"https://ph20backstage.azurewebsites.net/openingnight/{macString}/{escapedJson}"));
        }
        public async Task PlayerCanSeePuzzleCheck(AuthorizationHandlerContext authContext, IAuthorizationRequirement requirement)
        {
            PuzzleUser puzzleUser = await PuzzleUser.GetPuzzleUserForCurrentUser(dbContext, authContext.User, userManager);

            Puzzle puzzle = await GetPuzzleFromRoute();

            Event thisEvent = await GetEventFromRoute();

            if (thisEvent != null && puzzle != null)
            {
                Team team = await UserEventHelper.GetTeamForPlayer(dbContext, thisEvent, puzzleUser);

                if (team != null)
                {
                    IQueryable <PuzzleStatePerTeam> statesQ = PuzzleStateHelper.GetFullReadOnlyQuery(dbContext, thisEvent, puzzle, team);

                    if (statesQ.FirstOrDefault().UnlockedTime != null || thisEvent.AreAnswersAvailableNow)
                    {
                        authContext.Succeed(requirement);
                    }
                }
            }
        }
Пример #7
0
        private async Task SetupContext(int puzzleId)
        {
            Team = await UserEventHelper.GetTeamForPlayer(_context, Event, LoggedInUser);

            Puzzle = await _context.Puzzles.Where(
                (p) => p.ID == puzzleId).FirstOrDefaultAsync();

            PuzzleState = await(PuzzleStateHelper
                                .GetFullReadOnlyQuery(
                                    _context,
                                    Event,
                                    Puzzle,
                                    Team))
                          .FirstOrDefaultAsync();

            Submissions = await _context.Submissions.Where(
                (s) => s.Team == Team &&
                s.Puzzle == Puzzle)
                          .OrderBy(submission => submission.TimeSubmitted)
                          .ToListAsync();

            PuzzlesCausingGlobalLockout = await PuzzleStateHelper.PuzzlesCausingGlobalLockout(_context, Event, Team).ToListAsync();
        }
Пример #8
0
        public async Task <IActionResult> OnPostUnlock(int puzzleId, int unlockId)
        {
            Puzzle puzzle = await(from puzz in _context.Puzzles
                                  where puzz.ID == puzzleId
                                  select puzz).FirstOrDefaultAsync();

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

            // Restrict this page to whistle stop non-puzzles
            if (puzzle.MinutesOfEventLockout == 0 || puzzle.IsPuzzle)
            {
                return(NotFound());
            }

            Team team = await UserEventHelper.GetTeamForPlayer(_context, Event, LoggedInUser);

            using (var transaction = _context.Database.BeginTransaction())
            {
                var puzzleStateQuery           = PuzzleStateHelper.GetFullReadOnlyQuery(_context, Event, null, team);
                PuzzleStatePerTeam prereqState = await(from pspt in puzzleStateQuery
                                                       where pspt.PuzzleID == puzzle.ID
                                                       select pspt).FirstOrDefaultAsync();
                if (prereqState == null)
                {
                    return(NotFound());
                }

                // Only move forward if the prereq is open and unsolved
                if (prereqState.UnlockedTime == null || prereqState.SolvedTime != null)
                {
                    return(NotFound("Your team has already chosen and can't choose again"));
                }

                PuzzleStatePerTeam unlockState = await(from pspt in puzzleStateQuery
                                                       where pspt.PuzzleID == unlockId
                                                       select pspt).FirstOrDefaultAsync();
                if (unlockState == null)
                {
                    return(NotFound());
                }

                // The chosen puzzle must be locked (and unsolved)
                if (unlockState.UnlockedTime != null || unlockState.SolvedTime != null)
                {
                    return(NotFound("You've already chosen this puzzle"));
                }

                // Ensure the puzzle is actually one of the unlock options
                Prerequisites prereq = await(from pre in _context.Prerequisites
                                             where pre.PrerequisiteID == puzzleId && pre.PuzzleID == unlockId
                                             select pre).FirstOrDefaultAsync();
                if (prereq == null)
                {
                    return(NotFound());
                }

                await PuzzleStateHelper.SetSolveStateAsync(_context, Event, puzzle, team, DateTime.UtcNow);

                await PuzzleStateHelper.SetUnlockStateAsync(_context, Event, unlockState.Puzzle, team, DateTime.UtcNow);

                transaction.Commit();
            }

            Puzzle puzzleToUnlock = await(from p in _context.Puzzles
                                          where p.ID == unlockId
                                          select p).FirstOrDefaultAsync();

            string puzzleUrl;

            if (puzzleToUnlock.CustomURL != null)
            {
                puzzleUrl = PuzzleHelper.GetFormattedUrl(puzzleToUnlock, Event.ID);
            }
            else
            {
                puzzleUrl = puzzleToUnlock.PuzzleFile.UrlString;
            }

            return(Redirect(puzzleUrl));
        }
Пример #9
0
        /// <summary>
        /// Evaulates player submissions then either saves them to the database or returns an error to the caller
        /// </summary>
        public static async Task <SubmissionResponse> EvaluateSubmission(PuzzleServerContext context, PuzzleUser loggedInUser, Event thisEvent, int puzzleId, string submissionText, bool allowFreeformSharing)
        {
            //Query data needed to process submission
            Team team = await UserEventHelper.GetTeamForPlayer(context, thisEvent, loggedInUser);

            Puzzle puzzle = await context.Puzzles.Where(
                (p) => p.ID == puzzleId).FirstOrDefaultAsync();

            PuzzleStatePerTeam puzzleState = await(PuzzleStateHelper
                                                   .GetFullReadOnlyQuery(
                                                       context,
                                                       thisEvent,
                                                       puzzle,
                                                       team))
                                             .FirstAsync();

            List <Puzzle> puzzlesCausingGlobalLockout = await PuzzleStateHelper.PuzzlesCausingGlobalLockout(context, thisEvent, team).ToListAsync();

            // Return early for cases when there's obviously nothing we can do with the submission
            // The submission text is empty
            if (String.IsNullOrWhiteSpace(submissionText))
            {
                return(new SubmissionResponse()
                {
                    ResponseCode = SubmissionResponseCode.EmptySubmission
                });
            }

            // The puzzle is locked
            if (puzzle == null || puzzleState.UnlockedTime == null)
            {
                return(new SubmissionResponse()
                {
                    ResponseCode = SubmissionResponseCode.PuzzleLocked
                });
            }

            // The user or team isn't known
            if (loggedInUser == null || team == null)
            {
                return(new SubmissionResponse()
                {
                    ResponseCode = SubmissionResponseCode.Unauthorized
                });
            }

            // The event hasn't started yet
            if (DateTime.UtcNow < thisEvent.EventBegin)
            {
                return(new SubmissionResponse()
                {
                    ResponseCode = SubmissionResponseCode.Unauthorized
                });
            }

            // The team is locked out
            if (puzzleState.IsTeamLockedOut || puzzleState.IsEmailOnlyMode)
            {
                return(new SubmissionResponse()
                {
                    ResponseCode = SubmissionResponseCode.TeamLockedOut
                });
            }

            // The puzzle has already been solved
            if (puzzleState.SolvedTime != null)
            {
                return(new SubmissionResponse()
                {
                    ResponseCode = SubmissionResponseCode.AlreadySolved
                });
            }

            // The team is under a global lockout
            if (puzzlesCausingGlobalLockout.Count != 0 && !puzzlesCausingGlobalLockout.Contains(puzzle))
            {
                return(new SubmissionResponse()
                {
                    ResponseCode = SubmissionResponseCode.TeamLockedOut
                });
            }

            List <SubmissionView> submissionViews = await(from sub in context.Submissions
                                                          join user in context.PuzzleUsers on sub.Submitter equals user
                                                          join r in context.Responses on sub.Response equals r into responses
                                                          from response in responses.DefaultIfEmpty()
                                                          where sub.Team == team &&
                                                          sub.Puzzle == puzzle
                                                          orderby sub.TimeSubmitted
                                                          select new SubmissionView()
            {
                Submission      = sub,
                Response        = response,
                SubmitterName   = user.Name,
                FreeformReponse = sub.FreeformResponse,
                IsFreeform      = puzzle.IsFreeform
            }).ToListAsync();

            List <Submission> submissions = new List <Submission>(submissionViews.Count);

            foreach (SubmissionView submissionView in submissionViews)
            {
                submissions.Add(submissionView.Submission);
            }

            // The submission is a duplicate
            bool duplicateSubmission = (from sub in submissions
                                        where sub.SubmissionText == Response.FormatSubmission(submissionText)
                                        select sub).Any();

            if (duplicateSubmission)
            {
                return(new SubmissionResponse()
                {
                    ResponseCode = SubmissionResponseCode.DuplicateSubmission
                });
            }

            // Create submission and add it to list
            Submission submission = new Submission
            {
                TimeSubmitted        = DateTime.UtcNow,
                Puzzle               = puzzleState.Puzzle,
                Team                 = puzzleState.Team,
                Submitter            = loggedInUser,
                AllowFreeformSharing = allowFreeformSharing
            };

            string submissionTextToCheck = Response.FormatSubmission(submissionText);

            if (puzzle.IsFreeform)
            {
                submission.UnformattedSubmissionText = submissionText;
            }
            else
            {
                submission.SubmissionText = submissionText;
            }

            submission.Response = await context.Responses.Where(
                r => r.Puzzle.ID == puzzleId &&
                submissionTextToCheck == r.SubmittedText)
                                  .FirstOrDefaultAsync();

            submissions.Add(submission);

            // Update puzzle state if submission was correct
            if (submission.Response != null && submission.Response.IsSolution)
            {
                await PuzzleStateHelper.SetSolveStateAsync(context,
                                                           thisEvent,
                                                           submission.Puzzle,
                                                           submission.Team,
                                                           submission.TimeSubmitted);
            }
            else if (!puzzle.IsFreeform && submission.Response == null && thisEvent.IsAnswerSubmissionActive)
            {
                // We also determine if the puzzle should be set to email-only mode.
                if (IsPuzzleSubmissionLimitReached(
                        thisEvent,
                        submissions,
                        puzzleState))
                {
                    await PuzzleStateHelper.SetEmailOnlyModeAsync(context,
                                                                  thisEvent,
                                                                  submission.Puzzle,
                                                                  submission.Team,
                                                                  true);

                    var authors = await context.PuzzleAuthors.Where((pa) => pa.Puzzle == submission.Puzzle).Select((pa) => pa.Author.Email).ToListAsync();

                    MailHelper.Singleton.SendPlaintextBcc(authors,
                                                          $"{thisEvent.Name}: Team {submission.Team.Name} is in email mode for {submission.Puzzle.Name}",
                                                          "");
                }
                else
                {
                    // If the submission was incorrect and not a partial solution,
                    // we will do the lockout computations now.
                    DateTime?lockoutExpiryTime = ComputeLockoutExpiryTime(
                        thisEvent,
                        submissions,
                        puzzleState);

                    if (lockoutExpiryTime != null)
                    {
                        await PuzzleStateHelper.SetLockoutExpiryTimeAsync(context,
                                                                          thisEvent,
                                                                          submission.Puzzle,
                                                                          submission.Team,
                                                                          lockoutExpiryTime);
                    }
                }
            }

            context.Submissions.Add(submission);
            await context.SaveChangesAsync();

            // Send back responses for cases where the database has been updated
            // Correct response
            if (submission.Response != null && submission.Response.IsSolution)
            {
                return(new SubmissionResponse()
                {
                    ResponseCode = SubmissionResponseCode.Correct, CompleteResponse = submission.Response
                });
            }

            // Freeform response
            if (puzzle.IsFreeform)
            {
                return(new SubmissionResponse()
                {
                    ResponseCode = SubmissionResponseCode.Freeform, FreeformResponse = submission.FreeformResponse
                });
            }

            // Partial response
            if (submission.Response != null && !submission.Response.IsSolution)
            {
                return(new SubmissionResponse()
                {
                    ResponseCode = SubmissionResponseCode.Partial, CompleteResponse = submission.Response
                });
            }

            // Default to incorrect
            return(new SubmissionResponse()
            {
                ResponseCode = SubmissionResponseCode.Incorrect
            });
        }
Пример #10
0
        /// <summary>
        /// Returns whether the user is authorized to view the file
        /// </summary>
        /// <param name="eventId">The current event</param>
        /// <param name="puzzle">The puzzle the file belongs to</param>
        /// <param name="content">The file</param>
        private async Task <bool> IsAuthorized(int eventId, Puzzle puzzle, ContentFile content)
        {
            Event currentEvent = await(from ev in context.Events
                                       where ev.ID == eventId
                                       select ev).SingleAsync();
            PuzzleUser user = await PuzzleUser.GetPuzzleUserForCurrentUser(context, User, userManager);

            // Admins can see all files
            if (await user.IsAdminForEvent(context, currentEvent))
            {
                return(true);
            }

            // Authors can see all files attached to their puzzles
            if (await UserEventHelper.IsAuthorOfPuzzle(context, puzzle, user))
            {
                return(true);
            }

            Team team = await UserEventHelper.GetTeamForPlayer(context, currentEvent, user);

            if (team == null)
            {
                return(false);
            }

            // Once answers are available, so are all other files
            if (currentEvent.AreAnswersAvailableNow)
            {
                return(true);
            }

            PuzzleStatePerTeam puzzleState = await PuzzleStateHelper.GetFullReadOnlyQuery(context, currentEvent, puzzle, team).SingleAsync();

            switch (content.FileType)
            {
            case ContentFileType.Answer:
                // The positive case is already handled above by checking AreAnswersAvailableNow
                return(false);

            case ContentFileType.Puzzle:
            case ContentFileType.PuzzleMaterial:
                if (puzzleState.UnlockedTime != null)
                {
                    return(true);
                }
                else
                {
                    return(false);
                }

            case ContentFileType.SolveToken:
                if (puzzleState.SolvedTime != null)
                {
                    return(true);
                }
                else
                {
                    return(false);
                }

            default:
                throw new NotImplementedException();
            }
        }