345 lines
11 KiB
C#
345 lines
11 KiB
C#
using Fengling.AuthService.Data;
|
|
using Fengling.AuthService.Models;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using System.Security.Claims;
|
|
using System.Text.Json;
|
|
|
|
namespace Fengling.AuthService.Controllers;
|
|
|
|
[ApiController]
|
|
[Route("api/[controller]")]
|
|
[Authorize]
|
|
public class TenantsController : 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]
|
|
public async Task<ActionResult<object>> GetTenants(
|
|
[FromQuery] int page = 1,
|
|
[FromQuery] int pageSize = 10,
|
|
[FromQuery] string? name = null,
|
|
[FromQuery] string? tenantId = null,
|
|
[FromQuery] string? status = null)
|
|
{
|
|
var query = _context.Tenants.AsQueryable();
|
|
|
|
if (!string.IsNullOrEmpty(name))
|
|
{
|
|
query = query.Where(t => t.Name.Contains(name));
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(tenantId))
|
|
{
|
|
query = query.Where(t => t.TenantId.Contains(tenantId));
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(status))
|
|
{
|
|
query = query.Where(t => t.Status == status);
|
|
}
|
|
|
|
var totalCount = await query.CountAsync();
|
|
var tenants = await query
|
|
.OrderByDescending(t => t.CreatedAt)
|
|
.Skip((page - 1) * pageSize)
|
|
.Take(pageSize)
|
|
.ToListAsync();
|
|
|
|
var result = new List<object>();
|
|
|
|
foreach (var tenant in tenants)
|
|
{
|
|
var userCount = await _context.Users.CountAsync(u => u.TenantInfo.Id == tenant.Id && !u.IsDeleted);
|
|
result.Add(new
|
|
{
|
|
id = tenant.Id,
|
|
tenantId = tenant.TenantId,
|
|
name = tenant.Name,
|
|
contactName = tenant.ContactName,
|
|
contactEmail = tenant.ContactEmail,
|
|
contactPhone = tenant.ContactPhone,
|
|
maxUsers = tenant.MaxUsers,
|
|
userCount,
|
|
status = tenant.Status,
|
|
expiresAt = tenant.ExpiresAt,
|
|
description = tenant.Description,
|
|
createdAt = tenant.CreatedAt,
|
|
});
|
|
}
|
|
|
|
return Ok(new
|
|
{
|
|
items = result,
|
|
totalCount,
|
|
page,
|
|
pageSize
|
|
});
|
|
}
|
|
|
|
[HttpGet("{id}")]
|
|
public async Task<ActionResult<Tenant>> GetTenant(long id)
|
|
{
|
|
var tenant = await _context.Tenants.FindAsync(id);
|
|
if (tenant == null)
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
return Ok(new
|
|
{
|
|
id = tenant.Id,
|
|
tenantId = tenant.TenantId,
|
|
name = tenant.Name,
|
|
contactName = tenant.ContactName,
|
|
contactEmail = tenant.ContactEmail,
|
|
contactPhone = tenant.ContactPhone,
|
|
maxUsers = tenant.MaxUsers,
|
|
status = tenant.Status,
|
|
expiresAt = tenant.ExpiresAt,
|
|
description = tenant.Description,
|
|
createdAt = tenant.CreatedAt,
|
|
updatedAt = tenant.UpdatedAt,
|
|
});
|
|
}
|
|
|
|
[HttpGet("{tenantId}/users")]
|
|
public async Task<ActionResult<List<object>>> GetTenantUsers(string tenantId)
|
|
{
|
|
var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.TenantId == tenantId);
|
|
if (tenant == null)
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
var users = await _context.Users
|
|
.Where(u => u.TenantInfo.Id == tenant.Id && !u.IsDeleted)
|
|
.ToListAsync();
|
|
|
|
var result = users.Select(async u => new
|
|
{
|
|
id = u.Id,
|
|
userName = u.UserName,
|
|
email = u.Email,
|
|
realName = u.RealName,
|
|
tenantId = u.TenantInfo.Id,
|
|
roles = await _userManager.GetRolesAsync(u),
|
|
isActive = !u.LockoutEnabled || u.LockoutEnd == null || u.LockoutEnd < DateTimeOffset.UtcNow,
|
|
createdAt = u.CreatedTime,
|
|
});
|
|
|
|
return Ok(await Task.WhenAll(result));
|
|
}
|
|
|
|
[HttpGet("{tenantId}/roles")]
|
|
public async Task<ActionResult<List<object>>> GetTenantRoles(string tenantId)
|
|
{
|
|
var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.TenantId == tenantId);
|
|
if (tenant == null)
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
var roles = await _context.Roles
|
|
.Where(r => r.TenantId == tenant.Id)
|
|
.ToListAsync();
|
|
|
|
var result = roles.Select(r => new
|
|
{
|
|
id = r.Id,
|
|
name = r.Name,
|
|
displayName = r.DisplayName,
|
|
});
|
|
|
|
return Ok(result);
|
|
}
|
|
|
|
[HttpGet("{tenantId}/settings")]
|
|
public async Task<ActionResult<TenantSettings>> GetTenantSettings(string tenantId)
|
|
{
|
|
var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.TenantId == tenantId);
|
|
if (tenant == null)
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
var settings = new TenantSettings
|
|
{
|
|
AllowRegistration = false,
|
|
AllowedEmailDomains = "",
|
|
DefaultRoleId = null,
|
|
PasswordPolicy = new List<string> { "requireNumber", "requireLowercase" },
|
|
MinPasswordLength = 8,
|
|
SessionTimeout = 120,
|
|
};
|
|
|
|
return Ok(settings);
|
|
}
|
|
|
|
[HttpPut("{tenantId}/settings")]
|
|
public async Task<IActionResult> UpdateTenantSettings(string tenantId, TenantSettings settings)
|
|
{
|
|
var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.TenantId == tenantId);
|
|
if (tenant == null)
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
await CreateAuditLog("tenant", "update", "TenantSettings", tenant.Id, tenant.TenantId, null, JsonSerializer.Serialize(settings));
|
|
|
|
return NoContent();
|
|
}
|
|
|
|
[HttpPost]
|
|
public async Task<ActionResult<Tenant>> CreateTenant(CreateTenantDto dto)
|
|
{
|
|
var tenant = new Tenant
|
|
{
|
|
TenantId = dto.TenantId,
|
|
Name = dto.Name,
|
|
ContactName = dto.ContactName,
|
|
ContactEmail = dto.ContactEmail,
|
|
ContactPhone = dto.ContactPhone,
|
|
MaxUsers = dto.MaxUsers,
|
|
Description = dto.Description,
|
|
Status = dto.Status,
|
|
ExpiresAt = dto.ExpiresAt,
|
|
CreatedAt = DateTime.UtcNow,
|
|
};
|
|
|
|
_context.Tenants.Add(tenant);
|
|
await _context.SaveChangesAsync();
|
|
|
|
await CreateAuditLog("tenant", "create", "Tenant", tenant.Id, tenant.TenantId, null, JsonSerializer.Serialize(dto));
|
|
|
|
return CreatedAtAction(nameof(GetTenant), new { id = tenant.Id }, tenant);
|
|
}
|
|
|
|
[HttpPut("{id}")]
|
|
public async Task<IActionResult> UpdateTenant(long id, UpdateTenantDto dto)
|
|
{
|
|
var tenant = await _context.Tenants.FindAsync(id);
|
|
if (tenant == null)
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
var oldValue = JsonSerializer.Serialize(tenant);
|
|
|
|
tenant.Name = dto.Name;
|
|
tenant.ContactName = dto.ContactName;
|
|
tenant.ContactEmail = dto.ContactEmail;
|
|
tenant.ContactPhone = dto.ContactPhone;
|
|
tenant.MaxUsers = dto.MaxUsers;
|
|
tenant.Description = dto.Description;
|
|
tenant.Status = dto.Status;
|
|
tenant.ExpiresAt = dto.ExpiresAt;
|
|
tenant.UpdatedAt = DateTime.UtcNow;
|
|
|
|
await _context.SaveChangesAsync();
|
|
|
|
await CreateAuditLog("tenant", "update", "Tenant", tenant.Id, tenant.TenantId, oldValue, JsonSerializer.Serialize(tenant));
|
|
|
|
return NoContent();
|
|
}
|
|
|
|
[HttpDelete("{id}")]
|
|
public async Task<IActionResult> DeleteTenant(long id)
|
|
{
|
|
var tenant = await _context.Tenants.FindAsync(id);
|
|
if (tenant == null)
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
var oldValue = JsonSerializer.Serialize(tenant);
|
|
|
|
var users = await _context.Users.Where(u => u.TenantInfo.Id == tenant.Id).ToListAsync();
|
|
foreach (var user in users)
|
|
{
|
|
user.IsDeleted = true;
|
|
user.UpdatedTime = DateTime.UtcNow;
|
|
}
|
|
|
|
tenant.IsDeleted = true;
|
|
await _context.SaveChangesAsync();
|
|
|
|
await CreateAuditLog("tenant", "delete", "Tenant", tenant.Id, tenant.TenantId, 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();
|
|
}
|
|
}
|
|
|
|
public class CreateTenantDto
|
|
{
|
|
public string TenantId { get; set; } = string.Empty;
|
|
public string Name { get; set; } = string.Empty;
|
|
public string ContactName { get; set; } = string.Empty;
|
|
public string ContactEmail { get; set; } = string.Empty;
|
|
public string? ContactPhone { get; set; }
|
|
public int? MaxUsers { get; set; }
|
|
public string? Description { get; set; }
|
|
public string Status { get; set; } = "active";
|
|
public DateTime? ExpiresAt { get; set; }
|
|
}
|
|
|
|
public class UpdateTenantDto
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public string ContactName { get; set; } = string.Empty;
|
|
public string ContactEmail { get; set; } = string.Empty;
|
|
public string? ContactPhone { get; set; }
|
|
public int? MaxUsers { get; set; }
|
|
public string? Description { get; set; }
|
|
public string Status { get; set; } = "active";
|
|
public DateTime? ExpiresAt { get; set; }
|
|
}
|
|
|
|
public class TenantSettings
|
|
{
|
|
public bool AllowRegistration { get; set; }
|
|
public string AllowedEmailDomains { get; set; } = string.Empty;
|
|
public long? DefaultRoleId { get; set; }
|
|
public List<string> PasswordPolicy { get; set; } = new();
|
|
public int MinPasswordLength { get; set; } = 8;
|
|
public int SessionTimeout { get; set; } = 120;
|
|
}
|