From 2c67472894f6775eb4fd67e317b5088ac0a5b476 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Tue, 8 Jul 2025 00:08:35 +0800 Subject: [PATCH] :drunk: AI did something --- .../Permission/PermissionService.cs | 12 ++++- .../DysonNetwork.Shared.csproj | 5 +- .../Permission/MagicOnionPermissionFilter.cs | 49 +++++++++++++++++++ .../Permission/RequiredPermissionAttribute.cs | 18 +++++++ .../Services/IPermissionService.cs | 8 +++ .../Activity/ActivityService.cs | 4 +- DysonNetwork.Sphere/AppDatabase.cs | 40 ++++++--------- DysonNetwork.Sphere/Chat/ChatController.cs | 2 +- .../Chat/ChatRoomController.cs | 13 +++-- DysonNetwork.Sphere/Chat/ChatService.cs | 1 - .../WebReader/WebReaderController.cs | 4 +- .../Developer/CustomAppController.cs | 1 - .../Developer/DeveloperController.cs | 5 +- .../DysonNetwork.Sphere.csproj | 1 + .../Pages/Account/Profile.cshtml.cs | 2 +- .../Pages/Auth/Authorize.cshtml.cs | 6 +-- .../Pages/Auth/Login.cshtml.cs | 10 ++-- .../Pages/Auth/SelectFactor.cshtml | 1 - .../Pages/Auth/SelectFactor.cshtml.cs | 4 +- .../Pages/Auth/VerifyFactor.cshtml | 2 +- .../Pages/Auth/VerifyFactor.cshtml.cs | 12 ++--- .../Pages/Shared/_Layout.cshtml | 2 +- .../Pages/Spell/MagicSpellPage.cshtml | 2 +- .../Pages/Spell/MagicSpellPage.cshtml.cs | 4 +- .../Permission/RequiredPermissionAttribute.cs | 43 ++++++++++++++++ DysonNetwork.Sphere/Post/PostController.cs | 8 +-- DysonNetwork.Sphere/Post/PostService.cs | 2 +- DysonNetwork.Sphere/Program.cs | 4 +- .../Publisher/PublisherController.cs | 7 ++- .../Publisher/PublisherSubscriptionService.cs | 2 +- DysonNetwork.Sphere/Realm/RealmController.cs | 2 +- DysonNetwork.Sphere/Realm/RealmService.cs | 3 +- .../Safety/AbuseReportController.cs | 4 +- DysonNetwork.Sphere/Safety/SafetyService.cs | 2 +- .../Startup/ServiceCollectionExtensions.cs | 11 +++++ .../Sticker/StickerController.cs | 2 +- DysonNetwork.Sphere/Storage/FileController.cs | 2 +- .../Storage/Handlers/ActionLogFlushHandler.cs | 1 - DysonNetwork.Sphere/Storage/TusService.cs | 4 +- DysonNetwork.Sphere/Wallet/OrderController.cs | 2 +- DysonNetwork.Sphere/Wallet/PaymentService.cs | 3 +- .../Wallet/SubscriptionService.cs | 6 +-- .../Wallet/WalletController.cs | 2 +- 43 files changed, 221 insertions(+), 97 deletions(-) create mode 100644 DysonNetwork.Shared/Permission/MagicOnionPermissionFilter.cs create mode 100644 DysonNetwork.Shared/Permission/RequiredPermissionAttribute.cs create mode 100644 DysonNetwork.Shared/Services/IPermissionService.cs create mode 100644 DysonNetwork.Sphere/Permission/RequiredPermissionAttribute.cs diff --git a/DysonNetwork.Pass/Permission/PermissionService.cs b/DysonNetwork.Pass/Permission/PermissionService.cs index 860da3d..f790458 100644 --- a/DysonNetwork.Pass/Permission/PermissionService.cs +++ b/DysonNetwork.Pass/Permission/PermissionService.cs @@ -1,6 +1,9 @@ +using MagicOnion; +using MagicOnion.Server; using System.Text.Json; using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Models; +using DysonNetwork.Shared.Services; using Microsoft.EntityFrameworkCore; using NodaTime; @@ -9,7 +12,7 @@ namespace DysonNetwork.Pass.Permission; public class PermissionService( AppDatabase db, ICacheService cache -) +) : ServiceBase, IPermissionService { private static readonly TimeSpan CacheExpiration = TimeSpan.FromMinutes(1); @@ -195,4 +198,11 @@ public class PermissionService( Value = _SerializePermissionValue(value), }; } + + public async UnaryResult CheckPermission(string scope, string permission) + { + // Assuming the actor is always "user:current" for client-side checks + // You might need to adjust this based on how your client identifies itself + return await HasPermissionAsync("user:current", scope, permission); + } } \ No newline at end of file diff --git a/DysonNetwork.Shared/DysonNetwork.Shared.csproj b/DysonNetwork.Shared/DysonNetwork.Shared.csproj index 0ffbe5c..08dfb52 100644 --- a/DysonNetwork.Shared/DysonNetwork.Shared.csproj +++ b/DysonNetwork.Shared/DysonNetwork.Shared.csproj @@ -14,8 +14,9 @@ - - + + + diff --git a/DysonNetwork.Shared/Permission/MagicOnionPermissionFilter.cs b/DysonNetwork.Shared/Permission/MagicOnionPermissionFilter.cs new file mode 100644 index 0000000..d023d5b --- /dev/null +++ b/DysonNetwork.Shared/Permission/MagicOnionPermissionFilter.cs @@ -0,0 +1,49 @@ +using MagicOnion.Server.Filters; +using MagicOnion.Server; +using DysonNetwork.Shared.Services; +using Microsoft.Extensions.DependencyInjection; +using DysonNetwork.Shared.Models; +using System.Threading.Tasks; +using System; + +namespace DysonNetwork.Shared.Permission; + +public class MagicOnionPermissionFilter : IMagicOnionFilter +{ + private readonly IPermissionService _permissionService; + + public MagicOnionPermissionFilter(IPermissionService permissionService) + { + _permissionService = permissionService; + } + + public async ValueTask Invoke(ServiceContext context, RequiredPermissionAttribute attribute, Func next) + { + var httpContext = context.GetHttpContext(); + if (httpContext == null) + { + throw new InvalidOperationException("HttpContext is not available in ServiceContext."); + } + + if (httpContext.Items["CurrentUser"] is not Account currentUser) + { + throw new ReturnStatusException(MagicOnion.Grpc.StatusCode.PermissionDenied, "Unauthorized: Current user not found."); + } + + if (currentUser.IsSuperuser) + { + await next(context); + return; + } + + var actor = $"user:{currentUser.Id}"; + var hasPermission = await _permissionService.CheckPermission(actor, attribute.Scope, attribute.Permission); + + if (!hasPermission) + { + throw new ReturnStatusException(MagicOnion.Grpc.StatusCode.PermissionDenied, $"Permission {attribute.Scope}/{attribute.Permission} was required."); + } + + await next(context); + } +} \ No newline at end of file diff --git a/DysonNetwork.Shared/Permission/RequiredPermissionAttribute.cs b/DysonNetwork.Shared/Permission/RequiredPermissionAttribute.cs new file mode 100644 index 0000000..5905dbd --- /dev/null +++ b/DysonNetwork.Shared/Permission/RequiredPermissionAttribute.cs @@ -0,0 +1,18 @@ +using System; +using MagicOnion.Server.Filters; + +namespace DysonNetwork.Shared.Permission; + +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)] +public class RequiredPermissionAttribute : MagicOnionFilterAttribute +{ + public string Scope { get; } + public string Permission { get; } + + public RequiredPermissionAttribute(string scope, string permission) : base(typeof(MagicOnionPermissionFilter)) + { + Scope = scope; + Permission = permission; + Order = 999; // Ensure this runs after authentication filters + } +} \ No newline at end of file diff --git a/DysonNetwork.Shared/Services/IPermissionService.cs b/DysonNetwork.Shared/Services/IPermissionService.cs new file mode 100644 index 0000000..622180b --- /dev/null +++ b/DysonNetwork.Shared/Services/IPermissionService.cs @@ -0,0 +1,8 @@ +using MagicOnion; + +namespace DysonNetwork.Shared.Services; + +public interface IPermissionService : IService +{ + UnaryResult CheckPermission(string scope, string permission); +} \ No newline at end of file diff --git a/DysonNetwork.Sphere/Activity/ActivityService.cs b/DysonNetwork.Sphere/Activity/ActivityService.cs index 33f55bb..1f3300e 100644 --- a/DysonNetwork.Sphere/Activity/ActivityService.cs +++ b/DysonNetwork.Sphere/Activity/ActivityService.cs @@ -1,4 +1,4 @@ -using DysonNetwork.Sphere.Account; +using DysonNetwork.Shared.Models; using DysonNetwork.Sphere.Connection.WebReader; using DysonNetwork.Sphere.Discovery; using DysonNetwork.Sphere.Post; @@ -11,7 +11,7 @@ namespace DysonNetwork.Sphere.Activity; public class ActivityService( AppDatabase db, PublisherService pub, - RelationshipService rels, + DysonNetwork.Shared.Services.IRelationshipService rels, PostService ps, DiscoveryService ds ) diff --git a/DysonNetwork.Sphere/AppDatabase.cs b/DysonNetwork.Sphere/AppDatabase.cs index 881997e..75942cc 100644 --- a/DysonNetwork.Sphere/AppDatabase.cs +++ b/DysonNetwork.Sphere/AppDatabase.cs @@ -1,6 +1,7 @@ using System.Linq.Expressions; using System.Reflection; using DysonNetwork.Shared.Models; +using DysonNetwork.Sphere.Connection.WebReader; using DysonNetwork.Sphere.Post; using DysonNetwork.Sphere.Sticker; using Microsoft.EntityFrameworkCore; @@ -9,15 +10,6 @@ using Microsoft.EntityFrameworkCore.Query; using NodaTime; using Quartz; -namespace DysonNetwork.Sphere; - -public abstract class ModelBase -{ - public Instant CreatedAt { get; set; } - public Instant UpdatedAt { get; set; } - public Instant? DeletedAt { get; set; } -} - public class AppDatabase( DbContextOptions options, IConfiguration configuration @@ -26,18 +18,18 @@ public class AppDatabase( public DbSet Files { get; set; } public DbSet FileReferences { get; set; } - public DbSet Publishers { get; set; } + public DbSet Publishers { get; set; } public DbSet PublisherMembers { get; set; } public DbSet PublisherSubscriptions { get; set; } public DbSet PublisherFeatures { get; set; } - public DbSet Posts { get; set; } + public DbSet Posts { get; set; } public DbSet PostReactions { get; set; } public DbSet PostTags { get; set; } public DbSet PostCategories { get; set; } public DbSet PostCollections { get; set; } - public DbSet Realms { get; set; } + public DbSet Realms { get; set; } public DbSet RealmMembers { get; set; } public DbSet ChatRooms { get; set; } @@ -46,10 +38,10 @@ public class AppDatabase( public DbSet ChatRealtimeCall { get; set; } public DbSet ChatReactions { get; set; } - public DbSet Stickers { get; set; } + public DbSet Stickers { get; set; } public DbSet StickerPacks { get; set; } - public DbSet Wallets { get; set; } + public DbSet Wallets { get; set; } public DbSet WalletPockets { get; set; } public DbSet PaymentOrders { get; set; } public DbSet PaymentTransactions { get; set; } @@ -59,8 +51,8 @@ public class AppDatabase( public DbSet WalletSubscriptions { get; set; } public DbSet WalletCoupons { get; set; } - public DbSet WebArticles { get; set; } - public DbSet WebFeeds { get; set; } + public DbSet WebArticles { get; set; } + public DbSet WebFeeds { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { @@ -103,7 +95,7 @@ public class AppDatabase( .HasForeignKey(ps => ps.AccountId) .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity() + modelBuilder.Entity() .HasGeneratedTsVectorColumn(p => p.SearchVector, "simple", p => new { p.Title, p.Description, p.Content }) .HasIndex(p => p.SearchVector) .HasMethod("GIN"); @@ -118,25 +110,25 @@ public class AppDatabase( .HasForeignKey(s => s.AppId) .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity() + modelBuilder.Entity() .HasOne(p => p.RepliedPost) .WithMany() .HasForeignKey(p => p.RepliedPostId) .OnDelete(DeleteBehavior.Restrict); - modelBuilder.Entity() + modelBuilder.Entity() .HasOne(p => p.ForwardedPost) .WithMany() .HasForeignKey(p => p.ForwardedPostId) .OnDelete(DeleteBehavior.Restrict); - modelBuilder.Entity() + modelBuilder.Entity() .HasMany(p => p.Tags) .WithMany(t => t.Posts) .UsingEntity(j => j.ToTable("post_tag_links")); - modelBuilder.Entity() + modelBuilder.Entity() .HasMany(p => p.Categories) .WithMany(c => c.Posts) .UsingEntity(j => j.ToTable("post_category_links")); - modelBuilder.Entity() + modelBuilder.Entity() .HasMany(p => p.Collections) .WithMany(c => c.Posts) .UsingEntity(j => j.ToTable("post_collection_links")); @@ -189,11 +181,11 @@ public class AppDatabase( .HasForeignKey(m => m.SenderId) .OnDelete(DeleteBehavior.Cascade); - modelBuilder.Entity() + modelBuilder.Entity() .HasIndex(f => f.Url) .IsUnique(); - modelBuilder.Entity() + modelBuilder.Entity() .HasIndex(a => a.Url) .IsUnique(); diff --git a/DysonNetwork.Sphere/Chat/ChatController.cs b/DysonNetwork.Sphere/Chat/ChatController.cs index 747bbac..0c014c8 100644 --- a/DysonNetwork.Sphere/Chat/ChatController.cs +++ b/DysonNetwork.Sphere/Chat/ChatController.cs @@ -137,7 +137,7 @@ public partial class ChatController(AppDatabase db, ChatService cs, ChatRoomServ [HttpPost("{roomId:guid}/messages")] [Authorize] - [RequiredPermission("global", "chat.messages.create")] + [RequiredPermissionAttribute("global", "chat.messages.create")] public async Task SendMessage([FromBody] SendMessageRequest request, Guid roomId) { if (HttpContext.Items["CurrentUser"] is not Shared.Models.Account currentUser) return Unauthorized(); diff --git a/DysonNetwork.Sphere/Chat/ChatRoomController.cs b/DysonNetwork.Sphere/Chat/ChatRoomController.cs index 442ba53..31385d9 100644 --- a/DysonNetwork.Sphere/Chat/ChatRoomController.cs +++ b/DysonNetwork.Sphere/Chat/ChatRoomController.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.ComponentModel.DataAnnotations; using DysonNetwork.Shared.Models; -using DysonNetwork.Sphere.Account; +using DysonNetwork.Shared.Services; using DysonNetwork.Sphere.Localization; using DysonNetwork.Sphere.Permission; using DysonNetwork.Sphere.Realm; @@ -20,11 +20,10 @@ public class ChatRoomController( FileReferenceService fileRefService, ChatRoomService crs, RealmService rs, - ActionLogService als, - NotificationService nty, - RelationshipService rels, - IStringLocalizer localizer, - AccountEventService aes + DysonNetwork.Shared.Services.IActionLogService als, + DysonNetwork.Shared.Services.INotificationService nty, + DysonNetwork.Shared.Services.IRelationshipService rels, + DysonNetwork.Shared.Services.IAccountEventService aes ) : ControllerBase { [HttpGet("{id:guid}")] @@ -162,7 +161,7 @@ public class ChatRoomController( [HttpPost] [Authorize] - [RequiredPermission("global", "chat.create")] + [RequiredPermissionAttribute("global", "chat.create")] public async Task> CreateChatRoom(ChatRoomRequest request) { if (HttpContext.Items["CurrentUser"] is not Shared.Models.Account currentUser) return Unauthorized(); diff --git a/DysonNetwork.Sphere/Chat/ChatService.cs b/DysonNetwork.Sphere/Chat/ChatService.cs index 3475de0..4b17f04 100644 --- a/DysonNetwork.Sphere/Chat/ChatService.cs +++ b/DysonNetwork.Sphere/Chat/ChatService.cs @@ -1,6 +1,5 @@ using System.Text.RegularExpressions; using DysonNetwork.Shared.Models; -using DysonNetwork.Sphere.Account; using DysonNetwork.Sphere.Chat.Realtime; using DysonNetwork.Sphere.Connection; using DysonNetwork.Sphere.Storage; diff --git a/DysonNetwork.Sphere/Connection/WebReader/WebReaderController.cs b/DysonNetwork.Sphere/Connection/WebReader/WebReaderController.cs index c32bbb4..af21877 100644 --- a/DysonNetwork.Sphere/Connection/WebReader/WebReaderController.cs +++ b/DysonNetwork.Sphere/Connection/WebReader/WebReaderController.cs @@ -59,7 +59,7 @@ public class WebReaderController(WebReaderService reader, ILogger [HttpDelete("link/cache")] [Authorize] - [RequiredPermission("maintenance", "cache.scrap")] + [RequiredPermissionAttribute("maintenance", "cache.scrap")] public async Task InvalidateCache([FromQuery] string url) { if (string.IsNullOrEmpty(url)) @@ -76,7 +76,7 @@ public class WebReaderController(WebReaderService reader, ILogger [HttpDelete("cache/all")] [Authorize] - [RequiredPermission("maintenance", "cache.scrap")] + [RequiredPermissionAttribute("maintenance", "cache.scrap")] public async Task InvalidateAllCache() { await reader.InvalidateAllCachedPreviewsAsync(); diff --git a/DysonNetwork.Sphere/Developer/CustomAppController.cs b/DysonNetwork.Sphere/Developer/CustomAppController.cs index bbd20a7..7f2fe77 100644 --- a/DysonNetwork.Sphere/Developer/CustomAppController.cs +++ b/DysonNetwork.Sphere/Developer/CustomAppController.cs @@ -1,6 +1,5 @@ using System.ComponentModel.DataAnnotations; using DysonNetwork.Shared.Models; -using DysonNetwork.Sphere.Account; using DysonNetwork.Sphere.Publisher; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; diff --git a/DysonNetwork.Sphere/Developer/DeveloperController.cs b/DysonNetwork.Sphere/Developer/DeveloperController.cs index 32fc3ed..0a4a6ba 100644 --- a/DysonNetwork.Sphere/Developer/DeveloperController.cs +++ b/DysonNetwork.Sphere/Developer/DeveloperController.cs @@ -1,5 +1,4 @@ using DysonNetwork.Shared.Models; -using DysonNetwork.Sphere.Account; using DysonNetwork.Sphere.Permission; using DysonNetwork.Sphere.Publisher; using Microsoft.AspNetCore.Authorization; @@ -14,7 +13,7 @@ namespace DysonNetwork.Sphere.Developer; public class DeveloperController( AppDatabase db, PublisherService ps, - ActionLogService als + DysonNetwork.Shared.Services.IActionLogService als ) : ControllerBase { @@ -91,7 +90,7 @@ public class DeveloperController( [HttpPost("{name}/enroll")] [Authorize] - [RequiredPermission("global", "developers.create")] + [RequiredPermissionAttribute("global", "developers.create")] public async Task> EnrollDeveloperProgram(string name) { if (HttpContext.Items["CurrentUser"] is not Shared.Models.Account currentUser) return Unauthorized(); diff --git a/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj b/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj index aec2db8..9f32b49 100644 --- a/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj +++ b/DysonNetwork.Sphere/DysonNetwork.Sphere.csproj @@ -172,6 +172,7 @@ + diff --git a/DysonNetwork.Sphere/Pages/Account/Profile.cshtml.cs b/DysonNetwork.Sphere/Pages/Account/Profile.cshtml.cs index e3deecb..ad9703f 100644 --- a/DysonNetwork.Sphere/Pages/Account/Profile.cshtml.cs +++ b/DysonNetwork.Sphere/Pages/Account/Profile.cshtml.cs @@ -1,4 +1,4 @@ -using DysonNetwork.Sphere.Auth; +using DysonNetwork.Pass.Auth; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; diff --git a/DysonNetwork.Sphere/Pages/Auth/Authorize.cshtml.cs b/DysonNetwork.Sphere/Pages/Auth/Authorize.cshtml.cs index f1a5fe7..13d0011 100644 --- a/DysonNetwork.Sphere/Pages/Auth/Authorize.cshtml.cs +++ b/DysonNetwork.Sphere/Pages/Auth/Authorize.cshtml.cs @@ -1,14 +1,14 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; -using DysonNetwork.Sphere.Auth.OidcProvider.Services; +using DysonNetwork.Pass.Auth; using System.ComponentModel.DataAnnotations; using DysonNetwork.Shared.Models; -using DysonNetwork.Sphere.Auth.OidcProvider.Responses; +using DysonNetwork.Pass.Auth; using DysonNetwork.Sphere.Developer; namespace DysonNetwork.Sphere.Pages.Auth; -public class AuthorizeModel(OidcProviderService oidcService, IConfiguration configuration) : PageModel +public class AuthorizeModel( DysonNetwork.Pass.Auth.OidcProvider.Services.OidcProviderService oidcService, IConfiguration configuration) : PageModel { [BindProperty(SupportsGet = true)] public string? ReturnUrl { get; set; } diff --git a/DysonNetwork.Sphere/Pages/Auth/Login.cshtml.cs b/DysonNetwork.Sphere/Pages/Auth/Login.cshtml.cs index 6568e8d..e72de7e 100644 --- a/DysonNetwork.Sphere/Pages/Auth/Login.cshtml.cs +++ b/DysonNetwork.Sphere/Pages/Auth/Login.cshtml.cs @@ -2,8 +2,8 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using System.ComponentModel.DataAnnotations; using DysonNetwork.Shared.Models; -using DysonNetwork.Sphere.Auth; -using DysonNetwork.Sphere.Account; +using DysonNetwork.Pass.Auth; +using DysonNetwork.Shared.Services; using DysonNetwork.Sphere.Connection; using NodaTime; using Microsoft.EntityFrameworkCore; @@ -12,10 +12,10 @@ namespace DysonNetwork.Sphere.Pages.Auth { public class LoginModel( AppDatabase db, - AccountService accounts, - AuthService auth, + DysonNetwork.Shared.Services.IAccountService accounts, + DysonNetwork.Pass.Auth.AuthService auth, GeoIpService geo, - ActionLogService als + DysonNetwork.Shared.Services.IActionLogService als ) : PageModel { [BindProperty] [Required] public string Username { get; set; } = string.Empty; diff --git a/DysonNetwork.Sphere/Pages/Auth/SelectFactor.cshtml b/DysonNetwork.Sphere/Pages/Auth/SelectFactor.cshtml index d011f1a..915996d 100644 --- a/DysonNetwork.Sphere/Pages/Auth/SelectFactor.cshtml +++ b/DysonNetwork.Sphere/Pages/Auth/SelectFactor.cshtml @@ -1,6 +1,5 @@ @page "/web/auth/challenge/{id:guid}/select-factor" @using DysonNetwork.Shared.Models -@using DysonNetwork.Sphere.Account @model DysonNetwork.Sphere.Pages.Auth.SelectFactorModel @{ ViewData["Title"] = "Select Authentication Method"; diff --git a/DysonNetwork.Sphere/Pages/Auth/SelectFactor.cshtml.cs b/DysonNetwork.Sphere/Pages/Auth/SelectFactor.cshtml.cs index 2fc29ed..ed44568 100644 --- a/DysonNetwork.Sphere/Pages/Auth/SelectFactor.cshtml.cs +++ b/DysonNetwork.Sphere/Pages/Auth/SelectFactor.cshtml.cs @@ -2,8 +2,8 @@ using DysonNetwork.Shared.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.EntityFrameworkCore; -using DysonNetwork.Sphere.Auth; -using DysonNetwork.Sphere.Account; +using DysonNetwork.Pass.Auth; +using DysonNetwork.Pass.Account; namespace DysonNetwork.Sphere.Pages.Auth; diff --git a/DysonNetwork.Sphere/Pages/Auth/VerifyFactor.cshtml b/DysonNetwork.Sphere/Pages/Auth/VerifyFactor.cshtml index f4302ce..2881f1a 100644 --- a/DysonNetwork.Sphere/Pages/Auth/VerifyFactor.cshtml +++ b/DysonNetwork.Sphere/Pages/Auth/VerifyFactor.cshtml @@ -1,6 +1,6 @@ @page "/web/auth/challenge/{id:guid}/verify/{factorId:guid}" @using DysonNetwork.Shared.Models -@using DysonNetwork.Sphere.Account +@using DysonNetwork.Shared.Models @model DysonNetwork.Sphere.Pages.Auth.VerifyFactorModel @{ ViewData["Title"] = "Verify Your Identity"; diff --git a/DysonNetwork.Sphere/Pages/Auth/VerifyFactor.cshtml.cs b/DysonNetwork.Sphere/Pages/Auth/VerifyFactor.cshtml.cs index d21ffd4..f278dbf 100644 --- a/DysonNetwork.Sphere/Pages/Auth/VerifyFactor.cshtml.cs +++ b/DysonNetwork.Sphere/Pages/Auth/VerifyFactor.cshtml.cs @@ -3,18 +3,18 @@ using DysonNetwork.Shared.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.EntityFrameworkCore; -using DysonNetwork.Sphere.Auth; -using DysonNetwork.Sphere.Account; +using DysonNetwork.Shared.Services; +using DysonNetwork.Shared.Services; using NodaTime; namespace DysonNetwork.Sphere.Pages.Auth { public class VerifyFactorModel( AppDatabase db, - AccountService accounts, - AuthService auth, - ActionLogService als, - IConfiguration configuration, + DysonNetwork.Shared.Services.IAccountService accountService, + DysonNetwork.Pass.Auth.AuthService authService, + DysonNetwork.Shared.Services.IActionLogService actionLogService, + IConfiguration configuration, IHttpClientFactory httpClientFactory ) : PageModel diff --git a/DysonNetwork.Sphere/Pages/Shared/_Layout.cshtml b/DysonNetwork.Sphere/Pages/Shared/_Layout.cshtml index 3ba1725..4475ef4 100644 --- a/DysonNetwork.Sphere/Pages/Shared/_Layout.cshtml +++ b/DysonNetwork.Sphere/Pages/Shared/_Layout.cshtml @@ -1,4 +1,4 @@ -@using DysonNetwork.Sphere.Auth +using DysonNetwork.Pass.Auth; diff --git a/DysonNetwork.Sphere/Pages/Spell/MagicSpellPage.cshtml b/DysonNetwork.Sphere/Pages/Spell/MagicSpellPage.cshtml index 5f3e8dd..26abf2b 100644 --- a/DysonNetwork.Sphere/Pages/Spell/MagicSpellPage.cshtml +++ b/DysonNetwork.Sphere/Pages/Spell/MagicSpellPage.cshtml @@ -1,5 +1,5 @@ @page "/spells/{spellWord}" -@using DysonNetwork.Sphere.Account +@using DysonNetwork.Shared.Models @model DysonNetwork.Sphere.Pages.Spell.MagicSpellPage @{ diff --git a/DysonNetwork.Sphere/Pages/Spell/MagicSpellPage.cshtml.cs b/DysonNetwork.Sphere/Pages/Spell/MagicSpellPage.cshtml.cs index 45dc2b7..f76678f 100644 --- a/DysonNetwork.Sphere/Pages/Spell/MagicSpellPage.cshtml.cs +++ b/DysonNetwork.Sphere/Pages/Spell/MagicSpellPage.cshtml.cs @@ -1,4 +1,4 @@ -using DysonNetwork.Sphere.Account; +using DysonNetwork.Shared.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.EntityFrameworkCore; @@ -6,7 +6,7 @@ using NodaTime; namespace DysonNetwork.Sphere.Pages.Spell; -public class MagicSpellPage(AppDatabase db, MagicSpellService spells) : PageModel +public class MagicSpellPage(AppDatabase db, DysonNetwork.Shared.Services.IMagicSpellService magicSpellService) : PageModel { [BindProperty] public MagicSpell? CurrentSpell { get; set; } [BindProperty] public string? NewPassword { get; set; } diff --git a/DysonNetwork.Sphere/Permission/RequiredPermissionAttribute.cs b/DysonNetwork.Sphere/Permission/RequiredPermissionAttribute.cs new file mode 100644 index 0000000..ee38236 --- /dev/null +++ b/DysonNetwork.Sphere/Permission/RequiredPermissionAttribute.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using DysonNetwork.Shared.Services; +using MagicOnion; + +namespace DysonNetwork.Sphere.Permission; + +public class RequiredPermissionAttribute : TypeFilterAttribute +{ + public RequiredPermissionAttribute(string scope, string permission) : base(typeof(RequiredPermissionFilter)) + { + Arguments = new object[] { scope, permission }; + } + + private class RequiredPermissionFilter : IAsyncActionFilter + { + private readonly IPermissionService _permissionService; + private readonly string _scope; + private readonly string _permission; + + public RequiredPermissionFilter(IPermissionService permissionService, string scope, string permission) + { + _permissionService = permissionService; + _scope = scope; + _permission = permission; + } + + public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + // Assuming the actor is always "user:current" for client-side checks + // You might need to adjust this based on how your client identifies itself + var hasPermission = await _permissionService.CheckPermission(_scope, _permission); + + if (!hasPermission) + { + context.Result = new ForbidResult(); + return; + } + + await next(); + } + } +} \ No newline at end of file diff --git a/DysonNetwork.Sphere/Post/PostController.cs b/DysonNetwork.Sphere/Post/PostController.cs index 1632777..83392e0 100644 --- a/DysonNetwork.Sphere/Post/PostController.cs +++ b/DysonNetwork.Sphere/Post/PostController.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.Text.Json; using DysonNetwork.Shared.Models; -using DysonNetwork.Sphere.Account; +using DysonNetwork.Shared.Services; using DysonNetwork.Sphere.Permission; using DysonNetwork.Sphere.Publisher; using DysonNetwork.Sphere.Storage; @@ -19,8 +19,8 @@ public class PostController( AppDatabase db, PostService ps, PublisherService pub, - RelationshipService rels, - ActionLogService als + DysonNetwork.Shared.Services.IRelationshipService rels, + DysonNetwork.Shared.Services.IActionLogService als ) : ControllerBase { @@ -280,7 +280,7 @@ public class PostController( [HttpPost("{id:guid}/reactions")] [Authorize] - [RequiredPermission("global", "posts.react")] + [RequiredPermissionAttribute("global", "posts.react")] public async Task> ReactPost(Guid id, [FromBody] PostReactionRequest request) { HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue); diff --git a/DysonNetwork.Sphere/Post/PostService.cs b/DysonNetwork.Sphere/Post/PostService.cs index 35997bc..8cd588e 100644 --- a/DysonNetwork.Sphere/Post/PostService.cs +++ b/DysonNetwork.Sphere/Post/PostService.cs @@ -1,6 +1,6 @@ using System.Text.RegularExpressions; using DysonNetwork.Shared.Cache; -using DysonNetwork.Sphere.Account; +using DysonNetwork.Shared.Models; using DysonNetwork.Sphere.Connection.WebReader; using DysonNetwork.Sphere.Localization; using DysonNetwork.Sphere.Publisher; diff --git a/DysonNetwork.Sphere/Program.cs b/DysonNetwork.Sphere/Program.cs index 339ea33..e35aa43 100644 --- a/DysonNetwork.Sphere/Program.cs +++ b/DysonNetwork.Sphere/Program.cs @@ -48,7 +48,7 @@ var tusDiskStore = app.Services.GetRequiredService(); app.ConfigureAppMiddleware(builder.Configuration, tusDiskStore); // Map gRPC services -app.MapGrpcService(); -app.MapGrpcService(); +app.MapGrpcService(); +app.MapGrpcService(); app.Run(); \ No newline at end of file diff --git a/DysonNetwork.Sphere/Publisher/PublisherController.cs b/DysonNetwork.Sphere/Publisher/PublisherController.cs index 39764c9..a5c91f8 100644 --- a/DysonNetwork.Sphere/Publisher/PublisherController.cs +++ b/DysonNetwork.Sphere/Publisher/PublisherController.cs @@ -1,6 +1,5 @@ using System.ComponentModel.DataAnnotations; using DysonNetwork.Shared.Models; -using DysonNetwork.Sphere.Account; using DysonNetwork.Sphere.Permission; using DysonNetwork.Sphere.Realm; using DysonNetwork.Sphere.Storage; @@ -17,7 +16,7 @@ public class PublisherController( AppDatabase db, PublisherService ps, FileReferenceService fileRefService, - ActionLogService als) + DysonNetwork.Shared.Services.IActionLogService als) : ControllerBase { [HttpGet("{name}")] @@ -531,7 +530,7 @@ public class PublisherController( [HttpPost("{name}/features")] [Authorize] - [RequiredPermission("maintenance", "publishers.features")] + [RequiredPermissionAttribute("maintenance", "publishers.features")] public async Task> AddPublisherFeature(string name, [FromBody] PublisherFeatureRequest request) { @@ -555,7 +554,7 @@ public class PublisherController( [HttpDelete("{name}/features/{flag}")] [Authorize] - [RequiredPermission("maintenance", "publishers.features")] + [RequiredPermissionAttribute("maintenance", "publishers.features")] public async Task RemovePublisherFeature(string name, string flag) { var publisher = await db.Publishers diff --git a/DysonNetwork.Sphere/Publisher/PublisherSubscriptionService.cs b/DysonNetwork.Sphere/Publisher/PublisherSubscriptionService.cs index 2b73c40..4dfa9fd 100644 --- a/DysonNetwork.Sphere/Publisher/PublisherSubscriptionService.cs +++ b/DysonNetwork.Sphere/Publisher/PublisherSubscriptionService.cs @@ -1,6 +1,6 @@ using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Models; -using DysonNetwork.Sphere.Account; +using DysonNetwork.Pass.Account; using DysonNetwork.Sphere.Localization; using DysonNetwork.Sphere.Post; using DysonNetwork.Sphere.Storage; diff --git a/DysonNetwork.Sphere/Realm/RealmController.cs b/DysonNetwork.Sphere/Realm/RealmController.cs index fb5d0f7..9f4357a 100644 --- a/DysonNetwork.Sphere/Realm/RealmController.cs +++ b/DysonNetwork.Sphere/Realm/RealmController.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; using DysonNetwork.Shared.Models; -using DysonNetwork.Sphere.Account; +using DysonNetwork.Pass.Account; using DysonNetwork.Sphere.Storage; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; diff --git a/DysonNetwork.Sphere/Realm/RealmService.cs b/DysonNetwork.Sphere/Realm/RealmService.cs index 4699d3d..261e79b 100644 --- a/DysonNetwork.Sphere/Realm/RealmService.cs +++ b/DysonNetwork.Sphere/Realm/RealmService.cs @@ -1,12 +1,11 @@ using DysonNetwork.Shared.Models; -using DysonNetwork.Sphere.Account; using DysonNetwork.Sphere.Localization; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Localization; namespace DysonNetwork.Sphere.Realm; -public class RealmService(AppDatabase db, NotificationService nty, IStringLocalizer localizer) +public class RealmService(AppDatabase db, DysonNetwork.Shared.Services.INotificationService nty, IStringLocalizer localizer) { public async Task SendInviteNotify(RealmMember member) { diff --git a/DysonNetwork.Sphere/Safety/AbuseReportController.cs b/DysonNetwork.Sphere/Safety/AbuseReportController.cs index dfc4ec5..7d18e13 100644 --- a/DysonNetwork.Sphere/Safety/AbuseReportController.cs +++ b/DysonNetwork.Sphere/Safety/AbuseReportController.cs @@ -1,5 +1,5 @@ using System.ComponentModel.DataAnnotations; -using DysonNetwork.Sphere.Account; +using DysonNetwork.Pass.Account; using DysonNetwork.Sphere.Permission; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -122,7 +122,7 @@ public class AbuseReportController( [HttpPost("{id}/resolve")] [Authorize] - [RequiredPermission("safety", "reports.resolve")] + [RequiredPermissionAttribute("safety", "reports.resolve")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> ResolveReport(Guid id, [FromBody] ResolveReportRequest request) diff --git a/DysonNetwork.Sphere/Safety/SafetyService.cs b/DysonNetwork.Sphere/Safety/SafetyService.cs index f74a501..5a42a87 100644 --- a/DysonNetwork.Sphere/Safety/SafetyService.cs +++ b/DysonNetwork.Sphere/Safety/SafetyService.cs @@ -1,4 +1,4 @@ -using DysonNetwork.Sphere.Account; +using DysonNetwork.Pass.Account; using Microsoft.EntityFrameworkCore; using NodaTime; diff --git a/DysonNetwork.Sphere/Startup/ServiceCollectionExtensions.cs b/DysonNetwork.Sphere/Startup/ServiceCollectionExtensions.cs index 65e50bb..83d6814 100644 --- a/DysonNetwork.Sphere/Startup/ServiceCollectionExtensions.cs +++ b/DysonNetwork.Sphere/Startup/ServiceCollectionExtensions.cs @@ -27,6 +27,8 @@ using DysonNetwork.Sphere.Discovery; using DysonNetwork.Sphere.Safety; using DysonNetwork.Sphere.Wallet.PaymentHandlers; using tusdotnet.Stores; +using DysonNetwork.Shared.Etcd; +using DysonNetwork.Shared.Services; namespace DysonNetwork.Sphere.Startup; @@ -206,6 +208,15 @@ public static class ServiceCollectionExtensions services.AddScoped(); services.AddScoped(); services.AddScoped(); + + // Add MagicOnion services + services.AddMagicOnionService(); + services.AddMagicOnionService(); + services.AddMagicOnionService(); + services.AddMagicOnionService(); + services.AddMagicOnionService(); + services.AddMagicOnionService(); + services.AddMagicOnionService(); return services; } diff --git a/DysonNetwork.Sphere/Sticker/StickerController.cs b/DysonNetwork.Sphere/Sticker/StickerController.cs index 38e5005..4866d5f 100644 --- a/DysonNetwork.Sphere/Sticker/StickerController.cs +++ b/DysonNetwork.Sphere/Sticker/StickerController.cs @@ -271,7 +271,7 @@ public class StickerController(AppDatabase db, StickerService st) : ControllerBa public const int MaxStickersPerPack = 24; [HttpPost("{packId:guid}/content")] - [RequiredPermission("global", "stickers.create")] + [RequiredPermissionAttribute("global", "stickers.create")] public async Task CreateSticker(Guid packId, [FromBody] StickerRequest request) { if (HttpContext.Items["CurrentUser"] is not Shared.Models.Account currentUser) diff --git a/DysonNetwork.Sphere/Storage/FileController.cs b/DysonNetwork.Sphere/Storage/FileController.cs index 1a480e5..6589dac 100644 --- a/DysonNetwork.Sphere/Storage/FileController.cs +++ b/DysonNetwork.Sphere/Storage/FileController.cs @@ -111,7 +111,7 @@ public class FileController( [HttpPost("/maintenance/migrateReferences")] [Authorize] - [RequiredPermission("maintenance", "files.references")] + [RequiredPermissionAttribute("maintenance", "files.references")] public async Task MigrateFileReferences() { await rms.ScanAndMigrateReferences(); diff --git a/DysonNetwork.Sphere/Storage/Handlers/ActionLogFlushHandler.cs b/DysonNetwork.Sphere/Storage/Handlers/ActionLogFlushHandler.cs index 9fe896c..4c6996d 100644 --- a/DysonNetwork.Sphere/Storage/Handlers/ActionLogFlushHandler.cs +++ b/DysonNetwork.Sphere/Storage/Handlers/ActionLogFlushHandler.cs @@ -1,5 +1,4 @@ using DysonNetwork.Shared.Models; -using DysonNetwork.Sphere.Account; using EFCore.BulkExtensions; using Quartz; diff --git a/DysonNetwork.Sphere/Storage/TusService.cs b/DysonNetwork.Sphere/Storage/TusService.cs index 45393a9..b6fbbe2 100644 --- a/DysonNetwork.Sphere/Storage/TusService.cs +++ b/DysonNetwork.Sphere/Storage/TusService.cs @@ -1,7 +1,7 @@ using System.Net; using System.Text; using System.Text.Json; -using DysonNetwork.Sphere.Permission; +using DysonNetwork.Pass.Permission; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using tusdotnet.Interfaces; @@ -10,7 +10,7 @@ using tusdotnet.Models.Configuration; namespace DysonNetwork.Sphere.Storage; -public abstract class TusService +public class TusService(DefaultTusConfiguration config, ITusStore store) { public static DefaultTusConfiguration BuildConfiguration(ITusStore store) => new() { diff --git a/DysonNetwork.Sphere/Wallet/OrderController.cs b/DysonNetwork.Sphere/Wallet/OrderController.cs index 68c1eb2..5ebeb1c 100644 --- a/DysonNetwork.Sphere/Wallet/OrderController.cs +++ b/DysonNetwork.Sphere/Wallet/OrderController.cs @@ -1,5 +1,5 @@ using DysonNetwork.Shared.Models; -using DysonNetwork.Sphere.Auth; +using DysonNetwork.Pass.Auth; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; diff --git a/DysonNetwork.Sphere/Wallet/PaymentService.cs b/DysonNetwork.Sphere/Wallet/PaymentService.cs index c9cdc1e..fe85e9a 100644 --- a/DysonNetwork.Sphere/Wallet/PaymentService.cs +++ b/DysonNetwork.Sphere/Wallet/PaymentService.cs @@ -1,6 +1,5 @@ using System.Globalization; using DysonNetwork.Shared.Models; -using DysonNetwork.Sphere.Account; using DysonNetwork.Sphere.Localization; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; @@ -12,7 +11,7 @@ namespace DysonNetwork.Sphere.Wallet; public class PaymentService( AppDatabase db, WalletService wat, - NotificationService nty, + DysonNetwork.Shared.Services.INotificationService nty, IStringLocalizer localizer ) { diff --git a/DysonNetwork.Sphere/Wallet/SubscriptionService.cs b/DysonNetwork.Sphere/Wallet/SubscriptionService.cs index fddca78..628d840 100644 --- a/DysonNetwork.Sphere/Wallet/SubscriptionService.cs +++ b/DysonNetwork.Sphere/Wallet/SubscriptionService.cs @@ -1,7 +1,7 @@ using System.Text.Json; using DysonNetwork.Shared.Cache; using DysonNetwork.Shared.Models; -using DysonNetwork.Sphere.Account; +using DysonNetwork.Shared.Services; using DysonNetwork.Sphere.Localization; using DysonNetwork.Sphere.Storage; using DysonNetwork.Sphere.Wallet.PaymentHandlers; @@ -14,8 +14,8 @@ namespace DysonNetwork.Sphere.Wallet; public class SubscriptionService( AppDatabase db, PaymentService payment, - AccountService accounts, - NotificationService nty, + DysonNetwork.Shared.Services.IAccountService account, + DysonNetwork.Shared.Services.INotificationService nty, IStringLocalizer localizer, IConfiguration configuration, ICacheService cache, diff --git a/DysonNetwork.Sphere/Wallet/WalletController.cs b/DysonNetwork.Sphere/Wallet/WalletController.cs index 8548a5c..6af4ee6 100644 --- a/DysonNetwork.Sphere/Wallet/WalletController.cs +++ b/DysonNetwork.Sphere/Wallet/WalletController.cs @@ -75,7 +75,7 @@ public class WalletController(AppDatabase db, WalletService ws, PaymentService p [HttpPost("balance")] [Authorize] - [RequiredPermission("maintenance", "wallets.balance.modify")] + [RequiredPermissionAttribute("maintenance", "wallets.balance.modify")] public async Task> ModifyWalletBalance([FromBody] WalletBalanceRequest request) { var wallet = await ws.GetWalletAsync(request.AccountId);