예제 #1
0
        public async Task InvokeAsync(HttpContext context)
        {
            if (!context.Request.Path.StartsWithSegments("/event/live", StringComparison.OrdinalIgnoreCase))
            {
                await this.next(context).ConfigureAwait(false);

                return;
            }

            if (!context.WebSockets.IsWebSocketRequest)
            {
                context.Response.StatusCode = 400;
                return;
            }

            if (!context.Request.Query.ContainsKey("tk"))
            {
                context.Response.StatusCode = 401;
                context.Response.Body       = Stream.Null;
                return;
            }

            var values = context.Request.Query["tk"];

            if (values.Count == 0)
            {
                context.Response.StatusCode = 401;
                context.Response.Body       = Stream.Null;
                return;
            }

            var authToken = values[0];
            var session   = await employeeSessions.FetchEmployeeSessionAndEmployeeFromToken(authToken)
                            .Ensure(s => s.HasValue, "Employee was found")
                            .OnSuccess(s => s.Value)
                            .ConfigureAwait(false);

            if (session.IsFailure)
            {
                context.Response.StatusCode = 401;
                context.Response.Body       = Stream.Null;
            }

            WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false);

            using var consumer   = new DelegatingOrderEventConsumer(settings, constants, loggerFactory, session.Value.VenueId);
            consumer.OnNewEvent += async(_, data) =>
            {
                logger.LogDebug("Received order update message for web consumers. Transmitting to connected sockets.");
                await Broadcast(webSocket, Encoding.UTF8.GetBytes(data)).ConfigureAwait(false);
            };
            consumer.StartConsuming();

            await Receive(webSocket, async (_, buffer) =>
            {
                WebsocketStatusMessage message = null;
                try
                {
                    var rawMessage = Encoding.UTF8.GetString(buffer);
                    message        = JsonConvert.DeserializeObject <WebsocketStatusMessage>(rawMessage);
                }
                catch (Exception)
                {
                    await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Bad Request", CancellationToken.None).ConfigureAwait(false);
                    return;
                }

                if (message == null)
                {
                    await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Bad Request", CancellationToken.None).ConfigureAwait(false);
                    return;
                }

                var currentSession = await employeeSessions.FetchEmployeeSessionAndEmployeeFromToken(message.AuthToken)
                                     .Ensure(s => s.HasValue, "Employee was found")
                                     .OnSuccess(s => s.Value)
                                     .ConfigureAwait(false);

                if (currentSession.IsFailure)
                {
                    await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Unauthorised", CancellationToken.None).ConfigureAwait(false);
                }

                if (currentSession.Value.VenueId != session.Value.VenueId)
                {
                    await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Unauthorised", CancellationToken.None).ConfigureAwait(false);
                }
            }).ConfigureAwait(false);

            await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false);
        }
예제 #2
0
        private async Task<AuthorisationResult> EnforceRBACRequirements(AuthContext authContext, Microsoft.AspNetCore.Routing.RouteData routeData)
        {
            var userType = authContext.UserType;

            // Grab the route name via the [ActionName(...)] annotation on a controller method.
            // If you don't annotate your methods, or have not configured this route as authenticated, we won't apply any auth restrictions.
            var routeName = (string)routeData.Values["action"];

            if (routeName == null || !settings.Meta.AuthRequirements.ContainsKey(routeName))
            {
                return AuthorisationResult.InjectAndContinue;
            }

            var requirements = settings.Meta.AuthRequirements[routeName];

            Maybe<Employee> employee = null;
            Maybe<EmployeeRole> employeeRole = null;
            Maybe<User> user = null;

            if (userType != settings.Meta.AuthRequirements[routeName].UserType && UserType.Any != settings.Meta.AuthRequirements[routeName].UserType)
            {
                return AuthorisationResult.AbortUnauthorised;
            }

            if (authContext.AuthToken.HasNoValue)
            {
                return AuthorisationResult.AbortUnauthorised;
            }

            var authToken = authContext.AuthToken.Value;

            // Depending on the session type, attempt to fetch the equivalent model (User or Employee).
            if (userType == UserType.Employee)
            {
                employee = (await employeeSessions.FetchEmployeeSessionAndEmployeeFromToken(authToken).ConfigureAwait(false))
                  .OnBoth(x => x.IsSuccess
                    ? x.Value
                    : Maybe<Employee>.None
                  );

                if (employee.HasNoValue)
                {
                    return AuthorisationResult.AbortUnauthorised;
                }

                // For employees, their permissions are scoped to an employee role. First we fetch the role.
                employeeRole = (await employeeRoles.FetchEmployeeRole(employee.Value.RoleId).ConfigureAwait(false))
                  .OnBoth(x => x.IsSuccess
                    ? x.Value
                    : Maybe<EmployeeRole>.None
                  );

                // If the employee role wasn't found but the route didn't require any permissions, we let them through.
                if (employeeRole.HasNoValue && RouteRequiresAtLeastOnePermission(requirements))
                {
                    return AuthorisationResult.AbortUnauthorised;
                }

                // Verify each of the role permissions in turn, if one is missing and the route requires it, deny access.
                if (!RoleHasCorrectPermissions(employeeRole.Value, requirements))
                {
                    return AuthorisationResult.AbortUnauthorised;
                }
            }
            else if (userType == UserType.User)
            {
                user = (await userSessions.FetchUserSessionAndUserFromToken(authToken, (int)authContext.AuthType).ConfigureAwait(false))
                  .OnBoth(x => x.IsSuccess
                    ? x.Value
                    : Maybe<User>.None
                  );

                if (user.HasNoValue)
                {
                    return AuthorisationResult.AbortUnauthorised;
                }
            }

            // After verifying the user's permissions, update the context with relevant user data and continue.
            authContext.Employee = employee;
            authContext.EmployeeRole = employeeRole;
            authContext.User = user;

            return AuthorisationResult.Continue;
        }