Skip to content

Commit 145b580

Browse files
authored
feat: new sealed secret form (#739)
* feat: new sealed secrets schema * fix: sealed secret tests * fix: sealed secret schema * fix: improve get sealed secrets pem * fix: edit sealed secret metadata * fix: error handling in get sealed secrets pem * fix: remove service-account-token type
1 parent b2b43f2 commit 145b580

6 files changed

Lines changed: 65 additions & 195 deletions

File tree

src/api.authz.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ describe('API authz tests', () => {
383383

384384
const data = {
385385
name: 'demo',
386-
encryptedData: [{ key: 'foo', value: 'bar' }],
386+
encryptedData: { foo: 'encrypted-text-BAR' },
387387
type: 'kubernetes.io/opaque',
388388
}
389389
await agent
@@ -407,7 +407,7 @@ describe('API authz tests', () => {
407407

408408
const data = {
409409
name: 'demo',
410-
encryptedData: [{ key: 'foo', value: 'baz' }],
410+
encryptedData: { foo: 'encrypted-text-BAZ' },
411411
type: 'kubernetes.io/opaque',
412412
}
413413
await agent
@@ -431,7 +431,7 @@ describe('API authz tests', () => {
431431
test('team member cannot create others sealedsecret', async () => {
432432
const data = {
433433
name: 'demo',
434-
encryptedData: [{ key: 'foo', value: 'bar' }],
434+
encryptedData: { foo: 'encrypted-text-BAR' },
435435
type: 'kubernetes.io/opaque',
436436
}
437437
await agent
@@ -451,7 +451,7 @@ describe('API authz tests', () => {
451451
test('team member cannot update others sealedsecret', async () => {
452452
const data = {
453453
name: 'demo',
454-
encryptedData: [{ key: 'foo', value: 'baz' }],
454+
encryptedData: { foo: 'encrypted-text-BAZ' },
455455
type: 'kubernetes.io/opaque',
456456
}
457457
await agent

src/openapi/sealedsecret.yaml

Lines changed: 14 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ SealedSecret:
1616
- update
1717
- delete
1818
properties:
19-
id:
20-
type: string
2119
name:
2220
title: Name
2321
$ref: 'definitions.yaml#/idName'
@@ -32,62 +30,37 @@ SealedSecret:
3230
default: kubernetes.io/opaque
3331
enum:
3432
- kubernetes.io/opaque
35-
- kubernetes.io/service-account-token
3633
- kubernetes.io/dockercfg
3734
- kubernetes.io/dockerconfigjson
3835
- kubernetes.io/basic-auth
3936
- kubernetes.io/ssh-auth
4037
- kubernetes.io/tls
4138
encryptedData:
42-
type: array
43-
minItems: 1
44-
items:
45-
type: object
46-
properties:
47-
key:
48-
type: string
49-
value:
50-
type: string
51-
required:
52-
- key
53-
- value
39+
type: object
40+
properties:
41+
additionalProperties:
42+
type: string
5443
metadata:
5544
description: 'Standard objects metadata.'
5645
properties:
5746
annotations:
58-
type: array
59-
items:
60-
type: object
61-
properties:
62-
key:
63-
type: string
64-
value:
65-
type: string
66-
required:
67-
- key
68-
- value
47+
type: object
48+
properties:
49+
additionalProperties:
50+
type: string
6951
finalizers:
52+
type: array
7053
items:
7154
type: string
72-
type: array
7355
labels:
74-
type: array
75-
items:
76-
type: object
77-
properties:
78-
key:
79-
type: string
80-
value:
81-
type: string
82-
required:
83-
- key
84-
- value
85-
isDisabled:
86-
type: boolean
56+
type: object
57+
properties:
58+
additionalProperties:
59+
type: string
8760
required:
61+
- name
8862
- type
8963
- encryptedData
90-
- name
9164
type: object
9265

9366
AplSealedSecretSpec:
@@ -104,7 +77,6 @@ AplSealedSecretSpec:
10477
default: kubernetes.io/opaque
10578
enum:
10679
- kubernetes.io/opaque
107-
- kubernetes.io/service-account-token
10880
- kubernetes.io/dockercfg
10981
- kubernetes.io/dockerconfigjson
11082
- kubernetes.io/basic-auth
@@ -114,10 +86,6 @@ AplSealedSecretSpec:
11486
type: object
11587
additionalProperties:
11688
type: string
117-
decryptedData:
118-
type: object
119-
additionalProperties:
120-
type: string
12189
metadata:
12290
description: 'Standard objects metadata.'
12391
properties:

src/openapi/session.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ Session:
1818
user:
1919
$ref: 'user.yaml#/SessionUser'
2020
# readOnly: true
21+
sealedSecretsPEM:
22+
type: string
23+
readOnly: true
2124
defaultPlatformAdminEmail:
2225
type: string
2326
objectStorage:

src/otomi-stack.ts

Lines changed: 15 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ import {
6868
getServiceUrl,
6969
getValuesSchema,
7070
removeBlankAttributes,
71-
valueArrayToObject,
7271
} from 'src/utils'
7372
import {
7473
cleanEnv,
@@ -112,8 +111,8 @@ import {
112111
testPrivateRepoConnect,
113112
testPublicRepoConnect,
114113
} from './utils/codeRepoUtils'
115-
import { getAplObjectFromV1, getV1MergeObject, getV1ObjectFromApl, getV1SealedSecretFromApl } from './utils/manifests'
116-
import { getEncryptedData, sealedSecretManifest, SealedSecretManifestType } from './utils/sealedSecretUtils'
114+
import { getAplObjectFromV1, getV1MergeObject, getV1ObjectFromApl } from './utils/manifests'
115+
import { getSealedSecretsPEM, sealedSecretManifest, SealedSecretManifestType } from './utils/sealedSecretUtils'
117116
import { getKeycloakUsers, isValidUsername } from './utils/userUtils'
118117
import { ObjectStorageClient } from './utils/wizardUtils'
119118
import { fetchChartYaml, fetchWorkloadCatalog, NewHelmChartValues, sparseCloneChart } from './utils/workloadUtils'
@@ -2234,35 +2233,16 @@ export default class OtomiStack {
22342233
}
22352234

22362235
async createSealedSecret(teamId: string, data: SealedSecret): Promise<SealedSecret> {
2237-
const spec = {
2238-
...omit(data, 'encryptedData'),
2239-
decryptedData: valueArrayToObject(data.encryptedData),
2240-
metadata: {
2241-
annotations: valueArrayToObject(data.metadata?.annotations),
2242-
labels: valueArrayToObject(data.metadata?.labels),
2243-
finalizers: data.metadata?.finalizers,
2244-
},
2245-
}
22462236
const newSecret = await this.createAplSealedSecret(
22472237
teamId,
2248-
getAplObjectFromV1('AplTeamSecret', spec) as AplSecretRequest,
2238+
getAplObjectFromV1('AplTeamSecret', data) as AplSecretRequest,
22492239
)
2250-
return getV1SealedSecretFromApl(newSecret)
2240+
return getV1ObjectFromApl(newSecret) as SealedSecret
22512241
}
22522242

22532243
async createAplSealedSecret(teamId: string, data: AplSecretRequest): Promise<AplSecretResponse> {
2254-
const namespace = data.spec.namespace ?? `team-${teamId}`
22552244
try {
2256-
const encryptedData = await getEncryptedData(data.spec.decryptedData, data.metadata.name, namespace)
2257-
const sealedSecret = this.repoService.getTeamConfigService(teamId).createSealedSecret({
2258-
kind: 'AplTeamSecret',
2259-
metadata: data.metadata,
2260-
spec: {
2261-
...omit(data.spec, 'decryptedData'),
2262-
encryptedData,
2263-
namespace,
2264-
},
2265-
})
2245+
const sealedSecret = this.repoService.getTeamConfigService(teamId).createSealedSecret(data)
22662246
await this.saveTeamSealedSecret(sealedSecret)
22672247
await this.doTeamDeployment(
22682248
teamId,
@@ -2279,22 +2259,9 @@ export default class OtomiStack {
22792259
}
22802260

22812261
async editSealedSecret(teamId: string, name: string, data: SealedSecret): Promise<SealedSecret> {
2282-
const mergeObj = getV1MergeObject(omit(data, ['encryptedData', 'metadata'])) as DeepPartial<AplSecretRequest>
2283-
if (!mergeObj.spec) {
2284-
mergeObj.spec = {}
2285-
}
2286-
if (data.encryptedData) {
2287-
mergeObj.spec.decryptedData = valueArrayToObject(data.encryptedData)
2288-
}
2289-
if (data.metadata) {
2290-
mergeObj.spec.metadata = {
2291-
annotations: valueArrayToObject(data.metadata.annotations),
2292-
labels: valueArrayToObject(data.metadata.labels),
2293-
finalizers: data.metadata.finalizers,
2294-
}
2295-
}
2262+
const mergeObj = getV1MergeObject(data) as DeepPartial<AplSecretRequest>
22962263
const mergedSecret = await this.editAplSealedSecret(teamId, name, mergeObj)
2297-
return getV1SealedSecretFromApl(mergedSecret)
2264+
return getV1ObjectFromApl(mergedSecret) as SealedSecret
22982265
}
22992266

23002267
async editAplSealedSecret(
@@ -2305,16 +2272,11 @@ export default class OtomiStack {
23052272
): Promise<AplSecretResponse> {
23062273
const existingSecret = this.repoService.getTeamConfigService(teamId).getSealedSecret(name)
23072274
const namespace = data.spec?.namespace ?? existingSecret.spec.namespace ?? `team-${teamId}`
2308-
let encryptedData
2309-
if (data.spec?.decryptedData) {
2310-
encryptedData = await getEncryptedData(data.spec.decryptedData, data.metadata?.name || name, namespace)
2311-
}
23122275
const sealedSecret = patch
23132276
? this.repoService.getTeamConfigService(teamId).patchSealedSecret(name, {
23142277
metadata: data.metadata,
23152278
spec: {
2316-
...omit(data.spec, 'decryptedData'),
2317-
encryptedData,
2279+
encryptedData: data.spec?.encryptedData,
23182280
namespace,
23192281
},
23202282
})
@@ -2323,9 +2285,10 @@ export default class OtomiStack {
23232285
metadata: data.metadata,
23242286
spec: {
23252287
...existingSecret.spec,
2326-
...omit(data.spec, 'decryptedData'),
2327-
encryptedData,
2288+
encryptedData: data.spec?.encryptedData,
23282289
namespace,
2290+
immutable: data.spec?.immutable ?? existingSecret.spec.immutable,
2291+
metadata: data.spec?.metadata ?? existingSecret.spec.metadata,
23292292
},
23302293
} as AplSecretRequest)
23312294
await this.saveTeamSealedSecret(sealedSecret)
@@ -2355,26 +2318,24 @@ export default class OtomiStack {
23552318

23562319
async getSealedSecret(teamId: string, name: string): Promise<SealedSecret> {
23572320
const aplSecret = await this.getAplSealedSecret(teamId, name)
2358-
return getV1SealedSecretFromApl(aplSecret)
2321+
return getV1ObjectFromApl(aplSecret) as SealedSecret
23592322
}
23602323

23612324
async getAplSealedSecret(teamId: string, name: string): Promise<AplSecretResponse> {
23622325
const sealedSecret = this.repoService.getTeamConfigService(teamId).getSealedSecret(name)
2363-
const namespace = sealedSecret.spec.namespace ?? `team-${teamId}`
2364-
sealedSecret.spec.decryptedData = (await getSecretValues(name, namespace)) || {}
23652326
return sealedSecret
23662327
}
23672328

23682329
getAllSealedSecrets(): SealedSecret[] {
2369-
return this.getAllAplSealedSecrets().map(getV1SealedSecretFromApl)
2330+
return this.getAllAplSealedSecrets().map(getV1ObjectFromApl) as SealedSecret[]
23702331
}
23712332

23722333
getAllAplSealedSecrets(): AplSecretResponse[] {
23732334
return this.repoService.getAllSealedSecrets()
23742335
}
23752336

23762337
getSealedSecrets(teamId: string): SealedSecret[] {
2377-
return this.getAplSealedSecrets(teamId).map(getV1SealedSecretFromApl)
2338+
return this.getAplSealedSecrets(teamId).map(getV1ObjectFromApl) as SealedSecret[]
23782339
}
23792340

23802341
getAplSealedSecrets(teamId: string): AplSecretResponse[] {
@@ -2554,6 +2515,7 @@ export default class OtomiStack {
25542515
corrupt: rootStack.git.corrupt,
25552516
editor: this.editor,
25562517
inactivityTimeout: env.EDITOR_INACTIVITY_TIMEOUT,
2518+
sealedSecretsPEM: await getSealedSecretsPEM(),
25572519
user: user as SessionUser,
25582520
defaultPlatformAdminEmail: env.DEFAULT_PLATFORM_ADMIN_EMAIL,
25592521
objectStorage: {

src/utils/manifests.ts

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,5 @@
1-
import {
2-
AplKind,
3-
AplRequestObject,
4-
AplResponseObject,
5-
AplSecretResponse,
6-
DeepPartial,
7-
SealedSecret,
8-
ServiceSpec,
9-
V1ApiObject,
10-
} from '../otomi-models'
11-
import { cloneDeep, isEmpty, merge, omit } from 'lodash'
12-
import { mapObjectToKeyValueArray } from '../utils'
1+
import { cloneDeep, merge, omit } from 'lodash'
2+
import { AplKind, AplRequestObject, AplResponseObject, DeepPartial, ServiceSpec, V1ApiObject } from '../otomi-models'
133

144
export function getAplObjectFromV1(kind: AplKind, spec: V1ApiObject | ServiceSpec): AplRequestObject {
155
return {
@@ -30,22 +20,6 @@ export function getV1ObjectFromApl(aplObject: AplResponseObject): V1ApiObject {
3020
}
3121
}
3222

33-
export function getV1SealedSecretFromApl(aplSecret: AplSecretResponse): SealedSecret {
34-
const secret = omit(getV1ObjectFromApl(aplSecret), ['encryptedData', 'decryptedData', 'metadata']) as SealedSecret
35-
const { encryptedData, decryptedData, metadata } = aplSecret.spec
36-
secret.isDisabled = isEmpty(decryptedData)
37-
secret.encryptedData = Object.entries(encryptedData || {}).map(([key, value]) => ({
38-
key,
39-
value: decryptedData?.[key] || value,
40-
}))
41-
secret.metadata = {
42-
annotations: mapObjectToKeyValueArray(metadata?.annotations),
43-
labels: mapObjectToKeyValueArray(metadata?.labels),
44-
finalizers: metadata?.finalizers,
45-
}
46-
return secret
47-
}
48-
4923
export function getV1MergeObject(updates: DeepPartial<V1ApiObject | ServiceSpec>): DeepPartial<AplRequestObject> {
5024
return {
5125
metadata: updates.name

0 commit comments

Comments
 (0)