Repository navigation
Add typed settings persisted per site - #2766
Conversation
|
Review fixes pushed: nested validation (paths like limits.port), secrets only at the top level with FCMS2801 below it, reset with version 0 conflicts, writes on a context of their own, omitted secret keys keep their value, a redacted resolve, and the site invalidation bumps the cache generation. Spec text updated. origin/main (#2767) merged; the full suite passes with the SQLite write interceptors. |
…nested properties
|
Second round pushed: a site's first write keeps the instance default's omitted secrets, RedactedSetting.Version is the version of the row a write would replace (0 when none), a stored secret the class can no longer accept goes back to its default with a warning naming the key, an error path under a secret is cut to the secret key, and a nested property named like a secret stops the host. Spec states that the row, its event and its activity row are not atomic. Full suite passes (2636). |
…as shown and match secret names by word
|
Third round pushed: the redacted version is that of the row a write replaces even when it cannot be read, a site's first write inherits only from an instance row the read could read, and secret names are matched by words (last word password, passphrase, secret or token; last two words api key, private key, secret key, access key, client secret or connection string) instead of substrings, for the top-level secret and the nested refusal alike. SecretScrubber is unchanged. Spec notes that an inherited secret is a copy. Build, tests (2670) and pack pass. |
…ite rows for secrets
|
Fourth round pushed: the name rule now ignores trailing digits and a version marker, accepts plurals, apikey, pwd, signing key and encryption key, and is applied to the C# member name as well as the JSON name; an unreadable site row no longer supplies omitted secrets (they come from the document the read resolved to). Spec says the name rule is a safety net and [SettingsSecret] is the real control. Build, tests (2697) and pack pass. |
|
Pushed: a write that skips the site's unreadable row now logs SecretDropped for each secret key that row held and the document did not name, by key only. Test added. Build, tests (2698) and pack pass. |
Closes #2704
What it does
[Settings("area")]classes, registered withcms.Settings.Add<T>(); a bad registration stops the host (FCMS2801).cms_setting (id, site_id, key, json, version, updated_at, updated_by), oneAddSettingsmigration per provider. Instance defaults have a nullsite_id; two filtered unique indexes, and a site's rows go with the site.ISettings<T>(SDK):GetAsync(site)resolves site override, then instance default, then code default, as whole documents.SetAsync/SetDefaultAsync/ResetAsync/ResetDefaultAsyncwrite.SettingsChangedEventis raised after the commit (so no outbox-in-transaction API is needed; a write inside a transaction is refused withFCMS2807). It carries the area only; its source carries the site (empty for the instance defaults), so the existing activity entry is the site's row.descriptor.CreateSchema()), from System.Text.Json plus Description, Range, StringLength, Min/MaxLength, RegularExpression, EmailAddress, Url and Required.settings.{area}.manageinPermissionCatalogue(PermissionSource.Settings), which closes the item Permission catalogue, site-scoped roles and built-in roles #2700 left unticked.Security and correctness
SetAsyncneedssettings.{area}.managefor the site; the instance default needs a grant with no site limit and a role with no site (SettingsAccessDeniedException). Reads are not checked; the Management endpoint must check before showing a document.[SettingsSecret]or a nameSecretScrubber.NamesSecretmatches. Never in events, activity rows, logs, validation messages or schema defaults; the schema marks themwriteOnly. They are stored in the row as-is; encryption at rest is not done.FCMS2803), unknown key (FCMS2804, one error per key), wrong type, duplicate key or broken validation attribute (FCMS2805). Errors name the key, never the value. Reads are tolerant: a stored row the class can no longer read is skipped with a log line.UPDATE(or an insert, retried if another writer inserted first), so documents never mix and the last write stands. A caller that names the version it read getsFCMS2808if the row moved (internalSettingsService, for the Management endpoint'sIf-Match).cms.Settings.CacheDuration, zero turns it off). A write drops every scope of its area at once, and a read that raced with the write is not kept. Across instances there is no push: another instance sees a write when its own reads expire, so it lags by up toCacheDuration.FCMS2806; deleting a site removes its settings by foreign key and drops what the cache held for it.Acceptance
ASettingsClassIsReadableAndWritablePerSite,ASiteOverrideWinsOverTheInstanceDefaultWhichWinsOverTheCodeDefault,AReadGivesACopyThatTheCallerMayChangeChangingASettingRaisesTheEventAndWritesAnActivityRowForTheSite,ChangingTheInstanceDefaultIsAnEventAndARowOfNoSiteEvenWhenTheOperationHasOne,AWriteDropsWhatReadersKeepAcrossScopes,AnotherInstanceSeesAWriteWhenItsReadsExpire,ACacheDurationOfZeroReadsTheDatabaseEveryTimeTheSchemaComesFromTheTypeAndItsAttributesAndShowsNoSecretsettings.{area}.managein the catalogue:EveryAreaHasAPermissionInTheCatalogueAWriteWithoutThePermissionChangesNothingAndTellsNoOne,ASiteGrantChangesThatSiteAndNeverTheInstanceDefaultOrAnotherSite,AGrantLimitedToSitesOfARoleWithNoSiteIsNotAnInstanceGrant,ThePermissionIsPerAreaAndThePrincipalMustHaveTheManagementScope,TheSystemPrincipalMayWriteAndNoOperationAtAllMayNotAWrittenSecretIsInTheTableAndInNoEventActivityRowOrLogLine,AValidationErrorOfASecretNeverRepeatsItsValueADocumentWithAnUnknownKeyOrInvalidJsonIsRefusedWithValidationErrors,AValueThatBreaksAValidationAttributeIsRefusedForTheTypedWriteToo,AnUnknownAreaIsRefusedConcurrentWritesNeitherFailNorLoseARowAndEachRaisesItsEvent,AWriterThatNamesTheVersionItReadLosesToAnEarlierWriter,OnlyOneOfTwoFirstWritersThatNameVersionZeroWinsASiteThatDoesNotExistIsRefusedOnReadAndWrite,DeletingASiteRemovesItsSettingsAndWhatReadersKeptOfThemAStoredDocumentTheClassCannotReadFallsBackAndAnOldKeyDoesNotBreakTheRead,ABadRegistrationStopsTheHost,ASettingsClassThatTheHostDidNotAddCannotBeResolved,AWriteInsideATransactionIsRefusedBeforeItChangesAnything,ResettingAnAreaThatHasNoRowRaisesNothingLocal checks
dotnet build -c Release -warnaserror,dotnet test,dotnet pack -c Releaseand markdown lint pass.FluentCmsTestHostTests.ABootSeedsTheTypesAndQueriesThemInUnderTwoSecondshas a 2 s budget and failed in one of three isolated runs on this machine (2.8 s), unrelated to settings. Existing tests that list system tables, count migrations or count model entity types were updated forcms_setting.Dependencies
None. No new package; schema generation uses
System.Text.Json'sJsonSchemaExporter.