网站首页 网站源码
website
站点相关全部源代码,隐藏了一些关于服务器的信息
using System.Text.Json;
using Dpz.Core.Auth.Models;
using Dpz.Core.Auth.Service;
using Dpz.Core.EnumLibrary;
using Dpz.Core.Infrastructure;
using Dpz.Core.MongodbAccess;
using Dpz.Core.Public.Entity.Auth;
using Dpz.Core.Service;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MongoDB.Bson;
using MongoDB.Driver;
using OpenIddict.Abstractions;

namespace Dpz.Core.Auth.Controllers;

[Authorize(nameof(Permissions.System))]
public class ApplicationController(
    IOpenIddictApplicationManager openIddictApplicationManager,
    IRepository<DpzApplication> applicationRepository,
    IPinCodeValidator pinCodeValidator,
    ILogger<ApplicationController> logger
) : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    [HttpGet]
    public async Task<IActionResult> Page(string? keyword = null, int page = 1, int limit = 10)
    {
        if (page < 1)
        {
            page = 1;
        }
        if (limit < 1)
        {
            limit = 1;
        }

        var filterDefinition = Builders<DpzApplication>.Filter.Empty;

        if (!string.IsNullOrWhiteSpace(keyword))
        {
            keyword = keyword.Trim();
            var regex = new BsonRegularExpression(keyword, "i");
            filterDefinition &= Builders<DpzApplication>.Filter.Or(
                Builders<DpzApplication>.Filter.Regex(x => x.DisplayName, regex),
                Builders<DpzApplication>.Filter.Regex(x => x.ClientId, regex),
                Builders<DpzApplication>.Filter.Regex(x => x.RedirectUris, regex),
                Builders<DpzApplication>.Filter.Regex(x => x.PostLogoutRedirectUri, regex)
            );
        }

        var pagedList = await applicationRepository
            .SearchFor(filterDefinition)
            .SortByDescending(x => x.Id)
            .ToPagedListAsync(page, limit);

        var data = pagedList
            .Select(x => new PageApplicationModel
            {
                Id = x.Id.ToString(),
                ClientId = x.ClientId,
                DisplayName = x.DisplayName,
                ApplicationType = x.ApplicationType,
                ClientType = x.ClientType,
                RedirectUris = string.IsNullOrWhiteSpace(x.RedirectUris)
                    ? []
                    : x
                        .RedirectUris.Split(
                            ';',
                            StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries
                        )
                        .ToList(),
                PostLogoutRedirectUri = x.PostLogoutRedirectUri,
                Permissions = x.Permissions is { Count: > 0 } permissions
                    ? permissions.ToList()
                    : [],
            })
            .ToList();

        return Json(new LayuiPageWarp<PageApplicationModel>(data, pagedList.TotalItemCount));
    }

    [HttpGet]
    public async Task<IActionResult> Upsert(string? id = null)
    {
        DpzApplication? application = null;

        if (!string.IsNullOrWhiteSpace(id))
        {
            application = await openIddictApplicationManager.FindByIdAsync(id) as DpzApplication;
        }

        // 失败回填草稿
        if (
            application is null
            && TempData["AppDraft"] is string json
            && !string.IsNullOrWhiteSpace(json)
        )
        {
            try
            {
                var draft = JsonSerializer.Deserialize<AppDraft>(json);
                if (draft != null)
                {
                    application = new DpzApplication
                    {
                        ClientId = draft.ClientId,
                        ClientSecret = draft.ClientSecret,
                        DisplayName = draft.DisplayName,
                        RedirectUris = draft.RedirectUris,
                        PostLogoutRedirectUri = draft.PostLogoutRedirectUri,
                        ApplicationType = draft.ApplicationType,
                        ClientType = draft.ClientType,
                        Permissions = draft.Permissions?.ToList() ?? new List<string>(),
                        Properties = string.IsNullOrWhiteSpace(draft.Logo)
                            ? null
                            : new BsonDocument("Logo", draft.Logo),
                    };
                }
            }
            catch (Exception e)
            {
                logger.LogError(e, "反序列化失败");
            }
        }

        return View(application);
    }

    [HttpPost]
    public async Task<IActionResult> Create(
        DpzApplication application,
        string pinCode,
        string? logo
    )
    {
        var errors = new List<string>();
        if (string.IsNullOrWhiteSpace(application.ClientId))
        {
            errors.Add("请填写 ClientId");
        }
        if (string.IsNullOrWhiteSpace(application.DisplayName))
        {
            errors.Add("请填写显示名称");
        }
        if (string.IsNullOrWhiteSpace(application.ClientType))
        {
            errors.Add("请选择客户端类型");
        }
        if (
            string.Equals(
                application.ClientType,
                OpenIddictConstants.ClientTypes.Confidential,
                StringComparison.OrdinalIgnoreCase
            ) && string.IsNullOrWhiteSpace(application.ClientSecret)
        )
        {
            errors.Add("机密客户端需填写 ClientSecret");
        }

        var (success, message) = await pinCodeValidator.ValidateAsync(User.NameIdentifier, pinCode);
        if (!success)
        {
            errors.Add(message ?? "PIN 验证失败");
        }

        if (errors.Count > 0)
        {
            PersistDraft(application, logo);
            TempData["message"] = string.Join(";", errors);
            return RedirectToAction("Upsert");
        }

        if (!string.IsNullOrWhiteSpace(logo))
        {
            application.Properties ??= [];
            application.Properties["Logo"] = logo;
        }

        var secret = application.ClientSecret;
        application.ClientSecret = null;
        await openIddictApplicationManager.CreateAsync(application, secret);
        return RedirectToAction("Index");
    }

    [HttpPost]
    public async Task<IActionResult> Update(
        DpzApplication application,
        string pinCode,
        string? logo
    )
    {
        var errors = new List<string>();
        if (string.IsNullOrWhiteSpace(application.ClientId))
        {
            errors.Add("请填写 ClientId");
        }
        if (string.IsNullOrWhiteSpace(application.DisplayName))
        {
            errors.Add("请填写显示名称");
        }
        if (string.IsNullOrWhiteSpace(application.ClientType))
        {
            errors.Add("请选择客户端类型");
        }
        // 更新时不校验/不修改 ClientSecret(只在创建时设置)

        var (success, message) = await pinCodeValidator.ValidateAsync(User.NameIdentifier, pinCode);
        if (!success)
        {
            errors.Add(message ?? "PIN 验证失败");
        }

        if (errors.Count > 0)
        {
            PersistDraft(application, logo);
            TempData["message"] = string.Join(";", errors);
            return RedirectToAction("Upsert", new { id = application.Id });
        }

        // 读取现有实体,防止意外清空 ClientSecret
        if (
            await openIddictApplicationManager.FindByIdAsync(application.Id.ToString())
            is DpzApplication existing
        )
        {
            application.ClientSecret = existing.ClientSecret;
            // 防护:不允许在更新时变更 ClientId
            application.ClientId = existing.ClientId;
        }

        if (!string.IsNullOrWhiteSpace(logo))
        {
            application.Properties ??= [];
            application.Properties["Logo"] = logo;
        }

        await openIddictApplicationManager.UpdateAsync(application);
        return RedirectToAction("Index");
    }

    [HttpPost]
    public async Task<IActionResult> Delete(string id, string pinCode)
    {
        var (success, message) = await pinCodeValidator.ValidateAsync(User.NameIdentifier, pinCode);
        if (!success)
        {
            return Json(ResponseResult.ToFail(message));
        }

        var application = await openIddictApplicationManager.FindByIdAsync(id);
        if (application == null)
        {
            return Json(ResponseResult.ToFail("未找到该应用"));
        }
        await openIddictApplicationManager.DeleteAsync(application);
        return Json(ResponseResult.ToSuccess("删除成功"));
    }

    private void PersistDraft(DpzApplication application, string? logo)
    {
        var draft = new AppDraft
        {
            ClientId = application.ClientId,
            ClientSecret = application.ClientSecret,
            DisplayName = application.DisplayName,
            RedirectUris = application.RedirectUris,
            PostLogoutRedirectUri = application.PostLogoutRedirectUri,
            ApplicationType = application.ApplicationType,
            ClientType = application.ClientType,
            Permissions = application.Permissions,
            Logo = logo,
        };
        TempData["AppDraft"] = JsonSerializer.Serialize(draft);
    }
}

