using Fengling.AuthService.Data; using Fengling.AuthService.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Security.Claims; namespace Fengling.AuthService.Controllers; [ApiController] [Route("api/[controller]")] [Authorize] public class OAuthClientsController : ControllerBase { private readonly ApplicationDbContext _context; private readonly ILogger _logger; public OAuthClientsController( ApplicationDbContext context, ILogger logger) { _context = context; _logger = logger; } [HttpGet] public async Task> GetClients( [FromQuery] int page = 1, [FromQuery] int pageSize = 10, [FromQuery] string? displayName = null, [FromQuery] string? clientId = null, [FromQuery] string? status = null) { var query = _context.OAuthApplications.AsQueryable(); if (!string.IsNullOrEmpty(displayName)) { query = query.Where(c => c.DisplayName.Contains(displayName)); } if (!string.IsNullOrEmpty(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) .Take(pageSize) .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, }); return Ok(new { items = result, totalCount, page, pageSize }); } [HttpGet("{id}")] public async Task> GetClient(long id) { var client = await _context.OAuthApplications.FindAsync(id); if (client == null) { return NotFound(); } return Ok(new { id = client.Id, clientId = client.ClientId, displayName = client.DisplayName, redirectUris = client.RedirectUris, postLogoutRedirectUris = client.PostLogoutRedirectUris, scopes = client.Scopes, grantTypes = client.GrantTypes, clientType = client.ClientType, consentType = client.ConsentType, status = client.Status, description = client.Description, createdAt = client.CreatedAt, }); } [HttpGet("{id}/secret")] public async Task> GetClientSecret(long id) { var client = await _context.OAuthApplications.FindAsync(id); if (client == null) { return NotFound(); } return Ok(new { clientId = client.ClientId, clientSecret = client.ClientSecret, }); } [HttpPost] public async Task> CreateClient(CreateOAuthClientDto dto) { if (await _context.OAuthApplications.AnyAsync(c => c.ClientId == dto.ClientId)) { return BadRequest(new { message = "Client ID 已存在" }); } var client = new OAuthApplication { ClientId = dto.ClientId, ClientSecret = dto.ClientSecret, DisplayName = dto.DisplayName, RedirectUris = dto.RedirectUris, PostLogoutRedirectUris = dto.PostLogoutRedirectUris, Scopes = dto.Scopes, GrantTypes = dto.GrantTypes, ClientType = dto.ClientType, ConsentType = dto.ConsentType, Status = dto.Status, 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 }, client); } [HttpPut("{id}")] public async Task UpdateClient(long id, UpdateOAuthClientDto dto) { var client = await _context.OAuthApplications.FindAsync(id); if (client == null) { return NotFound(); } var oldValue = SerializeToJson(client); client.DisplayName = dto.DisplayName; client.RedirectUris = dto.RedirectUris; client.PostLogoutRedirectUris = dto.PostLogoutRedirectUris; client.Scopes = dto.Scopes; client.GrantTypes = dto.GrantTypes; client.ClientType = dto.ClientType; client.ConsentType = dto.ConsentType; client.Status = dto.Status; client.Description = dto.Description; await _context.SaveChangesAsync(); await CreateAuditLog("oauth", "update", "OAuthClient", client.Id, client.DisplayName, oldValue, SerializeToJson(client)); return NoContent(); } [HttpDelete("{id}")] public async Task DeleteClient(long id) { var client = await _context.OAuthApplications.FindAsync(id); if (client == null) { return NotFound(); } var oldValue = SerializeToJson(client); _context.OAuthApplications.Remove(client); await _context.SaveChangesAsync(); await CreateAuditLog("oauth", "delete", "OAuthClient", client.Id, client.DisplayName, oldValue); return NoContent(); } 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 string SerializeToJson(object obj) { return System.Text.Json.JsonSerializer.Serialize(obj, new System.Text.Json.JsonSerializerOptions { WriteIndented = false }); } } public class CreateOAuthClientDto { public string ClientId { get; set; } = string.Empty; public string ClientSecret { get; set; } = string.Empty; public string DisplayName { get; set; } = string.Empty; public string[] RedirectUris { get; set; } = Array.Empty(); public string[] PostLogoutRedirectUris { get; set; } = Array.Empty(); public string[] Scopes { get; set; } = Array.Empty(); public string[] GrantTypes { get; set; } = Array.Empty(); public string ClientType { get; set; } = "confidential"; public string ConsentType { get; set; } = "implicit"; public string Status { get; set; } = "active"; public string? Description { get; set; } } public class UpdateOAuthClientDto { public string DisplayName { get; set; } = string.Empty; public string[] RedirectUris { get; set; } = Array.Empty(); public string[] PostLogoutRedirectUris { get; set; } = Array.Empty(); public string[] Scopes { get; set; } = Array.Empty(); public string[] GrantTypes { get; set; } = Array.Empty(); public string ClientType { get; set; } = "confidential"; public string ConsentType { get; set; } = "implicit"; public string Status { get; set; } = "active"; public string? Description { get; set; } }