Skip to content

Commit cfc975d

Browse files
more useful shared functions
1 parent e262bfd commit cfc975d

3 files changed

Lines changed: 118 additions & 3 deletions

File tree

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using Microsoft.AspNetCore.Authorization;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Security.Claims;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
10+
namespace Shared.Authorisation
11+
{
12+
public static class AuthorizationExtensions {
13+
public static IServiceCollection AddClientCredentialsHandler(this IServiceCollection services) {
14+
services.AddSingleton<IAuthorizationHandler, ClientCredentialsHandler>();
15+
return services;
16+
}
17+
18+
public static IServiceCollection AddClientCredentialsOnlyPolicy(this IServiceCollection services) {
19+
services.AddAuthorization(options => {
20+
options.AddPolicy("ClientCredentialsOnly", policy =>
21+
policy.Requirements.Add(new ClientCredentialsRequirement()));
22+
});
23+
24+
return services;
25+
}
26+
27+
public static IServiceCollection AddPasswordTokenHandler(this IServiceCollection services)
28+
{
29+
services.AddSingleton<IAuthorizationHandler, PasswordTokenHandler>();
30+
return services;
31+
}
32+
33+
public static IServiceCollection AddPasswordTokenPolicy(this IServiceCollection services)
34+
{
35+
services.AddAuthorization(options =>
36+
{
37+
options.AddPolicy("PasswordToken", policy =>
38+
policy.Requirements.Add(new PasswordTokenRequirement()));
39+
});
40+
41+
return services;
42+
}
43+
44+
45+
private sealed class PasswordTokenRequirement : IAuthorizationRequirement;
46+
47+
private sealed class ClientCredentialsRequirement : IAuthorizationRequirement;
48+
49+
private sealed class PasswordTokenHandler : AuthorizationHandler<PasswordTokenRequirement> {
50+
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
51+
PasswordTokenRequirement requirement) {
52+
// Your logic from IsPasswordToken()
53+
Claim? userIdClaim = context.User.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
54+
55+
if (userIdClaim != null) {
56+
context.Succeed(requirement);
57+
}
58+
59+
return Task.CompletedTask;
60+
}
61+
}
62+
63+
private sealed class ClientCredentialsHandler : AuthorizationHandler<ClientCredentialsRequirement> {
64+
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
65+
ClientCredentialsRequirement requirement) {
66+
// A client credentials token *does not* include a NameIdentifier (user) claim.
67+
bool isUserToken = context.User.HasClaim(c => c.Type == ClaimTypes.NameIdentifier);
68+
69+
if (!isUserToken) {
70+
context.Succeed(requirement); // Valid client credentials token
71+
}
72+
73+
return Task.CompletedTask;
74+
}
75+
}
76+
77+
}
78+
}

Shared/Extensions/EndpointConventionBuilderExtensions.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,28 @@
1-
using Microsoft.AspNetCore.Builder;
1+
using System;
2+
using Microsoft.AspNetCore.Builder;
23
using Microsoft.AspNetCore.Http;
34

45
namespace Shared.Extensions;
56

67
public static class EndpointConventionBuilderExtensions
78
{
8-
public static RouteHandlerBuilder WithStandardProduces<TSuccess, TError>(this RouteHandlerBuilder builder)
9+
public static RouteHandlerBuilder WithStandardProduces<TSuccess, TError>(this RouteHandlerBuilder builder, Int32 successCode = StatusCodes.Status200OK)
910
{
10-
builder.Produces<TSuccess>(StatusCodes.Status200OK)
11+
builder.Produces<TSuccess>(successCode)
12+
.Produces<TError>(StatusCodes.Status400BadRequest)
13+
.Produces<TError>(StatusCodes.Status404NotFound)
14+
.Produces(StatusCodes.Status401Unauthorized)
15+
.Produces<TError>(StatusCodes.Status409Conflict)
16+
.Produces<TError>(StatusCodes.Status500InternalServerError)
17+
.Produces(StatusCodes.Status403Forbidden)
18+
.Produces<TError>(StatusCodes.Status501NotImplemented);
19+
20+
return builder;
21+
}
22+
23+
public static RouteHandlerBuilder WithStandardProduces<TError>(this RouteHandlerBuilder builder, Int32 successCode = StatusCodes.Status200OK)
24+
{
25+
builder.Produces(successCode)
1126
.Produces<TError>(StatusCodes.Status400BadRequest)
1227
.Produces<TError>(StatusCodes.Status404NotFound)
1328
.Produces(StatusCodes.Status401Unauthorized)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Text.Json;
6+
using System.Threading.Tasks;
7+
8+
namespace Shared.General
9+
{
10+
public class SnakeCaseNamingPolicy : JsonNamingPolicy
11+
{
12+
public override string ConvertName(string name)
13+
{
14+
// simple PascalCase to snake_case
15+
return string.Concat(
16+
name.Select((c, i) =>
17+
i > 0 && char.IsUpper(c) ? "_" + char.ToLower(c) : char.ToLower(c).ToString()
18+
)
19+
);
20+
}
21+
}
22+
}

0 commit comments

Comments
 (0)