consolidatedScopes.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import groupBy from 'lodash/groupBy';
  2. import invertBy from 'lodash/invertBy';
  3. import pick from 'lodash/pick';
  4. import {Permissions} from 'sentry/types';
  5. const PERMISSION_LEVELS = {
  6. read: 0,
  7. write: 1,
  8. admin: 2,
  9. };
  10. const HUMAN_RESOURCE_NAMES = {
  11. project: 'Project',
  12. team: 'Team',
  13. release: 'Release',
  14. event: 'Event',
  15. org: 'Organization',
  16. member: 'Member',
  17. };
  18. const DEFAULT_RESOURCE_PERMISSIONS: Permissions = {
  19. Project: 'no-access',
  20. Team: 'no-access',
  21. Release: 'no-access',
  22. Event: 'no-access',
  23. Organization: 'no-access',
  24. Member: 'no-access',
  25. };
  26. const PROJECT_RELEASES = 'project:releases';
  27. type PermissionLevelResources = {
  28. admin: string[];
  29. read: string[];
  30. write: string[];
  31. };
  32. /**
  33. * Numerical value of the scope where Admin is higher than Write,
  34. * which is higher than Read. Used to sort scopes by access.
  35. */
  36. const permissionLevel = (scope: string): number => {
  37. const permission = scope.split(':')[1];
  38. return PERMISSION_LEVELS[permission];
  39. };
  40. const compareScopes = (a: string, b: string) => permissionLevel(a) - permissionLevel(b);
  41. /**
  42. * Return the most permissive scope for each resource.
  43. *
  44. * Example:
  45. * Given the full list of scopes:
  46. * ['project:read', 'project:write', 'team:read', 'team:write', 'team:admin']
  47. *
  48. * this would return:
  49. * ['project:write', 'team:admin']
  50. */
  51. function topScopes(scopeList: string[]) {
  52. return Object.values(groupBy(scopeList, scope => scope.split(':')[0]))
  53. .map(scopes => scopes.sort(compareScopes))
  54. .map(scopes => scopes.pop());
  55. }
  56. /**
  57. * Convert into a list of Permissions, grouped by resource.
  58. *
  59. * This is used in the new/edit Sentry App form. That page displays permissions
  60. * in a per-Resource manner, meaning one row for Project, one for Organization, etc.
  61. *
  62. * This exposes scopes in a way that works for that UI.
  63. *
  64. * Example:
  65. * {
  66. * 'Project': 'read',
  67. * 'Organization': 'write',
  68. * 'Team': 'no-access',
  69. * ...
  70. * }
  71. */
  72. function toResourcePermissions(scopes: string[]): Permissions {
  73. const permissions = {...DEFAULT_RESOURCE_PERMISSIONS};
  74. let filteredScopes = [...scopes];
  75. // The scope for releases is `project:releases`, but instead of displaying
  76. // it as a permission of Project, we want to separate it out into its own
  77. // row for Releases.
  78. if (scopes.includes(PROJECT_RELEASES)) {
  79. permissions.Release = 'admin';
  80. filteredScopes = scopes.filter((scope: string) => scope !== PROJECT_RELEASES); // remove project:releases
  81. }
  82. topScopes(filteredScopes).forEach((scope: string | undefined) => {
  83. if (scope) {
  84. const [resource, permission] = scope.split(':');
  85. permissions[HUMAN_RESOURCE_NAMES[resource]] = permission;
  86. }
  87. });
  88. return permissions;
  89. }
  90. /**
  91. * Convert into a list of Permissions, grouped by access and including a
  92. * list of resources per access level.
  93. *
  94. * This is used in the Permissions Modal when installing an App. It displays
  95. * scopes in a per-Permission way, meaning one row for Read, one for Write,
  96. * and one for Admin.
  97. *
  98. * This exposes scopes in a way that works for that UI.
  99. *
  100. * Example:
  101. * {
  102. * read: ['Project', 'Organization'],
  103. * write: ['Member'],
  104. * admin: ['Release']
  105. * }
  106. */
  107. function toPermissions(scopes: string[]): PermissionLevelResources {
  108. const defaultPermissions = {read: [], write: [], admin: []};
  109. const resourcePermissions = toResourcePermissions(scopes);
  110. // Filter out the 'no-access' permissions
  111. const permissions = pick(invertBy(resourcePermissions), ['read', 'write', 'admin']);
  112. return {...defaultPermissions, ...permissions};
  113. }
  114. export {toPermissions, toResourcePermissions};