Beispiel #1
0
        public void OnResultExecuting(ResultExecutingContext context)
        {
            switch (context.Result)
            {
            case StatusCodeResult result:
            {
                var status = (HttpStatusCode)result.StatusCode;

                context.Result = ResultUtilities.Status(status);
                break;
            }

            case ObjectResult result:
            {
                var status = (HttpStatusCode)(result.StatusCode ?? 200);

                var type = result.Value?.GetType();

                // ensure something is returned, instead of an empty response
                if (type == null)
                {
                    context.Result = ResultUtilities.Status(status);
                }

                // wrap string responses
                else if (type == typeof(string))
                {
                    context.Result = ResultUtilities.Status(status, (string)result.Value);
                }

                break;
            }
            }
        }
Beispiel #2
0
        public Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            var reason = ModelSanitizer.Sanitize(context.HttpContext.Request.Query["reason"].ToString());

            if (reason != null && reason.Length >= MinReasonLength)
            {
                return(next());
            }

            context.Result = ResultUtilities.BadRequest("A valid reason must be provided for this action.");

            return(Task.CompletedTask);
        }
Beispiel #3
0
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            var control     = context.HttpContext.RequestServices.GetService <IWriteControl>();
            var environment = context.HttpContext.RequestServices.GetService <IHostEnvironment>();

            try
            {
                await using (await control.EnterAsync())
                    await next();
            }
            catch (WriteControlException e)
            {
                context.Result = ResultUtilities.Status(HttpStatusCode.ServiceUnavailable, e.ToStringWithTrace("Could not complete this request at the moment due to maintenance.", environment.IsProduction()));
            }
        }
Beispiel #4
0
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            var validator = context.HttpContext.RequestServices.GetService <IRecaptchaValidator>();

            var value = ModelSanitizer.Sanitize(context.HttpContext.Request.Query["recaptcha"].ToString());

            if (await validator.TryValidateAsync(value, context.HttpContext.RequestAborted))
            {
                await next();
            }

            else
            {
                context.Result = ResultUtilities.BadRequest("Could not verify reCAPTCHA token.");
            }
        }
Beispiel #5
0
        public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            if (context.Result != null)
            {
                return;
            }

            var cancellationToken = context.HttpContext.RequestAborted;

            var now = DateTime.UtcNow;

            // retrieve user
            var users  = context.HttpContext.RequestServices.GetService <IUserService>();
            var userId = context.HttpContext.Items.TryGetValue(AuthHandler.PayloadItemKey, out var token) ? ((AuthTokenPayload)token).UserId : default;

            var result = await users.GetAsync(userId, cancellationToken);

            if (!(result.Value is DbUser user))
            {
                context.Result = ResultUtilities.Forbidden($"Unknown user {userId}.");
                return;
            }

            // pass user down the pipeline
            context.HttpContext.Items[UserItemKey] = user;

            if (!user.HasPermissions(UserPermissions.Administrator)) // bypass checks for admin
            {
                // restriction check
                var restriction = user.Restrictions?.FirstOrDefault(r => r.EndTime == null || now < r.EndTime);

                if (Unrestricted && restriction != null)
                {
                    context.Result = ResultUtilities.Forbidden($"Cannot perform this action while you are restricted: {restriction.Reason ?? "<unknown reason>"}");
                    return;
                }

                // permission check
                if (!user.HasPermissions(Permissions))
                {
                    context.Result = ResultUtilities.Forbidden($"Insufficient permissions to perform this action. Required: {string.Join(", ", Permissions.ToFlags())}");
                    return;
                }
            }
        }
        public Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            switch (context.HttpContext.Request.Method)
            {
            case "POST":
            case "PUT":
                var query = ModelSanitizer.Sanitize(context.HttpContext.Request.Query["validate"].ToString());

                if (query != null && (!bool.TryParse(query, out var x) || x))     // specified and not "false"
                {
                    // at this point in an action filter, model is already validated, so we will simply short-circuit.
                    // we always with 422 even if there are no validation problems.
                    var problems = Array.Empty <ValidationProblem>();

                    context.Result = ResultUtilities.UnprocessableEntity(problems);

                    return(Task.CompletedTask);
                }

                break;
            }

            return(next());
        }
