configuration-management 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,113 +0,0 @@
1
- /*
2
- Copyright 2025 Adobe. All rights reserved.
3
- This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
- you may not use this file except in compliance with the License. You may obtain a copy
5
- of the License at http://www.apache.org/licenses/LICENSE-2.0
6
- */
7
-
8
- // Equivalent of Magento's env.php `crypt.key`: a project-wide secret used to
9
- // derive an AES-256-GCM key that protects sensitive system_config values at
10
- // rest in App Builder DB (aio-lib-state).
11
- //
12
- // Key material is taken from action params/env (never from request payload):
13
- // SYSTEM_CONFIG_CRYPT_KEY – preferred, dedicated secret
14
- // OAUTH_CLIENT_SECRET – fallback, the workspace's client secret
15
- //
16
- // Wire format for ciphertext (string):
17
- // enc:v1:<base64url(salt)>:<base64url(iv)>:<base64url(tag)>:<base64url(ct)>
18
- // `v1` lets us rotate the algorithm later without breaking previously stored
19
- // values. `salt` is per-record so the derived key changes even if the same
20
- // master secret is reused across records.
21
-
22
- const crypto = require('crypto')
23
-
24
- const ENC_PREFIX = 'enc:v1:'
25
- const KEY_BYTES = 32
26
- const IV_BYTES = 12
27
- const SALT_BYTES = 16
28
- const SCRYPT_PARAMS = { N: 16384, r: 8, p: 1 }
29
-
30
- function b64uEncode (buf) {
31
- return Buffer.from(buf).toString('base64url')
32
- }
33
-
34
- function b64uDecode (str) {
35
- return Buffer.from(str, 'base64url')
36
- }
37
-
38
- function resolveMasterSecret (params = {}) {
39
- const secret =
40
- params.SYSTEM_CONFIG_CRYPT_KEY ||
41
- params.OAUTH_CLIENT_SECRET ||
42
- process.env.SYSTEM_CONFIG_CRYPT_KEY ||
43
- process.env.OAUTH_CLIENT_SECRET ||
44
- ''
45
- if (!secret || typeof secret !== 'string' || secret.length < 8) {
46
- throw new Error(
47
- 'Encryption key not configured: set SYSTEM_CONFIG_CRYPT_KEY or OAUTH_CLIENT_SECRET'
48
- )
49
- }
50
- return secret
51
- }
52
-
53
- function deriveKey (masterSecret, salt) {
54
- return crypto.scryptSync(masterSecret, salt, KEY_BYTES, SCRYPT_PARAMS)
55
- }
56
-
57
- function isEncrypted (value) {
58
- return typeof value === 'string' && value.startsWith(ENC_PREFIX)
59
- }
60
-
61
- function encrypt (plaintext, params) {
62
- if (plaintext == null || plaintext === '') {
63
- return plaintext
64
- }
65
- const text = typeof plaintext === 'string' ? plaintext : String(plaintext)
66
- const secret = resolveMasterSecret(params)
67
- const salt = crypto.randomBytes(SALT_BYTES)
68
- const iv = crypto.randomBytes(IV_BYTES)
69
- const key = deriveKey(secret, salt)
70
- const cipher = crypto.createCipheriv('aes-256-gcm', key, iv)
71
- const ct = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()])
72
- const tag = cipher.getAuthTag()
73
- return [
74
- ENC_PREFIX,
75
- b64uEncode(salt),
76
- ':',
77
- b64uEncode(iv),
78
- ':',
79
- b64uEncode(tag),
80
- ':',
81
- b64uEncode(ct)
82
- ].join('')
83
- }
84
-
85
- function decrypt (encrypted, params) {
86
- if (!isEncrypted(encrypted)) {
87
- return encrypted
88
- }
89
- const body = encrypted.slice(ENC_PREFIX.length)
90
- const parts = body.split(':')
91
- if (parts.length !== 4) {
92
- throw new Error('Malformed encrypted value')
93
- }
94
- const [saltB64, ivB64, tagB64, ctB64] = parts
95
- const secret = resolveMasterSecret(params)
96
- const salt = b64uDecode(saltB64)
97
- const iv = b64uDecode(ivB64)
98
- const tag = b64uDecode(tagB64)
99
- const ct = b64uDecode(ctB64)
100
- const key = deriveKey(secret, salt)
101
- const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv)
102
- decipher.setAuthTag(tag)
103
- const pt = Buffer.concat([decipher.update(ct), decipher.final()])
104
- return pt.toString('utf8')
105
- }
106
-
107
- module.exports = {
108
- ENC_PREFIX,
109
- isEncrypted,
110
- encrypt,
111
- decrypt,
112
- resolveMasterSecret
113
- }
@@ -1,89 +0,0 @@
1
- /*
2
- Copyright 2025 Adobe. All rights reserved.
3
- This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
- you may not use this file except in compliance with the License. You may obtain a copy
5
- of the License at http://www.apache.org/licenses/LICENSE-2.0
6
- */
7
-
8
- // Mirrors Magento's core_config_data: (scope, scope_id, path, value).
9
- // aio-lib-state keys allow [a-zA-Z0-9_-] only, so we encode
10
- // scope=`default` scopeId=0 path=`web/secure/base_url`
11
- // as the state key
12
- // sysconfig__default__0__web__secure__base_url
13
-
14
- const STATE_KEY_PREFIX = 'sysconfig__'
15
- const SCOPES = ['default', 'websites', 'stores']
16
- const PATH_SEGMENT = /^[a-zA-Z0-9_-]+$/
17
- const SCOPE_ID_RE = /^[a-zA-Z0-9_-]+$/
18
- const SENSITIVE_PLACEHOLDER = '__SENSITIVE_UNCHANGED__'
19
- const USE_DEFAULT_SENTINEL = '__USE_DEFAULT__'
20
-
21
- function isValidPath (path) {
22
- if (typeof path !== 'string') return false
23
- const parts = path.split('/')
24
- if (parts.length !== 3) return false
25
- return parts.every((p) => PATH_SEGMENT.test(p))
26
- }
27
-
28
- function normalizeScope (scope) {
29
- if (!scope) return 'default'
30
- if (!SCOPES.includes(scope)) {
31
- throw new Error(`Invalid scope "${scope}". Expected one of: ${SCOPES.join(', ')}`)
32
- }
33
- return scope
34
- }
35
-
36
- function normalizeScopeId (scope, scopeId) {
37
- if (scope === 'default') return '0'
38
- const id = String(scopeId ?? '').trim()
39
- if (!id || !SCOPE_ID_RE.test(id)) {
40
- throw new Error(`Invalid scopeId "${scopeId}" for scope "${scope}"`)
41
- }
42
- return id
43
- }
44
-
45
- function toStateKey (scope, scopeId, path) {
46
- if (!isValidPath(path)) {
47
- throw new Error(`Invalid config path: ${path}`)
48
- }
49
- const s = normalizeScope(scope)
50
- const sid = normalizeScopeId(s, scopeId)
51
- return [STATE_KEY_PREFIX, s, '__', sid, '__', path.split('/').join('__')].join('')
52
- }
53
-
54
- /**
55
- * Magento-style fallback chain. When reading at store scope we look up:
56
- * stores:<storeId> → websites:<websiteId> → default:0
57
- * `parentWebsiteId` is supplied by the caller (resolved from /rest/V1/store/storeViews).
58
- */
59
- function buildInheritanceChain (scope, scopeId, parentWebsiteId) {
60
- const s = normalizeScope(scope)
61
- if (s === 'default') {
62
- return [{ scope: 'default', scopeId: '0' }]
63
- }
64
- if (s === 'websites') {
65
- return [
66
- { scope: 'websites', scopeId: normalizeScopeId('websites', scopeId) },
67
- { scope: 'default', scopeId: '0' }
68
- ]
69
- }
70
- // stores
71
- const chain = [{ scope: 'stores', scopeId: normalizeScopeId('stores', scopeId) }]
72
- if (parentWebsiteId !== undefined && parentWebsiteId !== null && String(parentWebsiteId) !== '') {
73
- chain.push({ scope: 'websites', scopeId: normalizeScopeId('websites', parentWebsiteId) })
74
- }
75
- chain.push({ scope: 'default', scopeId: '0' })
76
- return chain
77
- }
78
-
79
- module.exports = {
80
- STATE_KEY_PREFIX,
81
- SCOPES,
82
- SENSITIVE_PLACEHOLDER,
83
- USE_DEFAULT_SENTINEL,
84
- isValidPath,
85
- normalizeScope,
86
- normalizeScopeId,
87
- toStateKey,
88
- buildInheritanceChain
89
- }
package/web/styles.css DELETED
@@ -1 +0,0 @@
1
- @import './src/styles/index.css';