scope.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. // If you want to add a new scope, you only need to add a new key to the ORIGINAL_SCOPE object.
  2. export const ORIGINAL_SCOPE = {
  3. admin: {
  4. top: {},
  5. app: {},
  6. security: {},
  7. markdown: {},
  8. customize: {},
  9. import_data: {},
  10. exporet_data: {},
  11. data_transfer: {},
  12. external_notification: {},
  13. slack_integration: {},
  14. legacy_slack_integration: {},
  15. user_management: {},
  16. user_group_management: {},
  17. audit_log: {},
  18. plugin: {},
  19. ai_integration: {},
  20. full_text_search: {},
  21. },
  22. user: {
  23. info: {},
  24. external_account: {},
  25. password: {},
  26. api: {
  27. api_token: {},
  28. access_token: {},
  29. },
  30. in_app_notification: {},
  31. other: {},
  32. },
  33. base: {
  34. },
  35. } as const;
  36. export const ACTION = {
  37. READ: 'read',
  38. WRITE: 'write',
  39. } as const;
  40. type ACTION_TYPE = typeof ACTION[keyof typeof ACTION];
  41. export const ALL_SIGN = '*';
  42. export const ORIGINAL_SCOPE_WITH_ACTION = Object.values(ACTION).reduce(
  43. (acc, action) => {
  44. acc[action] = ORIGINAL_SCOPE;
  45. return acc;
  46. },
  47. {} as Record<ACTION_TYPE, typeof ORIGINAL_SCOPE>,
  48. );
  49. type FlattenObject<T> = {
  50. [K in keyof T]: T[K] extends object
  51. ? (keyof T[K] extends never
  52. ? K
  53. : `${K & string}:${FlattenObject<T[K]> & string}`)
  54. : K
  55. }[keyof T];
  56. type AddAllToScope<S extends string> =
  57. S extends `${infer X}:${infer Y}`
  58. ? `${X}:${typeof ALL_SIGN}` | `${X}:${AddAllToScope<Y>}` | S
  59. : S;
  60. type ScopeOnly = FlattenObject<typeof ORIGINAL_SCOPE_WITH_ACTION>;
  61. type ScopeWithAll = AddAllToScope<ScopeOnly> ;
  62. export type Scope = ScopeOnly | ScopeWithAll;
  63. // ScopeConstantsの型定義
  64. type ScopeConstantLeaf = Scope;
  65. type ScopeConstantNode<T> = {
  66. [K in keyof T as Uppercase<string & K>]: T[K] extends object
  67. ? (keyof T[K] extends never
  68. ? ScopeConstantLeaf
  69. : ScopeConstantNode<T[K]> & { ALL: Scope })
  70. : ScopeConstantLeaf
  71. };
  72. type ScopeConstantType = {
  73. [A in keyof typeof ORIGINAL_SCOPE_WITH_ACTION as Uppercase<string & A>]:
  74. ScopeConstantNode<typeof ORIGINAL_SCOPE> & { ALL: Scope }
  75. };
  76. const buildScopeConstants = (): ScopeConstantType => {
  77. const result = {} as Partial<ScopeConstantType>;
  78. const processObject = (obj: Record<string, any>, path: string[] = [], resultObj: Record<string, any>) => {
  79. Object.entries(obj).forEach(([key, value]) => {
  80. const upperKey = key.toUpperCase();
  81. const currentPath = [...path, key];
  82. const scopePath = currentPath.join(':');
  83. if (value == null) {
  84. return;
  85. }
  86. if (typeof value === 'object' && Object.keys(value).length === 0) {
  87. resultObj[upperKey] = `${scopePath}` as Scope;
  88. }
  89. else if (typeof value === 'object') {
  90. resultObj[upperKey] = {
  91. ALL: `${scopePath}:${ALL_SIGN}` as Scope,
  92. };
  93. processObject(value, currentPath, resultObj[upperKey]);
  94. }
  95. });
  96. };
  97. processObject(ORIGINAL_SCOPE_WITH_ACTION, [], result);
  98. return result as ScopeConstantType;
  99. };
  100. export const SCOPE = buildScopeConstants();
  101. export const isValidScope = (scope: string): boolean => {
  102. const scopeParts = scope.split(':').map(x => (x === '*' ? 'ALL' : x.toUpperCase()));
  103. let obj: any = SCOPE;
  104. scopeParts.forEach((part) => {
  105. if (obj[part] == null) {
  106. return false;
  107. }
  108. obj = obj[part];
  109. });
  110. return obj === scope;
  111. };
  112. export const isAllScope = (scope: string): scope is Scope => {
  113. return scope.endsWith(`:${ALL_SIGN}`);
  114. };
  115. const getAllScopeValues = (scopeObj: any): Scope[] => {
  116. const result: Scope[] = [];
  117. const traverse = (current: any): void => {
  118. if (typeof current !== 'object' || current === null) {
  119. if (typeof current === 'string') {
  120. result.push(current as Scope);
  121. }
  122. return;
  123. }
  124. Object.values(current).forEach((value) => {
  125. traverse(value);
  126. });
  127. };
  128. traverse(scopeObj);
  129. return result;
  130. };
  131. export const extractScopes = (scopes?: Scope[]): Scope[] => {
  132. if (scopes == null) {
  133. return [];
  134. }
  135. const result = new Set<Scope>(scopes);
  136. scopes.forEach((scope) => {
  137. if (!isAllScope(scope)) {
  138. return;
  139. }
  140. const scopeParts = scope.split(':').map(x => (x.toUpperCase()));
  141. let obj: any = SCOPE;
  142. scopeParts.forEach((part) => {
  143. if (part === ALL_SIGN) {
  144. return;
  145. }
  146. obj = obj[part];
  147. });
  148. getAllScopeValues(obj).forEach((value) => {
  149. result.add(value);
  150. });
  151. });
  152. return Array.from(result.values());
  153. };