Skip to content

Commit

Permalink
Make better use of IHasPreferredStatusCode (#1140)
Browse files Browse the repository at this point in the history
  • Loading branch information
Shane32 authored Aug 4, 2024
1 parent c48d21a commit 544a5e2
Show file tree
Hide file tree
Showing 9 changed files with 34 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ protected virtual void HandleNodeNotAuthorized(ValidationInfo info)
{
var resource = GenerateResourceDescription(info);
var err = info.Node == null ? new AccessDeniedError(resource) : new AccessDeniedError(resource, info.Context.Document.Source, info.Node);
err.PreferredStatusCode = HttpStatusCode.Unauthorized;
info.Context.ReportError(err);
}

Expand Down
5 changes: 4 additions & 1 deletion src/Transports.AspNetCore/Errors/AccessDeniedError.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ namespace GraphQL.Server.Transports.AspNetCore.Errors;
/// <summary>
/// Represents an error indicating that the user is not allowed access to the specified resource.
/// </summary>
public class AccessDeniedError : ValidationError
public class AccessDeniedError : ValidationError, IHasPreferredStatusCode
{
/// <inheritdoc cref="AccessDeniedError"/>
public AccessDeniedError(string resource)
Expand All @@ -29,4 +29,7 @@ public AccessDeniedError(string resource, GraphQLParser.ROM originalQuery, param
/// Returns the list of role memberships that would allow access to these node(s).
/// </summary>
public List<string>? RolesRequired { get; set; }

/// <inheritdoc/>
public HttpStatusCode PreferredStatusCode { get; set; } = HttpStatusCode.Forbidden;
}
2 changes: 1 addition & 1 deletion src/Transports.AspNetCore/Errors/FileCountExceededError.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ public FileCountExceededError()
}

/// <inheritdoc/>
public HttpStatusCode PreferredStatusCode => HttpStatusCode.RequestEntityTooLarge;
public HttpStatusCode PreferredStatusCode { get; set; } = HttpStatusCode.RequestEntityTooLarge;
}
2 changes: 1 addition & 1 deletion src/Transports.AspNetCore/Errors/FileSizeExceededError.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ public FileSizeExceededError()
}

