Catalog permission rules (#8766)
Add catalog permission rules Signed-off-by: Joon Park <joonp@spotify.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Add catalog permission rules.
|
||||
@@ -25,6 +25,7 @@ import { Location as Location_2 } from '@backstage/catalog-model';
|
||||
import { LocationSpec } from '@backstage/catalog-model';
|
||||
import { Logger as Logger_2 } from 'winston';
|
||||
import { Organizations } from 'aws-sdk';
|
||||
import { PermissionRule } from '@backstage/plugin-permission-node';
|
||||
import { PluginDatabaseManager } from '@backstage/backend-common';
|
||||
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import { ResourceEntityV1alpha1 } from '@backstage/catalog-model';
|
||||
@@ -303,6 +304,12 @@ export type CatalogEnvironment = {
|
||||
reader: UrlReader;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type CatalogPermissionRule = PermissionRule<
|
||||
Entity,
|
||||
EntitiesSearchFilter
|
||||
>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "CatalogProcessingEngine" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
@@ -1380,6 +1387,16 @@ export function parseEntityYaml(
|
||||
location: LocationSpec,
|
||||
): Iterable<CatalogProcessorResult>;
|
||||
|
||||
// @public
|
||||
export const permissionRules: {
|
||||
hasAnnotation: CatalogPermissionRule;
|
||||
hasLabel: CatalogPermissionRule;
|
||||
hasMetadata: CatalogPermissionRule;
|
||||
hasSpec: CatalogPermissionRule;
|
||||
isEntityKind: CatalogPermissionRule;
|
||||
isEntityOwner: CatalogPermissionRule;
|
||||
};
|
||||
|
||||
// Warning: (ae-missing-release-tag) "PlaceholderProcessor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"@backstage/config": "^0.1.11",
|
||||
"@backstage/errors": "^0.1.5",
|
||||
"@backstage/integration": "^0.7.0",
|
||||
"@backstage/plugin-permission-node": "^0.2.3",
|
||||
"@backstage/search-common": "^0.2.1",
|
||||
"@backstage/types": "^0.1.1",
|
||||
"@octokit/graphql": "^4.5.8",
|
||||
|
||||
@@ -28,3 +28,4 @@ export * from './util';
|
||||
export * from './processing';
|
||||
export * from './providers';
|
||||
export * from './service';
|
||||
export * from './permissions';
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './rules';
|
||||
export type { CatalogPermissionRule } from './types';
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPropertyRule } from './createPropertyRule';
|
||||
|
||||
describe('createPropertyRule', () => {
|
||||
const { name, description, apply, toQuery } = createPropertyRule('metadata');
|
||||
|
||||
it('formats the rule name correctly', () => {
|
||||
expect(name).toBe('HAS_METADATA');
|
||||
});
|
||||
|
||||
it('formats the rule description correctly', () => {
|
||||
expect(description).toBe(
|
||||
'Allow entities which have the specified metadata subfield.',
|
||||
);
|
||||
});
|
||||
|
||||
describe('apply', () => {
|
||||
describe('key only', () => {
|
||||
it('returns false when specified key is not present', () => {
|
||||
expect(
|
||||
apply(
|
||||
{
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'test-component',
|
||||
},
|
||||
},
|
||||
'org.name',
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true when specified key is present', () => {
|
||||
expect(
|
||||
apply(
|
||||
{
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'test-component',
|
||||
org: {
|
||||
name: 'test-org',
|
||||
},
|
||||
},
|
||||
},
|
||||
'org.name',
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('key and value', () => {
|
||||
it('returns false when specified key is not present', () => {
|
||||
expect(
|
||||
apply(
|
||||
{
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'test-component',
|
||||
},
|
||||
},
|
||||
'org.name',
|
||||
'test-org',
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false when specified value is not present', () => {
|
||||
expect(
|
||||
apply(
|
||||
{
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'test-component',
|
||||
org: {
|
||||
name: 'another-org',
|
||||
},
|
||||
},
|
||||
},
|
||||
'org.name',
|
||||
'test-org',
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true when specified key and value is present', () => {
|
||||
expect(
|
||||
apply(
|
||||
{
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'test-component',
|
||||
org: {
|
||||
name: 'test-org',
|
||||
},
|
||||
},
|
||||
},
|
||||
'org.name',
|
||||
'test-org',
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('toQuery', () => {
|
||||
it('returns an appropriate catalog-backend filter', () => {
|
||||
expect(toQuery('backstage.io/test-component')).toEqual({
|
||||
key: 'metadata.backstage.io/test-component',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { EntitiesSearchFilter } from '../../catalog/types';
|
||||
import { CatalogPermissionRule } from '../types';
|
||||
import { get } from 'lodash';
|
||||
|
||||
export function createPropertyRule(
|
||||
propertyType: 'metadata' | 'spec',
|
||||
): CatalogPermissionRule {
|
||||
return {
|
||||
name: `HAS_${propertyType.toUpperCase()}`,
|
||||
description: `Allow entities which have the specified ${propertyType} subfield.`,
|
||||
apply: (resource: Entity, key: string, value?: string) => {
|
||||
const foundValue = get(resource[propertyType], key);
|
||||
if (value !== undefined) {
|
||||
return value === foundValue;
|
||||
}
|
||||
return !!foundValue;
|
||||
},
|
||||
toQuery: (key: string, value?: string): EntitiesSearchFilter => ({
|
||||
key: `${propertyType}.${key}`,
|
||||
...(value !== undefined && { values: [value] }),
|
||||
}),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { hasAnnotation } from './hasAnnotation';
|
||||
|
||||
describe('hasAnnotation permission rule', () => {
|
||||
describe('apply', () => {
|
||||
it('returns false when specified annotation is not present', () => {
|
||||
expect(
|
||||
hasAnnotation.apply(
|
||||
{
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'test-component',
|
||||
annotations: {
|
||||
'other-annotation': 'foo',
|
||||
},
|
||||
},
|
||||
},
|
||||
'backstage.io/test-annotation',
|
||||
),
|
||||
).toEqual(false);
|
||||
});
|
||||
|
||||
it('returns false when no annotations are present', () => {
|
||||
expect(
|
||||
hasAnnotation.apply(
|
||||
{
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'test-component',
|
||||
},
|
||||
},
|
||||
'backstage.io/test-annotation',
|
||||
),
|
||||
).toEqual(false);
|
||||
});
|
||||
|
||||
it('returns true when specified annotation is present', () => {
|
||||
expect(
|
||||
hasAnnotation.apply(
|
||||
{
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'test-component',
|
||||
annotations: {
|
||||
'other-annotation': 'foo',
|
||||
'backstage.io/test-annotation': 'bar',
|
||||
},
|
||||
},
|
||||
},
|
||||
'backstage.io/test-annotation',
|
||||
),
|
||||
).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toQuery', () => {
|
||||
it('returns an appropriate catalog-backend filter', () => {
|
||||
expect(hasAnnotation.toQuery('backstage.io/test-annotation')).toEqual({
|
||||
key: 'metadata.annotations.backstage.io/test-annotation',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { EntitiesSearchFilter } from '../../catalog/types';
|
||||
import { CatalogPermissionRule } from '../types';
|
||||
|
||||
/**
|
||||
* A {@link CatalogPermissionRule} which filters for the presence of an
|
||||
* annotation on a given entity.
|
||||
* @public
|
||||
*/
|
||||
export const hasAnnotation: CatalogPermissionRule = {
|
||||
name: 'HAS_ANNOTATION',
|
||||
description:
|
||||
'Allow entities which are annotated with the specified annotation',
|
||||
apply: (resource: Entity, annotation: string) =>
|
||||
!!resource.metadata.annotations?.hasOwnProperty(annotation),
|
||||
toQuery: (annotation: string): EntitiesSearchFilter => ({
|
||||
key: `metadata.annotations.${annotation}`,
|
||||
}),
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { hasLabel } from './hasLabel';
|
||||
|
||||
describe('hasLabel permission rule', () => {
|
||||
describe('apply', () => {
|
||||
it('returns false when specified label is not present', () => {
|
||||
expect(
|
||||
hasLabel.apply(
|
||||
{
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'test-component',
|
||||
labels: {
|
||||
somelabel: 'foo',
|
||||
},
|
||||
},
|
||||
},
|
||||
'backstage.io/testlabel',
|
||||
),
|
||||
).toEqual(false);
|
||||
});
|
||||
|
||||
it('returns false when no annotations are present', () => {
|
||||
expect(
|
||||
hasLabel.apply(
|
||||
{
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'test-component',
|
||||
},
|
||||
},
|
||||
'backstage.io/testlabel',
|
||||
),
|
||||
).toEqual(false);
|
||||
});
|
||||
|
||||
it('returns true when specified annotation is present', () => {
|
||||
expect(
|
||||
hasLabel.apply(
|
||||
{
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'test-component',
|
||||
labels: {
|
||||
somelabel: 'foo',
|
||||
'backstage.io/testlabel': 'bar',
|
||||
},
|
||||
},
|
||||
},
|
||||
'backstage.io/testlabel',
|
||||
),
|
||||
).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toQuery', () => {
|
||||
it('returns an appropriate catalog-backend filter', () => {
|
||||
expect(hasLabel.toQuery('backstage.io/testlabel')).toEqual({
|
||||
key: 'metadata.labels.backstage.io/testlabel',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { EntitiesSearchFilter } from '../../catalog/types';
|
||||
import { CatalogPermissionRule } from '../types';
|
||||
|
||||
/**
|
||||
* A {@link CatalogPermissionRule} which filters for entities with a specified
|
||||
* label in its metadata.
|
||||
* @public
|
||||
*/
|
||||
export const hasLabel: CatalogPermissionRule = {
|
||||
name: 'HAS_LABEL',
|
||||
description: 'Allow entities which have the specified label metadata.',
|
||||
apply: (resource: Entity, label: string) =>
|
||||
!!resource.metadata.labels?.hasOwnProperty(label),
|
||||
toQuery: (label: string): EntitiesSearchFilter => ({
|
||||
key: `metadata.labels.${label}`,
|
||||
}),
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPropertyRule } from './createPropertyRule';
|
||||
|
||||
/**
|
||||
* A {@link CatalogPermissionRule} which filters for entities with the specified
|
||||
* metadata subfield. Also matches on values if value is provided.
|
||||
*
|
||||
* The key argument to the `apply` and `toQuery` methods can be nested, such as
|
||||
* 'field.nestedfield'.
|
||||
* @public
|
||||
*/
|
||||
export const hasMetadata = createPropertyRule('metadata');
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPropertyRule } from './createPropertyRule';
|
||||
|
||||
/**
|
||||
* A {@link CatalogPermissionRule} which filters for entities with the specified
|
||||
* spec subfield. Also matches on values if value is provided.
|
||||
*
|
||||
* The key argument to the `apply` and `toQuery` methods can be nested, such as
|
||||
* 'field.nestedfield'.
|
||||
* @public
|
||||
*/
|
||||
export const hasSpec = createPropertyRule('spec');
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { hasAnnotation } from './hasAnnotation';
|
||||
import { isEntityKind } from './isEntityKind';
|
||||
import { isEntityOwner } from './isEntityOwner';
|
||||
import { hasLabel } from './hasLabel';
|
||||
import { hasMetadata } from './hasMetadata';
|
||||
import { hasSpec } from './hasSpec';
|
||||
|
||||
/**
|
||||
* These permission rules can be used to conditionally filter catalog entities
|
||||
* or describe a user's access to the entities.
|
||||
* @public
|
||||
*/
|
||||
export const permissionRules = {
|
||||
hasAnnotation,
|
||||
hasLabel,
|
||||
hasMetadata,
|
||||
hasSpec,
|
||||
isEntityKind,
|
||||
isEntityOwner,
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { isEntityKind } from './isEntityKind';
|
||||
|
||||
describe('isEntityKind', () => {
|
||||
describe('apply', () => {
|
||||
it('returns true when entity is the correct kind', () => {
|
||||
const component: Entity = {
|
||||
apiVersion: 'a',
|
||||
kind: 'b',
|
||||
metadata: {
|
||||
name: 'some-component',
|
||||
},
|
||||
};
|
||||
expect(isEntityKind.apply(component, ['b'])).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false when entity is not the correct kind', () => {
|
||||
const component: Entity = {
|
||||
apiVersion: 'a',
|
||||
kind: 'b',
|
||||
metadata: {
|
||||
name: 'some-component',
|
||||
},
|
||||
};
|
||||
expect(isEntityKind.apply(component, ['c'])).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toQuery', () => {
|
||||
it('returns an appropriate catalog-backend filter', () => {
|
||||
expect(isEntityKind.toQuery(['b'])).toEqual({
|
||||
key: 'kind',
|
||||
values: ['b'],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { EntitiesSearchFilter } from '../../catalog/types';
|
||||
import { CatalogPermissionRule } from '../types';
|
||||
|
||||
/**
|
||||
* A {@link CatalogPermissionRule} which filters for entities with a specified
|
||||
* kind.
|
||||
* @public
|
||||
*/
|
||||
export const isEntityKind: CatalogPermissionRule = {
|
||||
name: 'IS_ENTITY_KIND',
|
||||
description: 'Allow entities with the specified kind',
|
||||
apply(resource: Entity, kinds: string[]) {
|
||||
const resourceKind = resource.kind.toLocaleLowerCase('en-US');
|
||||
return kinds.some(kind => kind.toLocaleLowerCase('en-US') === resourceKind);
|
||||
},
|
||||
toQuery(kinds: string[]): EntitiesSearchFilter {
|
||||
return {
|
||||
key: 'kind',
|
||||
values: kinds.map(kind => kind.toLocaleLowerCase('en-US')),
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { isEntityOwner } from './isEntityOwner';
|
||||
|
||||
describe('isEntityOwner', () => {
|
||||
describe('apply', () => {
|
||||
it('returns true when entity is owned by the given user', () => {
|
||||
const component: Entity = {
|
||||
apiVersion: 'a',
|
||||
kind: 'b',
|
||||
metadata: {
|
||||
name: 'some-component',
|
||||
},
|
||||
relations: [
|
||||
{
|
||||
type: 'ownedBy',
|
||||
target: {
|
||||
kind: 'user',
|
||||
namespace: 'default',
|
||||
name: 'spiderman',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(isEntityOwner.apply(component, ['user:default/spiderman'])).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it('returns false when entity is not owned by the given user', () => {
|
||||
const component: Entity = {
|
||||
apiVersion: 'a',
|
||||
kind: 'b',
|
||||
metadata: {
|
||||
name: 'some-component',
|
||||
},
|
||||
relations: [
|
||||
{
|
||||
type: 'ownedBy',
|
||||
target: {
|
||||
kind: 'user',
|
||||
namespace: 'default',
|
||||
name: 'green-goblin',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(isEntityOwner.apply(component, ['user:default/spiderman'])).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it('returns false when entity does not have an owner', () => {
|
||||
const component: Entity = {
|
||||
apiVersion: 'a',
|
||||
kind: 'b',
|
||||
metadata: {
|
||||
name: 'some-component',
|
||||
},
|
||||
};
|
||||
expect(isEntityOwner.apply(component, ['user:default/spiderman'])).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toQuery', () => {
|
||||
it('returns an appropriate catalog-backend filter', () => {
|
||||
expect(isEntityOwner.toQuery(['user:default/spiderman'])).toEqual({
|
||||
key: 'relations.ownedBy',
|
||||
values: ['user:default/spiderman'],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
Entity,
|
||||
RELATION_OWNED_BY,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { EntitiesSearchFilter } from '../../catalog/types';
|
||||
import { CatalogPermissionRule } from '../types';
|
||||
|
||||
/**
|
||||
* A {@link CatalogPermissionRule} which filters for entities with a specified
|
||||
* owner.
|
||||
* @public
|
||||
*/
|
||||
export const isEntityOwner: CatalogPermissionRule = {
|
||||
name: 'IS_ENTITY_OWNER',
|
||||
description: 'Allow entities owned by the current user',
|
||||
apply: (resource: Entity, claims: string[]) => {
|
||||
if (!resource.relations) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return resource.relations
|
||||
.filter(relation => relation.type === RELATION_OWNED_BY)
|
||||
.some(relation => claims.includes(stringifyEntityRef(relation.target)));
|
||||
},
|
||||
toQuery: (claims: string[]): EntitiesSearchFilter => ({
|
||||
key: 'relations.ownedBy',
|
||||
values: claims,
|
||||
}),
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2021 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { PermissionRule } from '@backstage/plugin-permission-node';
|
||||
import { EntitiesSearchFilter } from '../catalog/types';
|
||||
|
||||
/**
|
||||
* A conditional rule that can be used to filter catalog entities for an
|
||||
* authorization request. See
|
||||
* {@link @backstage/plugin-permission-node#PermissionRule} for more details.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type CatalogPermissionRule = PermissionRule<
|
||||
Entity,
|
||||
EntitiesSearchFilter
|
||||
>;
|
||||
Reference in New Issue
Block a user