manager-service/ManagerService/Services/ApiKeyDatabaseService.cs

126 lines
4.1 KiB
C#

using ManagerService.Data;
using ManagerService.Helpers;
using ManagerService.Service.Services;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace ManagerService.Services
{
public class ApiKeyDatabaseService
{
private readonly MyInfoMateDbContext _db;
private readonly IHexIdGeneratorService _idService = new HexIdGeneratorService();
public ApiKeyDatabaseService(MyInfoMateDbContext db)
{
_db = db;
}
/// <summary>
/// Creates a new API key with a hashed secret (returned once in plain text).
/// </summary>
public async Task<string> CreateAsync(string instanceId, string name, ApiKeyAppType appType)
{
var plainKey = "ak_" + Convert.ToBase64String(RandomNumberGenerator.GetBytes(32))
.Replace("+", "-").Replace("/", "_").TrimEnd('=');
var keyHash = TokensService.GenerateSHA256String(plainKey);
var apiKey = new ApiKey
{
Id = _idService.GenerateHexId(),
Name = name,
InstanceId = instanceId,
AppType = appType,
Key = null,
KeyHash = keyHash,
IsActive = true,
DateCreation = DateTime.UtcNow,
};
_db.ApiKeys.Add(apiKey);
await _db.SaveChangesAsync();
return plainKey;
}
/// <summary>
/// Returns (or creates) a persistent plain-text key for a given instance + appType (PIN flow).
/// </summary>
public async Task<string> GetOrCreateByPinAsync(string instanceId, ApiKeyAppType appType)
{
var existing = await _db.ApiKeys.FirstOrDefaultAsync(k =>
k.InstanceId == instanceId &&
k.AppType == appType &&
k.IsActive &&
k.Key != null);
if (existing != null)
return existing.Key!;
var plainKey = "ak_" + Convert.ToBase64String(RandomNumberGenerator.GetBytes(32))
.Replace("+", "-").Replace("/", "_").TrimEnd('=');
var apiKey = new ApiKey
{
Id = _idService.GenerateHexId(),
Name = $"Auto-{appType}-{instanceId}",
InstanceId = instanceId,
AppType = appType,
Key = plainKey,
KeyHash = null,
IsActive = true,
DateCreation = DateTime.UtcNow,
};
_db.ApiKeys.Add(apiKey);
await _db.SaveChangesAsync();
return plainKey;
}
/// <summary>Returns all API keys for an instance (without secret values).</summary>
public async Task<List<ApiKeyDTO>> GetByInstanceAsync(string instanceId)
{
return await _db.ApiKeys
.Where(k => k.InstanceId == instanceId)
.Select(k => new ApiKeyDTO
{
Id = k.Id,
Name = k.Name,
AppType = k.AppType,
IsActive = k.IsActive,
DateCreation = k.DateCreation,
DateExpiration = k.DateExpiration,
})
.ToListAsync();
}
/// <summary>Revokes (deactivates) an API key, verifying ownership.</summary>
public async Task<bool> RevokeAsync(string id, string callerInstanceId)
{
var key = await _db.ApiKeys.FindAsync(id);
if (key == null || key.InstanceId != callerInstanceId)
return false;
key.IsActive = false;
await _db.SaveChangesAsync();
return true;
}
}
public class ApiKeyDTO
{
public string Id { get; set; }
public string Name { get; set; }
public ApiKeyAppType AppType { get; set; }
public bool IsActive { get; set; }
public DateTime DateCreation { get; set; }
public DateTime? DateExpiration { get; set; }
}
}