fix: allow coreflow deployment input reads

This commit is contained in:
2026-04-28 16:57:54 +00:00
parent 1bed907f53
commit 865c8f2546
4 changed files with 77 additions and 1 deletions
+48
View File
@@ -246,6 +246,54 @@ tap.test('should push service config updates to connected coreflows', async (too
} }
}); });
tap.test('should allow cluster coreflows to read deployment inputs', async () => {
const cluster = await testClient.cluster.createCluster('Registry Coreflow Read Test Cluster');
const persistedCluster = await testCloudly.clusterManager.getConfigBy_ConfigID(cluster.id);
const clusterUser = await testCloudly.authManager.CUser.getInstance({
id: persistedCluster.data.userId,
});
const clusterToken = clusterUser.data.tokens?.[0]?.token;
expect(clusterToken).toBeTruthy();
const image = await testClient.image.createImage({
name: 'Registry Coreflow Read Test Image',
description: 'Image used by the coreflow read test',
});
const secretBundle = await testClient.secretbundle.createSecretBundle({
name: 'Registry Coreflow Read Test Secret Bundle',
description: 'Secret bundle used by the coreflow read test',
type: 'service',
includedSecretGroupIds: [],
includedTags: [],
imageClaims: [],
authorizations: [
{
environment: 'production',
secretAccessKey: 'registry-coreflow-read-test',
},
],
});
const coreflowClient = new cloudlyApiClient.CloudlyApiClient({
registerAs: 'coreflow',
cloudlyUrl: `http://${helpers.testCloudlyConfig.publicUrl}:${helpers.testCloudlyConfig.publicPort}`,
});
try {
await coreflowClient.start();
await coreflowClient.getIdentityByToken(clusterToken!, {
statefullIdentity: true,
tagConnection: true,
});
const clusterImage = await coreflowClient.image.getImageById(image.id);
const clusterSecretBundle = await coreflowClient.secretbundle.getSecretBundleById(secretBundle.id);
expect(clusterImage.id).toEqual(image.id);
expect(clusterSecretBundle.id).toEqual(secretBundle.id);
} finally {
await coreflowClient.stop();
}
});
tap.test('should expose platform desired state', async () => { tap.test('should expose platform desired state', async () => {
const capabilitiesResponse = await testClient.platform.getPlatformCapabilities(); const capabilitiesResponse = await testClient.platform.getPlatformCapabilities();
expect(capabilitiesResponse.capabilities.find((capability) => capability.id === 'database')).toBeTruthy(); expect(capabilitiesResponse.capabilities.find((capability) => capability.id === 'database')).toBeTruthy();
+16
View File
@@ -172,4 +172,20 @@ export class CloudlyAuthManager {
name: 'adminIdentityGuard', name: 'adminIdentityGuard',
}, },
); );
public adminOrClusterIdentityGuard = new plugins.smartguard.Guard<{
identity: plugins.servezoneInterfaces.data.IIdentity;
}>(
async (dataArg) => {
await plugins.smartguard.passGuardsOrReject(dataArg, [this.validIdentityGuard]);
const jwt = dataArg.identity.jwt;
const jwtData: IJwtData = await this.smartjwtInstance.verifyJWTAndGetData(jwt);
const user = await this.CUser.getInstance({ id: jwtData.userId });
return user.data.role === 'admin' || user.data.role === 'cluster';
},
{
failedHint: 'user is not admin or cluster.',
name: 'adminOrClusterIdentityGuard',
},
);
} }
+1 -1
View File
@@ -41,7 +41,7 @@ export class ImageManager {
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_GetImage>( this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_GetImage>(
new plugins.typedrequest.TypedHandler('getImage', async (reqArg, toolsArg) => { new plugins.typedrequest.TypedHandler('getImage', async (reqArg, toolsArg) => {
await toolsArg.passGuards([this.cloudlyRef.authManager.adminIdentityGuard], reqArg); await toolsArg.passGuards([this.cloudlyRef.authManager.adminOrClusterIdentityGuard], reqArg);
const image = await this.CImage.getInstance({ const image = await this.CImage.getInstance({
id: reqArg.imageId, id: reqArg.imageId,
}); });
@@ -54,6 +54,18 @@ export class CloudlySecretManager {
), ),
); );
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.secretbundle.IReq_GetSecretBundleById>(
new plugins.typedrequest.TypedHandler('getSecretBundleById', async (dataArg, toolsArg) => {
await toolsArg.passGuards([this.cloudlyRef.authManager.adminOrClusterIdentityGuard], dataArg);
const secretBundle = await SecretBundle.getInstance({
id: dataArg.secretBundleId,
});
return {
secretBundle: await secretBundle.createSavableObject(),
};
}),
);
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.secretbundle.IReq_CreateSecretBundle>( this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.secretbundle.IReq_CreateSecretBundle>(
new plugins.typedrequest.TypedHandler('createSecretBundle', async (dataArg) => { new plugins.typedrequest.TypedHandler('createSecretBundle', async (dataArg) => {
const secretBundle = new SecretBundle(); const secretBundle = new SecretBundle();