123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- import groupBy from 'lodash/groupBy';
- import invertBy from 'lodash/invertBy';
- import pick from 'lodash/pick';
- import {Permissions} from 'sentry/types';
- const PERMISSION_LEVELS = {
- read: 0,
- write: 1,
- admin: 2,
- };
- const HUMAN_RESOURCE_NAMES = {
- project: 'Project',
- team: 'Team',
- release: 'Release',
- event: 'Event',
- org: 'Organization',
- member: 'Member',
- };
- const DEFAULT_RESOURCE_PERMISSIONS: Permissions = {
- Project: 'no-access',
- Team: 'no-access',
- Release: 'no-access',
- Event: 'no-access',
- Organization: 'no-access',
- Member: 'no-access',
- };
- const PROJECT_RELEASES = 'project:releases';
- type PermissionLevelResources = {
- admin: string[];
- read: string[];
- write: string[];
- };
- /**
- * Numerical value of the scope where Admin is higher than Write,
- * which is higher than Read. Used to sort scopes by access.
- */
- const permissionLevel = (scope: string): number => {
- const permission = scope.split(':')[1];
- return PERMISSION_LEVELS[permission];
- };
- const compareScopes = (a: string, b: string) => permissionLevel(a) - permissionLevel(b);
- /**
- * Return the most permissive scope for each resource.
- *
- * Example:
- * Given the full list of scopes:
- * ['project:read', 'project:write', 'team:read', 'team:write', 'team:admin']
- *
- * this would return:
- * ['project:write', 'team:admin']
- */
- function topScopes(scopeList: string[]) {
- return Object.values(groupBy(scopeList, scope => scope.split(':')[0]))
- .map(scopes => scopes.sort(compareScopes))
- .map(scopes => scopes.pop());
- }
- /**
- * Convert into a list of Permissions, grouped by resource.
- *
- * This is used in the new/edit Sentry App form. That page displays permissions
- * in a per-Resource manner, meaning one row for Project, one for Organization, etc.
- *
- * This exposes scopes in a way that works for that UI.
- *
- * Example:
- * {
- * 'Project': 'read',
- * 'Organization': 'write',
- * 'Team': 'no-access',
- * ...
- * }
- */
- function toResourcePermissions(scopes: string[]): Permissions {
- const permissions = {...DEFAULT_RESOURCE_PERMISSIONS};
- let filteredScopes = [...scopes];
- // The scope for releases is `project:releases`, but instead of displaying
- // it as a permission of Project, we want to separate it out into its own
- // row for Releases.
- if (scopes.includes(PROJECT_RELEASES)) {
- permissions.Release = 'admin';
- filteredScopes = scopes.filter((scope: string) => scope !== PROJECT_RELEASES); // remove project:releases
- }
- topScopes(filteredScopes).forEach((scope: string | undefined) => {
- if (scope) {
- const [resource, permission] = scope.split(':');
- permissions[HUMAN_RESOURCE_NAMES[resource]] = permission;
- }
- });
- return permissions;
- }
- /**
- * Convert into a list of Permissions, grouped by access and including a
- * list of resources per access level.
- *
- * This is used in the Permissions Modal when installing an App. It displays
- * scopes in a per-Permission way, meaning one row for Read, one for Write,
- * and one for Admin.
- *
- * This exposes scopes in a way that works for that UI.
- *
- * Example:
- * {
- * read: ['Project', 'Organization'],
- * write: ['Member'],
- * admin: ['Release']
- * }
- */
- function toPermissions(scopes: string[]): PermissionLevelResources {
- const defaultPermissions = {read: [], write: [], admin: []};
- const resourcePermissions = toResourcePermissions(scopes);
- // Filter out the 'no-access' permissions
- const permissions = pick(invertBy(resourcePermissions), ['read', 'write', 'admin']);
- return {...defaultPermissions, ...permissions};
- }
- export {toPermissions, toResourcePermissions};
|