Adds a new JSON-bodied signature endpoint as a sibling of the
legacy PNG-based prosign/clisign routes. The legacy flow stays
intact: the TeX invoice templates (Bill_tex.cshtml,
Estimate_tex.cshtml) still consume the sign-{billingCode}-{id}.png
files the old endpoints write, and the new endpoint writes to a
distinct /signatures/ tree under UserFilesDirName. A future
migration commit will regenerate PNGs from the JSON payload and
decommission the PNG flow.
Scope
- New Signature entity (Yavsc.Server/Models/Billing/Signature.cs)
with FK to Estimate, FK to ApplicationUser (Signer), Type
(Pro/Client) enum, CoordinateMax (default 10_000), int[] Strokes
(native Npgsql mapping), CapturedAtUtc, FilePath. Multiple
versions per (EstimateId, Type) are allowed; the controller
reads the most recent.
- New Estimate.Signatures nav collection (InverseProperty) so the
composite index covers both sides of the relation.
- New DbSet<Signature> Signatures + composite index
(EstimateId, Type, CapturedAtUtc DESC) in ApplicationDbContext
OnModelCreating. DeleteBehavior.Cascade on Estimate deletion
cleans up signatures automatically.
- New EstimateSignatureFileHelper (Server/Helpers) with
ReceiveEstimateSignatureAsync(user, estimateId, type, payload).
Writes a yavsc.signature/v1 JSON envelope to
UserFilesDirName/{user}/signatures/sign-{type}-{estimateId}-{ticks}.json.
Quota update lives in the controller, not the helper, because
the helper has no DbContext access.
- New endpoint POST /api/bill/estimate/{id:long}/sign on
BillingController. Authz is body-driven (the bearer token is the
PostIt OAuth client, not the end user, so signerUserId is in
the JSON body, validated against Estimate.OwnerId/ClientId).
Returns 201 Created with the new Signature's metadata.
Plumbing
- SignatureSubmission (body type) lives next to BillingController
in the same file — small enough to keep colocated.
- The legacy prosign/clisign routes are untouched. They keep
the IFormFile PNG contract; the new endpoint is the JSON
counterpart.
Tests
- New EstimateSignatureFileHelperTests in Yavsc.Org.Tests
(8 tests, all green): filename format incl. lowercase type and
ticks, envelope v1 round-trip (parsed via JsonDocument, not
text matching), null payload rejected, non-positive
estimateId rejected. Disk side effects are isolated to a
per-test temp root via AbstractFileSystemHelpers.UserFilesDirName.
- Yavsc.Org.Tests full suite: 29/29 green.
- PostIt.Tests: 57/57 green (untouched by this commit).
- Builds: Yavsc.Server, Yavsc.Api, Yavsc.Org, Yavsc.Org.Tests
all compile clean.
Out of scope
- EF migration: the Signatures table doesn't exist in the
database yet. The migration is intentionally a separate
commit so the generated SQL can be reviewed against the
composite index and the int[] column type before it touches
any prod database. Until the migration lands, the new
endpoint will 500 on SaveChanges; the [DEV] button in
PostIt is the only call site, so this is acceptable.
- SignalR handler that opens the signature page on a
'devis received' push — commit 4.
448 lines
20 KiB
C#
448 lines
20 KiB
C#
using IdentityServer8.EntityFramework.Entities;
|
|
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
|
|
|
namespace Yavsc.Models
|
|
{
|
|
using Abstract.Identity;
|
|
using Abstract.Models.Messaging;
|
|
using Access;
|
|
using Attributes;
|
|
using Auth;
|
|
using Bank;
|
|
using Billing;
|
|
using Blog;
|
|
using Chat;
|
|
using Drawing;
|
|
using Forms;
|
|
using Haircut;
|
|
using Identity;
|
|
using IT.Evolution;
|
|
using IT.Fixing;
|
|
using Market;
|
|
using Messaging;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Musical;
|
|
using Musical.Profiles;
|
|
using Payment;
|
|
using Relationship;
|
|
using Server.Models.Calendar;
|
|
using Server.Models.EMailing;
|
|
using Server.Models.IT;
|
|
using Server.Models.IT.SourceCode;
|
|
using Streaming;
|
|
using Workflow;
|
|
using Workflow.Profiles;
|
|
using Yavsc.Models.Kyc;
|
|
|
|
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
|
|
{
|
|
public ApplicationDbContext()
|
|
{
|
|
}
|
|
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
|
|
{
|
|
if (Database.IsRelational())
|
|
{
|
|
Database.SetCommandTimeout(180);
|
|
}
|
|
}
|
|
|
|
protected override void OnModelCreating(ModelBuilder builder)
|
|
{
|
|
base.OnModelCreating(builder);
|
|
if (Database.IsNpgsql())
|
|
{
|
|
NOW_SQL = "LOCALTIMESTAMP";
|
|
}
|
|
else
|
|
{
|
|
NOW_SQL = "CURRENT_TIMESTAMP";
|
|
}
|
|
builder.UseIdentityByDefaultColumns();
|
|
|
|
// Customize the ASP.NET Identity model and override the defaults if needed.
|
|
// For example, you can rename the ASP.NET Identity table names and more.
|
|
// Add your customizations after calling base.OnModelCreating(builder);
|
|
builder.Entity<Contact>().HasKey(x => new { x.OwnerId, x.UserId });
|
|
builder.Entity<DeviceDeclaration>().Property(x => x.DeclarationDate).HasDefaultValueSql(NOW_SQL);
|
|
builder.Entity<BlogTag>().HasKey(x => new { x.PostId, x.TagId });
|
|
|
|
// Signature: composite index (EstimateId, Type,
|
|
// CapturedAtUtc DESC) to support the controller's
|
|
// "most recent signature per type" read pattern
|
|
// without an extra ORDER BY cost. The default
|
|
// EF-generated FK index on EstimateId alone is
|
|
// replaced by the composite to avoid duplicate
|
|
// indexes.
|
|
builder.Entity<Signature>()
|
|
.HasIndex(s => new { s.EstimateId, s.Type, s.CapturedAtUtc })
|
|
.IsDescending(false, false, true);
|
|
builder.Entity<Signature>()
|
|
.HasOne(s => s.Estimate)
|
|
.WithMany(e => e.Signatures)
|
|
.HasForeignKey(s => s.EstimateId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
builder.Entity<Signature>()
|
|
.Property(s => s.CoordinateMax)
|
|
.HasDefaultValue(10_000);
|
|
|
|
builder.Entity<ApplicationUser>().Property(u => u.FullName).IsRequired(false);
|
|
builder.Entity<ApplicationUser>().Property(u => u.DedicatedGoogleCalendar).IsRequired(false);
|
|
builder.Entity<ApplicationUser>().HasMany<ChatConnection>(c => c.Connections);
|
|
builder.Entity<ApplicationUser>().Property(u => u.Avatar).HasDefaultValue(YavscConstants.DefaultAvatar);
|
|
builder.Entity<ApplicationUser>().Property(u => u.DiskQuota).HasDefaultValue(YavscConstants.DefaultFSQ);
|
|
builder.Entity<ApplicationUser>().HasAlternateKey(u => u.Email);
|
|
builder.Entity<BlackListed>().HasOne<ApplicationUser>(bl => bl.User);
|
|
builder.Entity<BlackListed>().HasOne<ApplicationUser>(bl => bl.Owner);
|
|
builder.Entity<UserActivity>().HasKey(u => new { u.DoesCode, u.UserId });
|
|
builder.Entity<Instrumentation>().HasKey(u => new { u.InstrumentId, u.UserId });
|
|
builder.Entity<CircleAuthorizationToBlogPost>().HasKey(a => new { a.CircleId, a.BlogPostId });
|
|
builder.Entity<CircleMember>().HasKey(c => new { c.MemberId, c.CircleId });
|
|
builder.Entity<DismissClicked>().HasKey(c => new { uid = c.UserId, notid = c.NotificationId });
|
|
builder.Entity<HairTaintInstance>().HasKey(ti => new { ti.TaintId, ti.PrestationId });
|
|
builder.Entity<HyperLink>().HasKey(l => new { l.HRef, l.Method });
|
|
builder.Entity<Period>().HasKey(l => new { l.Start, l.End });
|
|
builder.Entity<Cratie.Option>().HasKey(o => new { o.Code, o.CodeScrutin });
|
|
builder.Entity<Notification>().Property(n => n.icon).HasDefaultValue("exclam");
|
|
builder.Entity<ChatRoomAccess>().HasKey(p => new { room = p.ChannelName, user = p.UserId });
|
|
builder.Entity<InstrumentRating>().HasAlternateKey(i => new { Instrument = i.InstrumentId, owner = i.OwnerId })
|
|
;
|
|
|
|
builder.Entity<Activity>().Property(a => a.ParentCode).IsRequired(false);
|
|
|
|
builder.Entity<Client>().Property("Id").UseIdentityAlwaysColumn();
|
|
builder.Entity<ClientSecret>().Property("Id").UseIdentityAlwaysColumn();
|
|
builder.Entity<ClientScope>().Property("Id").UseIdentityAlwaysColumn();
|
|
builder.Entity<ClientIdPRestriction>().Property("Id").UseIdentityAlwaysColumn();
|
|
builder.Entity<ClientProperty>().Property("Id").UseIdentityAlwaysColumn();
|
|
builder.Entity<ClientPostLogoutRedirectUri>().Property("Id").UseIdentityAlwaysColumn();
|
|
builder.Entity<ClientRedirectUri>().Property("Id").UseIdentityAlwaysColumn();
|
|
builder.Entity<ClientCorsOrigin>().Property("Id").UseIdentityAlwaysColumn();
|
|
builder.Entity<ClientGrantType>().Property("Id").UseIdentityAlwaysColumn();
|
|
builder.Entity<ClientClaim>().Property("Id").UseIdentityAlwaysColumn();
|
|
builder.Entity<ApiResource>().Property("Id").UseIdentityAlwaysColumn();
|
|
builder.Entity<ApiScope>().Property("Id").UseIdentityAlwaysColumn();
|
|
|
|
builder.Entity<DeviceFlowCodes>().HasKey(e => new { e.UserCode, e.DeviceCode });
|
|
builder.Entity<PersistedGrant>().HasKey(e => e.Key);
|
|
|
|
// builder.Entity<IdentityUserLogin<String>>().HasKey(i=> new { i.LoginProvider, i.UserId, i.ProviderKey });
|
|
builder.Entity<ClientSecret>().HasOne<Client>().WithMany(e => e.ClientSecrets).HasForeignKey(e => e.ClientId);
|
|
builder.Entity<ClientScope>().HasOne<Client>().WithMany(e => e.AllowedScopes).HasForeignKey(e => e.ClientId);
|
|
builder.Entity<ClientIdPRestriction>().HasOne<Client>().WithMany(e => e.IdentityProviderRestrictions).HasForeignKey(e => e.ClientId);
|
|
builder.Entity<ClientProperty>().HasOne<Client>().WithMany(e => e.Properties).HasForeignKey(e => e.ClientId);
|
|
builder.Entity<ClientPostLogoutRedirectUri>().HasOne<Client>().WithMany(e => e.PostLogoutRedirectUris).HasForeignKey(e => e.ClientId);
|
|
builder.Entity<ClientRedirectUri>().HasOne<Client>().WithMany(e => e.RedirectUris).HasForeignKey(e => e.ClientId);
|
|
builder.Entity<ClientCorsOrigin>().HasOne<Client>().WithMany(e => e.AllowedCorsOrigins).HasForeignKey(e => e.ClientId);
|
|
builder.Entity<ClientGrantType>().HasOne<Client>().WithMany(e => e.AllowedGrantTypes).HasForeignKey(e => e.ClientId);
|
|
builder.Entity<ApiResourceSecret>().HasOne<ApiResource>().WithMany(e => e.Secrets).HasForeignKey(e => e.ApiResourceId);
|
|
builder.Entity<ApiResourceScope>().HasOne<ApiResource>().WithMany(e => e.Scopes).HasForeignKey(e => e.ApiResourceId);
|
|
builder.Entity<ApiResourceClaim>().HasOne<ApiResource>().WithMany(e => e.UserClaims).HasForeignKey(e => e.ApiResourceId);
|
|
builder.Entity<ApiResourceProperty>().HasOne<ApiResource>().WithMany(e => e.Properties).HasForeignKey(e => e.ApiResourceId);
|
|
builder.Entity<ApiScopeClaim>().HasOne<ApiScope>().WithMany(e => e.UserClaims).HasForeignKey(e => e.ScopeId);
|
|
builder.Entity<ApiScopeProperty>().HasOne<ApiScope>().WithMany(e => e.Properties).HasForeignKey(e => e.ScopeId);
|
|
|
|
foreach (var et in builder.Model.GetEntityTypes())
|
|
{
|
|
if (et.ClrType.GetInterface("IBaseTrackedEntity") != null)
|
|
et.FindProperty("DateCreated").SetAfterSaveBehavior(Microsoft.EntityFrameworkCore.Metadata.PropertySaveBehavior.Ignore);
|
|
}
|
|
|
|
// ── TrustToken ───────────────────────────────────────────────────────
|
|
builder.Entity<TrustToken>(e =>
|
|
{
|
|
e.HasIndex(t => t.TokenHash).IsUnique(); // idempotence garantie en DB
|
|
e.Property(t => t.TokenHash).HasMaxLength(128).IsRequired();
|
|
e.Property(t => t.TokenSource).HasMaxLength(32).IsRequired();
|
|
});
|
|
|
|
// ── TrustDeclaration ─────────────────────────────────────────────────
|
|
builder.Entity<TrustDeclaration>(e =>
|
|
{
|
|
e.HasOne(d => d.Subject)
|
|
.WithMany(t => t.Declarations)
|
|
.HasForeignKey(d => d.TrustTokenId)
|
|
.OnDelete(DeleteBehavior.Restrict); // on ne supprime pas un token qui a des déclarations
|
|
|
|
// DeclarantTokenId est une FK vers TrustToken mais sans navigation
|
|
// pour éviter les cycles EF
|
|
e.HasIndex(d => d.TrustTokenId);
|
|
e.HasIndex(d => d.Status);
|
|
e.HasIndex(d => d.SubmittedAt);
|
|
e.Property(d => d.Content).HasMaxLength(2000);
|
|
});
|
|
|
|
// ── DeclarationFlag ──────────────────────────────────────────────────
|
|
builder.Entity<DeclarationFlag>(e =>
|
|
{
|
|
e.HasOne(f => f.Declaration)
|
|
.WithMany(d => d.Flags)
|
|
.HasForeignKey(f => f.DeclarationId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
e.HasOne(f => f.Pattern)
|
|
.WithMany()
|
|
.HasForeignKey(f => f.PatternId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
e.Property(f => f.MatchExcerpt).HasMaxLength(100);
|
|
});
|
|
|
|
// ── RegexAlertPattern ────────────────────────────────────────────────
|
|
builder.Entity<RegexAlertPattern>(e =>
|
|
{
|
|
e.Property(p => p.Pattern).HasMaxLength(500).IsRequired();
|
|
e.Property(p => p.Description).HasMaxLength(200);
|
|
e.HasIndex(p => p.IsActive);
|
|
});
|
|
|
|
// ── ModerationLog ────────────────────────────────────────────────────
|
|
builder.Entity<ModerationLog>(e =>
|
|
{
|
|
e.HasOne(l => l.Declaration)
|
|
.WithMany()
|
|
.HasForeignKey(l => l.DeclarationId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
e.HasIndex(l => l.DeclarationId);
|
|
e.HasIndex(l => l.ModeratorId);
|
|
e.HasIndex(l => l.Timestamp);
|
|
|
|
// Log immuable — pas de update autorisé
|
|
e.ToTable(tb => tb.HasCheckConstraint("CK_ModerationLog_Immutable", "1=1"));
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Activities referenced on this site
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public DbSet<Activity> Activities { get; set; }
|
|
|
|
public DbSet<UserActivity> UserActivities { get; set; }
|
|
|
|
/// <summary>
|
|
/// Users posts
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public DbSet<BlogPost> BlogSpot { get; set; }
|
|
|
|
/// <summary>
|
|
/// Skills powered by this site
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public DbSet<Skill> SiteSkills { get; set; }
|
|
|
|
/// <summary>
|
|
/// Circle members
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public DbSet<CircleMember> CircleMembers { get; set; }
|
|
|
|
/// <summary>
|
|
/// Special commands, talking about
|
|
/// a given place and date.
|
|
/// </summary>
|
|
public DbSet<RdvQuery> RdvQueries { get; set; }
|
|
|
|
public DbSet<HairCutQuery> HairCutQueries { get; set; }
|
|
public DbSet<HairPrestation> HairPrestation { get; set; }
|
|
|
|
public DbSet<HairMultiCutQuery> HairMultiCutQueries { get; set; }
|
|
public DbSet<PerformerProfile> Performers { get; set; }
|
|
|
|
public DbSet<Estimate> Estimates { get; set; }
|
|
public DbSet<Signature> Signatures { get; set; }
|
|
public DbSet<AccountBalance> BankStatus { get; set; }
|
|
public DbSet<BalanceImpact> BalanceImpact { get; set; }
|
|
|
|
/// <summary>
|
|
/// References all declared external NativeConfidential devices
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public DbSet<DeviceDeclaration> DeviceDeclaration { get; set; }
|
|
|
|
public DbSet<Service> Services { get; set; }
|
|
public DbSet<Product> Products { get; set; }
|
|
|
|
public DbSet<ExceptionSIREN> ExceptionsSIREN { get; set; }
|
|
|
|
public DbSet<Location> Locations { get; set; }
|
|
|
|
public DbSet<Tag> Tags { get; set; }
|
|
|
|
public DbSet<BlogTag> TagsDomain { get; set; }
|
|
|
|
public DbSet<EstimateTemplate> EstimateTemplates { get; set; }
|
|
|
|
public DbSet<Contact> Contact { get; set; }
|
|
|
|
public DbSet<ClientProviderInfo> ClientProviderInfo { get; set; }
|
|
|
|
public DbSet<BlackListed> BlackListed { get; set; }
|
|
|
|
public DbSet<MusicalPreference> MusicalPreference { get; set; }
|
|
|
|
public DbSet<MusicalTendency> MusicalTendency { get; set; }
|
|
|
|
public DbSet<Instrument> Instrument { get; set; }
|
|
|
|
[ActivitySettings]
|
|
public DbSet<DjSettings> DjSettings { get; set; }
|
|
|
|
[ActivitySettings]
|
|
public DbSet<Instrumentation> Instrumentation { get; set; }
|
|
|
|
[ActivitySettings]
|
|
public DbSet<FormationSettings> FormationSettings { get; set; }
|
|
|
|
[ActivitySettings]
|
|
public DbSet<MusicLoverSettings> GeneralSettings { get; set; }
|
|
public DbSet<CoWorking> CoWorking { get; set; }
|
|
|
|
private void AddTimestamps(string userId)
|
|
{
|
|
var entities =
|
|
ChangeTracker.Entries()
|
|
.Where(x => x.Entity.GetType().GetInterface(nameof(ITrackedEntity)) != null
|
|
&& (x.State == EntityState.Added || x.State == EntityState.Modified));
|
|
|
|
|
|
foreach (var entity in entities)
|
|
{
|
|
if (entity.State == EntityState.Added)
|
|
{
|
|
((ITrackedEntity)entity.Entity).DateCreated = DateTime.Now;
|
|
((ITrackedEntity)entity.Entity).UserCreated = userId;
|
|
}
|
|
|
|
((ITrackedEntity)entity.Entity).DateModified = DateTime.Now;
|
|
((ITrackedEntity)entity.Entity).UserModified = userId;
|
|
}
|
|
}
|
|
|
|
public int SaveChanges(string userId)
|
|
{
|
|
AddTimestamps(userId);
|
|
return base.SaveChanges();
|
|
}
|
|
|
|
public async Task<int> SaveChangesAsync(string userId, CancellationToken ctoken = default(CancellationToken))
|
|
{
|
|
AddTimestamps(userId);
|
|
return await base.SaveChangesAsync(ctoken);
|
|
}
|
|
|
|
public Task<int> SaveChangesAsync()
|
|
{
|
|
return base.SaveChangesAsync();
|
|
}
|
|
|
|
public DbSet<Circle> Circle { get; set; }
|
|
|
|
public DbSet<CircleAuthorizationToBlogPost> CircleAuthorizationToBlogPost { get; set; }
|
|
|
|
public DbSet<CommandForm> CommandForm { get; set; }
|
|
|
|
public DbSet<Form> Form { get; set; }
|
|
|
|
public DbSet<Ban> Ban { get; set; }
|
|
|
|
public DbSet<HairTaint> HairTaint { get; set; }
|
|
|
|
public DbSet<Color> Color { get; set; }
|
|
|
|
public DbSet<Notification> Notification { get; set; }
|
|
|
|
public DbSet<DismissClicked> DismissClicked { get; set; }
|
|
|
|
|
|
[ActivitySettings]
|
|
public DbSet<BrusherProfile> BrusherProfile { get; set; }
|
|
|
|
public DbSet<BankIdentity> BankIdentity { get; set; }
|
|
|
|
public DbSet<PayPalPayment> PayPalPayment { get; set; }
|
|
|
|
public DbSet<HyperLink> HyperLink { get; set; }
|
|
|
|
public DbSet<Period> Period { get; set; }
|
|
|
|
public DbSet<BlogTag> BlogTag { get; set; }
|
|
|
|
public DbSet<ApplicationUser> ApplicationUser { get; set; }
|
|
|
|
public DbSet<Feature> Feature { get; set; }
|
|
|
|
public DbSet<Bug> Bug { get; set; }
|
|
|
|
public DbSet<Comment> Comment { get; set; }
|
|
|
|
public DbSet<Announce> Announce { get; set; }
|
|
|
|
// TODO remove and opt for for memory only storing,
|
|
// as long as it must be set empty each time the service is restarted,
|
|
// and that chatting should be kept as must as possible independent from db context
|
|
public DbSet<ChatConnection> ChatConnection { get; set; }
|
|
|
|
public DbSet<ChatRoom> ChatRoom { get; set; }
|
|
|
|
public DbSet<MailingTemplate> MailingTemplate { get; set; }
|
|
|
|
public DbSet<GitRepositoryReference> GitRepositoryReference { get; set; }
|
|
|
|
public DbSet<Project> Project { get; set; }
|
|
|
|
[Obsolete("use signaled flows")]
|
|
public DbSet<LiveFlow> LiveFlow { get; set; }
|
|
|
|
public DbSet<ChatRoomAccess> ChatRoomAccess { get; set; }
|
|
|
|
public DbSet<InstrumentRating> InstrumentRating { get; set; }
|
|
|
|
|
|
public DbSet<BlogSpotPublication> blogSpotPublications { get; set; }
|
|
|
|
public DbSet<UploadedFile> UploadedFiles { get; set; }
|
|
|
|
public DbSet<BlogAttachedFile> BlogAttachedFiles { get; set; }
|
|
|
|
public DbSet<Client> Clients { get; set; }
|
|
public DbSet<ClientIdPRestriction> ClientIdPRestrictions { get; set; }
|
|
public DbSet<ClientProperty> ClientProperties { get; set; }
|
|
|
|
public DbSet<ClientCorsOrigin> ClientCorsOrigins { get; set; }
|
|
public DbSet<ClientSecret> ClientSecrets { get; set; }
|
|
public DbSet<ClientScope> ClientScopes { get; set; }
|
|
public DbSet<ClientGrantType> ClientGrantTypes { get; set; }
|
|
public DbSet<ClientClaim> ClientClaims { get; set; }
|
|
|
|
public DbSet<ClientRedirectUri> ClientRedirectUris { get; set; }
|
|
|
|
|
|
public DbSet<ClientPostLogoutRedirectUri> ClientPostLogoutRedirectUris { get; set; }
|
|
public DbSet<IdentityResource> IdentityResources { get; set; }
|
|
public DbSet<IdentityResourceClaim> IdentityResourceClaims { get; set; }
|
|
public DbSet<IdentityResourceProperty> IdentityResourceProperties { get; set; }
|
|
public DbSet<ApiResource> ApiResources { get; set; }
|
|
public DbSet<ApiResourceSecret> ApiResourceSecrets { get; set; }
|
|
public DbSet<ApiResourceScope> ApiResourceScopes { get; set; }
|
|
public DbSet<ApiResourceClaim> ApiResourceClaims { get; set; }
|
|
public DbSet<ApiResourceProperty> ApiResourceProperties { get; set; }
|
|
public DbSet<ApiScope> ApiScopes { get; set; }
|
|
public DbSet<ApiScopeClaim> ApiScopeClaims { get; set; }
|
|
public DbSet<ApiScopeProperty> ApiScopeProperties { get; set; }
|
|
public DbSet<PersistedGrant> PersistedGrants { get; set; }
|
|
public DbSet<DeviceFlowCodes> DeviceFlowCodes { get; set; }
|
|
|
|
public string NOW_SQL { get; private set; }
|
|
|
|
public DbSet<TrustToken> TrustTokens { get; set; }
|
|
public DbSet<TrustDeclaration> TrustDeclarations { get; set; }
|
|
public DbSet<RegexAlertPattern> RegexAlertPatterns { get; set; }
|
|
|
|
public DbSet<ModerationLog> ModerationLogs { get; set; }
|
|
}
|
|
}
|