using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using Keyapis.EF.DataAnnotations; using Keyapis.EF.Extensions; using Keyapis.EF.ValueConverters; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; // ReSharper disable once CheckNamespace namespace Keyapis.EF.Kms.V1.Data.KmsDb; public interface IKmsDbContext : IAsyncDisposable, IDisposable { public DbSet KeyInfos { get; set; } public Task SaveChangesAsync(CancellationToken cancellationToken = default); } public abstract class KmsDbContextBase : DbContext, IKmsDbContext { protected KmsDbContextBase(DbContextOptions options) : base(options) { } public DbSet KeyInfos { get; set; } = null!; protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.HasPostgresExtension("pg_trgm"); foreach(var entity in builder.Model.GetEntityTypes()) { foreach(var property in entity.GetProperties()) { var memberInfo = property.PropertyInfo ?? (MemberInfo ?)property.FieldInfo; if (memberInfo == null) continue; var customRangeAttribute = memberInfo.GetCustomAttribute(); if (customRangeAttribute != null) { var actualType = Nullable.GetUnderlyingType(property.ClrType) ?? property.ClrType; if (actualType == typeof(string) || typeof(IEnumerable).IsAssignableFrom(actualType)) { property.SetMaxLength(customRangeAttribute.Maximum > int.MaxValue ? int.MaxValue : (int)customRangeAttribute.Maximum); } } var customRequiredAttribute = memberInfo.GetCustomAttribute(); if (customRequiredAttribute != null) { property.IsNullable = false; } } } builder.Entity(e => { // Для поля id дополнительные конфигурации свойства не нужны // Для поля id сопоствалений с фильтром и сортировкой не найденно // Для поля public_key дополнительные конфигурации свойства не нужны // Для поля public_key сопоствалений с фильтром и сортировкой не найденно // Для поля encrypted_private_key дополнительные конфигурации свойства не нужны // Для поля encrypted_private_key сопоствалений с фильтром и сортировкой не найденно e.Property(p => p.CreatedAt) .HasColumnType("timestamp without time zone") .HasConversion(DateTimeUtcConverter.Instance); // Для поля created_at сопоствалений с фильтром и сортировкой не найденно // WARNING Для поля KeyInfo.expired_at не указан диапазон e.Property(p => p.ExpiredAt) .HasColumnType("timestamp without time zone") .HasConversion(DateTimeUtcConverter.Instance); // Для поля expired_at сопоствалений с фильтром и сортировкой не найденно }); builder.HasDefaultSchema("kms"); } } /// /// Пара ключей. /// # Описание модели /// [ Display(Name = @"Пара ключей"), Description(@"Описание модели") ] public partial class KeyInfoModel { /// /// Идентификатор. /// # Тип: Guid /// [ Display(Name = @"Идентификатор"), Description(@"Тип: Guid"), CustomRequired, CustomPattern(@"^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$") ] public Guid Id { get; set; } /// /// Публичный ключ. /// # Диапазон: 0..800 /// [ Display(Name = @"Публичный ключ"), Description(@"Диапазон: 0..800"), CustomRequired, CustomRange(0, 800), Editable(false) ] public string PublicKey { get; set; } = null!; /// /// Приватный ключ в зашифрованном виде. /// # Диапазон: 0..4000 /// [ Display(Name = @"Приватный ключ в зашифрованном виде"), Description(@"Диапазон: 0..4000"), CustomRequired, CustomRange(0, 4000), Editable(false) ] public string EncryptedPrivateKey { get; set; } = null!; /// /// Дата и время создания ключа /// [ Display(Name = @"Дата и время создания ключа"), Editable(false) ] public DateTime CreatedAt { get; set; } /// /// Дата и время истечения жизни ключа /// [ Display(Name = @"Дата и время истечения жизни ключа"), Editable(false) ] public DateTime ExpiredAt { get; set; } /// /// Ошибка генерации /// [ Display(Name = @"Ошибка генерации") ] public partial class GeneratingErrorModel { /// /// Ошибка генерации ключа в сервисе KMS /// [ Display(Name = @"Ошибка генерации ключа в сервисе KMS") ] public KeyInfoModel.GeneratingErrorModel.TransactionErrorModel? Transaction { get; set; } /// /// Ошибка сохранения публичного ключа в сервисе JWKS /// [ Display(Name = @"Ошибка сохранения публичного ключа в сервисе JWKS") ] public KeyInfoModel.GeneratingErrorModel.PublishingKeyErrorModel? Publishing { get; set; } /// /// Ошибка генерации ключа в сервисе KMS. /// Причины: /// - Отсутсвует связанность с базой данных /// [ Display(Name = @"Ошибка генерации ключа в сервисе KMS"), Description(@"Причины: - Отсутсвует связанность с базой данных") ] public partial class TransactionErrorModel { } /// /// Ошибка сохранения публичного ключа в сервисе JWKS. /// Причины: /// - Отсутсвует связанность с сервисом JWKS /// [ Display(Name = @"Ошибка сохранения публичного ключа в сервисе JWKS"), Description(@"Причины: - Отсутсвует связанность с сервисом JWKS") ] public partial class PublishingKeyErrorModel { } } } /// /// Задача на инициализацию ключей. /// Очередь key.kms.key_initialization /// [ Display(Name = @"Задача на инициализацию ключей"), Description(@"Очередь key.kms.key_initialization") ] public partial class KeyInitializationTaskModel { /// /// Идентификатор запроса /// [ Display(Name = @"Идентификатор запроса"), CustomRequired ] public string RequestId { get; set; } = null!; // WARNING Не удалось найти свойство навигации для поля KeyInitializationTask.request_id } /// /// Запрос на создание nested JWT токена /// [ Display(Name = @"Запрос на создание nested JWT токена") ] public partial class PostKmsJwtCreateJweRequestModel { /// /// Claims (данные включаемые в тело токена) /// [ Display(Name = @"Claims"), CustomRequired ] public PostKmsJwtCreateJweRequestModel.PayloadDataModel Data { get; set; } = null!; /// /// Публичный ключ для шифрования /// [ Display(Name = @"Публичный ключ для шифрования"), CustomRequired ] public string PublicKey { get; set; } = null!; /// /// Claims (данные включаемые в тело токена) /// [ Display(Name = @"Claims") ] public partial class PayloadDataModel { /// /// Словарь значений. /// Ключи и значения являются строками. /// # Диапазон: 1..128 /// [ Display(Name = @"Словарь значений"), Description(@"Ключи и значения являются строками. Диапазон: 1..128"), CustomRange(1, 128) ] public Dictionary Values { get; set; } = new(0); } } /// /// Ответ на запрос на формирование токена /// [ Display(Name = @"Ответ на запрос на формирование токена") ] public partial class PostKmsJwtCreateJweResponseModel { /// /// Nested JWT токен /// [ Display(Name = @"Nested JWT токен") ] public string? Data { get; set; } /// /// Ошибка /// [ Display(Name = @"Ошибка") ] public PostKmsJwtCreateJweResponseModel.ErrorModel? Error { get; set; } /// /// Ошибка /// [ Display(Name = @"Ошибка") ] public partial class ErrorModel { /// /// Ошибка валидации /// [ Display(Name = @"Ошибка валидации") ] public ValidationErrorModel? Validation { get; set; } /// /// Отсутствует ключ для подписания /// [ Display(Name = @"Отсутствует ключ для подписания") ] public PostKmsJwtCreateJweResponseModel.ErrorModel.PrivateKeyNotFoundModel? PrivateKeyNotFound { get; set; } /// /// Отсутствует ключ для подписания /// [ Display(Name = @"Отсутствует ключ для подписания") ] public partial class PrivateKeyNotFoundModel { } } } /// /// Ошибки валидации. /// Эти проверки выполняются до обращения в базу данных /// [ Display(Name = @"Ошибки валидации"), Description(@"Эти проверки выполняются до обращения в базу данных") ] public partial class ValidationErrorModel { /// /// Путь к полю в формате наименования прото /// [ Display(Name = @"Путь к полю в формате наименования прото"), CustomRequired ] public string Path { get; set; } = null!; /// /// Валидационное сообщение /// [ Display(Name = @"Валидационное сообщение"), CustomRequired ] public string Message { get; set; } = null!; } /// /// Запрос на получение ключей /// [ Display(Name = @"Запрос на получение ключей") ] public partial class GetKmsKeyListRequestModel { } /// /// Ответ на запрос на получение ключей /// [ Display(Name = @"Ответ на запрос на получение ключей") ] public partial class GetKmsKeyListResponseModel { /// /// Ключ. /// В ключе должен отсутсвовать encrypted_private_key /// [ Display(Name = @"Ключ"), Description(@"В ключе должен отсутсвовать encrypted_private_key") ] public KeyInfoModel? Data { get; set; } } /// /// Запрос на генерацию пары ключей /// [ Display(Name = @"Запрос на генерацию пары ключей") ] public partial class PostKmsKeyGenerateRequestModel { } /// /// Ответ на запрос на генерацию пары ключей /// [ Display(Name = @"Ответ на запрос на генерацию пары ключей") ] public partial class PostKmsKeyGenerateResponseModel { /// /// Идентификатор. /// # Тип: Guid /// [ Display(Name = @"Идентификатор"), Description(@"Тип: Guid"), CustomPattern(@"^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$") ] public Guid Data { get; set; } /// /// Ошибка /// [ Display(Name = @"Ошибка") ] public PostKmsKeyGenerateResponseModel.ErrorModel? Error { get; set; } /// /// Ошибка /// [ Display(Name = @"Ошибка") ] public partial class ErrorModel { /// /// Ошибка генерации ключей в сервисе KMS /// [ Display(Name = @"Ошибка генерации ключей в сервисе KMS") ] public KeyInfoModel.GeneratingErrorModel? Generating { get; set; } } } /// /// Запрос проверки доступности сервиса /// [ Display(Name = @"Запрос проверки доступности сервиса") ] public partial class GetSystemStatusRequestModel { } /// /// Ответ на запрос проверки доступности сервиса /// [ Display(Name = @"Ответ на запрос проверки доступности сервиса") ] public partial class GetSystemStatusResponseModel { }