@@ -82,33 +82,62 @@ describe('credentials', () => {
|
||||
).not.toMatch(/my-token/);
|
||||
});
|
||||
|
||||
it('should have a serializable form', () => {
|
||||
expect(
|
||||
String(createCredentialsWithServicePrincipal('my-service')),
|
||||
).toMatchInlineSnapshot(
|
||||
`"{"$$type":"@backstage/BackstageCredentials","type":"service","subject":"my-service"}"`,
|
||||
it('should have a serializable form both as strings and as JSON', () => {
|
||||
const simpleService = createCredentialsWithServicePrincipal('my-service');
|
||||
expect(String(simpleService)).toMatchInlineSnapshot(
|
||||
`"backstageCredentials{servicePrincipal{my-service}}"`,
|
||||
);
|
||||
expect(
|
||||
String(
|
||||
createCredentialsWithUserPrincipal('user:default/mock', 'my-token'),
|
||||
),
|
||||
).toMatchInlineSnapshot(
|
||||
`"{"$$type":"@backstage/BackstageCredentials","type":"user","userEntityRef":"user:default/mock"}"`,
|
||||
expect(JSON.stringify(simpleService)).toMatchInlineSnapshot(
|
||||
`"{"$$type":"@backstage/BackstageCredentials","version":"v1","principal":{"type":"service","subject":"my-service"}}"`,
|
||||
);
|
||||
expect(
|
||||
String(
|
||||
createCredentialsWithUserPrincipal(
|
||||
'user:default/mock',
|
||||
'my-token',
|
||||
undefined,
|
||||
'my-actor',
|
||||
),
|
||||
),
|
||||
).toMatchInlineSnapshot(
|
||||
`"{"$$type":"@backstage/BackstageCredentials","type":"user","userEntityRef":"user:default/mock","actor":{"type":"service","subject":"my-actor"}}"`,
|
||||
|
||||
const serviceWithAccessRestrictions = createCredentialsWithServicePrincipal(
|
||||
'my-service',
|
||||
undefined,
|
||||
{
|
||||
permissionNames: ['perm'],
|
||||
permissionAttributes: {
|
||||
action: ['read'],
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(String(createCredentialsWithNonePrincipal())).toMatchInlineSnapshot(
|
||||
`"{"$$type":"@backstage/BackstageCredentials","type":"none"}"`,
|
||||
expect(String(serviceWithAccessRestrictions)).toMatchInlineSnapshot(
|
||||
`"backstageCredentials{servicePrincipal{my-service,accessRestrictions=cXWOJgUirHkHNZIowUi/YO5nwEwhTicC38iXi2XTYCk}}"`,
|
||||
);
|
||||
expect(JSON.stringify(serviceWithAccessRestrictions)).toMatchInlineSnapshot(
|
||||
`"{"$$type":"@backstage/BackstageCredentials","version":"v1","principal":{"type":"service","subject":"my-service","accessRestrictions":{"permissionNames":["perm"],"permissionAttributes":{"action":["read"]}}}}"`,
|
||||
);
|
||||
|
||||
const simpleUser = createCredentialsWithUserPrincipal(
|
||||
'user:default/mock',
|
||||
'my-token',
|
||||
);
|
||||
expect(String(simpleUser)).toMatchInlineSnapshot(
|
||||
`"backstageCredentials{userPrincipal{user:default/mock}}"`,
|
||||
);
|
||||
expect(JSON.stringify(simpleUser)).toMatchInlineSnapshot(
|
||||
`"{"$$type":"@backstage/BackstageCredentials","version":"v1","principal":{"type":"user","userEntityRef":"user:default/mock"}}"`,
|
||||
);
|
||||
|
||||
const userWithActor = createCredentialsWithUserPrincipal(
|
||||
'user:default/mock',
|
||||
'my-token',
|
||||
undefined,
|
||||
'my-actor',
|
||||
);
|
||||
expect(String(userWithActor)).toMatchInlineSnapshot(
|
||||
`"backstageCredentials{userPrincipal{user:default/mock,actor={servicePrincipal{my-actor}}}}"`,
|
||||
);
|
||||
expect(JSON.stringify(userWithActor)).toMatchInlineSnapshot(
|
||||
`"{"$$type":"@backstage/BackstageCredentials","version":"v1","principal":{"type":"user","userEntityRef":"user:default/mock","actor":{"type":"service","subject":"my-actor"}}}"`,
|
||||
);
|
||||
|
||||
const none = createCredentialsWithNonePrincipal();
|
||||
expect(String(none)).toMatchInlineSnapshot(
|
||||
`"backstageCredentials{nonePrincipal}"`,
|
||||
);
|
||||
expect(JSON.stringify(none)).toMatchInlineSnapshot(
|
||||
`"{"$$type":"@backstage/BackstageCredentials","version":"v1","principal":{"type":"none"}}"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,35 +22,32 @@ import {
|
||||
BackstageUserPrincipal,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { InternalBackstageCredentials } from './types';
|
||||
import { createHash } from 'crypto';
|
||||
|
||||
export function createCredentialsWithServicePrincipal(
|
||||
sub: string,
|
||||
token?: string,
|
||||
accessRestrictions?: BackstagePrincipalAccessRestrictions,
|
||||
): InternalBackstageCredentials<BackstageServicePrincipal> {
|
||||
const principal = createServicePrincipal(sub, accessRestrictions);
|
||||
const result = {
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
version: 'v1',
|
||||
principal: {
|
||||
type: 'service',
|
||||
subject: sub,
|
||||
accessRestrictions,
|
||||
},
|
||||
principal,
|
||||
} as const;
|
||||
Object.defineProperty(result, 'token', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: token,
|
||||
});
|
||||
Object.defineProperty(result, 'toString', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: () =>
|
||||
JSON.stringify({
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
type: 'service',
|
||||
subject: sub,
|
||||
}),
|
||||
Object.defineProperties(result, {
|
||||
token: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: token,
|
||||
},
|
||||
toString: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => `backstageCredentials{${principal}}`,
|
||||
},
|
||||
});
|
||||
return result;
|
||||
}
|
||||
@@ -61,55 +58,47 @@ export function createCredentialsWithUserPrincipal(
|
||||
expiresAt?: Date,
|
||||
actor?: string,
|
||||
): InternalBackstageCredentials<BackstageUserPrincipal> {
|
||||
const principal = createUserPrincipal(
|
||||
sub,
|
||||
actor ? createServicePrincipal(actor) : undefined,
|
||||
);
|
||||
const result = {
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
version: 'v1',
|
||||
expiresAt,
|
||||
principal: {
|
||||
type: 'user',
|
||||
userEntityRef: sub,
|
||||
...(actor && {
|
||||
actor: { type: 'service', subject: actor } as const,
|
||||
}),
|
||||
},
|
||||
principal,
|
||||
} as const;
|
||||
Object.defineProperty(result, 'token', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: token,
|
||||
});
|
||||
Object.defineProperty(result, 'toString', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: () =>
|
||||
JSON.stringify({
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
type: 'user',
|
||||
userEntityRef: sub,
|
||||
...(actor && {
|
||||
actor: { type: 'service', subject: actor },
|
||||
}),
|
||||
}),
|
||||
Object.defineProperties(result, {
|
||||
token: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: token,
|
||||
},
|
||||
toString: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => `backstageCredentials{${principal}}`,
|
||||
},
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
export function createCredentialsWithNonePrincipal(): InternalBackstageCredentials<BackstageNonePrincipal> {
|
||||
const principal = createNonePrincipal();
|
||||
const result = {
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
version: 'v1',
|
||||
principal: {
|
||||
type: 'none',
|
||||
},
|
||||
principal,
|
||||
} as const;
|
||||
Object.defineProperty(result, 'toString', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: () =>
|
||||
JSON.stringify({
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
type: 'none',
|
||||
}),
|
||||
Object.defineProperties(result, {
|
||||
toString: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => `backstageCredentials{${principal}}`,
|
||||
},
|
||||
});
|
||||
return result;
|
||||
}
|
||||
@@ -135,3 +124,74 @@ export function toInternalBackstageCredentials(
|
||||
|
||||
return internalCredentials;
|
||||
}
|
||||
|
||||
function createServicePrincipal(
|
||||
sub: string,
|
||||
accessRestrictions?: BackstagePrincipalAccessRestrictions,
|
||||
): BackstageServicePrincipal {
|
||||
const result = {
|
||||
type: 'service',
|
||||
subject: sub,
|
||||
accessRestrictions,
|
||||
} as const;
|
||||
Object.defineProperties(result, {
|
||||
toString: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
let parts = sub;
|
||||
if (accessRestrictions) {
|
||||
const hash = createHash('sha256')
|
||||
.update(JSON.stringify(accessRestrictions))
|
||||
.digest('base64')
|
||||
.replace(/=+$/, '');
|
||||
parts += `,accessRestrictions=${hash}`;
|
||||
}
|
||||
return `servicePrincipal{${parts}}`;
|
||||
},
|
||||
},
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function createUserPrincipal(
|
||||
userEntityRef: string,
|
||||
actor?: BackstageServicePrincipal,
|
||||
): BackstageUserPrincipal {
|
||||
const result = {
|
||||
type: 'user',
|
||||
userEntityRef,
|
||||
actor,
|
||||
} as const;
|
||||
Object.defineProperties(result, {
|
||||
toString: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => {
|
||||
let parts = userEntityRef;
|
||||
if (actor) {
|
||||
parts += `,actor={${actor}}`;
|
||||
}
|
||||
return `userPrincipal{${parts}}`;
|
||||
},
|
||||
},
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function createNonePrincipal(): BackstageNonePrincipal {
|
||||
const result = {
|
||||
type: 'none',
|
||||
} as const;
|
||||
Object.defineProperties(result, {
|
||||
toString: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => 'nonePrincipal',
|
||||
},
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -173,12 +173,12 @@ describe('mockCredentials', () => {
|
||||
|
||||
it('should have a serializable form', () => {
|
||||
expect(String(mockCredentials.service('my-service'))).toMatchInlineSnapshot(
|
||||
`"{"$$type":"@backstage/MockBackstageCredentials","type":"service","subject":"my-service"}"`,
|
||||
`"mockCredentials{servicePrincipal{my-service}}"`,
|
||||
);
|
||||
expect(
|
||||
String(mockCredentials.user('user:default/mock')),
|
||||
).toMatchInlineSnapshot(
|
||||
`"{"$$type":"@backstage/MockBackstageCredentials","type":"user","userEntityRef":"user:default/mock"}"`,
|
||||
`"mockCredentials{userPrincipal{user:default/mock}}"`,
|
||||
);
|
||||
expect(
|
||||
String(
|
||||
@@ -187,10 +187,10 @@ describe('mockCredentials', () => {
|
||||
}),
|
||||
),
|
||||
).toMatchInlineSnapshot(
|
||||
`"{"$$type":"@backstage/MockBackstageCredentials","type":"user","userEntityRef":"user:default/mock","actor":{"type":"service","subject":"my-actor"}}"`,
|
||||
`"mockCredentials{userPrincipal{user:default/mock,actor={my-actor}}}"`,
|
||||
);
|
||||
expect(String(mockCredentials.none())).toMatchInlineSnapshot(
|
||||
`"{"$$type":"@backstage/MockBackstageCredentials","type":"none"}"`,
|
||||
`"mockCredentials{nonePrincipal}"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -80,14 +80,13 @@ export namespace mockCredentials {
|
||||
$$type: '@backstage/BackstageCredentials',
|
||||
principal: { type: 'none' },
|
||||
} as const;
|
||||
Object.defineProperty(result, 'toString', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: () =>
|
||||
JSON.stringify({
|
||||
$$type: '@backstage/MockBackstageCredentials',
|
||||
type: 'none',
|
||||
}),
|
||||
Object.defineProperties(result, {
|
||||
toString: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: () => `mockCredentials{nonePrincipal}`,
|
||||
},
|
||||
});
|
||||
return result;
|
||||
}
|
||||
@@ -131,23 +130,20 @@ export namespace mockCredentials {
|
||||
}),
|
||||
},
|
||||
} as const;
|
||||
Object.defineProperty(result, 'toString', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: () =>
|
||||
JSON.stringify({
|
||||
$$type: '@backstage/MockBackstageCredentials',
|
||||
type: 'user',
|
||||
userEntityRef,
|
||||
...(options?.actor && {
|
||||
actor: { type: 'service', subject: options.actor.subject },
|
||||
}),
|
||||
}),
|
||||
});
|
||||
Object.defineProperty(result, 'token', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: user.token(),
|
||||
Object.defineProperties(result, {
|
||||
toString: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: () =>
|
||||
`mockCredentials{userPrincipal{${userEntityRef}${
|
||||
options?.actor ? `,actor={${options.actor.subject}}` : ''
|
||||
}}}`,
|
||||
},
|
||||
token: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: user.token(),
|
||||
},
|
||||
});
|
||||
return result;
|
||||
}
|
||||
@@ -260,15 +256,17 @@ export namespace mockCredentials {
|
||||
...(accessRestrictions ? { accessRestrictions } : {}),
|
||||
},
|
||||
} as const;
|
||||
Object.defineProperty(result, 'toString', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: () =>
|
||||
JSON.stringify({
|
||||
$$type: '@backstage/MockBackstageCredentials',
|
||||
type: 'service',
|
||||
subject,
|
||||
}),
|
||||
Object.defineProperties(result, {
|
||||
toString: {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: () =>
|
||||
`mockCredentials{servicePrincipal{${subject}${
|
||||
accessRestrictions
|
||||
? `,accessRestrictions=${JSON.stringify(accessRestrictions)}`
|
||||
: ''
|
||||
}}}`,
|
||||
},
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user