/// <inheritdoc/>
public HttpStatusCode PreferredStatusCode => HttpStatusCode.RequestEntityTooLarge;
public HttpStatusCode PreferredStatusCode { get; set; } = HttpStatusCode.RequestEntityTooLarge;
}
5 changes: 4 additions & 1 deletion src/Transports.AspNetCore/Errors/InvalidContentTypeError.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ namespace GraphQL.Server.Transports.AspNetCore.Errors;
/// <summary>
/// Represents an error indicating that the content-type is invalid, for example, could not be parsed or is not supported.
/// </summary>
public class InvalidContentTypeError : RequestError
public class InvalidContentTypeError : RequestError, IHasPreferredStatusCode
{
/// <inheritdoc cref="InvalidContentTypeError"/>
public InvalidContentTypeError() : base("Invalid 'Content-Type' header.") { }

/// <inheritdoc cref="InvalidContentTypeError"/>
public InvalidContentTypeError(string message) : base("Invalid 'Content-Type' header: " + message) { }

/// <inheritdoc/>
public HttpStatusCode PreferredStatusCode { get; set; } = HttpStatusCode.UnsupportedMediaType;
}
11 changes: 5 additions & 6 deletions src/Transports.AspNetCore/GraphQLHttpMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ protected virtual async Task InvokeAsync(HttpContext context, RequestDelegate ne
}
catch (ExecutionError ex) // catches FileCountExceededError, FileSizeExceededError, InvalidMapError
{
await WriteErrorResponseAsync(context, ex is IHasPreferredStatusCode sc ? sc.PreferredStatusCode : HttpStatusCode.BadRequest, ex);
await WriteErrorResponseAsync(context, ex);
return null;
}
catch (Exception ex) // catches JSON deserialization exceptions
Expand Down Expand Up @@ -1046,19 +1046,19 @@ await webSocket.CloseAsync(
/// Writes an access denied message to the output with status code <c>401 Unauthorized</c> when the user is not authenticated.
/// </summary>
protected virtual Task HandleNotAuthenticatedAsync(HttpContext context, RequestDelegate next)
=> WriteErrorResponseAsync(context, HttpStatusCode.Unauthorized, new AccessDeniedError("schema"));
=> WriteErrorResponseAsync(context, new AccessDeniedError("schema") { PreferredStatusCode = HttpStatusCode.Unauthorized });

/// <summary>
/// Writes an access denied message to the output with status code <c>403 Forbidden</c> when the user fails the role checks.
/// </summary>
protected virtual Task HandleNotAuthorizedRoleAsync(HttpContext context, RequestDelegate next)
=> WriteErrorResponseAsync(context, HttpStatusCode.Forbidden, new AccessDeniedError("schema") { RolesRequired = _options.AuthorizedRoles });
=> WriteErrorResponseAsync(context, new AccessDeniedError("schema") { RolesRequired = _options.AuthorizedRoles });

/// <summary>
/// Writes an access denied message to the output with status code <c>403 Forbidden</c> when the user fails the policy check.
/// </summary>
protected virtual Task HandleNotAuthorizedPolicyAsync(HttpContext context, RequestDelegate next, AuthorizationResult authorizationResult)
=> WriteErrorResponseAsync(context, HttpStatusCode.Forbidden, new AccessDeniedError("schema") { PolicyRequired = _options.AuthorizedPolicy, PolicyAuthorizationResult = authorizationResult });
=> WriteErrorResponseAsync(context, new AccessDeniedError("schema") { PolicyRequired = _options.AuthorizedPolicy, PolicyAuthorizationResult = authorizationResult });

/// <summary>
/// Writes a '400 JSON body text could not be parsed.' message to the output.
Expand Down Expand Up @@ -1095,15 +1095,14 @@ protected virtual Task HandleWebSocketSubProtocolNotSupportedAsync(HttpContext c
/// Writes a '415 Invalid Content-Type header: could not be parsed.' message to the output.
/// </summary>
protected virtual Task HandleContentTypeCouldNotBeParsedErrorAsync(HttpContext context, RequestDelegate next)
=> WriteErrorResponseAsync(context, HttpStatusCode.UnsupportedMediaType, new InvalidContentTypeError($"value '{context.Request.ContentType}' could not be parsed."));
=> WriteErrorResponseAsync(context, new InvalidContentTypeError($"value '{context.Request.ContentType}' could not be parsed."));

/// <summary>
/// Writes a '415 Invalid Content-Type header: non-supported media type.' message to the output.
/// </summary>
protected virtual Task HandleInvalidContentTypeErrorAsync(HttpContext context, RequestDelegate next)
=> WriteErrorResponseAsync(
context,
HttpStatusCode.UnsupportedMediaType,
_options.ReadFormOnPost
? new InvalidContentTypeError($"non-supported media type '{context.Request.ContentType}'. Must be '{MEDIATYPE_JSON}', '{MEDIATYPE_GRAPHQL}' or a form body.")
: new InvalidContentTypeError($"non-supported media type '{context.Request.ContentType}'. Must be '{MEDIATYPE_JSON}' or '{MEDIATYPE_GRAPHQL}'.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,13 @@ namespace GraphQL.Server.Transports.AspNetCore
}
namespace GraphQL.Server.Transports.AspNetCore.Errors
{
public class AccessDeniedError : GraphQL.Validation.ValidationError
public class AccessDeniedError : GraphQL.Validation.ValidationError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public AccessDeniedError(string resource) { }
public AccessDeniedError(string resource, GraphQLParser.ROM originalQuery, params GraphQLParser.AST.ASTNode[] nodes) { }
public Microsoft.AspNetCore.Authorization.AuthorizationResult? PolicyAuthorizationResult { get; set; }
public string? PolicyRequired { get; set; }
public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
public System.Collections.Generic.List<string>? RolesRequired { get; set; }
}
public class BatchedRequestsNotSupportedError : GraphQL.Execution.RequestError
Expand All @@ -212,12 +213,12 @@ namespace GraphQL.Server.Transports.AspNetCore.Errors
public class FileCountExceededError : GraphQL.Execution.RequestError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public FileCountExceededError() { }
public System.Net.HttpStatusCode PreferredStatusCode { get; }
public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
}
public class FileSizeExceededError : GraphQL.Execution.RequestError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public FileSizeExceededError() { }
public System.Net.HttpStatusCode PreferredStatusCode { get; }
public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
}
public class HttpMethodValidationError : GraphQL.Validation.ValidationError
{
Expand All @@ -227,10 +228,11 @@ namespace GraphQL.Server.Transports.AspNetCore.Errors
{
System.Net.HttpStatusCode PreferredStatusCode { get; }
}
public class InvalidContentTypeError : GraphQL.Execution.RequestError
public class InvalidContentTypeError : GraphQL.Execution.RequestError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public InvalidContentTypeError() { }
public InvalidContentTypeError(string message) { }
public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
}
public class InvalidMapError : GraphQL.Execution.RequestError
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,12 +210,13 @@ namespace GraphQL.Server.Transports.AspNetCore
}
namespace GraphQL.Server.Transports.AspNetCore.Errors
{
public class AccessDeniedError : GraphQL.Validation.ValidationError
public class AccessDeniedError : GraphQL.Validation.ValidationError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public AccessDeniedError(string resource) { }
public AccessDeniedError(string resource, GraphQLParser.ROM originalQuery, params GraphQLParser.AST.ASTNode[] nodes) { }
public Microsoft.AspNetCore.Authorization.AuthorizationResult? PolicyAuthorizationResult { get; set; }
public string? PolicyRequired { get; set; }
public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
public System.Collections.Generic.List<string>? RolesRequired { get; set; }
}
public class BatchedRequestsNotSupportedError : GraphQL.Execution.RequestError
Expand All @@ -230,12 +231,12 @@ namespace GraphQL.Server.Transports.AspNetCore.Errors
public class FileCountExceededError : GraphQL.Execution.RequestError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public FileCountExceededError() { }
public System.Net.HttpStatusCode PreferredStatusCode { get; }
public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
}
public class FileSizeExceededError : GraphQL.Execution.RequestError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public FileSizeExceededError() { }
public System.Net.HttpStatusCode PreferredStatusCode { get; }
public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
}
public class HttpMethodValidationError : GraphQL.Validation.ValidationError
{
Expand All @@ -245,10 +246,11 @@ namespace GraphQL.Server.Transports.AspNetCore.Errors
{
System.Net.HttpStatusCode PreferredStatusCode { get; }
}
public class InvalidContentTypeError : GraphQL.Execution.RequestError
public class InvalidContentTypeError : GraphQL.Execution.RequestError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public InvalidContentTypeError() { }
public InvalidContentTypeError(string message) { }
public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
}
public class InvalidMapError : GraphQL.Execution.RequestError
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,13 @@ namespace GraphQL.Server.Transports.AspNetCore
}
namespace GraphQL.Server.Transports.AspNetCore.Errors
{
public class AccessDeniedError : GraphQL.Validation.ValidationError
public class AccessDeniedError : GraphQL.Validation.ValidationError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public AccessDeniedError(string resource) { }
public AccessDeniedError(string resource, GraphQLParser.ROM originalQuery, params GraphQLParser.AST.ASTNode[] nodes) { }
public Microsoft.AspNetCore.Authorization.AuthorizationResult? PolicyAuthorizationResult { get; set; }
public string? PolicyRequired { get; set; }
public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
public System.Collections.Generic.List<string>? RolesRequired { get; set; }
}
public class BatchedRequestsNotSupportedError : GraphQL.Execution.RequestError
Expand All @@ -212,12 +213,12 @@ namespace GraphQL.Server.Transports.AspNetCore.Errors
public class FileCountExceededError : GraphQL.Execution.RequestError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public FileCountExceededError() { }
public System.Net.HttpStatusCode PreferredStatusCode { get; }
public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
}
public class FileSizeExceededError : GraphQL.Execution.RequestError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public FileSizeExceededError() { }
public System.Net.HttpStatusCode PreferredStatusCode { get; }
public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
}
public class HttpMethodValidationError : GraphQL.Validation.ValidationError
{
Expand All @@ -227,10 +228,11 @@ namespace GraphQL.Server.Transports.AspNetCore.Errors
{
System.Net.HttpStatusCode PreferredStatusCode { get; }
}
public class InvalidContentTypeError : GraphQL.Execution.RequestError
public class InvalidContentTypeError : GraphQL.Execution.RequestError, GraphQL.Server.Transports.AspNetCore.Errors.IHasPreferredStatusCode
{
public InvalidContentTypeError() { }
public InvalidContentTypeError(string message) { }
public System.Net.HttpStatusCode PreferredStatusCode { get; set; }
}
public class InvalidMapError : GraphQL.Execution.RequestError
{
Expand Down

0 comments on commit 544a5e2

Please sign in to comment.