Repository navigation
feat!: secure configJson credentials, slimmer client contract (#5, #8, #9) - #10
Merged
Merged
Conversation
Closes #8. - All operations of ApiConfiguration, reads included, require ROLE_ADMIN. - Type schemas mark secret keys with "writeOnly": true. - Marked keys are encrypted at rest (CredentialEncryption, onFlush listener), masked as "********" in responses and validator logs, and decrypted only in ApiClientFactory. - On PUT/PATCH an omitted key or the mask keeps the stored secret, null removes it. - New command app:api-configuration:encrypt-secrets (idempotent) for existing rows. - Fix app:api:test-connection calling non-existent getCredentials(). BREAKING CHANGE: reads require ROLE_ADMIN; secrets are masked in API responses; getConfigJson() returns enc:v1: ciphertext for marked keys; a usable CREDENTIALS_ENCRYPTION_KEY is required once a secret is stored; ApiClientFactory, ApiConfigurationValidator and CreateApiConfigurationCommand take an additional ConfigSecrets argument. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…M [*] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ma-validated config [*] Closes #9. - ApiClientInterface keeps what every source supports; getCustomers(), getTodoLists() and hasChanges() move to CustomerAware-, TodoListAwareApiClientInterface and ChangeProbeInterface. - authenticate(): void throws AuthenticationFailedException with the transport exception as previous. - ApiClientFactory::create() validates the decrypted config against the extension's schema.json and throws InvalidConfigurationException naming the field. - HealthProbeInterface for types without a full client; the shared ApiConfigurationHealthChecker serves route and command. - Health operation normalizes nested arrays raw (api_sub_level), so metadata and error keep their keys in JSON-LD. BREAKING CHANGE: ApiClientInterface loses getCustomers(), getTodoLists() and hasChanges(); authenticate() returns void and throws; invalid configs are rejected by ApiClientFactory before createClient(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The command read ApiConfiguration::getFileConfig(), which no longer exists since file sources are configured by directory (Jira XML), and failed with a PHP error. Build the client instead and use FileApiClientInterface::validateFile()/parseFile(), so the format checks live with the type, not in the command. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… 500 [*] Closes #5. With no schema provider registered, SchemaRegistry returns `anyOf: []`, which opis rejects while parsing the schema; the uncaught exception became HTTP 500 on every write. Add a violation instead (HTTP 422). Also declare symfony/validator, used directly but only installed transitively. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The unified anyOf schema of SchemaRegistry cannot resolve a type schema's local #/definitions references. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Member
Author
|
@eluhr Breaking Changes in 0.5.0-beta1 upcoming - FYI |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #5, closes #8, closes #9.
Summary
#8 — credentials in
configJsonApiConfiguration, reads and/healthincluded, requiresROLE_ADMIN."writeOnly": true(nested objects,$ref,oneOf, list items, maps).CredentialEncryption, DoctrineonFlushlistener), masked as********in responses and validator logs, and decrypted only inApiClientFactory.PUT/PATCH: an omitted key or the mask keeps the stored secret,nullremoves it, nothing carries over whentypechanges.SecretEncryptionException, never clear text. Configurations without secrets need no key.app:api-configuration:encrypt-secrets [--dry-run]encrypts existing rows, idempotent.#9 — client contract
ApiClientInterfacekeeps what every source supports;getCustomers(),getTodoLists(),hasChanges()move toCustomerAwareApiClientInterface,TodoListAwareApiClientInterface,ChangeProbeInterface.authenticate(): voidthrowsAuthenticationFailedExceptionwith the transport exception asprevious.ApiClientFactory::create()validates the decrypted config against the extension'sschema.json(InvalidConfigurationExceptionnames the field;_-prefixed keys are skipped).HealthProbeInterfacefor types without a full client (schema-only types, MCP);ApiConfigurationHealthCheckeris shared by the health route and command.metadata/errorkeys in JSON-LD (api_sub_level).Fixes
anyOf must have at least one element); the validator now reports a violation (422). Root cause stays in SchemaRegistry::getUnifiedSchema() emits invalid empty anyOf when no providers are registered openapi-json-schema-bundle#3.symfony/validatordeclared; it was used directly but only installed transitively (CI resolved 8.1).app:api:test-connectionandapp:api:validate-filecalled non-existent entity methods (getCredentials(),getFileConfig()); both now go through the client,validate-filedelegates toFileApiClientInterface::validateFile()/parseFile().Breaking changes
Listed in
CHANGELOG.md; release as 0.5.0.ROLE_ADMIN(no option to restoreROLE_USER).getConfigJson()returnsenc:v1:…for marked keys; code reading secrets from the entity directly must useConfigSecrets::decrypt().CREDENTIALS_ENCRYPTION_KEYis required once a secret is stored.ApiClientInterfaceslimmer;authenticate()returnsvoidand throws.ApiClientFactoryrejects configs that violate the extension schema.ApiClientFactory,ApiConfigurationValidator,CreateApiConfigurationCommand,ApiConfigurationHealthProvider,ApiConfigurationHealthCommand(autowired consumers unaffected).Required downstream changes
dmstr_api_platform_utils.credential_encryption.key: '%env(base64:CREDENTIALS_ENCRYPTION_KEY)%'—CredentialEncryptionexpects raw bytes,dmstr:generate-encryption-keyprints base64. Then runapp:api-configuration:encrypt-secretsonce."writeOnly": truetopassword,token,client_secretand equivalents in every type schema (adapters in the applications, flowable bundle, MCP type). Unmarked keys stay clear text.authenticate(): void, rethrow viaAuthenticationFailedException::fromPrevious($apiName, $e);hasChanges()must fail open — the Jira XML client currently returnsfalseon error;isset()checks increateClient()can go.instanceofchecks beforegetCustomers(),getTodoLists(),hasChanges(); a client withoutChangeProbeInterfacecounts as changed. CatchAuthenticationFailedExceptioninstead of checking abool.buildAuthorizeUrl()): decrypt withConfigSecrets::decrypt()./api/admin/api_configurations(including/health) needROLE_ADMIN.Tests
44 tests, 119 assertions (GitHub Actions, PHP 8.4): encryption round trip and idempotence, masking, keep-on-update rules, missing/wrong key, re-encryption command and
onFlushlistener against SQLite,ROLE_ADMINon every operation, factory schema validation, health probe vs. client fallback, health operation metadata. Known limitation, not changed here: the validator checks against the unifiedanyOfschema ofSchemaRegistry, which cannot resolve a type schema's local$ref: #/definitions/…(opisUnresolved reference). No type schema in the known applications uses local refs;ApiClientFactoryvalidates against the extension's own schema and is not affected.Not covered end-to-end (needs a kernel): the 403 over HTTP and the JSON-LD output of
/health.🤖 Generated with Claude Code