Beispiel #7
0
        public void ConfigureServices(IServiceCollection services)
        {
            // configuration
            services.AddSingleton(_configuration) // root
            .AddHostedService <ConfigurationReloader>();

            // kestrel
            services.Configure <ServerOptions>(_configuration.GetSection("Server"))
            .Configure <KestrelServerOptions>(o =>
            {
                var server = o.ApplicationServices.GetService <IOptionsMonitor <ServerOptions> >().CurrentValue;

                if (server.HttpPortDev != null)
                {
                    o.ListenLocalhost(server.HttpPortDev.Value);
                }

                if (server.HttpPort != null)
                {
                    o.ListenAnyIP(server.HttpPort.Value);
                }

                if (server.HttpsPort != null && server.CertificatePath != null)
                {
                    o.ListenAnyIP(server.HttpsPort.Value, l => l.UseHttps(server.CertificatePath, server.CertificatePassword));
                }

                o.Limits.MaxRequestBufferSize       = 1024 * 64;           // 16 KiB
                o.Limits.MaxRequestLineSize         = 1024 * 8;            // 8 KiB
                o.Limits.MaxRequestHeadersTotalSize = 1024 * 8;            // 8 KiB
                o.Limits.MaxRequestBodySize         = 1024 * 256;          // 16 KiB
            })
            .AddResponseCompression(o =>
            {
                o.Providers.Add <GzipCompressionProvider>();
                o.Providers.Add <BrotliCompressionProvider>();
            })
            .AddResponseCaching(o =>
            {
                o.UseCaseSensitivePaths = false;

                // this is for static files
                o.SizeLimit       = long.MaxValue;
                o.MaximumBodySize = long.MaxValue;
            });

            // mvc
            services.AddMvcCore(m =>
            {
                m.Filters.Add <PrimitiveResponseWrapperFilter>();
                m.Filters.Add <RequestValidateQueryFilter>();

                m.OutputFormatters.RemoveType <StringOutputFormatter>();

                // model sanitizing binder
                var modelBinder = new ModelSanitizerModelBinderProvider(m.ModelBinderProviders);

                m.ModelBinderProviders.Clear();
                m.ModelBinderProviders.Add(modelBinder);
            })
            .AddNewtonsoftJson(o =>
            {
                o.SerializerSettings.ContractResolver = new DefaultContractResolver
                {
                    NamingStrategy = ModelNamingStrategy
                };

                o.SerializerSettings.Converters.Add(new StringEnumConverter
                {
                    NamingStrategy     = ModelNamingStrategy,
                    AllowIntegerValues = true
                });
            })
            .AddApiExplorer()
            .AddAuthorization()
            .AddFormatterMappings()
            .AddDataAnnotations()
            .AddCors()
            .AddTestControllers()
            .AddControllersAsServices();

            services.Configure <ApiBehaviorOptions>(o =>
            {
                o.SuppressMapClientErrors          = true;
                o.InvalidModelStateResponseFactory = c =>
                {
                    static string fixFieldCasing(string str)
                    => string.Join('.', str.Split('.').Select(s => ModelNamingStrategy.GetPropertyName(s, false)));

                    var problems = c.ModelState
                                   .Where(x => !x.Value.IsContainerNode && x.Value.Errors.Count != 0)
                                   .Select(x =>
                    {
                        var(field, entry) = x;

                        return(new ValidationProblem
                        {
                            Field = ModelNamingStrategy.GetPropertyName(fixFieldCasing(field), false),
                            Messages = entry.Errors.ToArray(e => e.ErrorMessage ?? e.Exception.ToStringWithTrace(null, _environment.IsProduction()))
                        });
                    })
                                   .ToArray();

                    return(ResultUtilities.UnprocessableEntity(problems));
                };