file class AppDraft
{
    public string? ClientId { get; init; }
    public string? ClientSecret { get; init; }
    public string? DisplayName { get; init; }
    public string? RedirectUris { get; init; }
    public string? PostLogoutRedirectUri { get; init; }
    public string? ApplicationType { get; init; }
    public string? ClientType { get; init; }
    public List<string>? Permissions { get; init; }
    public string? Logo { get; init; }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

上述代码是一个 ASP.NET Core MVC 控制器,名为 ApplicationController,用于管理与 OpenIddict 应用程序相关的操作。该控制器实现了对应用程序的增、删、改、查功能,并且使用 MongoDB 作为数据存储。以下是代码的主要功能和结构的详细解释:

1. 控制器的基本结构

  • 控制器类ApplicationController 继承自 Controller,并通过构造函数注入了多个服务,包括 OpenIddict 应用程序管理器、MongoDB 仓储、PIN 码验证器和日志记录器。
  • 授权:使用 [Authorize(nameof(Permissions.System))] 特性,确保只有具有特定权限的用户才能访问该控制器的操作。

2. 主要操作

  • Index:返回应用程序的主视图。

  • Page:处理分页查询,支持根据关键字搜索应用程序。它使用 MongoDB 的过滤器构建查询,并返回符合条件的应用程序列表和总记录数。

  • Upsert:用于创建或更新应用程序的视图。根据传入的 id 参数判断是创建新应用程序还是编辑现有应用程序。如果找不到应用程序且存在草稿数据,则尝试从草稿中恢复数据。

  • Create:处理创建新应用程序的 POST 请求。它会验证输入的必要字段(如 ClientId、DisplayName、ClientType 等),并在验证失败时保存草稿数据。成功创建后,重定向到 Index 页面。

  • Update:处理更新现有应用程序的 POST 请求。与 Create 类似,它会验证输入并确保在更新时不修改 ClientId 和 ClientSecret。成功更新后,重定向到 Index 页面。

  • Delete:处理删除应用程序的 POST 请求。它会验证 PIN 码并查找应用程序,如果找到则删除该应用程序。

3. 辅助方法

  • PersistDraft:用于保存应用程序的草稿数据到 TempData,以便在表单验证失败时能够回填数据。

4. 数据模型

  • AppDraft:一个简单的类,用于表示应用程序的草稿数据。它包含了应用程序的各个属性,如 ClientId、ClientSecret、DisplayName 等。

5. 数据库操作

  • 使用 IRepository<DpzApplication> 接口与 MongoDB 进行交互,执行 CRUD 操作。通过 SearchFor 方法进行查询,并使用 ToPagedListAsync 方法实现分页。

6. 错误处理

  • 在创建和更新操作中,使用 List<string> 收集错误信息,并在验证失败时将错误信息存储在 TempData 中,以便在视图中显示。

总结

该控制器提供了一个完整的应用程序管理功能,允许用户通过 Web 界面创建、更新、删除和查询 OpenIddict 应用程序。它结合了 MongoDB 数据存储、输入验证和错误处理,确保了操作的安全性和用户体验。

loading