Compare commits
No commits in common. "9f02398eb927f29239efea33184b2246ded5b216" and "7c92208c40a214c5af83f68ca43039ceb933189e" have entirely different histories.
9f02398eb9
...
7c92208c40
@ -1,31 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
|
||||||
using Microsoft.AspNetCore.Mvc.ActionConstraints;
|
|
||||||
|
|
||||||
namespace Fengling.AuthService.Configuration;
|
|
||||||
|
|
||||||
public sealed class FormValueRequiredAttribute(string name) : ActionMethodSelectorAttribute
|
|
||||||
{
|
|
||||||
public override bool IsValidForRequest(RouteContext context, ActionDescriptor action)
|
|
||||||
{
|
|
||||||
if (string.Equals(context.HttpContext.Request.Method, "GET", StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
string.Equals(context.HttpContext.Request.Method, "HEAD", StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
string.Equals(context.HttpContext.Request.Method, "DELETE", StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
string.Equals(context.HttpContext.Request.Method, "TRACE", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(context.HttpContext.Request.ContentType))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!context.HttpContext.Request.ContentType.StartsWith("application/x-www-form-urlencoded",
|
|
||||||
StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !string.IsNullOrEmpty(context.HttpContext.Request.Form[name]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,9 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.AspNetCore.Identity;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using OpenIddict.Abstractions;
|
|
||||||
using Quartz;
|
|
||||||
|
|
||||||
namespace Fengling.AuthService.Configuration;
|
namespace Fengling.AuthService.Configuration;
|
||||||
|
|
||||||
@ -14,83 +11,48 @@ public static class OpenIddictSetup
|
|||||||
IConfiguration configuration
|
IConfiguration configuration
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
||||||
services.Configure<IdentityOptions>(options =>
|
|
||||||
{
|
|
||||||
options.ClaimsIdentity.UserNameClaimType = OpenIddictConstants.Claims.Name;
|
|
||||||
options.ClaimsIdentity.UserIdClaimType = OpenIddictConstants.Claims.Subject;
|
|
||||||
options.ClaimsIdentity.RoleClaimType = OpenIddictConstants.Claims.Role;
|
|
||||||
options.ClaimsIdentity.EmailClaimType = OpenIddictConstants.Claims.Email;
|
|
||||||
options.SignIn.RequireConfirmedAccount = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
services.AddQuartz(options =>
|
|
||||||
{
|
|
||||||
options.UseSimpleTypeLoader();
|
|
||||||
options.UseInMemoryStore();
|
|
||||||
});
|
|
||||||
var isTesting = configuration.GetValue<bool>("Testing", false);
|
var isTesting = configuration.GetValue<bool>("Testing", false);
|
||||||
|
|
||||||
var builder = services.AddOpenIddict();
|
var builder = services.AddOpenIddict();
|
||||||
|
|
||||||
builder.AddCore(options =>
|
builder.AddCore(options =>
|
||||||
{
|
{
|
||||||
options.UseEntityFrameworkCore()
|
options.UseEntityFrameworkCore().UseDbContext<Data.ApplicationDbContext>();
|
||||||
.UseDbContext<Platform.Infrastructure.PlatformDbContext>();
|
|
||||||
options.UseQuartz();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!isTesting)
|
if (!isTesting)
|
||||||
{
|
{
|
||||||
builder.AddServer(options =>
|
builder.AddServer(options =>
|
||||||
{
|
{
|
||||||
options.SetIssuer(configuration["OpenIddict:Issuer"] ?? "http://localhost:5132");
|
options.SetIssuer(configuration["OpenIddict:Issuer"] ?? "https://auth.fengling.local");
|
||||||
|
|
||||||
options.SetAuthorizationEndpointUris("connect/authorize")
|
|
||||||
.SetIntrospectionEndpointUris("connect/introspect")
|
|
||||||
.SetEndSessionEndpointUris("connect/endsession")
|
|
||||||
.SetTokenEndpointUris("connect/token")
|
|
||||||
.SetUserInfoEndpointUris("connect/userinfo")
|
|
||||||
.SetEndUserVerificationEndpointUris("connect/verify");
|
|
||||||
|
|
||||||
options.AllowAuthorizationCodeFlow()
|
|
||||||
.AllowHybridFlow()
|
|
||||||
.AllowClientCredentialsFlow()
|
|
||||||
.AllowRefreshTokenFlow();
|
|
||||||
|
|
||||||
options.AddDevelopmentEncryptionCertificate()
|
options.AddDevelopmentEncryptionCertificate()
|
||||||
.AddDevelopmentSigningCertificate();
|
.AddDevelopmentSigningCertificate();
|
||||||
|
|
||||||
options.DisableAccessTokenEncryption();
|
options.AllowAuthorizationCodeFlow()
|
||||||
|
.AllowPasswordFlow()
|
||||||
|
.AllowRefreshTokenFlow()
|
||||||
|
.RequireProofKeyForCodeExchange();
|
||||||
|
|
||||||
options.RegisterScopes(OpenIddictConstants.Scopes.OfflineAccess, OpenIddictConstants.Scopes.Email,
|
options.RegisterScopes(
|
||||||
OpenIddictConstants.Scopes.Profile, OpenIddictConstants.Scopes.OpenId,
|
"openid",
|
||||||
OpenIddictConstants.Permissions.Scopes.Roles,
|
"profile",
|
||||||
|
"email",
|
||||||
"api",
|
"api",
|
||||||
"auth_server_admin");
|
"offline_access"
|
||||||
|
);
|
||||||
options
|
|
||||||
.UseReferenceAccessTokens()
|
|
||||||
.UseReferenceRefreshTokens()
|
|
||||||
.UseAspNetCore()
|
|
||||||
.DisableTransportSecurityRequirement()
|
|
||||||
.EnableAuthorizationEndpointPassthrough()
|
|
||||||
.EnableEndSessionEndpointPassthrough()
|
|
||||||
.EnableTokenEndpointPassthrough()
|
|
||||||
.EnableUserInfoEndpointPassthrough()
|
|
||||||
.EnableStatusCodePagesIntegration();
|
|
||||||
|
|
||||||
options.SetAccessTokenLifetime(TimeSpan.FromHours(24));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.AddValidation(options =>
|
builder.AddValidation(options =>
|
||||||
{
|
{
|
||||||
options.UseLocalServer();
|
options.UseLocalServer();
|
||||||
options.UseAspNetCore();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);
|
services.AddAuthentication(options =>
|
||||||
|
{
|
||||||
|
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||||
|
});
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
|
using Fengling.AuthService.Data;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
|
using Fengling.AuthService.Models;
|
||||||
using Fengling.Platform.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@ -12,11 +11,11 @@ namespace Fengling.AuthService.Controllers;
|
|||||||
[Authorize]
|
[Authorize]
|
||||||
public class AccessLogsController : ControllerBase
|
public class AccessLogsController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly PlatformDbContext _context;
|
private readonly ApplicationDbContext _context;
|
||||||
private readonly ILogger<AccessLogsController> _logger;
|
private readonly ILogger<AccessLogsController> _logger;
|
||||||
|
|
||||||
public AccessLogsController(
|
public AccessLogsController(
|
||||||
PlatformDbContext context,
|
ApplicationDbContext context,
|
||||||
ILogger<AccessLogsController> logger)
|
ILogger<AccessLogsController> logger)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
|
|||||||
@ -1,10 +1,6 @@
|
|||||||
|
using Fengling.AuthService.Data;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
|
using Fengling.AuthService.Models;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
|
|
||||||
using Fengling.Platform.Infrastructure;
|
|
||||||
using Fengling.AuthService.ViewModels;
|
using Fengling.AuthService.ViewModels;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
|
|
||||||
using Fengling.Platform.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
@ -14,14 +10,25 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
namespace Fengling.AuthService.Controllers;
|
namespace Fengling.AuthService.Controllers;
|
||||||
|
|
||||||
[Route("account")]
|
[Route("account")]
|
||||||
public class AccountController(
|
public class AccountController : Controller
|
||||||
|
{
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||||
|
private readonly ApplicationDbContext _dbContext;
|
||||||
|
private readonly ILogger<AccountController> _logger;
|
||||||
|
|
||||||
|
public AccountController(
|
||||||
UserManager<ApplicationUser> userManager,
|
UserManager<ApplicationUser> userManager,
|
||||||
SignInManager<ApplicationUser> signInManager,
|
SignInManager<ApplicationUser> signInManager,
|
||||||
PlatformDbContext dbContext,
|
ApplicationDbContext dbContext,
|
||||||
ILogger<AccountController> logger,
|
ILogger<AccountController> logger)
|
||||||
PlatformDbContext platformDbContext)
|
{
|
||||||
: Controller
|
_userManager = userManager;
|
||||||
{
|
_signInManager = signInManager;
|
||||||
|
_dbContext = dbContext;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet("login")]
|
[HttpGet("login")]
|
||||||
public IActionResult Login(string returnUrl = "/")
|
public IActionResult Login(string returnUrl = "/")
|
||||||
{
|
{
|
||||||
@ -37,14 +44,14 @@ public class AccountController(
|
|||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = await userManager.FindByNameAsync(model.Username);
|
var user = await _userManager.FindByNameAsync(model.Username);
|
||||||
if (user == null || user.IsDeleted)
|
if (user == null || user.IsDeleted)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError(string.Empty, "用户名或密码错误");
|
ModelState.AddModelError(string.Empty, "用户名或密码错误");
|
||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await signInManager.PasswordSignInAsync(user, model.Password, model.RememberMe, true);
|
var result = await _signInManager.PasswordSignInAsync(user, model.Password, model.RememberMe, false);
|
||||||
if (!result.Succeeded)
|
if (!result.Succeeded)
|
||||||
{
|
{
|
||||||
if (result.IsLockedOut)
|
if (result.IsLockedOut)
|
||||||
@ -76,12 +83,12 @@ public class AccountController(
|
|||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
var tenant = await platformDbContext.Tenants
|
var defaultTenant = await _dbContext.Tenants
|
||||||
.FirstOrDefaultAsync(t => t.TenantCode == model.TenantCode);
|
.FirstOrDefaultAsync(t => t.TenantId == "default");
|
||||||
|
|
||||||
if (tenant == null)
|
if (defaultTenant == null)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError(string.Empty, $"系统配置错误:未找到租户{model.TenantCode}");
|
ModelState.AddModelError(string.Empty, "系统配置错误:未找到默认租户");
|
||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,10 +98,10 @@ public class AccountController(
|
|||||||
Email = model.Email,
|
Email = model.Email,
|
||||||
NormalizedUserName = model.Username.ToUpper(),
|
NormalizedUserName = model.Username.ToUpper(),
|
||||||
NormalizedEmail = model.Email.ToUpper(),
|
NormalizedEmail = model.Email.ToUpper(),
|
||||||
TenantInfo = new TenantInfo(tenant)
|
TenantInfo = new TenantInfo(defaultTenant.Id, defaultTenant.TenantId, defaultTenant.Name)
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = await userManager.CreateAsync(user, model.Password);
|
var result = await _userManager.CreateAsync(user, model.Password);
|
||||||
if (!result.Succeeded)
|
if (!result.Succeeded)
|
||||||
{
|
{
|
||||||
foreach (var error in result.Errors)
|
foreach (var error in result.Errors)
|
||||||
@ -104,23 +111,23 @@ public class AccountController(
|
|||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
await signInManager.SignInAsync(user, isPersistent: false);
|
await _signInManager.SignInAsync(user, isPersistent: false);
|
||||||
return LocalRedirect(model.ReturnUrl);
|
return LocalRedirect(model.ReturnUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("profile")]
|
[HttpGet("profile")]
|
||||||
[HttpGet("settings")]
|
[HttpGet("settings")]
|
||||||
[HttpGet("~/connect/logout")]
|
[HttpGet("logout")]
|
||||||
public IActionResult NotImplemented()
|
public IActionResult NotImplemented()
|
||||||
{
|
{
|
||||||
return RedirectToAction("Index", "Dashboard");
|
return RedirectToAction("Index", "Dashboard");
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("~/connect/logout")]
|
[HttpPost("logout")]
|
||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
public async Task<IActionResult> LogoutPost()
|
public async Task<IActionResult> LogoutPost()
|
||||||
{
|
{
|
||||||
await signInManager.SignOutAsync();
|
await _signInManager.SignOutAsync();
|
||||||
return Redirect("/");
|
return Redirect("/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
|
using Fengling.AuthService.Data;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
|
using Fengling.AuthService.Models;
|
||||||
using Fengling.Platform.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@ -12,11 +11,11 @@ namespace Fengling.AuthService.Controllers;
|
|||||||
[Authorize]
|
[Authorize]
|
||||||
public class AuditLogsController : ControllerBase
|
public class AuditLogsController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly PlatformDbContext _context;
|
private readonly ApplicationDbContext _context;
|
||||||
private readonly ILogger<AuditLogsController> _logger;
|
private readonly ILogger<AuditLogsController> _logger;
|
||||||
|
|
||||||
public AuditLogsController(
|
public AuditLogsController(
|
||||||
PlatformDbContext context,
|
ApplicationDbContext context,
|
||||||
ILogger<AuditLogsController> logger)
|
ILogger<AuditLogsController> logger)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
|
using Fengling.AuthService.Data;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
|
using Fengling.AuthService.Models;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
|
|
||||||
using Fengling.Platform.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
@ -9,16 +7,15 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using OpenIddict.Abstractions;
|
using OpenIddict.Abstractions;
|
||||||
using OpenIddict.Server.AspNetCore;
|
using OpenIddict.Server.AspNetCore;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Fengling.AuthService.Configuration;
|
|
||||||
using Fengling.AuthService.ViewModels;
|
using Fengling.AuthService.ViewModels;
|
||||||
using Microsoft.AspNetCore;
|
using Microsoft.AspNetCore;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using Swashbuckle.AspNetCore.Annotations;
|
|
||||||
using static OpenIddict.Abstractions.OpenIddictConstants;
|
using static OpenIddict.Abstractions.OpenIddictConstants;
|
||||||
|
|
||||||
namespace Fengling.AuthService.Controllers;
|
namespace Fengling.AuthService.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("connect")]
|
||||||
public class AuthorizationController(
|
public class AuthorizationController(
|
||||||
IOpenIddictApplicationManager applicationManager,
|
IOpenIddictApplicationManager applicationManager,
|
||||||
IOpenIddictAuthorizationManager authorizationManager,
|
IOpenIddictAuthorizationManager authorizationManager,
|
||||||
@ -29,99 +26,8 @@ public class AuthorizationController(
|
|||||||
: Controller
|
: Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
[Authorize, FormValueRequired("submit.Accept")]
|
[HttpGet("authorize")]
|
||||||
[Tags("submit.Accept")]
|
[HttpPost("authorize")]
|
||||||
[HttpPost("~/connect/authorize"), ValidateAntiForgeryToken]
|
|
||||||
[SwaggerIgnore]
|
|
||||||
public async Task<IActionResult> Accept()
|
|
||||||
{
|
|
||||||
var request = HttpContext.GetOpenIddictServerRequest() ??
|
|
||||||
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
|
|
||||||
|
|
||||||
// Retrieve the profile of the logged in user.
|
|
||||||
var user = await userManager.GetUserAsync(User) ??
|
|
||||||
throw new InvalidOperationException("The user details cannot be retrieved.");
|
|
||||||
if (user == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("The user details cannot be retrieved.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the application details from the database.
|
|
||||||
var application = await applicationManager.FindByClientIdAsync(request.ClientId!) ??
|
|
||||||
throw new InvalidOperationException(
|
|
||||||
"Details concerning the calling client application cannot be found.");
|
|
||||||
|
|
||||||
// Retrieve the permanent authorizations associated with the user and the calling client application.
|
|
||||||
var authorizations = await authorizationManager.FindAsync(
|
|
||||||
subject: await userManager.GetUserIdAsync(user),
|
|
||||||
client: await applicationManager.GetIdAsync(application),
|
|
||||||
status: OpenIddictConstants.Statuses.Valid,
|
|
||||||
type: OpenIddictConstants.AuthorizationTypes.Permanent,
|
|
||||||
scopes: request.GetScopes()).ToListAsync();
|
|
||||||
|
|
||||||
// Note: the same check is already made in the other action but is repeated
|
|
||||||
// here to ensure a malicious user can't abuse this POST-only endpoint and
|
|
||||||
// force it to return a valid response without the external authorization.
|
|
||||||
if (!authorizations.Any() &&
|
|
||||||
await applicationManager.HasConsentTypeAsync(application, OpenIddictConstants.ConsentTypes.External))
|
|
||||||
{
|
|
||||||
return Forbid(
|
|
||||||
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
|
|
||||||
properties: new AuthenticationProperties(new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
[OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.ConsentRequired,
|
|
||||||
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
|
|
||||||
"The logged in user is not allowed to access this client application."
|
|
||||||
}!));
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme);
|
|
||||||
|
|
||||||
var principal = result.Principal!;
|
|
||||||
|
|
||||||
// Note: in this sample, the granted scopes match the requested scope
|
|
||||||
// but you may want to allow the user to uncheck specific scopes.
|
|
||||||
// For that, simply restrict the list of scopes before calling SetScopes.
|
|
||||||
principal.SetScopes(request.GetScopes());
|
|
||||||
principal.SetResources(await scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync());
|
|
||||||
|
|
||||||
// Automatically create a permanent authorization to avoid requiring explicit consent
|
|
||||||
// for future authorization or token requests containing the same scopes.
|
|
||||||
var authorization = authorizations.LastOrDefault();
|
|
||||||
if (authorization == null)
|
|
||||||
{
|
|
||||||
authorization = await authorizationManager.CreateAsync(
|
|
||||||
principal: principal,
|
|
||||||
subject: await userManager.GetUserIdAsync(user)!,
|
|
||||||
client: await applicationManager.GetIdAsync(application) ?? string.Empty,
|
|
||||||
type: OpenIddictConstants.AuthorizationTypes.Permanent,
|
|
||||||
scopes: principal.GetScopes());
|
|
||||||
}
|
|
||||||
|
|
||||||
principal.SetAuthorizationId(await authorizationManager.GetIdAsync(authorization));
|
|
||||||
|
|
||||||
foreach (var claim in principal.Claims)
|
|
||||||
{
|
|
||||||
claim.SetDestinations(GetDestinations(claim, principal));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
|
|
||||||
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Authorize, FormValueRequired("submit.Deny")]
|
|
||||||
[Tags("submit.Deny")]
|
|
||||||
[SwaggerIgnore]
|
|
||||||
[HttpPost("~/connect/authorize"), ValidateAntiForgeryToken]
|
|
||||||
// Notify OpenIddict that the authorization grant has been denied by the resource owner
|
|
||||||
// to redirect the user agent to the client application using the appropriate response_mode.
|
|
||||||
public IActionResult Deny() => Forbid(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
|
|
||||||
|
|
||||||
|
|
||||||
[HttpGet("~/connect/authorize")]
|
|
||||||
[HttpPost("~/connect/authorize")]
|
|
||||||
[Tags("Authorize")]
|
|
||||||
[IgnoreAntiforgeryToken]
|
|
||||||
public async Task<IActionResult> Authorize()
|
public async Task<IActionResult> Authorize()
|
||||||
{
|
{
|
||||||
var request = HttpContext.GetOpenIddictServerRequest() ??
|
var request = HttpContext.GetOpenIddictServerRequest() ??
|
||||||
@ -250,10 +156,8 @@ public class AuthorizationController(
|
|||||||
|
|
||||||
// At this point, no authorization was found in the database and an error must be returned
|
// At this point, no authorization was found in the database and an error must be returned
|
||||||
// if the client application specified prompt=none in the authorization request.
|
// if the client application specified prompt=none in the authorization request.
|
||||||
case OpenIddictConstants.ConsentTypes.Explicit
|
case OpenIddictConstants.ConsentTypes.Explicit when request.HasPromptValue(OpenIddictConstants.PromptValues.None):
|
||||||
when request.HasPromptValue(OpenIddictConstants.PromptValues.None):
|
case OpenIddictConstants.ConsentTypes.Systematic when request.HasPromptValue(OpenIddictConstants.PromptValues.None):
|
||||||
case OpenIddictConstants.ConsentTypes.Systematic
|
|
||||||
when request.HasPromptValue(OpenIddictConstants.PromptValues.None):
|
|
||||||
return Forbid(
|
return Forbid(
|
||||||
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
|
||||||
properties: new AuthenticationProperties(new Dictionary<string, string>
|
properties: new AuthenticationProperties(new Dictionary<string, string>
|
||||||
@ -266,17 +170,13 @@ public class AuthorizationController(
|
|||||||
|
|
||||||
// In every other case, render the consent form.
|
// In every other case, render the consent form.
|
||||||
default:
|
default:
|
||||||
return View(new AuthorizeViewModel(await applicationManager.GetDisplayNameAsync(application),
|
return View(new AuthorizeViewModel(await applicationManager.GetDisplayNameAsync(application),request.Scope));
|
||||||
request.Scope));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private IEnumerable<string> GetDestinations(Claim claim, ClaimsPrincipal principal)
|
private IEnumerable<string> GetDestinations(Claim claim, ClaimsPrincipal principal)
|
||||||
{
|
{
|
||||||
// Note: by default, claims are NOT automatically included in access and identity tokens.
|
// Note: by default, claims are NOT automatically included in the access and identity tokens.
|
||||||
// To allow OpenIddict to serialize them, you must attach them a destination, that specifies
|
// To allow OpenIddict to serialize them, you must attach them a destination, that specifies
|
||||||
// whether they should be included in access tokens, in identity tokens or in both.
|
// whether they should be included in access tokens, in identity tokens or in both.
|
||||||
|
|
||||||
@ -314,54 +214,4 @@ public class AuthorizationController(
|
|||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(AuthenticationSchemes = OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)]
|
|
||||||
[HttpGet("~/connect/userinfo")]
|
|
||||||
public async Task<IActionResult> UserInfo()
|
|
||||||
{
|
|
||||||
var user = await userManager.GetUserAsync(User) ??
|
|
||||||
throw new InvalidOperationException("The user details cannot be retrieved.");
|
|
||||||
|
|
||||||
// 获取用户的角色
|
|
||||||
var roles = await userManager.GetRolesAsync(user);
|
|
||||||
|
|
||||||
// 获取用户的租户信息
|
|
||||||
var tenantInfo = user.TenantInfo;
|
|
||||||
|
|
||||||
var claims = new List<Claim>
|
|
||||||
{
|
|
||||||
new(OpenIddictConstants.Claims.Subject, await userManager.GetUserIdAsync(user)),
|
|
||||||
new(OpenIddictConstants.Claims.Name, user.UserName!),
|
|
||||||
new(OpenIddictConstants.Claims.PreferredUsername, user.UserName!)
|
|
||||||
};
|
|
||||||
if (claims == null) throw new ArgumentNullException(nameof(claims));
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(user.Email))
|
|
||||||
{
|
|
||||||
claims.Add(new(OpenIddictConstants.Claims.Email, user.Email!));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加角色 claims
|
|
||||||
foreach (var role in roles)
|
|
||||||
{
|
|
||||||
claims.Add(new(OpenIddictConstants.Claims.Role, role));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加自定义 tenant 相关 claims
|
|
||||||
claims.Add(new Claim("tenant_id", tenantInfo.TenantId.ToString()));
|
|
||||||
claims.Add(new Claim("tenant_code", tenantInfo.TenantCode));
|
|
||||||
claims.Add(new Claim("tenant_name", tenantInfo.TenantName));
|
|
||||||
|
|
||||||
return Ok(new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
["sub"] = await userManager.GetUserIdAsync(user),
|
|
||||||
["name"] = user.UserName ?? "Anonymous",
|
|
||||||
["preferred_username"] = user.UserName ?? "Anonymous",
|
|
||||||
["email"] = user.Email ?? "",
|
|
||||||
["role"] = roles.ToArray(),
|
|
||||||
["tenant_id"] = tenantInfo.TenantId.ToString(),
|
|
||||||
["tenant_code"] = tenantInfo?.TenantCode ?? "",
|
|
||||||
["tenant_name"] = tenantInfo?.TenantName ?? ""
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,7 +1,5 @@
|
|||||||
|
using Fengling.AuthService.Data;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
|
using Fengling.AuthService.Models;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
|
|
||||||
using Fengling.Platform.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore;
|
using Microsoft.AspNetCore;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
@ -10,7 +8,6 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using OpenIddict.Abstractions;
|
using OpenIddict.Abstractions;
|
||||||
using OpenIddict.Server.AspNetCore;
|
using OpenIddict.Server.AspNetCore;
|
||||||
using static OpenIddict.Abstractions.OpenIddictConstants;
|
using static OpenIddict.Abstractions.OpenIddictConstants;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Fengling.AuthService.Controllers;
|
namespace Fengling.AuthService.Controllers;
|
||||||
|
|
||||||
@ -46,87 +43,29 @@ public class LogoutController : ControllerBase
|
|||||||
var request = HttpContext.GetOpenIddictServerRequest() ??
|
var request = HttpContext.GetOpenIddictServerRequest() ??
|
||||||
throw new InvalidOperationException("OpenIddict request is null");
|
throw new InvalidOperationException("OpenIddict request is null");
|
||||||
|
|
||||||
// 标准做法:先尝试从 id_token_hint 中提取客户端信息
|
|
||||||
string? clientId = request.ClientId;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(clientId) && !string.IsNullOrEmpty(request.IdTokenHint))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 从 id_token_hint 中提取 client_id (azp claim 或 aud claim)
|
|
||||||
var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal;
|
|
||||||
if (principal != null)
|
|
||||||
{
|
|
||||||
// 尝试从 azp (authorized party) claim 获取
|
|
||||||
clientId = principal.GetClaim(Claims.AuthorizedParty);
|
|
||||||
|
|
||||||
// 如果没有 azp,尝试从 aud (audience) claim 获取
|
|
||||||
if (string.IsNullOrEmpty(clientId))
|
|
||||||
{
|
|
||||||
clientId = principal.GetClaim(Claims.Audience);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "Failed to extract client_id from id_token_hint");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行登出
|
|
||||||
var result = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme);
|
var result = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme);
|
||||||
if (result.Succeeded)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
await _signInManager.SignOutAsync();
|
await _signInManager.SignOutAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理 post_logout_redirect_uri
|
if (request.ClientId != null)
|
||||||
if (!string.IsNullOrEmpty(clientId))
|
|
||||||
{
|
{
|
||||||
var application = await _applicationManager.FindByClientIdAsync(clientId);
|
var application = await _applicationManager.FindByClientIdAsync(request.ClientId);
|
||||||
if (application != null)
|
if (application != null)
|
||||||
{
|
{
|
||||||
var registeredUris = await _applicationManager.GetPostLogoutRedirectUrisAsync(application);
|
var postLogoutRedirectUri = await _applicationManager.GetPostLogoutRedirectUrisAsync(application);
|
||||||
|
|
||||||
// 如果提供了 post_logout_redirect_uri,验证它是否在注册的 URI 列表中
|
|
||||||
if (!string.IsNullOrEmpty(request.PostLogoutRedirectUri))
|
if (!string.IsNullOrEmpty(request.PostLogoutRedirectUri))
|
||||||
{
|
{
|
||||||
if (registeredUris.Contains(request.PostLogoutRedirectUri))
|
if (postLogoutRedirectUri.Contains(request.PostLogoutRedirectUri))
|
||||||
{
|
{
|
||||||
// 如果提供了 state,需要附加到重定向 URI
|
return Redirect(request.PostLogoutRedirectUri);
|
||||||
var redirectUri = request.PostLogoutRedirectUri;
|
|
||||||
if (!string.IsNullOrEmpty(request.State))
|
|
||||||
{
|
|
||||||
var separator = redirectUri.Contains('?') ? "&" : "?";
|
|
||||||
redirectUri = $"{redirectUri}{separator}state={Uri.EscapeDataString(request.State)}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return Redirect(redirectUri);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogWarning(
|
|
||||||
"Post-logout redirect URI {Uri} is not registered for client {ClientId}",
|
|
||||||
request.PostLogoutRedirectUri,
|
|
||||||
clientId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 如果没有提供 post_logout_redirect_uri,使用第一个注册的 URI
|
|
||||||
var defaultUri = registeredUris.FirstOrDefault();
|
|
||||||
if (!string.IsNullOrEmpty(defaultUri))
|
|
||||||
{
|
|
||||||
_logger.LogInformation(
|
|
||||||
"Using default post-logout redirect URI for client {ClientId}",
|
|
||||||
clientId);
|
|
||||||
return Redirect(defaultUri);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果无法确定重定向地址,返回默认页面
|
|
||||||
return Redirect("/");
|
return Redirect("/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
|
using Fengling.AuthService.Data;
|
||||||
|
using Fengling.AuthService.Models;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using OpenIddict.Abstractions;
|
using OpenIddict.Abstractions;
|
||||||
|
using System.Security.Claims;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace Fengling.AuthService.Controllers;
|
namespace Fengling.AuthService.Controllers;
|
||||||
@ -10,13 +14,16 @@ namespace Fengling.AuthService.Controllers;
|
|||||||
[Authorize]
|
[Authorize]
|
||||||
public class OAuthClientsController : ControllerBase
|
public class OAuthClientsController : ControllerBase
|
||||||
{
|
{
|
||||||
|
private readonly ApplicationDbContext _context;
|
||||||
private readonly IOpenIddictApplicationManager _applicationManager;
|
private readonly IOpenIddictApplicationManager _applicationManager;
|
||||||
private readonly ILogger<OAuthClientsController> _logger;
|
private readonly ILogger<OAuthClientsController> _logger;
|
||||||
|
|
||||||
public OAuthClientsController(
|
public OAuthClientsController(
|
||||||
|
ApplicationDbContext context,
|
||||||
IOpenIddictApplicationManager applicationManager,
|
IOpenIddictApplicationManager applicationManager,
|
||||||
ILogger<OAuthClientsController> logger)
|
ILogger<OAuthClientsController> logger)
|
||||||
{
|
{
|
||||||
|
_context = context;
|
||||||
_applicationManager = applicationManager;
|
_applicationManager = applicationManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
@ -26,60 +33,54 @@ public class OAuthClientsController : ControllerBase
|
|||||||
[FromQuery] int page = 1,
|
[FromQuery] int page = 1,
|
||||||
[FromQuery] int pageSize = 10,
|
[FromQuery] int pageSize = 10,
|
||||||
[FromQuery] string? displayName = null,
|
[FromQuery] string? displayName = null,
|
||||||
[FromQuery] string? clientId = null)
|
[FromQuery] string? clientId = null,
|
||||||
|
[FromQuery] string? status = null)
|
||||||
{
|
{
|
||||||
var applications = _applicationManager.ListAsync();
|
var query = _context.OAuthApplications.AsQueryable();
|
||||||
var clientList = new List<object>();
|
|
||||||
|
|
||||||
await foreach (var application in applications)
|
if (!string.IsNullOrEmpty(displayName))
|
||||||
{
|
{
|
||||||
var clientIdValue = await _applicationManager.GetClientIdAsync(application);
|
query = query.Where(c => c.DisplayName.Contains(displayName));
|
||||||
var displayNameValue = await _applicationManager.GetDisplayNameAsync(application);
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(displayName) && !displayNameValue.Contains(displayName, StringComparison.OrdinalIgnoreCase))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(clientId) && !clientIdValue.Contains(clientId, StringComparison.OrdinalIgnoreCase))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var clientType = await _applicationManager.GetClientTypeAsync(application);
|
|
||||||
var consentType = await _applicationManager.GetConsentTypeAsync(application);
|
|
||||||
var permissions = await _applicationManager.GetPermissionsAsync(application);
|
|
||||||
var redirectUrisStrings = await _applicationManager.GetRedirectUrisAsync(application);
|
|
||||||
var postLogoutRedirectUrisStrings = await _applicationManager.GetPostLogoutRedirectUrisAsync(application);
|
|
||||||
var redirectUris = redirectUrisStrings;
|
|
||||||
var postLogoutRedirectUris = postLogoutRedirectUrisStrings;
|
|
||||||
|
|
||||||
clientList.Add(new
|
|
||||||
{
|
|
||||||
id = application,
|
|
||||||
clientId = clientIdValue,
|
|
||||||
displayName = displayNameValue,
|
|
||||||
redirectUris = redirectUris.Select(u => u.ToString()).ToArray(),
|
|
||||||
postLogoutRedirectUris = postLogoutRedirectUris.Select(u => u.ToString()).ToArray(),
|
|
||||||
scopes = permissions
|
|
||||||
.Where(p => p.StartsWith(OpenIddictConstants.Permissions.Prefixes.Scope))
|
|
||||||
.Select(p => p.Substring(OpenIddictConstants.Permissions.Prefixes.Scope.Length)),
|
|
||||||
grantTypes = permissions
|
|
||||||
.Where(p => p.StartsWith(OpenIddictConstants.Permissions.Prefixes.GrantType))
|
|
||||||
.Select(p => p.Substring(OpenIddictConstants.Permissions.Prefixes.GrantType.Length)),
|
|
||||||
clientType = clientType?.ToString(),
|
|
||||||
consentType = consentType?.ToString(),
|
|
||||||
status = "active",
|
|
||||||
permissions
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var sortedClients = clientList
|
if (!string.IsNullOrEmpty(clientId))
|
||||||
.OrderByDescending(c => (c as dynamic).clientId)
|
{
|
||||||
|
query = query.Where(c => c.ClientId.Contains(clientId));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(status))
|
||||||
|
{
|
||||||
|
query = query.Where(c => c.Status == status);
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalCount = await query.CountAsync();
|
||||||
|
var clients = await query
|
||||||
|
.OrderByDescending(c => c.CreatedAt)
|
||||||
.Skip((page - 1) * pageSize)
|
.Skip((page - 1) * pageSize)
|
||||||
.Take(pageSize)
|
.Take(pageSize)
|
||||||
.ToList();
|
.ToListAsync();
|
||||||
|
|
||||||
|
var result = clients.Select(c => new
|
||||||
|
{
|
||||||
|
id = c.Id,
|
||||||
|
clientId = c.ClientId,
|
||||||
|
displayName = c.DisplayName,
|
||||||
|
redirectUris = c.RedirectUris,
|
||||||
|
postLogoutRedirectUris = c.PostLogoutRedirectUris,
|
||||||
|
scopes = c.Scopes,
|
||||||
|
grantTypes = c.GrantTypes,
|
||||||
|
clientType = c.ClientType,
|
||||||
|
consentType = c.ConsentType,
|
||||||
|
status = c.Status,
|
||||||
|
description = c.Description,
|
||||||
|
createdAt = c.CreatedAt,
|
||||||
|
updatedAt = c.UpdatedAt,
|
||||||
|
});
|
||||||
|
|
||||||
return Ok(new
|
return Ok(new
|
||||||
{
|
{
|
||||||
items = sortedClients,
|
items = result,
|
||||||
totalCount = clientList.Count,
|
totalCount,
|
||||||
page,
|
page,
|
||||||
pageSize
|
pageSize
|
||||||
});
|
});
|
||||||
@ -126,46 +127,36 @@ public class OAuthClientsController : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
public async Task<ActionResult<object>> GetClient(string id)
|
public async Task<ActionResult<OAuthApplication>> GetClient(long id)
|
||||||
{
|
{
|
||||||
var application = await _applicationManager.FindByIdAsync(id);
|
var client = await _context.OAuthApplications.FindAsync(id);
|
||||||
if (application == null)
|
if (client == null)
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
var clientIdValue = await _applicationManager.GetClientIdAsync(application);
|
|
||||||
var displayNameValue = await _applicationManager.GetDisplayNameAsync(application);
|
|
||||||
var clientType = await _applicationManager.GetClientTypeAsync(application);
|
|
||||||
var consentType = await _applicationManager.GetConsentTypeAsync(application);
|
|
||||||
var permissions = await _applicationManager.GetPermissionsAsync(application);
|
|
||||||
var redirectUris = await _applicationManager.GetRedirectUrisAsync(application);
|
|
||||||
var postLogoutRedirectUris = await _applicationManager.GetPostLogoutRedirectUrisAsync(application);
|
|
||||||
|
|
||||||
return Ok(new
|
return Ok(new
|
||||||
{
|
{
|
||||||
id = id,
|
id = client.Id,
|
||||||
clientId = clientIdValue,
|
clientId = client.ClientId,
|
||||||
displayName = displayNameValue,
|
displayName = client.DisplayName,
|
||||||
redirectUris = redirectUris.Select(u => u.ToString()).ToArray(),
|
redirectUris = client.RedirectUris,
|
||||||
postLogoutRedirectUris = postLogoutRedirectUris.Select(u => u.ToString()).ToArray(),
|
postLogoutRedirectUris = client.PostLogoutRedirectUris,
|
||||||
scopes = permissions
|
scopes = client.Scopes,
|
||||||
.Where(p => p.StartsWith(OpenIddictConstants.Permissions.Prefixes.Scope))
|
grantTypes = client.GrantTypes,
|
||||||
.Select(p => p.Substring(OpenIddictConstants.Permissions.Prefixes.Scope.Length)),
|
clientType = client.ClientType,
|
||||||
grantTypes = permissions
|
consentType = client.ConsentType,
|
||||||
.Where(p => p.StartsWith(OpenIddictConstants.Permissions.Prefixes.GrantType))
|
status = client.Status,
|
||||||
.Select(p => p.Substring(OpenIddictConstants.Permissions.Prefixes.GrantType.Length)),
|
description = client.Description,
|
||||||
clientType = clientType?.ToString(),
|
createdAt = client.CreatedAt,
|
||||||
consentType = consentType?.ToString(),
|
updatedAt = client.UpdatedAt,
|
||||||
status = "active",
|
|
||||||
permissions
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<ActionResult<object>> CreateClient([FromBody] CreateOAuthClientDto dto)
|
public async Task<ActionResult<object>> CreateClient([FromBody] CreateOAuthClientDto dto)
|
||||||
{
|
{
|
||||||
var existingClient = await _applicationManager.FindByClientIdAsync(dto.ClientId);
|
var existingClient = await _context.OAuthApplications.FirstOrDefaultAsync(c => c.ClientId == dto.ClientId);
|
||||||
if (existingClient != null)
|
if (existingClient != null)
|
||||||
{
|
{
|
||||||
return BadRequest(new { message = "Client ID 已存在", clientId = dto.ClientId });
|
return BadRequest(new { message = "Client ID 已存在", clientId = dto.ClientId });
|
||||||
@ -177,16 +168,9 @@ public class OAuthClientsController : ControllerBase
|
|||||||
{
|
{
|
||||||
ClientId = dto.ClientId,
|
ClientId = dto.ClientId,
|
||||||
ClientSecret = clientSecret,
|
ClientSecret = clientSecret,
|
||||||
DisplayName = dto.DisplayName,
|
DisplayName = dto.DisplayName
|
||||||
ClientType = string.IsNullOrEmpty(dto.ClientType) ? "confidential" : dto.ClientType,
|
|
||||||
ConsentType = string.IsNullOrEmpty(dto.ConsentType) ? "explicit" : dto.ConsentType,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Authorization);
|
|
||||||
descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.EndSession);
|
|
||||||
descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Token);
|
|
||||||
descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Introspection);
|
|
||||||
|
|
||||||
foreach (var uri in (dto.RedirectUris ?? Array.Empty<string>()).Where(u => Uri.TryCreate(u, UriKind.Absolute, out _)))
|
foreach (var uri in (dto.RedirectUris ?? Array.Empty<string>()).Where(u => Uri.TryCreate(u, UriKind.Absolute, out _)))
|
||||||
{
|
{
|
||||||
descriptor.RedirectUris.Add(new Uri(uri));
|
descriptor.RedirectUris.Add(new Uri(uri));
|
||||||
@ -199,196 +183,205 @@ public class OAuthClientsController : ControllerBase
|
|||||||
|
|
||||||
foreach (var grantType in dto.GrantTypes ?? Array.Empty<string>())
|
foreach (var grantType in dto.GrantTypes ?? Array.Empty<string>())
|
||||||
{
|
{
|
||||||
descriptor.Permissions.Add(OpenIddictConstants.Permissions.Prefixes.GrantType + grantType);
|
descriptor.Permissions.Add(grantType);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var scope in dto.Scopes ?? Array.Empty<string>())
|
foreach (var scope in dto.Scopes ?? Array.Empty<string>())
|
||||||
{
|
{
|
||||||
descriptor.Permissions.Add(OpenIddictConstants.Permissions.Prefixes.Scope + scope);
|
descriptor.Permissions.Add(scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
var application = await _applicationManager.CreateAsync(descriptor);
|
await _applicationManager.CreateAsync(descriptor);
|
||||||
var applicationId = await _applicationManager.GetIdAsync(application);
|
|
||||||
|
|
||||||
return CreatedAtAction(nameof(GetClient), new { id = applicationId }, new
|
var client = new OAuthApplication
|
||||||
{
|
{
|
||||||
id = applicationId,
|
ClientId = dto.ClientId,
|
||||||
clientId = dto.ClientId,
|
ClientSecret = clientSecret,
|
||||||
clientSecret = clientSecret,
|
DisplayName = dto.DisplayName,
|
||||||
displayName = dto.DisplayName,
|
RedirectUris = dto.RedirectUris ?? Array.Empty<string>(),
|
||||||
status = "active"
|
PostLogoutRedirectUris = dto.PostLogoutRedirectUris ?? Array.Empty<string>(),
|
||||||
|
Scopes = dto.Scopes ?? new[] { "openid", "profile", "email", "api" },
|
||||||
|
GrantTypes = dto.GrantTypes ?? new[] { "authorization_code", "refresh_token" },
|
||||||
|
ClientType = dto.ClientType ?? "confidential",
|
||||||
|
ConsentType = dto.ConsentType ?? "explicit",
|
||||||
|
Status = dto.Status ?? "active",
|
||||||
|
Description = dto.Description,
|
||||||
|
CreatedAt = DateTime.UtcNow,
|
||||||
|
};
|
||||||
|
|
||||||
|
_context.OAuthApplications.Add(client);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
await CreateAuditLog("oauth", "create", "OAuthClient", client.Id, client.DisplayName, null, SerializeToJson(dto));
|
||||||
|
|
||||||
|
return CreatedAtAction(nameof(GetClient), new { id = client.Id }, new
|
||||||
|
{
|
||||||
|
client.Id,
|
||||||
|
client.ClientId,
|
||||||
|
client.ClientSecret,
|
||||||
|
client.DisplayName,
|
||||||
|
client.Status,
|
||||||
|
client.CreatedAt
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id}/generate-secret")]
|
[HttpPost("{id}/generate-secret")]
|
||||||
public async Task<ActionResult<object>> GenerateSecret(string id)
|
public async Task<ActionResult<object>> GenerateSecret(long id)
|
||||||
{
|
{
|
||||||
var application = await _applicationManager.FindByIdAsync(id);
|
var client = await _context.OAuthApplications.FindAsync(id);
|
||||||
if (application == null)
|
if (client == null)
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
var clientIdValue = await _applicationManager.GetClientIdAsync(application);
|
|
||||||
var displayNameValue = await _applicationManager.GetDisplayNameAsync(application);
|
|
||||||
var clientType = await _applicationManager.GetClientTypeAsync(application);
|
|
||||||
var consentType = await _applicationManager.GetConsentTypeAsync(application);
|
|
||||||
var permissions = await _applicationManager.GetPermissionsAsync(application);
|
|
||||||
var redirectUris = await _applicationManager.GetRedirectUrisAsync(application);
|
|
||||||
var postLogoutRedirectUris = await _applicationManager.GetPostLogoutRedirectUrisAsync(application);
|
|
||||||
|
|
||||||
var newSecret = GenerateSecureSecret();
|
var newSecret = GenerateSecureSecret();
|
||||||
|
client.ClientSecret = newSecret;
|
||||||
|
client.UpdatedAt = DateTime.UtcNow;
|
||||||
|
|
||||||
|
var application = await _applicationManager.FindByClientIdAsync(client.ClientId);
|
||||||
|
if (application != null)
|
||||||
|
{
|
||||||
var descriptor = new OpenIddictApplicationDescriptor
|
var descriptor = new OpenIddictApplicationDescriptor
|
||||||
{
|
{
|
||||||
ClientId = clientIdValue,
|
ClientId = client.ClientId,
|
||||||
ClientSecret = newSecret,
|
ClientSecret = newSecret,
|
||||||
DisplayName = displayNameValue,
|
DisplayName = client.DisplayName
|
||||||
ClientType = clientType,
|
|
||||||
ConsentType = consentType,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var permission in permissions)
|
|
||||||
{
|
|
||||||
descriptor.Permissions.Add(permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var uriString in redirectUris)
|
|
||||||
{
|
|
||||||
if (Uri.TryCreate(uriString, UriKind.Absolute, out var uri))
|
|
||||||
{
|
|
||||||
descriptor.RedirectUris.Add(uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var uriString in postLogoutRedirectUris)
|
|
||||||
{
|
|
||||||
if (Uri.TryCreate(uriString, UriKind.Absolute, out var uri))
|
|
||||||
{
|
|
||||||
descriptor.PostLogoutRedirectUris.Add(uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await _applicationManager.UpdateAsync(application, descriptor);
|
await _applicationManager.UpdateAsync(application, descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
await CreateAuditLog("oauth", "generate_secret", "OAuthClient", client.Id, client.DisplayName, "[REDACTED]", "[REDACTED]");
|
||||||
|
|
||||||
return Ok(new
|
return Ok(new
|
||||||
{
|
{
|
||||||
clientId = clientIdValue,
|
clientId = client.ClientId,
|
||||||
clientSecret = newSecret,
|
clientSecret = newSecret,
|
||||||
message = "新密钥已生成,请妥善保管,刷新后将无法再次查看"
|
message = "新密钥已生成,请妥善保管,刷新后将无法再次查看"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
[HttpPost("{id}/toggle-status")]
|
||||||
public async Task<IActionResult> DeleteClient(string id)
|
public async Task<ActionResult<object>> ToggleStatus(long id)
|
||||||
{
|
{
|
||||||
var application = await _applicationManager.FindByIdAsync(id);
|
var client = await _context.OAuthApplications.FindAsync(id);
|
||||||
|
if (client == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldStatus = client.Status;
|
||||||
|
client.Status = client.Status == "active" ? "inactive" : "active";
|
||||||
|
client.UpdatedAt = DateTime.UtcNow;
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
await CreateAuditLog("oauth", "toggle_status", "OAuthClient", client.Id, client.DisplayName, oldStatus, client.Status);
|
||||||
|
|
||||||
|
return Ok(new
|
||||||
|
{
|
||||||
|
clientId = client.ClientId,
|
||||||
|
oldStatus,
|
||||||
|
newStatus = client.Status,
|
||||||
|
message = $"客户端状态已从 {oldStatus} 更改为 {client.Status}"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id}")]
|
||||||
|
public async Task<IActionResult> UpdateClient(long id, [FromBody] UpdateOAuthClientDto dto)
|
||||||
|
{
|
||||||
|
var client = await _context.OAuthApplications.FindAsync(id);
|
||||||
|
if (client == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
var application = await _applicationManager.FindByClientIdAsync(client.ClientId);
|
||||||
if (application == null)
|
if (application == null)
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
var clientIdValue = await _applicationManager.GetClientIdAsync(application);
|
var descriptor = new OpenIddictApplicationDescriptor
|
||||||
|
{
|
||||||
|
ClientId = client.ClientId,
|
||||||
|
ClientSecret = client.ClientSecret,
|
||||||
|
DisplayName = dto.DisplayName ?? client.DisplayName
|
||||||
|
};
|
||||||
|
|
||||||
try
|
var redirectUris = dto.RedirectUris ?? client.RedirectUris;
|
||||||
|
foreach (var uri in redirectUris.Where(u => Uri.TryCreate(u, UriKind.Absolute, out _)))
|
||||||
{
|
{
|
||||||
await _applicationManager.DeleteAsync(application);
|
descriptor.RedirectUris.Add(new Uri(uri));
|
||||||
_logger.LogInformation("Deleted OpenIddict application {ClientId}", clientIdValue);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
|
var postLogoutUris = dto.PostLogoutRedirectUris ?? client.PostLogoutRedirectUris;
|
||||||
|
foreach (var uri in postLogoutUris.Where(u => Uri.TryCreate(u, UriKind.Absolute, out _)))
|
||||||
{
|
{
|
||||||
_logger.LogWarning(ex, "Failed to delete OpenIddict application for client {ClientId}", clientIdValue);
|
descriptor.PostLogoutRedirectUris.Add(new Uri(uri));
|
||||||
return BadRequest(new { message = ex.Message });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var grantTypes = dto.GrantTypes ?? client.GrantTypes;
|
||||||
|
foreach (var grantType in grantTypes)
|
||||||
|
{
|
||||||
|
descriptor.Permissions.Add(grantType);
|
||||||
|
}
|
||||||
|
|
||||||
|
var scopes = dto.Scopes ?? client.Scopes;
|
||||||
|
foreach (var scope in scopes)
|
||||||
|
{
|
||||||
|
descriptor.Permissions.Add(scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _applicationManager.UpdateAsync(application, descriptor);
|
||||||
|
|
||||||
|
client.DisplayName = dto.DisplayName ?? client.DisplayName;
|
||||||
|
client.RedirectUris = redirectUris;
|
||||||
|
client.PostLogoutRedirectUris = postLogoutUris;
|
||||||
|
client.Scopes = scopes;
|
||||||
|
client.GrantTypes = grantTypes;
|
||||||
|
client.ClientType = dto.ClientType ?? client.ClientType;
|
||||||
|
client.ConsentType = dto.ConsentType ?? client.ConsentType;
|
||||||
|
client.Status = dto.Status ?? client.Status;
|
||||||
|
client.Description = dto.Description ?? client.Description;
|
||||||
|
client.UpdatedAt = DateTime.UtcNow;
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
await CreateAuditLog("oauth", "update", "OAuthClient", client.Id, client.DisplayName, null, SerializeToJson(client));
|
||||||
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut("{id}")]
|
[HttpDelete("{id}")]
|
||||||
public async Task<IActionResult> UpdateClient(string id, [FromBody] UpdateOAuthClientDto dto)
|
public async Task<IActionResult> DeleteClient(long id)
|
||||||
{
|
{
|
||||||
var application = await _applicationManager.FindByIdAsync(id);
|
var client = await _context.OAuthApplications.FindAsync(id);
|
||||||
if (application == null)
|
if (client == null)
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
var clientIdValue = await _applicationManager.GetClientIdAsync(application);
|
try
|
||||||
var displayNameValue = await _applicationManager.GetDisplayNameAsync(application);
|
|
||||||
var clientType = await _applicationManager.GetClientTypeAsync(application);
|
|
||||||
var consentType = await _applicationManager.GetConsentTypeAsync(application);
|
|
||||||
var redirectUris = await _applicationManager.GetRedirectUrisAsync(application);
|
|
||||||
var postLogoutRedirectUris = await _applicationManager.GetPostLogoutRedirectUrisAsync(application);
|
|
||||||
var existingPermissions = await _applicationManager.GetPermissionsAsync(application);
|
|
||||||
|
|
||||||
var descriptor = new OpenIddictApplicationDescriptor
|
|
||||||
{
|
{
|
||||||
ClientId = clientIdValue,
|
var application = await _applicationManager.FindByClientIdAsync(client.ClientId);
|
||||||
DisplayName = dto.DisplayName ?? displayNameValue,
|
if (application != null)
|
||||||
ClientType = string.IsNullOrEmpty(dto.ClientType) ? clientType?.ToString() : dto.ClientType,
|
|
||||||
ConsentType = string.IsNullOrEmpty(dto.ConsentType) ? consentType?.ToString() : dto.ConsentType,
|
|
||||||
};
|
|
||||||
|
|
||||||
descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Authorization);
|
|
||||||
descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.EndSession);
|
|
||||||
descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Token);
|
|
||||||
descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Introspection);
|
|
||||||
|
|
||||||
if (dto.RedirectUris != null)
|
|
||||||
{
|
{
|
||||||
foreach (var uri in dto.RedirectUris.Where(u => Uri.TryCreate(u, UriKind.Absolute, out _)))
|
await _applicationManager.DeleteAsync(application);
|
||||||
{
|
_logger.LogInformation("Deleted OpenIddict application {ClientId}", client.ClientId);
|
||||||
descriptor.RedirectUris.Add(new Uri(uri));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
foreach (var uriString in redirectUris)
|
_logger.LogWarning(ex, "Failed to delete OpenIddict application for client {ClientId}", client.ClientId);
|
||||||
{
|
|
||||||
if (Uri.TryCreate(uriString, UriKind.Absolute, out var uri))
|
|
||||||
{
|
|
||||||
descriptor.RedirectUris.Add(uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dto.PostLogoutRedirectUris != null)
|
_context.OAuthApplications.Remove(client);
|
||||||
{
|
await _context.SaveChangesAsync();
|
||||||
foreach (var uri in dto.PostLogoutRedirectUris.Where(u => Uri.TryCreate(u, UriKind.Absolute, out _)))
|
|
||||||
{
|
|
||||||
descriptor.PostLogoutRedirectUris.Add(new Uri(uri));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var uriString in postLogoutRedirectUris)
|
|
||||||
{
|
|
||||||
if (Uri.TryCreate(uriString, UriKind.Absolute, out var uri))
|
|
||||||
{
|
|
||||||
descriptor.PostLogoutRedirectUris.Add(uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var grantTypes = dto.GrantTypes ?? existingPermissions
|
await CreateAuditLog("oauth", "delete", "OAuthClient", client.Id, client.DisplayName, SerializeToJson(client));
|
||||||
.Where(p => p.StartsWith(OpenIddictConstants.Permissions.Prefixes.GrantType))
|
|
||||||
.Select(p => p.Substring(OpenIddictConstants.Permissions.Prefixes.GrantType.Length));
|
|
||||||
|
|
||||||
var scopes = dto.Scopes ?? existingPermissions
|
|
||||||
.Where(p => p.StartsWith(OpenIddictConstants.Permissions.Prefixes.Scope))
|
|
||||||
.Select(p => p.Substring(OpenIddictConstants.Permissions.Prefixes.Scope.Length));
|
|
||||||
|
|
||||||
foreach (var grantType in grantTypes)
|
|
||||||
{
|
|
||||||
descriptor.Permissions.Add(OpenIddictConstants.Permissions.Prefixes.GrantType + grantType);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var scope in scopes)
|
|
||||||
{
|
|
||||||
descriptor.Permissions.Add(OpenIddictConstants.Permissions.Prefixes.Scope + scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
await _applicationManager.UpdateAsync(application, descriptor);
|
|
||||||
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
@ -399,6 +392,38 @@ public class OAuthClientsController : ControllerBase
|
|||||||
var bytes = RandomNumberGenerator.GetBytes(length);
|
var bytes = RandomNumberGenerator.GetBytes(length);
|
||||||
return new string(bytes.Select(b => chars[b % chars.Length]).ToArray());
|
return new string(bytes.Select(b => chars[b % chars.Length]).ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task CreateAuditLog(string operation, string action, string targetType, long? targetId, string? targetName, string? oldValue = null, string? newValue = null)
|
||||||
|
{
|
||||||
|
var userName = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.Identity?.Name ?? "system";
|
||||||
|
var tenantId = User.FindFirstValue("TenantId");
|
||||||
|
|
||||||
|
var log = new AuditLog
|
||||||
|
{
|
||||||
|
Operator = userName,
|
||||||
|
TenantId = tenantId,
|
||||||
|
Operation = operation,
|
||||||
|
Action = action,
|
||||||
|
TargetType = targetType,
|
||||||
|
TargetId = targetId,
|
||||||
|
TargetName = targetName,
|
||||||
|
IpAddress = HttpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown",
|
||||||
|
Status = "success",
|
||||||
|
OldValue = oldValue,
|
||||||
|
NewValue = newValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
_context.AuditLogs.Add(log);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string SerializeToJson(object obj)
|
||||||
|
{
|
||||||
|
return System.Text.Json.JsonSerializer.Serialize(obj, new System.Text.Json.JsonSerializerOptions
|
||||||
|
{
|
||||||
|
WriteIndented = false
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CreateOAuthClientDto
|
public class CreateOAuthClientDto
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
|
using Fengling.AuthService.Data;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
|
using Fengling.AuthService.Models;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
|
|
||||||
using Fengling.Platform.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
@ -15,13 +13,13 @@ namespace Fengling.AuthService.Controllers;
|
|||||||
[Authorize]
|
[Authorize]
|
||||||
public class RolesController : ControllerBase
|
public class RolesController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly PlatformDbContext _context;
|
private readonly ApplicationDbContext _context;
|
||||||
private readonly RoleManager<ApplicationRole> _roleManager;
|
private readonly RoleManager<ApplicationRole> _roleManager;
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
private readonly ILogger<RolesController> _logger;
|
private readonly ILogger<RolesController> _logger;
|
||||||
|
|
||||||
public RolesController(
|
public RolesController(
|
||||||
PlatformDbContext context,
|
ApplicationDbContext context,
|
||||||
RoleManager<ApplicationRole> roleManager,
|
RoleManager<ApplicationRole> roleManager,
|
||||||
UserManager<ApplicationUser> userManager,
|
UserManager<ApplicationUser> userManager,
|
||||||
ILogger<RolesController> logger)
|
ILogger<RolesController> logger)
|
||||||
@ -124,7 +122,8 @@ public class RolesController : ControllerBase
|
|||||||
id = u.Id,
|
id = u.Id,
|
||||||
userName = u.UserName,
|
userName = u.UserName,
|
||||||
email = u.Email,
|
email = u.Email,
|
||||||
tenantId = u.TenantInfo.TenantId,
|
realName = u.RealName,
|
||||||
|
tenantId = u.TenantInfo.Id,
|
||||||
roles = await _userManager.GetRolesAsync(u),
|
roles = await _userManager.GetRolesAsync(u),
|
||||||
isActive = !u.LockoutEnabled || u.LockoutEnd == null || u.LockoutEnd < DateTimeOffset.UtcNow,
|
isActive = !u.LockoutEnabled || u.LockoutEnd == null || u.LockoutEnd < DateTimeOffset.UtcNow,
|
||||||
createdAt = u.CreatedTime,
|
createdAt = u.CreatedTime,
|
||||||
|
|||||||
@ -1,25 +1,26 @@
|
|||||||
|
using Fengling.AuthService.Data;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
|
using Fengling.AuthService.Models;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
|
|
||||||
using Fengling.Platform.Infrastructure;
|
|
||||||
using Fengling.Platform.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using OpenIddict.Abstractions;
|
|
||||||
|
|
||||||
namespace Fengling.AuthService.Controllers;
|
namespace Fengling.AuthService.Controllers;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public class StatsController(
|
public class StatsController : ControllerBase
|
||||||
PlatformDbContext context,
|
|
||||||
IOpenIddictApplicationManager applicationManager,
|
|
||||||
ILogger<StatsController> logger,
|
|
||||||
PlatformDbContext platformDbContext)
|
|
||||||
: ControllerBase
|
|
||||||
{
|
{
|
||||||
|
private readonly ApplicationDbContext _context;
|
||||||
|
private readonly ILogger<StatsController> _logger;
|
||||||
|
|
||||||
|
public StatsController(
|
||||||
|
ApplicationDbContext context,
|
||||||
|
ILogger<StatsController> logger)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet("dashboard")]
|
[HttpGet("dashboard")]
|
||||||
public async Task<ActionResult<object>> GetDashboardStats()
|
public async Task<ActionResult<object>> GetDashboardStats()
|
||||||
@ -27,10 +28,10 @@ public class StatsController(
|
|||||||
var today = DateTime.UtcNow.Date;
|
var today = DateTime.UtcNow.Date;
|
||||||
var tomorrow = today.AddDays(1);
|
var tomorrow = today.AddDays(1);
|
||||||
|
|
||||||
var userCount = await context.Users.CountAsync(u => !u.IsDeleted);
|
var userCount = await _context.Users.CountAsync(u => !u.IsDeleted);
|
||||||
var tenantCount = await platformDbContext.Tenants.CountAsync(t => !t.IsDeleted);
|
var tenantCount = await _context.Tenants.CountAsync(t => !t.IsDeleted);
|
||||||
var oauthClientCount = await CountOAuthClientsAsync();
|
var oauthClientCount = await _context.OAuthApplications.CountAsync();
|
||||||
var todayAccessCount = await context.AccessLogs
|
var todayAccessCount = await _context.AccessLogs
|
||||||
.CountAsync(l => l.CreatedAt >= today && l.CreatedAt < tomorrow);
|
.CountAsync(l => l.CreatedAt >= today && l.CreatedAt < tomorrow);
|
||||||
|
|
||||||
return Ok(new
|
return Ok(new
|
||||||
@ -42,17 +43,6 @@ public class StatsController(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> CountOAuthClientsAsync()
|
|
||||||
{
|
|
||||||
var count = 0;
|
|
||||||
var applications = applicationManager.ListAsync();
|
|
||||||
await foreach (var _ in applications)
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("system")]
|
[HttpGet("system")]
|
||||||
public ActionResult<object> GetSystemStats()
|
public ActionResult<object> GetSystemStats()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,49 +1,54 @@
|
|||||||
|
using Fengling.AuthService.Data;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
|
using Fengling.AuthService.Models;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
|
|
||||||
using Fengling.Platform.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
|
|
||||||
using Fengling.Platform.Infrastructure;
|
|
||||||
|
|
||||||
namespace Fengling.AuthService.Controllers;
|
namespace Fengling.AuthService.Controllers;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public class TenantsController(
|
public class TenantsController : ControllerBase
|
||||||
PlatformDbContext context,
|
|
||||||
UserManager<ApplicationUser> userManager,
|
|
||||||
ILogger<TenantsController> logger,
|
|
||||||
PlatformDbContext platformDbContext)
|
|
||||||
: ControllerBase
|
|
||||||
{
|
{
|
||||||
|
private readonly ApplicationDbContext _context;
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly ILogger<TenantsController> _logger;
|
||||||
|
|
||||||
|
public TenantsController(
|
||||||
|
ApplicationDbContext context,
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
ILogger<TenantsController> logger)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_userManager = userManager;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<ActionResult<object>> GetTenants(
|
public async Task<ActionResult<object>> GetTenants(
|
||||||
[FromQuery] int page = 1,
|
[FromQuery] int page = 1,
|
||||||
[FromQuery] int pageSize = 10,
|
[FromQuery] int pageSize = 10,
|
||||||
[FromQuery] string? name = null,
|
[FromQuery] string? name = null,
|
||||||
[FromQuery] string? tenantCode = null,
|
[FromQuery] string? tenantId = null,
|
||||||
[FromQuery] TenantStatus? status = null)
|
[FromQuery] string? status = null)
|
||||||
{
|
{
|
||||||
var query = platformDbContext.Tenants.AsQueryable();
|
var query = _context.Tenants.AsQueryable();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(name))
|
if (!string.IsNullOrEmpty(name))
|
||||||
{
|
{
|
||||||
query = query.Where(t => t.Name.Contains(name));
|
query = query.Where(t => t.Name.Contains(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(tenantCode))
|
if (!string.IsNullOrEmpty(tenantId))
|
||||||
{
|
{
|
||||||
query = query.Where(t => t.TenantCode.Contains(tenantCode));
|
query = query.Where(t => t.TenantId.Contains(tenantId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.HasValue)
|
if (!string.IsNullOrEmpty(status))
|
||||||
{
|
{
|
||||||
query = query.Where(t => t.Status == status);
|
query = query.Where(t => t.Status == status);
|
||||||
}
|
}
|
||||||
@ -59,11 +64,11 @@ public class TenantsController(
|
|||||||
|
|
||||||
foreach (var tenant in tenants)
|
foreach (var tenant in tenants)
|
||||||
{
|
{
|
||||||
var userCount = await context.Users.CountAsync(u => u.TenantInfo.TenantId == tenant.Id && !u.IsDeleted);
|
var userCount = await _context.Users.CountAsync(u => u.TenantInfo.Id == tenant.Id && !u.IsDeleted);
|
||||||
result.Add(new
|
result.Add(new
|
||||||
{
|
{
|
||||||
id = tenant.Id,
|
id = tenant.Id,
|
||||||
tenantId = tenant.Id,
|
tenantId = tenant.TenantId,
|
||||||
name = tenant.Name,
|
name = tenant.Name,
|
||||||
contactName = tenant.ContactName,
|
contactName = tenant.ContactName,
|
||||||
contactEmail = tenant.ContactEmail,
|
contactEmail = tenant.ContactEmail,
|
||||||
@ -89,7 +94,7 @@ public class TenantsController(
|
|||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
public async Task<ActionResult<Tenant>> GetTenant(long id)
|
public async Task<ActionResult<Tenant>> GetTenant(long id)
|
||||||
{
|
{
|
||||||
var tenant = await platformDbContext.Tenants.FindAsync(id);
|
var tenant = await _context.Tenants.FindAsync(id);
|
||||||
if (tenant == null)
|
if (tenant == null)
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
@ -98,7 +103,7 @@ public class TenantsController(
|
|||||||
return Ok(new
|
return Ok(new
|
||||||
{
|
{
|
||||||
id = tenant.Id,
|
id = tenant.Id,
|
||||||
tenantId = tenant.Id,
|
tenantId = tenant.TenantId,
|
||||||
name = tenant.Name,
|
name = tenant.Name,
|
||||||
contactName = tenant.ContactName,
|
contactName = tenant.ContactName,
|
||||||
contactEmail = tenant.ContactEmail,
|
contactEmail = tenant.ContactEmail,
|
||||||
@ -113,21 +118,16 @@ public class TenantsController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{tenantId}/users")]
|
[HttpGet("{tenantId}/users")]
|
||||||
public async Task<ActionResult<List<object>>> GetTenantUsers(long tenantId)
|
public async Task<ActionResult<List<object>>> GetTenantUsers(string tenantId)
|
||||||
{
|
{
|
||||||
var tenant = await platformDbContext.Tenants
|
var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.TenantId == tenantId);
|
||||||
.FirstOrDefaultAsync(t => t.Id == tenantId);
|
|
||||||
if (tenant == null)
|
if (tenant == null)
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
var users = await context.Users
|
var users = await _context.Users
|
||||||
.Where(u =>
|
.Where(u => u.TenantInfo.Id == tenant.Id && !u.IsDeleted)
|
||||||
u.TenantInfo != null
|
|
||||||
&&
|
|
||||||
u.TenantInfo.TenantId == tenant.Id
|
|
||||||
&& !u.IsDeleted)
|
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var result = users.Select(async u => new
|
var result = users.Select(async u => new
|
||||||
@ -135,8 +135,9 @@ public class TenantsController(
|
|||||||
id = u.Id,
|
id = u.Id,
|
||||||
userName = u.UserName,
|
userName = u.UserName,
|
||||||
email = u.Email,
|
email = u.Email,
|
||||||
tenantId = u.TenantInfo?.TenantId,
|
realName = u.RealName,
|
||||||
roles = await userManager.GetRolesAsync(u),
|
tenantId = u.TenantInfo.Id,
|
||||||
|
roles = await _userManager.GetRolesAsync(u),
|
||||||
isActive = !u.LockoutEnabled || u.LockoutEnd == null || u.LockoutEnd < DateTimeOffset.UtcNow,
|
isActive = !u.LockoutEnabled || u.LockoutEnd == null || u.LockoutEnd < DateTimeOffset.UtcNow,
|
||||||
createdAt = u.CreatedTime,
|
createdAt = u.CreatedTime,
|
||||||
});
|
});
|
||||||
@ -145,16 +146,15 @@ public class TenantsController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{tenantId}/roles")]
|
[HttpGet("{tenantId}/roles")]
|
||||||
public async Task<ActionResult<List<object>>> GetTenantRoles(long tenantId)
|
public async Task<ActionResult<List<object>>> GetTenantRoles(string tenantId)
|
||||||
{
|
{
|
||||||
var tenant = await platformDbContext.Tenants
|
var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.TenantId == tenantId);
|
||||||
.FirstOrDefaultAsync(t => t.Id == tenantId);
|
|
||||||
if (tenant == null)
|
if (tenant == null)
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
var roles = await context.Roles
|
var roles = await _context.Roles
|
||||||
.Where(r => r.TenantId == tenant.Id)
|
.Where(r => r.TenantId == tenant.Id)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
@ -169,10 +169,9 @@ public class TenantsController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{tenantId}/settings")]
|
[HttpGet("{tenantId}/settings")]
|
||||||
public async Task<ActionResult<TenantSettings>> GetTenantSettings(long tenantId)
|
public async Task<ActionResult<TenantSettings>> GetTenantSettings(string tenantId)
|
||||||
{
|
{
|
||||||
var tenant = await platformDbContext.Tenants
|
var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.TenantId == tenantId);
|
||||||
.FirstOrDefaultAsync(t => t.Id == tenantId);
|
|
||||||
if (tenant == null)
|
if (tenant == null)
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
@ -192,16 +191,15 @@ public class TenantsController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut("{tenantId}/settings")]
|
[HttpPut("{tenantId}/settings")]
|
||||||
public async Task<IActionResult> UpdateTenantSettings(long tenantId, TenantSettings settings)
|
public async Task<IActionResult> UpdateTenantSettings(string tenantId, TenantSettings settings)
|
||||||
{
|
{
|
||||||
var tenant = await platformDbContext.Tenants.FirstOrDefaultAsync(t => t.Id == tenantId);
|
var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.TenantId == tenantId);
|
||||||
if (tenant == null)
|
if (tenant == null)
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
await CreateAuditLog("tenant", "update", "TenantSettings", tenant.Id, tenant.Name, null,
|
await CreateAuditLog("tenant", "update", "TenantSettings", tenant.Id, tenant.TenantId, null, JsonSerializer.Serialize(settings));
|
||||||
JsonSerializer.Serialize(settings));
|
|
||||||
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
@ -211,23 +209,22 @@ public class TenantsController(
|
|||||||
{
|
{
|
||||||
var tenant = new Tenant
|
var tenant = new Tenant
|
||||||
{
|
{
|
||||||
TenantCode = dto.TenantCode,
|
TenantId = dto.TenantId,
|
||||||
Name = dto.TenantName,
|
Name = dto.Name,
|
||||||
ContactName = dto.ContactName,
|
ContactName = dto.ContactName,
|
||||||
ContactEmail = dto.ContactEmail,
|
ContactEmail = dto.ContactEmail,
|
||||||
ContactPhone = dto.ContactPhone,
|
ContactPhone = dto.ContactPhone,
|
||||||
MaxUsers = dto.MaxUsers,
|
MaxUsers = dto.MaxUsers,
|
||||||
Description = dto.Description,
|
Description = dto.Description,
|
||||||
|
Status = dto.Status,
|
||||||
ExpiresAt = dto.ExpiresAt,
|
ExpiresAt = dto.ExpiresAt,
|
||||||
Status = TenantStatus.Active,
|
|
||||||
CreatedAt = DateTime.UtcNow,
|
CreatedAt = DateTime.UtcNow,
|
||||||
};
|
};
|
||||||
|
|
||||||
platformDbContext.Tenants.Add(tenant);
|
_context.Tenants.Add(tenant);
|
||||||
await context.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
await CreateAuditLog("tenant", "create", "Tenant", tenant.Id, tenant.TenantCode,
|
await CreateAuditLog("tenant", "create", "Tenant", tenant.Id, tenant.TenantId, null, JsonSerializer.Serialize(dto));
|
||||||
null, JsonSerializer.Serialize(dto));
|
|
||||||
|
|
||||||
return CreatedAtAction(nameof(GetTenant), new { id = tenant.Id }, tenant);
|
return CreatedAtAction(nameof(GetTenant), new { id = tenant.Id }, tenant);
|
||||||
}
|
}
|
||||||
@ -235,24 +232,27 @@ public class TenantsController(
|
|||||||
[HttpPut("{id}")]
|
[HttpPut("{id}")]
|
||||||
public async Task<IActionResult> UpdateTenant(long id, UpdateTenantDto dto)
|
public async Task<IActionResult> UpdateTenant(long id, UpdateTenantDto dto)
|
||||||
{
|
{
|
||||||
var tenant = await platformDbContext.Tenants.FindAsync(id);
|
var tenant = await _context.Tenants.FindAsync(id);
|
||||||
if (tenant == null)
|
if (tenant == null)
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
var oldValue = JsonSerializer.Serialize(tenant);
|
var oldValue = JsonSerializer.Serialize(tenant);
|
||||||
|
|
||||||
tenant.Name = dto.Name;
|
tenant.Name = dto.Name;
|
||||||
tenant.ContactName = dto.ContactName;
|
tenant.ContactName = dto.ContactName;
|
||||||
tenant.ContactEmail = dto.ContactEmail;
|
tenant.ContactEmail = dto.ContactEmail;
|
||||||
tenant.ContactPhone = dto.ContactPhone;
|
tenant.ContactPhone = dto.ContactPhone;
|
||||||
|
tenant.MaxUsers = dto.MaxUsers;
|
||||||
|
tenant.Description = dto.Description;
|
||||||
|
tenant.Status = dto.Status;
|
||||||
|
tenant.ExpiresAt = dto.ExpiresAt;
|
||||||
tenant.UpdatedAt = DateTime.UtcNow;
|
tenant.UpdatedAt = DateTime.UtcNow;
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
await context.SaveChangesAsync();
|
await CreateAuditLog("tenant", "update", "Tenant", tenant.Id, tenant.TenantId, oldValue, JsonSerializer.Serialize(tenant));
|
||||||
|
|
||||||
await CreateAuditLog("tenant", "update", "Tenant", tenant.Id, tenant.Name, oldValue,
|
|
||||||
JsonSerializer.Serialize(tenant));
|
|
||||||
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
@ -260,7 +260,7 @@ public class TenantsController(
|
|||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
public async Task<IActionResult> DeleteTenant(long id)
|
public async Task<IActionResult> DeleteTenant(long id)
|
||||||
{
|
{
|
||||||
var tenant = await platformDbContext.Tenants.FindAsync(id);
|
var tenant = await _context.Tenants.FindAsync(id);
|
||||||
if (tenant == null)
|
if (tenant == null)
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
@ -268,7 +268,7 @@ public class TenantsController(
|
|||||||
|
|
||||||
var oldValue = JsonSerializer.Serialize(tenant);
|
var oldValue = JsonSerializer.Serialize(tenant);
|
||||||
|
|
||||||
var users = await context.Users.Where(u => u.TenantInfo.TenantId == tenant.Id).ToListAsync();
|
var users = await _context.Users.Where(u => u.TenantInfo.Id == tenant.Id).ToListAsync();
|
||||||
foreach (var user in users)
|
foreach (var user in users)
|
||||||
{
|
{
|
||||||
user.IsDeleted = true;
|
user.IsDeleted = true;
|
||||||
@ -276,16 +276,14 @@ public class TenantsController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
tenant.IsDeleted = true;
|
tenant.IsDeleted = true;
|
||||||
tenant.UpdatedAt = DateTime.UtcNow;
|
await _context.SaveChangesAsync();
|
||||||
await context.SaveChangesAsync();
|
|
||||||
|
|
||||||
await CreateAuditLog("tenant", "delete", "Tenant", tenant.Id, tenant.Name, oldValue);
|
await CreateAuditLog("tenant", "delete", "Tenant", tenant.Id, tenant.TenantId, oldValue);
|
||||||
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CreateAuditLog(string operation, string action, string targetType, long? targetId,
|
private async Task CreateAuditLog(string operation, string action, string targetType, long? targetId, string? targetName, string? oldValue = null, string? newValue = null)
|
||||||
string? targetName, string? oldValue = null, string? newValue = null)
|
|
||||||
{
|
{
|
||||||
var userName = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.Identity?.Name ?? "system";
|
var userName = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.Identity?.Name ?? "system";
|
||||||
var tenantId = User.FindFirstValue("TenantId");
|
var tenantId = User.FindFirstValue("TenantId");
|
||||||
@ -305,21 +303,23 @@ public class TenantsController(
|
|||||||
NewValue = newValue,
|
NewValue = newValue,
|
||||||
};
|
};
|
||||||
|
|
||||||
context.AuditLogs.Add(log);
|
_context.AuditLogs.Add(log);
|
||||||
await context.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public record CreateTenantDto(
|
public class CreateTenantDto
|
||||||
string TenantCode,
|
{
|
||||||
string TenantName,
|
public string TenantId { get; set; } = string.Empty;
|
||||||
string ContactName,
|
public string Name { get; set; } = string.Empty;
|
||||||
string ContactEmail,
|
public string ContactName { get; set; } = string.Empty;
|
||||||
string ContactPhone,
|
public string ContactEmail { get; set; } = string.Empty;
|
||||||
int? MaxUsers,
|
public string? ContactPhone { get; set; }
|
||||||
string? Description,
|
public int? MaxUsers { get; set; }
|
||||||
string Status,
|
public string? Description { get; set; }
|
||||||
DateTime? ExpiresAt);
|
public string Status { get; set; } = "active";
|
||||||
|
public DateTime? ExpiresAt { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class UpdateTenantDto
|
public class UpdateTenantDto
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
|
using Fengling.AuthService.Data;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
|
using Fengling.AuthService.Models;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
|
|
||||||
using Fengling.Platform.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
@ -25,13 +23,14 @@ public class TokenController(
|
|||||||
ILogger<TokenController> logger)
|
ILogger<TokenController> logger)
|
||||||
: ControllerBase
|
: ControllerBase
|
||||||
{
|
{
|
||||||
|
private readonly ILogger<TokenController> _logger = logger;
|
||||||
|
|
||||||
[HttpPost("token")]
|
[HttpPost("token")]
|
||||||
public async Task<IActionResult> Exchange()
|
public async Task<IActionResult> Exchange()
|
||||||
{
|
{
|
||||||
var request = HttpContext.GetOpenIddictServerRequest() ??
|
var request = HttpContext.GetOpenIddictServerRequest() ??
|
||||||
throw new InvalidOperationException("OpenIddict request is null");
|
throw new InvalidOperationException("OpenIddict request is null");
|
||||||
var result = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
|
var result = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme);
|
||||||
|
|
||||||
if (request.IsAuthorizationCodeGrantType())
|
if (request.IsAuthorizationCodeGrantType())
|
||||||
{
|
{
|
||||||
@ -97,9 +96,9 @@ public class TokenController(
|
|||||||
var claims = new List<Claim>
|
var claims = new List<Claim>
|
||||||
{
|
{
|
||||||
new(Claims.Subject, await userManager.GetUserIdAsync(user)),
|
new(Claims.Subject, await userManager.GetUserIdAsync(user)),
|
||||||
new(Claims.Name, await userManager.GetUserNameAsync(user) ?? throw new InvalidOperationException()),
|
new(Claims.Name, await userManager.GetUserNameAsync(user)),
|
||||||
new(Claims.Email, await userManager.GetEmailAsync(user) ?? ""),
|
new(Claims.Email, await userManager.GetEmailAsync(user) ?? ""),
|
||||||
new("tenant_id", user.TenantInfo.TenantId.ToString())
|
new("tenant_id", user.TenantInfo.Id.ToString())
|
||||||
};
|
};
|
||||||
|
|
||||||
var roles = await userManager.GetRolesAsync(user);
|
var roles = await userManager.GetRolesAsync(user);
|
||||||
@ -111,12 +110,6 @@ public class TokenController(
|
|||||||
var identity = new ClaimsIdentity(claims, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
|
var identity = new ClaimsIdentity(claims, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
|
||||||
var principal = new ClaimsPrincipal(identity);
|
var principal = new ClaimsPrincipal(identity);
|
||||||
|
|
||||||
// 设置 Claim Destinations
|
|
||||||
foreach (var claim in principal.Claims)
|
|
||||||
{
|
|
||||||
claim.SetDestinations(GetDestinations(claim, principal));
|
|
||||||
}
|
|
||||||
|
|
||||||
principal.SetScopes(request.GetScopes());
|
principal.SetScopes(request.GetScopes());
|
||||||
principal.SetResources(await scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync());
|
principal.SetResources(await scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync());
|
||||||
principal.SetAuthorizationId(await authorizationManager.GetIdAsync(authorization));
|
principal.SetAuthorizationId(await authorizationManager.GetIdAsync(authorization));
|
||||||
@ -206,14 +199,8 @@ public class TokenController(
|
|||||||
|
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
// 明确处理租户 ID - 这是业务关键信息
|
|
||||||
case "tenant_id":
|
|
||||||
yield return OpenIddictConstants.Destinations.AccessToken;
|
|
||||||
yield break;
|
|
||||||
|
|
||||||
// Never include the security stamp in the access and identity tokens, as it's a secret value.
|
// Never include the security stamp in the access and identity tokens, as it's a secret value.
|
||||||
case "AspNet.Identity.SecurityStamp":
|
case "AspNet.Identity.SecurityStamp": yield break;
|
||||||
yield break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
yield return OpenIddictConstants.Destinations.AccessToken;
|
yield return OpenIddictConstants.Destinations.AccessToken;
|
||||||
@ -246,9 +233,9 @@ public class TokenController(
|
|||||||
var claims = new List<Claim>
|
var claims = new List<Claim>
|
||||||
{
|
{
|
||||||
new(Claims.Subject, await userManager.GetUserIdAsync(user)),
|
new(Claims.Subject, await userManager.GetUserIdAsync(user)),
|
||||||
new(Claims.Name, await userManager.GetUserNameAsync(user) ?? string.Empty),
|
new(Claims.Name, await userManager.GetUserNameAsync(user)),
|
||||||
new(Claims.Email, await userManager.GetEmailAsync(user) ?? ""),
|
new(Claims.Email, await userManager.GetEmailAsync(user) ?? ""),
|
||||||
new("tenant_id", user.TenantInfo.TenantId.ToString())
|
new("tenant_id", user.TenantInfo.Id.ToString())
|
||||||
};
|
};
|
||||||
|
|
||||||
var roles = await userManager.GetRolesAsync(user);
|
var roles = await userManager.GetRolesAsync(user);
|
||||||
@ -260,12 +247,6 @@ public class TokenController(
|
|||||||
var identity = new ClaimsIdentity(claims, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
|
var identity = new ClaimsIdentity(claims, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
|
||||||
var principal = new ClaimsPrincipal(identity);
|
var principal = new ClaimsPrincipal(identity);
|
||||||
|
|
||||||
// 设置 Claim Destinations
|
|
||||||
foreach (var claim in principal.Claims)
|
|
||||||
{
|
|
||||||
claim.SetDestinations(GetDestinations(claim, principal));
|
|
||||||
}
|
|
||||||
|
|
||||||
principal.SetScopes(request.GetScopes());
|
principal.SetScopes(request.GetScopes());
|
||||||
principal.SetResources(await scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync());
|
principal.SetResources(await scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync());
|
||||||
|
|
||||||
|
|||||||
@ -1,25 +1,34 @@
|
|||||||
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
|
using Fengling.AuthService.Data;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
|
using Fengling.AuthService.Models;
|
||||||
using Fengling.Platform.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
|
|
||||||
|
|
||||||
namespace Fengling.AuthService.Controllers;
|
namespace Fengling.AuthService.Controllers;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public class UsersController(
|
public class UsersController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ApplicationDbContext _context;
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly RoleManager<ApplicationRole> _roleManager;
|
||||||
|
private readonly ILogger<UsersController> _logger;
|
||||||
|
|
||||||
|
public UsersController(
|
||||||
|
ApplicationDbContext context,
|
||||||
UserManager<ApplicationUser> userManager,
|
UserManager<ApplicationUser> userManager,
|
||||||
RoleManager<ApplicationRole> roleManager,
|
RoleManager<ApplicationRole> roleManager,
|
||||||
ILogger<UsersController> logger,
|
ILogger<UsersController> logger)
|
||||||
PlatformDbContext platformDbContext)
|
{
|
||||||
: ControllerBase
|
_context = context;
|
||||||
{
|
_userManager = userManager;
|
||||||
|
_roleManager = roleManager;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<ActionResult<object>> GetUsers(
|
public async Task<ActionResult<object>> GetUsers(
|
||||||
@ -27,9 +36,9 @@ public class UsersController(
|
|||||||
[FromQuery] int pageSize = 10,
|
[FromQuery] int pageSize = 10,
|
||||||
[FromQuery] string? userName = null,
|
[FromQuery] string? userName = null,
|
||||||
[FromQuery] string? email = null,
|
[FromQuery] string? email = null,
|
||||||
[FromQuery] string? tenantCode = null)
|
[FromQuery] string? tenantId = null)
|
||||||
{
|
{
|
||||||
var query = platformDbContext.Users.AsQueryable();
|
var query = _context.Users.AsQueryable();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(userName))
|
if (!string.IsNullOrEmpty(userName))
|
||||||
{
|
{
|
||||||
@ -41,9 +50,9 @@ public class UsersController(
|
|||||||
query = query.Where(u => u.Email != null && u.Email.Contains(email));
|
query = query.Where(u => u.Email != null && u.Email.Contains(email));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(tenantCode))
|
if (!string.IsNullOrEmpty(tenantId))
|
||||||
{
|
{
|
||||||
query = query.Where(u => u.TenantInfo.TenantCode == tenantCode);
|
query = query.Where(u => u.TenantInfo.Id.ToString() == tenantId);
|
||||||
}
|
}
|
||||||
|
|
||||||
var totalCount = await query.CountAsync();
|
var totalCount = await query.CountAsync();
|
||||||
@ -58,8 +67,10 @@ public class UsersController(
|
|||||||
id = u.Id,
|
id = u.Id,
|
||||||
userName = u.UserName,
|
userName = u.UserName,
|
||||||
email = u.Email,
|
email = u.Email,
|
||||||
tenantId = u.TenantInfo.TenantId,
|
realName = u.RealName,
|
||||||
roles = (await userManager.GetRolesAsync(u)).ToList(),
|
phone = u.Phone,
|
||||||
|
tenantId = u.TenantInfo.Id,
|
||||||
|
roles = (await _userManager.GetRolesAsync(u)).ToList(),
|
||||||
emailConfirmed = u.EmailConfirmed,
|
emailConfirmed = u.EmailConfirmed,
|
||||||
isActive = !u.LockoutEnabled || u.LockoutEnd == null || u.LockoutEnd < DateTimeOffset.UtcNow,
|
isActive = !u.LockoutEnabled || u.LockoutEnd == null || u.LockoutEnd < DateTimeOffset.UtcNow,
|
||||||
createdAt = u.CreatedTime,
|
createdAt = u.CreatedTime,
|
||||||
@ -77,20 +88,22 @@ public class UsersController(
|
|||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
public async Task<ActionResult<object>> GetUser(long id)
|
public async Task<ActionResult<object>> GetUser(long id)
|
||||||
{
|
{
|
||||||
var user = await platformDbContext.Users.FindAsync(id);
|
var user = await _context.Users.FindAsync(id);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
var roles = await userManager.GetRolesAsync(user);
|
var roles = await _userManager.GetRolesAsync(user);
|
||||||
|
|
||||||
return Ok(new
|
return Ok(new
|
||||||
{
|
{
|
||||||
id = user.Id,
|
id = user.Id,
|
||||||
userName = user.UserName,
|
userName = user.UserName,
|
||||||
email = user.Email,
|
email = user.Email,
|
||||||
tenantId = user.TenantInfo.TenantId,
|
realName = user.RealName,
|
||||||
|
phone = user.Phone,
|
||||||
|
tenantId = user.TenantInfo.Id,
|
||||||
roles,
|
roles,
|
||||||
emailConfirmed = user.EmailConfirmed,
|
emailConfirmed = user.EmailConfirmed,
|
||||||
isActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow,
|
isActive = !user.LockoutEnabled || user.LockoutEnd == null || user.LockoutEnd < DateTimeOffset.UtcNow,
|
||||||
@ -102,11 +115,11 @@ public class UsersController(
|
|||||||
public async Task<ActionResult<ApplicationUser>> CreateUser(CreateUserDto dto)
|
public async Task<ActionResult<ApplicationUser>> CreateUser(CreateUserDto dto)
|
||||||
{
|
{
|
||||||
var tenantId = dto.TenantId ?? 0;
|
var tenantId = dto.TenantId ?? 0;
|
||||||
Tenant? tenant = null;
|
Tenant tenant = null;
|
||||||
|
|
||||||
if (tenantId != 0)
|
if (tenantId != 0)
|
||||||
{
|
{
|
||||||
tenant = await platformDbContext.Tenants.FindAsync(tenantId);
|
tenant = await _context.Tenants.FindAsync(tenantId);
|
||||||
if (tenant == null)
|
if (tenant == null)
|
||||||
{
|
{
|
||||||
return BadRequest("Invalid tenant ID");
|
return BadRequest("Invalid tenant ID");
|
||||||
@ -117,33 +130,35 @@ public class UsersController(
|
|||||||
{
|
{
|
||||||
UserName = dto.UserName,
|
UserName = dto.UserName,
|
||||||
Email = dto.Email,
|
Email = dto.Email,
|
||||||
TenantInfo = new TenantInfo(tenant!),
|
RealName = dto.RealName,
|
||||||
|
Phone = dto.Phone,
|
||||||
|
TenantInfo = new TenantInfo(tenantId, tenant?.TenantId ?? "default", tenant?.Name ?? "默认租户"),
|
||||||
EmailConfirmed = dto.EmailConfirmed,
|
EmailConfirmed = dto.EmailConfirmed,
|
||||||
CreatedTime = DateTimeOffset.UtcNow,
|
CreatedTime = DateTime.UtcNow,
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = await userManager.CreateAsync(user, dto.Password);
|
var result = await _userManager.CreateAsync(user, dto.Password);
|
||||||
if (!result.Succeeded)
|
if (!result.Succeeded)
|
||||||
{
|
{
|
||||||
return BadRequest(result.Errors);
|
return BadRequest(result.Errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dto.RoleIds.Any())
|
if (dto.RoleIds != null && dto.RoleIds.Any())
|
||||||
{
|
{
|
||||||
foreach (var roleId in dto.RoleIds)
|
foreach (var roleId in dto.RoleIds)
|
||||||
{
|
{
|
||||||
var role = await roleManager.FindByIdAsync(roleId.ToString());
|
var role = await _roleManager.FindByIdAsync(roleId.ToString());
|
||||||
if (role != null)
|
if (role != null)
|
||||||
{
|
{
|
||||||
await userManager.AddToRoleAsync(user, role.Name!);
|
await _userManager.AddToRoleAsync(user, role.Name!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dto.IsActive)
|
if (!dto.IsActive)
|
||||||
{
|
{
|
||||||
await userManager.SetLockoutEnabledAsync(user, true);
|
await _userManager.SetLockoutEnabledAsync(user, true);
|
||||||
await userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue);
|
await _userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
await CreateAuditLog("user", "create", "User", user.Id, user.UserName, null, SerializeToJson(dto));
|
await CreateAuditLog("user", "create", "User", user.Id, user.UserName, null, SerializeToJson(dto));
|
||||||
@ -154,7 +169,7 @@ public class UsersController(
|
|||||||
[HttpPut("{id}")]
|
[HttpPut("{id}")]
|
||||||
public async Task<IActionResult> UpdateUser(long id, UpdateUserDto dto)
|
public async Task<IActionResult> UpdateUser(long id, UpdateUserDto dto)
|
||||||
{
|
{
|
||||||
var user = await platformDbContext.Users.FindAsync(id);
|
var user = await _context.Users.FindAsync(id);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
@ -163,21 +178,23 @@ public class UsersController(
|
|||||||
var oldValue = System.Text.Json.JsonSerializer.Serialize(user);
|
var oldValue = System.Text.Json.JsonSerializer.Serialize(user);
|
||||||
|
|
||||||
user.Email = dto.Email;
|
user.Email = dto.Email;
|
||||||
|
user.RealName = dto.RealName;
|
||||||
|
user.Phone = dto.Phone;
|
||||||
user.EmailConfirmed = dto.EmailConfirmed;
|
user.EmailConfirmed = dto.EmailConfirmed;
|
||||||
user.UpdatedTime = DateTimeOffset.UtcNow;
|
user.UpdatedTime = DateTime.UtcNow;
|
||||||
|
|
||||||
if (dto.IsActive)
|
if (dto.IsActive)
|
||||||
{
|
{
|
||||||
await userManager.SetLockoutEnabledAsync(user, false);
|
await _userManager.SetLockoutEnabledAsync(user, false);
|
||||||
await userManager.SetLockoutEndDateAsync(user, null);
|
await _userManager.SetLockoutEndDateAsync(user, null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await userManager.SetLockoutEnabledAsync(user, true);
|
await _userManager.SetLockoutEnabledAsync(user, true);
|
||||||
await userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue);
|
await _userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
await platformDbContext.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
await CreateAuditLog("user", "update", "User", user.Id, user.UserName, oldValue, System.Text.Json.JsonSerializer.Serialize(user));
|
await CreateAuditLog("user", "update", "User", user.Id, user.UserName, oldValue, System.Text.Json.JsonSerializer.Serialize(user));
|
||||||
|
|
||||||
@ -187,14 +204,14 @@ public class UsersController(
|
|||||||
[HttpPut("{id}/password")]
|
[HttpPut("{id}/password")]
|
||||||
public async Task<IActionResult> ResetPassword(long id, ResetPasswordDto dto)
|
public async Task<IActionResult> ResetPassword(long id, ResetPasswordDto dto)
|
||||||
{
|
{
|
||||||
var user = await userManager.FindByIdAsync(id.ToString());
|
var user = await _userManager.FindByIdAsync(id.ToString());
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
var token = await userManager.GeneratePasswordResetTokenAsync(user);
|
var token = await _userManager.GeneratePasswordResetTokenAsync(user);
|
||||||
var result = await userManager.ResetPasswordAsync(user, token, dto.NewPassword);
|
var result = await _userManager.ResetPasswordAsync(user, token, dto.NewPassword);
|
||||||
|
|
||||||
if (!result.Succeeded)
|
if (!result.Succeeded)
|
||||||
{
|
{
|
||||||
@ -209,7 +226,7 @@ public class UsersController(
|
|||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
public async Task<IActionResult> DeleteUser(long id)
|
public async Task<IActionResult> DeleteUser(long id)
|
||||||
{
|
{
|
||||||
var user = await platformDbContext.Users.FindAsync(id);
|
var user = await _context.Users.FindAsync(id);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
@ -218,7 +235,7 @@ public class UsersController(
|
|||||||
var oldValue = System.Text.Json.JsonSerializer.Serialize(user);
|
var oldValue = System.Text.Json.JsonSerializer.Serialize(user);
|
||||||
user.IsDeleted = true;
|
user.IsDeleted = true;
|
||||||
user.UpdatedTime = DateTime.UtcNow;
|
user.UpdatedTime = DateTime.UtcNow;
|
||||||
await platformDbContext.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
await CreateAuditLog("user", "delete", "User", user.Id, user.UserName, oldValue);
|
await CreateAuditLog("user", "delete", "User", user.Id, user.UserName, oldValue);
|
||||||
|
|
||||||
@ -245,8 +262,8 @@ public class UsersController(
|
|||||||
NewValue = newValue,
|
NewValue = newValue,
|
||||||
};
|
};
|
||||||
|
|
||||||
platformDbContext.AuditLogs.Add(log);
|
_context.AuditLogs.Add(log);
|
||||||
await platformDbContext.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private string SerializeToJson(object obj)
|
private string SerializeToJson(object obj)
|
||||||
|
|||||||
106
Data/ApplicationDbContext.cs
Normal file
106
Data/ApplicationDbContext.cs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
using Fengling.AuthService.Models;
|
||||||
|
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Fengling.AuthService.Data;
|
||||||
|
|
||||||
|
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, long>
|
||||||
|
{
|
||||||
|
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
|
||||||
|
: base(options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbSet<OAuthApplication> OAuthApplications { get; set; }
|
||||||
|
public DbSet<Tenant> Tenants { get; set; }
|
||||||
|
public DbSet<AccessLog> AccessLogs { get; set; }
|
||||||
|
public DbSet<AuditLog> AuditLogs { get; set; }
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder builder)
|
||||||
|
{
|
||||||
|
base.OnModelCreating(builder);
|
||||||
|
|
||||||
|
builder.Entity<ApplicationUser>(entity =>
|
||||||
|
{
|
||||||
|
entity.Property(e => e.RealName).HasMaxLength(100);
|
||||||
|
entity.Property(e => e.Phone).HasMaxLength(20);
|
||||||
|
entity.HasIndex(e => e.Phone).IsUnique();
|
||||||
|
|
||||||
|
entity.OwnsOne(e => e.TenantInfo, navigationBuilder =>
|
||||||
|
{
|
||||||
|
navigationBuilder.Property(e => e.Id).HasColumnName("TenantId");
|
||||||
|
navigationBuilder.Property(e => e.TenantId).HasColumnName("TenantCode");
|
||||||
|
navigationBuilder.Property(e => e.Name).HasColumnName("TenantName");
|
||||||
|
navigationBuilder.WithOwner();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Entity<ApplicationRole>(entity =>
|
||||||
|
{
|
||||||
|
entity.Property(e => e.Description).HasMaxLength(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Entity<OAuthApplication>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.Id);
|
||||||
|
entity.HasIndex(e => e.ClientId).IsUnique();
|
||||||
|
entity.Property(e => e.ClientId).HasMaxLength(100);
|
||||||
|
entity.Property(e => e.ClientSecret).HasMaxLength(200);
|
||||||
|
entity.Property(e => e.DisplayName).HasMaxLength(100);
|
||||||
|
entity.Property(e => e.ClientType).HasMaxLength(20);
|
||||||
|
entity.Property(e => e.ConsentType).HasMaxLength(20);
|
||||||
|
entity.Property(e => e.Status).HasMaxLength(20);
|
||||||
|
entity.Property(e => e.Description).HasMaxLength(500);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Entity<Tenant>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.Id);
|
||||||
|
entity.HasIndex(e => e.TenantId).IsUnique();
|
||||||
|
entity.Property(e => e.TenantId).HasMaxLength(50);
|
||||||
|
entity.Property(e => e.Name).HasMaxLength(100);
|
||||||
|
entity.Property(e => e.ContactName).HasMaxLength(50);
|
||||||
|
entity.Property(e => e.ContactEmail).HasMaxLength(100);
|
||||||
|
entity.Property(e => e.ContactPhone).HasMaxLength(20);
|
||||||
|
entity.Property(e => e.Status).HasMaxLength(20);
|
||||||
|
entity.Property(e => e.Description).HasMaxLength(500);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Entity<AccessLog>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.Id);
|
||||||
|
entity.HasIndex(e => e.CreatedAt);
|
||||||
|
entity.HasIndex(e => e.UserName);
|
||||||
|
entity.HasIndex(e => e.TenantId);
|
||||||
|
entity.HasIndex(e => e.Action);
|
||||||
|
entity.HasIndex(e => e.Status);
|
||||||
|
entity.Property(e => e.UserName).HasMaxLength(50);
|
||||||
|
entity.Property(e => e.TenantId).HasMaxLength(50);
|
||||||
|
entity.Property(e => e.Action).HasMaxLength(20);
|
||||||
|
entity.Property(e => e.Resource).HasMaxLength(200);
|
||||||
|
entity.Property(e => e.Method).HasMaxLength(10);
|
||||||
|
entity.Property(e => e.IpAddress).HasMaxLength(50);
|
||||||
|
entity.Property(e => e.UserAgent).HasMaxLength(500);
|
||||||
|
entity.Property(e => e.Status).HasMaxLength(20);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Entity<AuditLog>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.Id);
|
||||||
|
entity.HasIndex(e => e.CreatedAt);
|
||||||
|
entity.HasIndex(e => e.Operator);
|
||||||
|
entity.HasIndex(e => e.TenantId);
|
||||||
|
entity.HasIndex(e => e.Operation);
|
||||||
|
entity.HasIndex(e => e.Action);
|
||||||
|
entity.Property(e => e.Operator).HasMaxLength(50);
|
||||||
|
entity.Property(e => e.TenantId).HasMaxLength(50);
|
||||||
|
entity.Property(e => e.Operation).HasMaxLength(20);
|
||||||
|
entity.Property(e => e.Action).HasMaxLength(20);
|
||||||
|
entity.Property(e => e.TargetType).HasMaxLength(50);
|
||||||
|
entity.Property(e => e.TargetName).HasMaxLength(100);
|
||||||
|
entity.Property(e => e.IpAddress).HasMaxLength(50);
|
||||||
|
entity.Property(e => e.Description).HasMaxLength(500);
|
||||||
|
entity.Property(e => e.Status).HasMaxLength(20);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Data/ApplicationDbContextFactory.cs
Normal file
16
Data/ApplicationDbContextFactory.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using Fengling.AuthService.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Design;
|
||||||
|
|
||||||
|
namespace Fengling.AuthService.Data;
|
||||||
|
|
||||||
|
public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
|
||||||
|
{
|
||||||
|
public ApplicationDbContext CreateDbContext(string[] args)
|
||||||
|
{
|
||||||
|
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
|
||||||
|
optionsBuilder.UseNpgsql("Host=192.168.100.10;Port=5432;Database=fengling_auth;Username=movingsam;Password=sl52788542");
|
||||||
|
|
||||||
|
return new ApplicationDbContext(optionsBuilder.Options);
|
||||||
|
}
|
||||||
|
}
|
||||||
150
Data/SeedData.cs
Normal file
150
Data/SeedData.cs
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
using Fengling.AuthService.Models;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Fengling.AuthService.Data;
|
||||||
|
|
||||||
|
public static class SeedData
|
||||||
|
{
|
||||||
|
public static async Task Initialize(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
using var scope = serviceProvider.CreateScope();
|
||||||
|
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||||
|
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
|
||||||
|
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<ApplicationRole>>();
|
||||||
|
|
||||||
|
await context.Database.EnsureCreatedAsync();
|
||||||
|
|
||||||
|
var defaultTenant = await context.Tenants
|
||||||
|
.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(t => t.TenantId == "default");
|
||||||
|
if (defaultTenant == null)
|
||||||
|
{
|
||||||
|
defaultTenant = new Tenant
|
||||||
|
{
|
||||||
|
TenantId = "default",
|
||||||
|
Name = "默认租户",
|
||||||
|
ContactName = "系统管理员",
|
||||||
|
ContactEmail = "admin@fengling.local",
|
||||||
|
ContactPhone = "13800138000",
|
||||||
|
MaxUsers = 1000,
|
||||||
|
Description = "系统默认租户",
|
||||||
|
Status = "active",
|
||||||
|
CreatedAt = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
context.Tenants.Add(defaultTenant);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
var adminRole = await roleManager.FindByNameAsync("Admin");
|
||||||
|
if (adminRole == null)
|
||||||
|
{
|
||||||
|
adminRole = new ApplicationRole
|
||||||
|
{
|
||||||
|
Name = "Admin",
|
||||||
|
DisplayName = "管理员",
|
||||||
|
Description = "System administrator",
|
||||||
|
TenantId = defaultTenant.Id,
|
||||||
|
IsSystem = true,
|
||||||
|
Permissions = new List<string>
|
||||||
|
{
|
||||||
|
"user.manage", "user.view",
|
||||||
|
"role.manage", "role.view",
|
||||||
|
"tenant.manage", "tenant.view",
|
||||||
|
"oauth.manage", "oauth.view",
|
||||||
|
"log.view", "system.config"
|
||||||
|
},
|
||||||
|
CreatedTime = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
await roleManager.CreateAsync(adminRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
var userRole = await roleManager.FindByNameAsync("User");
|
||||||
|
if (userRole == null)
|
||||||
|
{
|
||||||
|
userRole = new ApplicationRole
|
||||||
|
{
|
||||||
|
Name = "User",
|
||||||
|
DisplayName = "普通用户",
|
||||||
|
Description = "Regular user",
|
||||||
|
TenantId = defaultTenant.Id,
|
||||||
|
IsSystem = true,
|
||||||
|
Permissions = new List<string> { "user.view" },
|
||||||
|
CreatedTime = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
await roleManager.CreateAsync(userRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
var adminUser = await userManager.FindByNameAsync("admin");
|
||||||
|
if (adminUser == null)
|
||||||
|
{
|
||||||
|
adminUser = new ApplicationUser
|
||||||
|
{
|
||||||
|
UserName = "admin",
|
||||||
|
Email = "admin@fengling.local",
|
||||||
|
RealName = "系统管理员",
|
||||||
|
Phone = "13800138000",
|
||||||
|
TenantInfo = new TenantInfo(defaultTenant.Id, defaultTenant.TenantId, defaultTenant.Name),
|
||||||
|
EmailConfirmed = true,
|
||||||
|
IsDeleted = false,
|
||||||
|
CreatedTime = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await userManager.CreateAsync(adminUser, "Admin@123");
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
await userManager.AddToRoleAsync(adminUser, "Admin");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testUser = await userManager.FindByNameAsync("testuser");
|
||||||
|
if (testUser == null)
|
||||||
|
{
|
||||||
|
testUser = new ApplicationUser
|
||||||
|
{
|
||||||
|
UserName = "testuser",
|
||||||
|
Email = "test@fengling.local",
|
||||||
|
RealName = "测试用户",
|
||||||
|
Phone = "13900139000",
|
||||||
|
TenantInfo = new TenantInfo(defaultTenant.Id, defaultTenant.TenantId, defaultTenant.Name),
|
||||||
|
EmailConfirmed = true,
|
||||||
|
IsDeleted = false,
|
||||||
|
CreatedTime = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await userManager.CreateAsync(testUser, "Test@123");
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
await userManager.AddToRoleAsync(testUser, "User");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var consoleClient = await context.OAuthApplications
|
||||||
|
.FirstOrDefaultAsync(c => c.ClientId == "fengling-console");
|
||||||
|
if (consoleClient == null)
|
||||||
|
{
|
||||||
|
consoleClient = new OAuthApplication
|
||||||
|
{
|
||||||
|
ClientId = "fengling-console",
|
||||||
|
ClientSecret = null,
|
||||||
|
DisplayName = "Fengling Console",
|
||||||
|
RedirectUris = new[] {
|
||||||
|
"http://localhost:5777/auth/callback",
|
||||||
|
"https://console.fengling.local/auth/callback"
|
||||||
|
},
|
||||||
|
PostLogoutRedirectUris = new[] {
|
||||||
|
"http://localhost:5777/",
|
||||||
|
"https://console.fengling.local/"
|
||||||
|
},
|
||||||
|
Scopes = new[] { "api", "offline_access", "openid", "profile", "email" },
|
||||||
|
GrantTypes = new[] { "authorization_code", "refresh_token" },
|
||||||
|
ClientType = "public",
|
||||||
|
ConsentType = "implicit",
|
||||||
|
Status = "active",
|
||||||
|
CreatedAt = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
context.OAuthApplications.Add(consoleClient);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,8 +6,6 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="NetCorePal.Extensions.AspNetCore" />
|
|
||||||
<PackageReference Include="OpenIddict.Quartz" />
|
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" />
|
<PackageReference Include="Swashbuckle.AspNetCore" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@ -34,9 +32,4 @@
|
|||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Fengling.Platform\Fengling.Platform.Domain\Fengling.Platform.Domain.csproj" />
|
|
||||||
<ProjectReference Include="..\Fengling.Platform\Fengling.Platform.Infrastructure\Fengling.Platform.Infrastructure.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
639
Migrations/20260205165820_InitialCreate.Designer.cs
generated
Normal file
639
Migrations/20260205165820_InitialCreate.Designer.cs
generated
Normal file
@ -0,0 +1,639 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Fengling.AuthService.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Fengling.AuthService.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("20260205165820_InitialCreate")]
|
||||||
|
partial class InitialCreate
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.2")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.AuthService.Models.AccessLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("Action")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<int>("Duration")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("ErrorMessage")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<string>("Method")
|
||||||
|
.HasMaxLength(10)
|
||||||
|
.HasColumnType("character varying(10)");
|
||||||
|
|
||||||
|
b.Property<string>("RequestData")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Resource")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)");
|
||||||
|
|
||||||
|
b.Property<string>("ResponseData")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<string>("TenantId")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<string>("UserAgent")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("character varying(500)");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Action");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedAt");
|
||||||
|
|
||||||
|
b.HasIndex("Status");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
|
b.HasIndex("UserName");
|
||||||
|
|
||||||
|
b.ToTable("AccessLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.AuthService.Models.ApplicationRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedTime")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("IsSystem")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.PrimitiveCollection<List<string>>("Permissions")
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.Property<long?>("TenantId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("RoleNameIndex");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.AuthService.Models.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedTime")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Phone")
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("RealName")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedTime")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasDatabaseName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UserNameIndex");
|
||||||
|
|
||||||
|
b.HasIndex("Phone")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.AuthService.Models.AuditLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("Action")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("character varying(500)");
|
||||||
|
|
||||||
|
b.Property<string>("ErrorMessage")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<string>("NewValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("OldValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Operation")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<string>("Operator")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<long?>("TargetId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<string>("TargetName")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("TargetType")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<string>("TenantId")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Action");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedAt");
|
||||||
|
|
||||||
|
b.HasIndex("Operation");
|
||||||
|
|
||||||
|
b.HasIndex("Operator");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
|
b.ToTable("AuditLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.AuthService.Models.OAuthApplication", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClientId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("ClientSecret")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)");
|
||||||
|
|
||||||
|
b.Property<string>("ClientType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<string>("ConsentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("character varying(500)");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.PrimitiveCollection<string[]>("GrantTypes")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.PrimitiveCollection<string[]>("PostLogoutRedirectUris")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.PrimitiveCollection<string[]>("RedirectUris")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.PrimitiveCollection<string[]>("Scopes")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("OAuthApplications");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.AuthService.Models.Tenant", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ContactEmail")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("ContactName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<string>("ContactPhone")
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("character varying(500)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("ExpiresAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<int?>("MaxUsers")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<string>("TenantId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Tenants");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<long>("RoleId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<long>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<long>("UserId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<long>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<long>("UserId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<long>", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("UserId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<long>("RoleId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<long>", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("UserId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.AuthService.Models.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.OwnsOne("Fengling.AuthService.Models.TenantInfo", "TenantInfo", b1 =>
|
||||||
|
{
|
||||||
|
b1.Property<long>("ApplicationUserId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b1.Property<long>("Id")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("TenantId");
|
||||||
|
|
||||||
|
b1.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasColumnName("TenantName");
|
||||||
|
|
||||||
|
b1.Property<string>("TenantId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasColumnName("TenantCode");
|
||||||
|
|
||||||
|
b1.HasKey("ApplicationUserId");
|
||||||
|
|
||||||
|
b1.ToTable("AspNetUsers");
|
||||||
|
|
||||||
|
b1.WithOwner()
|
||||||
|
.HasForeignKey("ApplicationUserId");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.Navigation("TenantInfo")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.AuthService.Models.ApplicationRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<long>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.AuthService.Models.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<long>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.AuthService.Models.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<long>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.AuthService.Models.ApplicationRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Fengling.AuthService.Models.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<long>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.AuthService.Models.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
420
Migrations/20260205165820_InitialCreate.cs
Normal file
420
Migrations/20260205165820_InitialCreate.cs
Normal file
@ -0,0 +1,420 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Fengling.AuthService.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class InitialCreate : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AccessLogs",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
UserName = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
|
||||||
|
TenantId = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
|
||||||
|
Action = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
||||||
|
Resource = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||||
|
Method = table.Column<string>(type: "character varying(10)", maxLength: 10, nullable: true),
|
||||||
|
IpAddress = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
|
||||||
|
UserAgent = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
||||||
|
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
||||||
|
Duration = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
RequestData = table.Column<string>(type: "text", nullable: true),
|
||||||
|
ResponseData = table.Column<string>(type: "text", nullable: true),
|
||||||
|
ErrorMessage = table.Column<string>(type: "text", nullable: true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AccessLogs", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetRoles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
Description = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||||
|
CreatedTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||||
|
TenantId = table.Column<long>(type: "bigint", nullable: true),
|
||||||
|
IsSystem = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
DisplayName = table.Column<string>(type: "text", nullable: true),
|
||||||
|
Permissions = table.Column<List<string>>(type: "text[]", nullable: true),
|
||||||
|
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
NormalizedName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUsers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
RealName = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
|
||||||
|
Phone = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: true),
|
||||||
|
TenantId = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
TenantCode = table.Column<string>(type: "text", nullable: false),
|
||||||
|
TenantName = table.Column<string>(type: "text", nullable: false),
|
||||||
|
CreatedTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||||
|
UpdatedTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||||
|
IsDeleted = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
UserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
NormalizedUserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
Email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
NormalizedEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
EmailConfirmed = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
PasswordHash = table.Column<string>(type: "text", nullable: true),
|
||||||
|
SecurityStamp = table.Column<string>(type: "text", nullable: true),
|
||||||
|
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true),
|
||||||
|
PhoneNumber = table.Column<string>(type: "text", nullable: true),
|
||||||
|
PhoneNumberConfirmed = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
TwoFactorEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
LockoutEnd = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||||
|
LockoutEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
AccessFailedCount = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AuditLogs",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
Operator = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||||
|
TenantId = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
|
||||||
|
Operation = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
||||||
|
Action = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
||||||
|
TargetType = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: true),
|
||||||
|
TargetId = table.Column<long>(type: "bigint", nullable: true),
|
||||||
|
TargetName = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
|
||||||
|
IpAddress = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||||
|
Description = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
||||||
|
OldValue = table.Column<string>(type: "text", nullable: true),
|
||||||
|
NewValue = table.Column<string>(type: "text", nullable: true),
|
||||||
|
ErrorMessage = table.Column<string>(type: "text", nullable: true),
|
||||||
|
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AuditLogs", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "OAuthApplications",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
ClientId = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
ClientSecret = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: true),
|
||||||
|
DisplayName = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
RedirectUris = table.Column<string[]>(type: "text[]", nullable: false),
|
||||||
|
PostLogoutRedirectUris = table.Column<string[]>(type: "text[]", nullable: false),
|
||||||
|
Scopes = table.Column<string[]>(type: "text[]", nullable: false),
|
||||||
|
GrantTypes = table.Column<string[]>(type: "text[]", nullable: false),
|
||||||
|
ClientType = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
||||||
|
ConsentType = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
||||||
|
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
||||||
|
Description = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_OAuthApplications", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Tenants",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
TenantId = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||||
|
Name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
ContactName = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||||
|
ContactEmail = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
ContactPhone = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: true),
|
||||||
|
MaxUsers = table.Column<int>(type: "integer", nullable: true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||||
|
Description = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: true),
|
||||||
|
Status = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
|
||||||
|
ExpiresAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||||
|
IsDeleted = table.Column<bool>(type: "boolean", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Tenants", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetRoleClaims",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
RoleId = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
ClaimType = table.Column<string>(type: "text", nullable: true),
|
||||||
|
ClaimValue = table.Column<string>(type: "text", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
|
||||||
|
column: x => x.RoleId,
|
||||||
|
principalTable: "AspNetRoles",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserClaims",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
UserId = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
ClaimType = table.Column<string>(type: "text", nullable: true),
|
||||||
|
ClaimValue = table.Column<string>(type: "text", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserLogins",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
LoginProvider = table.Column<string>(type: "text", nullable: false),
|
||||||
|
ProviderKey = table.Column<string>(type: "text", nullable: false),
|
||||||
|
ProviderDisplayName = table.Column<string>(type: "text", nullable: true),
|
||||||
|
UserId = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserRoles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserId = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
RoleId = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
|
||||||
|
column: x => x.RoleId,
|
||||||
|
principalTable: "AspNetRoles",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserTokens",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserId = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
LoginProvider = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Value = table.Column<string>(type: "text", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AccessLogs_Action",
|
||||||
|
table: "AccessLogs",
|
||||||
|
column: "Action");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AccessLogs_CreatedAt",
|
||||||
|
table: "AccessLogs",
|
||||||
|
column: "CreatedAt");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AccessLogs_Status",
|
||||||
|
table: "AccessLogs",
|
||||||
|
column: "Status");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AccessLogs_TenantId",
|
||||||
|
table: "AccessLogs",
|
||||||
|
column: "TenantId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AccessLogs_UserName",
|
||||||
|
table: "AccessLogs",
|
||||||
|
column: "UserName");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetRoleClaims_RoleId",
|
||||||
|
table: "AspNetRoleClaims",
|
||||||
|
column: "RoleId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "RoleNameIndex",
|
||||||
|
table: "AspNetRoles",
|
||||||
|
column: "NormalizedName",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetUserClaims_UserId",
|
||||||
|
table: "AspNetUserClaims",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetUserLogins_UserId",
|
||||||
|
table: "AspNetUserLogins",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetUserRoles_RoleId",
|
||||||
|
table: "AspNetUserRoles",
|
||||||
|
column: "RoleId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "EmailIndex",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
column: "NormalizedEmail");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetUsers_Phone",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
column: "Phone",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "UserNameIndex",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
column: "NormalizedUserName",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AuditLogs_Action",
|
||||||
|
table: "AuditLogs",
|
||||||
|
column: "Action");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AuditLogs_CreatedAt",
|
||||||
|
table: "AuditLogs",
|
||||||
|
column: "CreatedAt");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AuditLogs_Operation",
|
||||||
|
table: "AuditLogs",
|
||||||
|
column: "Operation");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AuditLogs_Operator",
|
||||||
|
table: "AuditLogs",
|
||||||
|
column: "Operator");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AuditLogs_TenantId",
|
||||||
|
table: "AuditLogs",
|
||||||
|
column: "TenantId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_OAuthApplications_ClientId",
|
||||||
|
table: "OAuthApplications",
|
||||||
|
column: "ClientId",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Tenants_TenantId",
|
||||||
|
table: "Tenants",
|
||||||
|
column: "TenantId",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AccessLogs");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetRoleClaims");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUserClaims");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUserLogins");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUserRoles");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUserTokens");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AuditLogs");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "OAuthApplications");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Tenants");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetRoles");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUsers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
636
Migrations/ApplicationDbContextModelSnapshot.cs
Normal file
636
Migrations/ApplicationDbContextModelSnapshot.cs
Normal file
@ -0,0 +1,636 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Fengling.AuthService.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Fengling.AuthService.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.2")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.AuthService.Models.AccessLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("Action")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<int>("Duration")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("ErrorMessage")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<string>("Method")
|
||||||
|
.HasMaxLength(10)
|
||||||
|
.HasColumnType("character varying(10)");
|
||||||
|
|
||||||
|
b.Property<string>("RequestData")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Resource")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)");
|
||||||
|
|
||||||
|
b.Property<string>("ResponseData")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<string>("TenantId")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<string>("UserAgent")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("character varying(500)");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Action");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedAt");
|
||||||
|
|
||||||
|
b.HasIndex("Status");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
|
b.HasIndex("UserName");
|
||||||
|
|
||||||
|
b.ToTable("AccessLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.AuthService.Models.ApplicationRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedTime")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("IsSystem")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.PrimitiveCollection<List<string>>("Permissions")
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.Property<long?>("TenantId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("RoleNameIndex");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.AuthService.Models.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedTime")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Phone")
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("RealName")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedTime")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasDatabaseName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UserNameIndex");
|
||||||
|
|
||||||
|
b.HasIndex("Phone")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.AuthService.Models.AuditLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("Action")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("character varying(500)");
|
||||||
|
|
||||||
|
b.Property<string>("ErrorMessage")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<string>("NewValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("OldValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Operation")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<string>("Operator")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<long?>("TargetId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<string>("TargetName")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("TargetType")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<string>("TenantId")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Action");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedAt");
|
||||||
|
|
||||||
|
b.HasIndex("Operation");
|
||||||
|
|
||||||
|
b.HasIndex("Operator");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
|
b.ToTable("AuditLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.AuthService.Models.OAuthApplication", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClientId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("ClientSecret")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)");
|
||||||
|
|
||||||
|
b.Property<string>("ClientType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<string>("ConsentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("character varying(500)");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.PrimitiveCollection<string[]>("GrantTypes")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.PrimitiveCollection<string[]>("PostLogoutRedirectUris")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.PrimitiveCollection<string[]>("RedirectUris")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.PrimitiveCollection<string[]>("Scopes")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("OAuthApplications");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.AuthService.Models.Tenant", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ContactEmail")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("ContactName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<string>("ContactPhone")
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("character varying(500)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("ExpiresAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<int?>("MaxUsers")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)");
|
||||||
|
|
||||||
|
b.Property<string>("TenantId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Tenants");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<long>("RoleId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<long>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<long>("UserId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<long>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<long>("UserId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<long>", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("UserId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<long>("RoleId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<long>", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("UserId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Fengling.AuthService.Models.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.OwnsOne("Fengling.AuthService.Models.TenantInfo", "TenantInfo", b1 =>
|
||||||
|
{
|
||||||
|
b1.Property<long>("ApplicationUserId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b1.Property<long>("Id")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("TenantId");
|
||||||
|
|
||||||
|
b1.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasColumnName("TenantName");
|
||||||
|
|
||||||
|
b1.Property<string>("TenantId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text")
|
||||||
|
.HasColumnName("TenantCode");
|
||||||
|
|
||||||
|
b1.HasKey("ApplicationUserId");
|
||||||
|
|
||||||
|
b1.ToTable("AspNetUsers");
|
||||||
|
|
||||||
|
b1.WithOwner()
|
||||||
|
.HasForeignKey("ApplicationUserId");
|
||||||
|
});
|
||||||
|
|
||||||
|
b.Navigation("TenantInfo")
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<long>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.AuthService.Models.ApplicationRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<long>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.AuthService.Models.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<long>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.AuthService.Models.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<long>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.AuthService.Models.ApplicationRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Fengling.AuthService.Models.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<long>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Fengling.AuthService.Models.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
Models/AccessLog.cs
Normal file
43
Models/AccessLog.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Fengling.AuthService.Models;
|
||||||
|
|
||||||
|
public class AccessLog
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string? UserName { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string? TenantId { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(20)]
|
||||||
|
public string Action { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[MaxLength(200)]
|
||||||
|
public string? Resource { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(10)]
|
||||||
|
public string? Method { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string? IpAddress { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(500)]
|
||||||
|
public string? UserAgent { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(20)]
|
||||||
|
public string Status { get; set; } = "success";
|
||||||
|
|
||||||
|
public int Duration { get; set; }
|
||||||
|
|
||||||
|
public string? RequestData { get; set; }
|
||||||
|
|
||||||
|
public string? ResponseData { get; set; }
|
||||||
|
|
||||||
|
public string? ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
}
|
||||||
14
Models/ApplicationRole.cs
Normal file
14
Models/ApplicationRole.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Fengling.AuthService.Models;
|
||||||
|
|
||||||
|
public class ApplicationRole : IdentityRole<long>
|
||||||
|
{
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public DateTime CreatedTime { get; set; } = DateTime.UtcNow;
|
||||||
|
public long? TenantId { get; set; }
|
||||||
|
public bool IsSystem { get; set; }
|
||||||
|
public string? DisplayName { get; set; }
|
||||||
|
public List<string>? Permissions { get; set; }
|
||||||
|
}
|
||||||
13
Models/ApplicationUser.cs
Normal file
13
Models/ApplicationUser.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
|
||||||
|
namespace Fengling.AuthService.Models;
|
||||||
|
|
||||||
|
public class ApplicationUser : IdentityUser<long>
|
||||||
|
{
|
||||||
|
public string? RealName { get; set; }
|
||||||
|
public string? Phone { get; set; }
|
||||||
|
public TenantInfo TenantInfo { get; set; } = null!;
|
||||||
|
public DateTime CreatedTime { get; set; } = DateTime.UtcNow;
|
||||||
|
public DateTime? UpdatedTime { get; set; }
|
||||||
|
public bool IsDeleted { get; set; }
|
||||||
|
}
|
||||||
47
Models/AuditLog.cs
Normal file
47
Models/AuditLog.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Fengling.AuthService.Models;
|
||||||
|
|
||||||
|
public class AuditLog
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(50)]
|
||||||
|
[Required]
|
||||||
|
public string Operator { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string? TenantId { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(20)]
|
||||||
|
public string Operation { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[MaxLength(20)]
|
||||||
|
public string Action { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string? TargetType { get; set; }
|
||||||
|
|
||||||
|
public long? TargetId { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string? TargetName { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string IpAddress { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[MaxLength(500)]
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
public string? OldValue { get; set; }
|
||||||
|
|
||||||
|
public string? NewValue { get; set; }
|
||||||
|
|
||||||
|
public string? ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(20)]
|
||||||
|
public string Status { get; set; } = "success";
|
||||||
|
|
||||||
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
}
|
||||||
21
Models/OAuthApplication.cs
Normal file
21
Models/OAuthApplication.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Fengling.AuthService.Models;
|
||||||
|
|
||||||
|
public class OAuthApplication
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
public string ClientId { get; set; } = string.Empty;
|
||||||
|
public string? ClientSecret { get; set; }
|
||||||
|
public string DisplayName { get; set; } = string.Empty;
|
||||||
|
public string[] RedirectUris { get; set; } = Array.Empty<string>();
|
||||||
|
public string[] PostLogoutRedirectUris { get; set; } = Array.Empty<string>();
|
||||||
|
public string[] Scopes { get; set; } = Array.Empty<string>();
|
||||||
|
public string[] GrantTypes { get; set; } = Array.Empty<string>();
|
||||||
|
public string ClientType { get; set; } = "public";
|
||||||
|
public string ConsentType { get; set; } = "implicit";
|
||||||
|
public string Status { get; set; } = "active";
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
public DateTime? UpdatedAt { get; set; }
|
||||||
|
}
|
||||||
63
Models/Tenant.cs
Normal file
63
Models/Tenant.cs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Fengling.AuthService.Models;
|
||||||
|
|
||||||
|
public class Tenant
|
||||||
|
{
|
||||||
|
private long _id;
|
||||||
|
private string _tenantId;
|
||||||
|
private string _name;
|
||||||
|
|
||||||
|
[Key]
|
||||||
|
public long Id
|
||||||
|
{
|
||||||
|
get => _id;
|
||||||
|
set => _id = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MaxLength(50)]
|
||||||
|
[Required]
|
||||||
|
public string TenantId
|
||||||
|
{
|
||||||
|
get => _tenantId;
|
||||||
|
set => _tenantId = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MaxLength(100)]
|
||||||
|
[Required]
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get => _name;
|
||||||
|
set => _name = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MaxLength(50)]
|
||||||
|
[Required]
|
||||||
|
public string ContactName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[MaxLength(100)]
|
||||||
|
[Required]
|
||||||
|
[EmailAddress]
|
||||||
|
public string ContactEmail { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[MaxLength(20)]
|
||||||
|
public string? ContactPhone { get; set; }
|
||||||
|
|
||||||
|
public int? MaxUsers { get; set; }
|
||||||
|
|
||||||
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
|
||||||
|
[MaxLength(500)]
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(20)]
|
||||||
|
public string Status { get; set; } = "active";
|
||||||
|
|
||||||
|
public DateTime? ExpiresAt { get; set; }
|
||||||
|
|
||||||
|
public DateTime? UpdatedAt { get; set; }
|
||||||
|
|
||||||
|
public bool IsDeleted { get; set; }
|
||||||
|
|
||||||
|
public TenantInfo Info => new(Id, TenantId, Name);
|
||||||
|
}
|
||||||
3
Models/TenantInfo.cs
Normal file
3
Models/TenantInfo.cs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
namespace Fengling.AuthService.Models;
|
||||||
|
|
||||||
|
public record TenantInfo(long Id, string TenantId, string Name);
|
||||||
39
Program.cs
39
Program.cs
@ -1,13 +1,11 @@
|
|||||||
using System.Reflection;
|
|
||||||
using Fengling.AuthService.Configuration;
|
using Fengling.AuthService.Configuration;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
|
using Fengling.AuthService.Data;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
|
using Fengling.AuthService.Models;
|
||||||
using Fengling.Platform.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.OpenApi;
|
using Microsoft.OpenApi;
|
||||||
using NetCorePal.Extensions.DependencyInjection;
|
using OpenTelemetry;
|
||||||
using OpenTelemetry.Resources;
|
using OpenTelemetry.Resources;
|
||||||
using OpenTelemetry.Trace;
|
using OpenTelemetry.Trace;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
@ -23,17 +21,23 @@ Log.Logger = new LoggerConfiguration()
|
|||||||
builder.Host.UseSerilog();
|
builder.Host.UseSerilog();
|
||||||
|
|
||||||
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
|
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
|
||||||
builder.Services.AddDbContext<PlatformDbContext>(options =>
|
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
{
|
{
|
||||||
|
if (connectionString.StartsWith("DataSource="))
|
||||||
|
{
|
||||||
|
options.UseInMemoryDatabase(connectionString);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
options.UseNpgsql(connectionString);
|
options.UseNpgsql(connectionString);
|
||||||
options.UseOpenIddict();
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddRazorPages();
|
builder.Services.AddRazorPages();
|
||||||
builder.Services.AddControllersWithViews();
|
builder.Services.AddControllersWithViews();
|
||||||
|
|
||||||
builder.Services.AddIdentity<ApplicationUser, ApplicationRole>()
|
builder.Services.AddIdentity<ApplicationUser, ApplicationRole>()
|
||||||
.AddEntityFrameworkStores<PlatformDbContext>()
|
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||||
.AddDefaultTokenProviders();
|
.AddDefaultTokenProviders();
|
||||||
|
|
||||||
builder.Services.AddAuthentication(options =>
|
builder.Services.AddAuthentication(options =>
|
||||||
@ -63,15 +67,6 @@ builder.Services.AddControllersWithViews();
|
|||||||
builder.Services.AddHealthChecks()
|
builder.Services.AddHealthChecks()
|
||||||
.AddNpgSql(builder.Configuration.GetConnectionString("DefaultConnection")!);
|
.AddNpgSql(builder.Configuration.GetConnectionString("DefaultConnection")!);
|
||||||
|
|
||||||
|
|
||||||
builder.Services.AddRepositories(typeof(PlatformDbContext).Assembly);
|
|
||||||
builder.Services.AddMediatR(x => x.RegisterServicesFromAssemblies(
|
|
||||||
typeof(PlatformDbContext).Assembly,
|
|
||||||
Assembly.GetExecutingAssembly())
|
|
||||||
.AddCommandLockBehavior()
|
|
||||||
.AddKnownExceptionValidationBehavior()
|
|
||||||
.AddUnitOfWorkBehaviors()
|
|
||||||
);
|
|
||||||
builder.Services.AddSwaggerGen(options =>
|
builder.Services.AddSwaggerGen(options =>
|
||||||
{
|
{
|
||||||
options.SwaggerDoc("v1", new OpenApiInfo
|
options.SwaggerDoc("v1", new OpenApiInfo
|
||||||
@ -98,17 +93,9 @@ var app = builder.Build();
|
|||||||
|
|
||||||
using (var scope = app.Services.CreateScope())
|
using (var scope = app.Services.CreateScope())
|
||||||
{
|
{
|
||||||
await scope.InitializeAsync();
|
await SeedData.Initialize(scope.ServiceProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
app.UseCors(x =>
|
|
||||||
{
|
|
||||||
x.SetIsOriginAllowed(origin => true)
|
|
||||||
.AllowAnyHeader()
|
|
||||||
.AllowAnyMethod()
|
|
||||||
.AllowCredentials()
|
|
||||||
.Build();
|
|
||||||
});
|
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
|
|||||||
@ -4,27 +4,23 @@ namespace Fengling.AuthService.ViewModels;
|
|||||||
|
|
||||||
public class RegisterViewModel
|
public class RegisterViewModel
|
||||||
{
|
{
|
||||||
[Required(ErrorMessage = "租户编号不能为空")]
|
|
||||||
[StringLength(10,MinimumLength = 4,ErrorMessage = "租户编号长度必须在4·10个字符之间")]
|
|
||||||
public string TenantCode { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "用户名不能为空")]
|
[Required(ErrorMessage = "用户名不能为空")]
|
||||||
[StringLength(50, MinimumLength = 3, ErrorMessage = "用户名长度必须在3-50个字符之间")]
|
[StringLength(50, MinimumLength = 3, ErrorMessage = "用户名长度必须在3-50个字符之间")]
|
||||||
public string Username { get; set; } = default!;
|
public string Username { get; set; }
|
||||||
|
|
||||||
[Required(ErrorMessage = "邮箱不能为空")]
|
[Required(ErrorMessage = "邮箱不能为空")]
|
||||||
[EmailAddress(ErrorMessage = "请输入有效的邮箱地址")]
|
[EmailAddress(ErrorMessage = "请输入有效的邮箱地址")]
|
||||||
public string Email { get; set; }= default!;
|
public string Email { get; set; }
|
||||||
|
|
||||||
[Required(ErrorMessage = "密码不能为空")]
|
[Required(ErrorMessage = "密码不能为空")]
|
||||||
[StringLength(100, MinimumLength = 6, ErrorMessage = "密码长度必须在6-100个字符之间")]
|
[StringLength(100, MinimumLength = 6, ErrorMessage = "密码长度必须在6-100个字符之间")]
|
||||||
[DataType(DataType.Password)]
|
[DataType(DataType.Password)]
|
||||||
public string Password { get; set; }= default!;
|
public string Password { get; set; }
|
||||||
|
|
||||||
[Required(ErrorMessage = "确认密码不能为空")]
|
[Required(ErrorMessage = "确认密码不能为空")]
|
||||||
[DataType(DataType.Password)]
|
[DataType(DataType.Password)]
|
||||||
[Compare("Password", ErrorMessage = "两次输入的密码不一致")]
|
[Compare("Password", ErrorMessage = "两次输入的密码不一致")]
|
||||||
public string ConfirmPassword { get; set; }= default!;
|
public string ConfirmPassword { get; set; }
|
||||||
|
|
||||||
public string ReturnUrl { get; set; }= default!;
|
public string ReturnUrl { get; set; }
|
||||||
}
|
}
|
||||||
@ -1,4 +1,3 @@
|
|||||||
@using Microsoft.Extensions.Primitives
|
|
||||||
@model Fengling.AuthService.ViewModels.AuthorizeViewModel
|
@model Fengling.AuthService.ViewModels.AuthorizeViewModel
|
||||||
|
|
||||||
@{
|
@{
|
||||||
@ -11,8 +10,7 @@
|
|||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="text-center mb-8">
|
<div class="text-center mb-8">
|
||||||
<div class="inline-flex h-16 w-16 items-center justify-center rounded-full bg-primary/10 mb-4">
|
<div class="inline-flex h-16 w-16 items-center justify-center rounded-full bg-primary/10 mb-4">
|
||||||
<svg class="h-8 w-8 text-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
<svg class="h-8 w-8 text-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@ -29,8 +27,7 @@
|
|||||||
<div class="p-6 border-b border-border">
|
<div class="p-6 border-b border-border">
|
||||||
<div class="flex items-start gap-4">
|
<div class="flex items-start gap-4">
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<div
|
<div class="h-12 w-12 rounded-lg bg-primary flex items-center justify-center text-primary-foreground text-lg font-semibold">
|
||||||
class="h-12 w-12 rounded-lg bg-primary flex items-center justify-center text-primary-foreground text-lg font-semibold">
|
|
||||||
@(Model.ApplicationName?.Substring(0, Math.Min(1, Model.ApplicationName.Length)).ToUpper() ?? "A")
|
@(Model.ApplicationName?.Substring(0, Math.Min(1, Model.ApplicationName.Length)).ToUpper() ?? "A")
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -52,10 +49,7 @@
|
|||||||
@foreach (var scope in Model.Scopes)
|
@foreach (var scope in Model.Scopes)
|
||||||
{
|
{
|
||||||
<div class="flex items-start gap-3 p-3 rounded-lg bg-muted/50">
|
<div class="flex items-start gap-3 p-3 rounded-lg bg-muted/50">
|
||||||
<svg class="h-5 w-5 text-primary mt-0.5 flex-shrink-0"
|
<svg class="h-5 w-5 text-primary mt-0.5 flex-shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
|
||||||
stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
|
||||||
stroke-linejoin="round">
|
|
||||||
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
|
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
|
||||||
<polyline points="22 4 12 14.01 9 11.01"/>
|
<polyline points="22 4 12 14.01 9 11.01"/>
|
||||||
</svg>
|
</svg>
|
||||||
@ -76,9 +70,7 @@
|
|||||||
<!-- Warning Section -->
|
<!-- Warning Section -->
|
||||||
<div class="p-4 bg-destructive/5 border-t border-border">
|
<div class="p-4 bg-destructive/5 border-t border-border">
|
||||||
<div class="flex items-start gap-2">
|
<div class="flex items-start gap-2">
|
||||||
<svg class="h-4 w-4 text-destructive mt-0.5 flex-shrink-0" xmlns="http://www.w3.org/2000/svg"
|
<svg class="h-4 w-4 text-destructive mt-0.5 flex-shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
|
||||||
stroke-linejoin="round">
|
|
||||||
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/>
|
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/>
|
||||||
<path d="M12 9v4"/>
|
<path d="M12 9v4"/>
|
||||||
<path d="M12 17h.01"/>
|
<path d="M12 17h.01"/>
|
||||||
@ -91,28 +83,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Action Buttons -->
|
<!-- Action Buttons -->
|
||||||
<form asp-controller="Authorization" asp-action="Authorize" method="post" class="mt-6 space-y-3">
|
<form method="post" class="mt-6 space-y-3">
|
||||||
@* Flow the request parameters so they can be received by the Accept/Reject actions: *@
|
|
||||||
@foreach (var parameter in Context.Request.HasFormContentType ?
|
|
||||||
(IEnumerable<KeyValuePair<string, StringValues>>) Context.Request.Form : Context.Request.Query)
|
|
||||||
{
|
|
||||||
<input type="hidden" name="@parameter.Key" value="@parameter.Value" />
|
|
||||||
}
|
|
||||||
|
|
||||||
<div class="grid grid-cols-2 gap-3">
|
<div class="grid grid-cols-2 gap-3">
|
||||||
<button type="submit" name="submit.Accept" value="Yes"
|
<button type="submit" name="action" value="accept"
|
||||||
class="inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 bg-primary text-primary-foreground hover:bg-primary/90 h-11 px-8 shadow">
|
class="inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 bg-primary text-primary-foreground hover:bg-primary/90 h-11 px-8 shadow">
|
||||||
<svg class="mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
<svg class="mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
|
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
|
||||||
<polyline points="22 4 12 14.01 9 11.01"/>
|
<polyline points="22 4 12 14.01 9 11.01"/>
|
||||||
</svg>
|
</svg>
|
||||||
授权
|
授权
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" name="submit.Deny" value="No"
|
<button type="submit" name="action" value="deny"
|
||||||
class="inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-11 px-8">
|
class="inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 border border-input bg-background hover:bg-accent hover:text-accent-foreground h-11 px-8">
|
||||||
<svg class="mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
<svg class="mr-2 h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<line x1="18" y1="6" x2="6" y2="18"/>
|
<line x1="18" y1="6" x2="6" y2="18"/>
|
||||||
<line x1="6" y1="6" x2="18" y2="18"/>
|
<line x1="6" y1="6" x2="18" y2="18"/>
|
||||||
</svg>
|
</svg>
|
||||||
@ -131,7 +114,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
@functions {
|
@functions {
|
||||||
|
|
||||||
private string GetScopeDisplayName(string scope)
|
private string GetScopeDisplayName(string scope)
|
||||||
{
|
{
|
||||||
return scope switch
|
return scope switch
|
||||||
@ -161,5 +143,4 @@
|
|||||||
_ => "自定义权限范围"
|
_ => "自定义权限范围"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"DefaultConnection": "Host=81.68.223.70;Port=15432;Database=fengling_auth;Username=movingsam;Password=sl52788542"
|
"DefaultConnection": "DataSource=:memory:"
|
||||||
},
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
{
|
{
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"DefaultConnection": "Host=81.68.223.70;Port=15432;Database=fengling_auth;Username=movingsam;Password=sl52788542"
|
"DefaultConnection": "Host=192.168.100.10;Port=5432;Database=fengling_auth;Username=movingsam;Password=sl52788542"
|
||||||
},
|
},
|
||||||
"Jwt": {
|
"Jwt": {
|
||||||
"Issuer": "http://localhost:5132",
|
"Issuer": "https://auth.fengling.local",
|
||||||
"Audience": "fengling-api",
|
"Audience": "fengling-api",
|
||||||
"Secret": "FenglingAuthSecretKey2024!ChangeThisInProduction!"
|
"Secret": "FenglingAuthSecretKey2024!ChangeThisInProduction!"
|
||||||
},
|
},
|
||||||
"OpenIddict": {
|
"OpenIddict": {
|
||||||
"Issuer": "http://localhost:5132",
|
"Issuer": "https://auth.fengling.local",
|
||||||
"Audience": "fengling-api"
|
"Audience": "fengling-api"
|
||||||
},
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -1,68 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<configuration>
|
|
||||||
<startup>
|
|
||||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
|
|
||||||
</startup>
|
|
||||||
<runtime>
|
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
|
||||||
<dependentAssembly>
|
|
||||||
<assemblyIdentity name="Microsoft.Build" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
|
||||||
<bindingRedirect oldVersion="0.0.0.0-15.1.0.0" newVersion="15.1.0.0" />
|
|
||||||
</dependentAssembly>
|
|
||||||
</assemblyBinding>
|
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
|
||||||
<dependentAssembly>
|
|
||||||
<assemblyIdentity name="Microsoft.Build.Framework" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
|
||||||
<bindingRedirect oldVersion="0.0.0.0-15.1.0.0" newVersion="15.1.0.0" />
|
|
||||||
</dependentAssembly>
|
|
||||||
</assemblyBinding>
|
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
|
||||||
<dependentAssembly>
|
|
||||||
<assemblyIdentity name="Microsoft.Build.Utilities.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
|
||||||
<bindingRedirect oldVersion="0.0.0.0-15.1.0.0" newVersion="15.1.0.0" />
|
|
||||||
</dependentAssembly>
|
|
||||||
</assemblyBinding>
|
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
|
||||||
<dependentAssembly>
|
|
||||||
<assemblyIdentity name="Microsoft.Build.Tasks.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
|
||||||
<bindingRedirect oldVersion="0.0.0.0-15.1.0.0" newVersion="15.1.0.0" />
|
|
||||||
</dependentAssembly>
|
|
||||||
</assemblyBinding>
|
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
|
||||||
<dependentAssembly>
|
|
||||||
<assemblyIdentity name="Microsoft.IO.Redist" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
|
||||||
<bindingRedirect oldVersion="0.0.0.0-6.1.0.0" newVersion="6.1.0.0" />
|
|
||||||
</dependentAssembly>
|
|
||||||
</assemblyBinding>
|
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
|
||||||
<dependentAssembly>
|
|
||||||
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
|
||||||
<bindingRedirect oldVersion="0.0.0.0-4.0.4.0" newVersion="4.0.4.0" />
|
|
||||||
</dependentAssembly>
|
|
||||||
</assemblyBinding>
|
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
|
||||||
<dependentAssembly>
|
|
||||||
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
|
||||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
|
||||||
</dependentAssembly>
|
|
||||||
</assemblyBinding>
|
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
|
||||||
<dependentAssembly>
|
|
||||||
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
|
||||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
|
|
||||||
</dependentAssembly>
|
|
||||||
</assemblyBinding>
|
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
|
||||||
<dependentAssembly>
|
|
||||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
|
||||||
<bindingRedirect oldVersion="0.0.0.0-6.0.1.0" newVersion="6.0.1.0" />
|
|
||||||
</dependentAssembly>
|
|
||||||
</assemblyBinding>
|
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
|
||||||
<dependentAssembly>
|
|
||||||
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
|
||||||
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
|
|
||||||
</dependentAssembly>
|
|
||||||
</assemblyBinding>
|
|
||||||
</runtime>
|
|
||||||
</configuration>
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,171 +0,0 @@
|
|||||||
{
|
|
||||||
"runtimeTarget": {
|
|
||||||
"name": ".NETCoreApp,Version=v8.0",
|
|
||||||
"signature": ""
|
|
||||||
},
|
|
||||||
"compilationOptions": {},
|
|
||||||
"targets": {
|
|
||||||
".NETCoreApp,Version=v8.0": {
|
|
||||||
"Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost/5.0.0-2.25567.12": {
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Build.Locator": "1.10.2",
|
|
||||||
"Newtonsoft.Json": "13.0.3",
|
|
||||||
"System.Collections.Immutable": "9.0.0",
|
|
||||||
"System.CommandLine": "2.0.0-rtm.25509.106"
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.dll": {}
|
|
||||||
},
|
|
||||||
"resources": {
|
|
||||||
"cs/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
|
|
||||||
"locale": "cs"
|
|
||||||
},
|
|
||||||
"de/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
|
|
||||||
"locale": "de"
|
|
||||||
},
|
|
||||||
"es/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
|
|
||||||
"locale": "es"
|
|
||||||
},
|
|
||||||
"fr/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
|
|
||||||
"locale": "fr"
|
|
||||||
},
|
|
||||||
"it/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
|
|
||||||
"locale": "it"
|
|
||||||
},
|
|
||||||
"ja/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
|
|
||||||
"locale": "ja"
|
|
||||||
},
|
|
||||||
"ko/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
|
|
||||||
"locale": "ko"
|
|
||||||
},
|
|
||||||
"pl/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
|
|
||||||
"locale": "pl"
|
|
||||||
},
|
|
||||||
"pt-BR/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
|
|
||||||
"locale": "pt-BR"
|
|
||||||
},
|
|
||||||
"ru/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
|
|
||||||
"locale": "ru"
|
|
||||||
},
|
|
||||||
"tr/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
|
|
||||||
"locale": "tr"
|
|
||||||
},
|
|
||||||
"zh-Hans/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
|
|
||||||
"locale": "zh-Hans"
|
|
||||||
},
|
|
||||||
"zh-Hant/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll": {
|
|
||||||
"locale": "zh-Hant"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.Build.Locator/1.10.2": {
|
|
||||||
"runtime": {
|
|
||||||
"lib/net8.0/Microsoft.Build.Locator.dll": {
|
|
||||||
"assemblyVersion": "1.0.0.0",
|
|
||||||
"fileVersion": "1.10.2.26959"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Newtonsoft.Json/13.0.3": {
|
|
||||||
"runtime": {
|
|
||||||
"lib/net6.0/Newtonsoft.Json.dll": {
|
|
||||||
"assemblyVersion": "13.0.0.0",
|
|
||||||
"fileVersion": "13.0.3.27908"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"System.Collections.Immutable/9.0.0": {
|
|
||||||
"runtime": {
|
|
||||||
"lib/net8.0/System.Collections.Immutable.dll": {
|
|
||||||
"assemblyVersion": "9.0.0.0",
|
|
||||||
"fileVersion": "9.0.24.52809"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"System.CommandLine/2.0.0-rtm.25509.106": {
|
|
||||||
"runtime": {
|
|
||||||
"lib/net8.0/System.CommandLine.dll": {
|
|
||||||
"assemblyVersion": "2.0.0.0",
|
|
||||||
"fileVersion": "2.0.25.51006"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": {
|
|
||||||
"lib/net8.0/cs/System.CommandLine.resources.dll": {
|
|
||||||
"locale": "cs"
|
|
||||||
},
|
|
||||||
"lib/net8.0/de/System.CommandLine.resources.dll": {
|
|
||||||
"locale": "de"
|
|
||||||
},
|
|
||||||
"lib/net8.0/es/System.CommandLine.resources.dll": {
|
|
||||||
"locale": "es"
|
|
||||||
},
|
|
||||||
"lib/net8.0/fr/System.CommandLine.resources.dll": {
|
|
||||||
"locale": "fr"
|
|
||||||
},
|
|
||||||
"lib/net8.0/it/System.CommandLine.resources.dll": {
|
|
||||||
"locale": "it"
|
|
||||||
},
|
|
||||||
"lib/net8.0/ja/System.CommandLine.resources.dll": {
|
|
||||||
"locale": "ja"
|
|
||||||
},
|
|
||||||
"lib/net8.0/ko/System.CommandLine.resources.dll": {
|
|
||||||
"locale": "ko"
|
|
||||||
},
|
|
||||||
"lib/net8.0/pl/System.CommandLine.resources.dll": {
|
|
||||||
"locale": "pl"
|
|
||||||
},
|
|
||||||
"lib/net8.0/pt-BR/System.CommandLine.resources.dll": {
|
|
||||||
"locale": "pt-BR"
|
|
||||||
},
|
|
||||||
"lib/net8.0/ru/System.CommandLine.resources.dll": {
|
|
||||||
"locale": "ru"
|
|
||||||
},
|
|
||||||
"lib/net8.0/tr/System.CommandLine.resources.dll": {
|
|
||||||
"locale": "tr"
|
|
||||||
},
|
|
||||||
"lib/net8.0/zh-Hans/System.CommandLine.resources.dll": {
|
|
||||||
"locale": "zh-Hans"
|
|
||||||
},
|
|
||||||
"lib/net8.0/zh-Hant/System.CommandLine.resources.dll": {
|
|
||||||
"locale": "zh-Hant"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"libraries": {
|
|
||||||
"Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost/5.0.0-2.25567.12": {
|
|
||||||
"type": "project",
|
|
||||||
"serviceable": false,
|
|
||||||
"sha512": ""
|
|
||||||
},
|
|
||||||
"Microsoft.Build.Locator/1.10.2": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-F+nLS7IpgtslyxNvtD6Jalnf5WU08lu8yfJBNQl3cbEF3AMUphs4t7nPuRYaaU8QZyGrqtVi7i73LhAe/yHx7A==",
|
|
||||||
"path": "microsoft.build.locator/1.10.2",
|
|
||||||
"hashPath": "microsoft.build.locator.1.10.2.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Newtonsoft.Json/13.0.3": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==",
|
|
||||||
"path": "newtonsoft.json/13.0.3",
|
|
||||||
"hashPath": "newtonsoft.json.13.0.3.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"System.Collections.Immutable/9.0.0": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w==",
|
|
||||||
"path": "system.collections.immutable/9.0.0",
|
|
||||||
"hashPath": "system.collections.immutable.9.0.0.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"System.CommandLine/2.0.0-rtm.25509.106": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-IdCQOFNHQfK0hu3tzWOHFJLMaiEOR/4OynmOh+IfukrTIsCR4TTDm7lpuXQyMZ0eRfIyUcz06gHGJNlILAq/6A==",
|
|
||||||
"path": "system.commandline/2.0.0-rtm.25509.106",
|
|
||||||
"hashPath": "system.commandline.2.0.0-rtm.25509.106.nupkg.sha512"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"runtimeOptions": {
|
|
||||||
"tfm": "net8.0",
|
|
||||||
"framework": {
|
|
||||||
"name": "Microsoft.NETCore.App",
|
|
||||||
"version": "8.0.0"
|
|
||||||
},
|
|
||||||
"rollForward": "Major",
|
|
||||||
"configProperties": {
|
|
||||||
"System.Reflection.Metadata.MetadataUpdater.IsSupported": false,
|
|
||||||
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user