♻️ Still don't know what I am doing
This commit is contained in:
@ -16,10 +16,9 @@ public class DysonTokenAuthHandler(
|
||||
IOptionsMonitor<DysonTokenAuthOptions> options,
|
||||
ILoggerFactory logger,
|
||||
UrlEncoder encoder,
|
||||
ISystemClock clock,
|
||||
AuthService.AuthServiceClient auth
|
||||
)
|
||||
: AuthenticationHandler<DysonTokenAuthOptions>(options, logger, encoder, clock)
|
||||
: AuthenticationHandler<DysonTokenAuthOptions>(options, logger, encoder)
|
||||
{
|
||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
|
72
DysonNetwork.Shared/Auth/PermissionMiddleware.cs
Normal file
72
DysonNetwork.Shared/Auth/PermissionMiddleware.cs
Normal file
@ -0,0 +1,72 @@
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using Grpc.Core;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace DysonNetwork.Shared.Auth
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||
public class RequiredPermissionAttribute(string area, string key) : Attribute
|
||||
{
|
||||
public string Area { get; set; } = area;
|
||||
public string Key { get; } = key;
|
||||
}
|
||||
|
||||
public class PermissionMiddleware(RequestDelegate next)
|
||||
{
|
||||
public async Task InvokeAsync(HttpContext httpContext, PermissionService.PermissionServiceClient permissionService, ILogger<PermissionMiddleware> logger)
|
||||
{
|
||||
var endpoint = httpContext.GetEndpoint();
|
||||
|
||||
var attr = endpoint?.Metadata
|
||||
.OfType<RequiredPermissionAttribute>()
|
||||
.FirstOrDefault();
|
||||
|
||||
if (attr != null)
|
||||
{
|
||||
if (httpContext.Items["CurrentUser"] is not Account currentUser)
|
||||
{
|
||||
httpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||
await httpContext.Response.WriteAsync("Unauthorized");
|
||||
return;
|
||||
}
|
||||
|
||||
// Assuming Account proto has a bool field 'is_superuser' which is generated as 'IsSuperuser'
|
||||
if (currentUser.IsSuperuser)
|
||||
{
|
||||
// Bypass the permission check for performance
|
||||
await next(httpContext);
|
||||
return;
|
||||
}
|
||||
|
||||
var actor = $"user:{currentUser.Id}";
|
||||
|
||||
try
|
||||
{
|
||||
var permResp = await permissionService.HasPermissionAsync(new HasPermissionRequest
|
||||
{
|
||||
Actor = actor,
|
||||
Area = attr.Area,
|
||||
Key = attr.Key
|
||||
});
|
||||
|
||||
if (!permResp.HasPermission)
|
||||
{
|
||||
httpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||
await httpContext.Response.WriteAsync($"Permission {attr.Area}/{attr.Key} was required.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (RpcException ex)
|
||||
{
|
||||
logger.LogError(ex, "gRPC call to PermissionService failed while checking permission {Area}/{Key} for actor {Actor}", attr.Area, attr.Key, actor);
|
||||
httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
|
||||
await httpContext.Response.WriteAsync("Error checking permissions.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await next(httpContext);
|
||||
}
|
||||
}
|
||||
}
|
@ -16,9 +16,9 @@ public static class DysonAuthStartup
|
||||
{
|
||||
var etcdClient = sp.GetRequiredService<IEtcdClient>();
|
||||
var config = sp.GetRequiredService<IConfiguration>();
|
||||
var clientCertPath = config["ClientCert:Path"];
|
||||
var clientKeyPath = config["ClientKey:Path"];
|
||||
var clientCertPassword = config["ClientCert:Password"];
|
||||
var clientCertPath = config["Service:ClientCert"];
|
||||
var clientKeyPath = config["Service:ClientKey"];
|
||||
var clientCertPassword = config["Service:CertPassword"];
|
||||
|
||||
return GrpcClientHelper
|
||||
.CreateAuthServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword)
|
||||
|
@ -19,8 +19,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="MaxMind.GeoIP2" Version="5.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.7" />
|
||||
<PackageReference Include="NetTopologySuite" Version="2.6.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
|
@ -33,17 +33,6 @@ public static class GrpcClientHelper
|
||||
return response.Kvs[0].Value.ToStringUtf8();
|
||||
}
|
||||
|
||||
public static AccountService.AccountServiceClient CreateAccountServiceClient(
|
||||
string url,
|
||||
string clientCertPath,
|
||||
string clientKeyPath,
|
||||
string? clientCertPassword = null
|
||||
)
|
||||
{
|
||||
return new AccountService.AccountServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath,
|
||||
clientCertPassword));
|
||||
}
|
||||
|
||||
public static async Task<AccountService.AccountServiceClient> CreateAccountServiceClient(
|
||||
IEtcdClient etcdClient,
|
||||
string clientCertPath,
|
||||
@ -51,22 +40,11 @@ public static class GrpcClientHelper
|
||||
string? clientCertPassword = null
|
||||
)
|
||||
{
|
||||
var url = await GetServiceUrlFromEtcd(etcdClient, "AccountService");
|
||||
var url = await GetServiceUrlFromEtcd(etcdClient, "DysonNetwork.Pass");
|
||||
return new AccountService.AccountServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath,
|
||||
clientCertPassword));
|
||||
}
|
||||
|
||||
public static AuthService.AuthServiceClient CreateAuthServiceClient(
|
||||
string url,
|
||||
string clientCertPath,
|
||||
string clientKeyPath,
|
||||
string? clientCertPassword = null
|
||||
)
|
||||
{
|
||||
return new AuthService.AuthServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath,
|
||||
clientCertPassword));
|
||||
}
|
||||
|
||||
public static async Task<AuthService.AuthServiceClient> CreateAuthServiceClient(
|
||||
IEtcdClient etcdClient,
|
||||
string clientCertPath,
|
||||
@ -74,22 +52,11 @@ public static class GrpcClientHelper
|
||||
string? clientCertPassword = null
|
||||
)
|
||||
{
|
||||
var url = await GetServiceUrlFromEtcd(etcdClient, "AuthService");
|
||||
var url = await GetServiceUrlFromEtcd(etcdClient, "DysonNetwork.Pass");
|
||||
return new AuthService.AuthServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath,
|
||||
clientCertPassword));
|
||||
}
|
||||
|
||||
public static PusherService.PusherServiceClient CreatePusherServiceClient(
|
||||
string url,
|
||||
string clientCertPath,
|
||||
string clientKeyPath,
|
||||
string? clientCertPassword = null
|
||||
)
|
||||
{
|
||||
return new PusherService.PusherServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath,
|
||||
clientCertPassword));
|
||||
}
|
||||
|
||||
public static async Task<PusherService.PusherServiceClient> CreatePusherServiceClient(
|
||||
IEtcdClient etcdClient,
|
||||
string clientCertPath,
|
||||
@ -97,7 +64,7 @@ public static class GrpcClientHelper
|
||||
string? clientCertPassword = null
|
||||
)
|
||||
{
|
||||
var url = await GetServiceUrlFromEtcd(etcdClient, "PusherService");
|
||||
var url = await GetServiceUrlFromEtcd(etcdClient, "DysonNetwork.Pusher");
|
||||
return new PusherService.PusherServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath,
|
||||
clientCertPassword));
|
||||
}
|
||||
|
@ -195,6 +195,7 @@ message LevelingInfo {
|
||||
service AccountService {
|
||||
// Account Operations
|
||||
rpc GetAccount(GetAccountRequest) returns (Account) {}
|
||||
rpc GetAccountBatch(GetAccountBatchRequest) returns (GetAccountBatchResponse) {}
|
||||
rpc CreateAccount(CreateAccountRequest) returns (Account) {}
|
||||
rpc UpdateAccount(UpdateAccountRequest) returns (Account) {}
|
||||
rpc DeleteAccount(DeleteAccountRequest) returns (google.protobuf.Empty) {}
|
||||
@ -243,6 +244,14 @@ message GetAccountRequest {
|
||||
string id = 1; // Account ID to retrieve
|
||||
}
|
||||
|
||||
message GetAccountBatchRequest {
|
||||
repeated string id = 1; // Account ID to retrieve
|
||||
}
|
||||
|
||||
message GetAccountBatchResponse {
|
||||
repeated Account accounts = 1; // List of accounts
|
||||
}
|
||||
|
||||
message CreateAccountRequest {
|
||||
string name = 1; // Required: Unique username
|
||||
string nick = 2; // Optional: Display name
|
||||
|
@ -36,6 +36,12 @@ service PusherService {
|
||||
|
||||
// Sends a push notification to a list of users.
|
||||
rpc SendPushNotificationToUsers(SendPushNotificationToUsersRequest) returns (google.protobuf.Empty) {}
|
||||
|
||||
// Unsubscribes a device from push notifications.
|
||||
rpc UnsubscribePushNotifications(UnsubscribePushNotificationsRequest) returns (google.protobuf.Empty) {}
|
||||
|
||||
// Gets the WebSocket connection status for a device or user.
|
||||
rpc GetWebsocketConnectionStatus(GetWebsocketConnectionStatusRequest) returns (GetWebsocketConnectionStatusResponse) {}
|
||||
}
|
||||
|
||||
// Represents an email message.
|
||||
@ -108,3 +114,18 @@ message SendPushNotificationToUsersRequest {
|
||||
repeated string user_ids = 1;
|
||||
PushNotification notification = 2;
|
||||
}
|
||||
|
||||
message UnsubscribePushNotificationsRequest {
|
||||
string device_id = 1;
|
||||
}
|
||||
|
||||
message GetWebsocketConnectionStatusRequest {
|
||||
oneof id {
|
||||
string device_id = 1;
|
||||
string user_id = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message GetWebsocketConnectionStatusResponse {
|
||||
bool is_connected = 1;
|
||||
}
|
||||
|
47
DysonNetwork.Shared/Registry/ServiceHelper.cs
Normal file
47
DysonNetwork.Shared/Registry/ServiceHelper.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using dotnet_etcd.interfaces;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace DysonNetwork.Shared.Registry;
|
||||
|
||||
public static class ServiceHelper
|
||||
{
|
||||
public static IServiceCollection AddPusherService(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<PusherService.PusherServiceClient>(sp =>
|
||||
{
|
||||
var etcdClient = sp.GetRequiredService<IEtcdClient>();
|
||||
var config = sp.GetRequiredService<IConfiguration>();
|
||||
var clientCertPath = config["Service:ClientCert"];
|
||||
var clientKeyPath = config["Service:ClientKey"];
|
||||
var clientCertPassword = config["Service:CertPassword"];
|
||||
|
||||
return GrpcClientHelper
|
||||
.CreatePusherServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddAccountService(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<AccountService.AccountServiceClient>(sp =>
|
||||
{
|
||||
var etcdClient = sp.GetRequiredService<IEtcdClient>();
|
||||
var config = sp.GetRequiredService<IConfiguration>();
|
||||
var clientCertPath = config["Service:ClientCert"];
|
||||
var clientKeyPath = config["Service:ClientKey"];
|
||||
var clientCertPassword = config["Service:CertPassword"];
|
||||
|
||||
return GrpcClientHelper
|
||||
.CreateAccountServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
@ -8,16 +8,20 @@ namespace DysonNetwork.Shared.Registry;
|
||||
|
||||
public class ServiceRegistry(IEtcdClient etcd, ILogger<ServiceRegistry> logger)
|
||||
{
|
||||
public async Task RegisterService(string serviceName, string serviceUrl, long leaseTtlSeconds = 60, CancellationToken cancellationToken = default)
|
||||
public async Task RegisterService(string serviceName, string serviceUrl, long leaseTtlSeconds = 60,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var key = $"/services/{serviceName}";
|
||||
var leaseResponse = await etcd.LeaseGrantAsync(new LeaseGrantRequest { TTL = leaseTtlSeconds });
|
||||
var leaseResponse = await etcd.LeaseGrantAsync(
|
||||
new LeaseGrantRequest { TTL = leaseTtlSeconds },
|
||||
cancellationToken: cancellationToken
|
||||
);
|
||||
await etcd.PutAsync(new PutRequest
|
||||
{
|
||||
Key = ByteString.CopyFrom(key, Encoding.UTF8),
|
||||
Value = ByteString.CopyFrom(serviceUrl, Encoding.UTF8),
|
||||
Lease = leaseResponse.ID
|
||||
});
|
||||
}, cancellationToken: cancellationToken);
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
|
Reference in New Issue
Block a user