hasPermission.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. import type { RequiredPermission } from '#shared/types/permission.ts'
  3. /**
  4. * Check if the access can be granted for the given permissions.
  5. *
  6. * Examples:
  7. * hasPermission('ticket.agent', [...]) # access to certain permission key
  8. * hasPermission['ticket.agent', 'ticket.customer'], [...]) # access to one of permission keys
  9. *
  10. * hasPermission('user_preferences.calendar+ticket.agent', [...]) # access must have two permission keys
  11. *
  12. * hasPermission('admin.*', [...]) # access if one sub key access exists
  13. *
  14. * @param {Array<string>|string} requiredPermission - The permissions which are required.
  15. * @param {Array<string>} permissions - The available permission.
  16. *
  17. * @returns {boolean}
  18. */
  19. const hasPermission = (
  20. requiredPermission: RequiredPermission,
  21. permissions: Array<string>,
  22. // eslint-disable-next-line sonarjs/cognitive-complexity
  23. ): boolean => {
  24. const requiredPermissions = Array.isArray(requiredPermission)
  25. ? requiredPermission
  26. : [requiredPermission]
  27. // Available with any permission.
  28. if (requiredPermissions.length === 0 || requiredPermissions.includes('*')) {
  29. return true
  30. }
  31. // If a permission is needed, but no permission was given, permission will not be granted.
  32. if (permissions.length === 0) return false
  33. for (const localRequirePermission of requiredPermissions) {
  34. // The permission can be combined with a 'AND', then every single permission needs to match.
  35. const localRequiredPermissions = localRequirePermission.split('+')
  36. let accessGranted = false
  37. for (const requiredPermissionItem of localRequiredPermissions) {
  38. let singleAccessGranted = false
  39. // Check first if a permission with wildcard is matching.
  40. if (requiredPermissionItem.includes('*')) {
  41. const regexRequiredPermission = new RegExp(
  42. requiredPermissionItem.replace('.', '\\.').replace('*', '.+'),
  43. )
  44. singleAccessGranted = permissions.some((permission) =>
  45. regexRequiredPermission.test(permission),
  46. )
  47. }
  48. // If not already a wildcard permission match exists, check for a direct permission.
  49. if (!singleAccessGranted) {
  50. const partsRequiredPermission = requiredPermissionItem.split('.')
  51. let checkPartsRequiredPermission = ''
  52. for (const partRequiredPermission of partsRequiredPermission) {
  53. if (checkPartsRequiredPermission) checkPartsRequiredPermission += '.'
  54. checkPartsRequiredPermission += partRequiredPermission
  55. singleAccessGranted = permissions.includes(
  56. checkPartsRequiredPermission,
  57. )
  58. if (singleAccessGranted) break
  59. }
  60. }
  61. accessGranted = singleAccessGranted
  62. // If one permission not exists, no access can be granted for this required permission.
  63. if (!accessGranted) break
  64. }
  65. // If one required permission matches, the access can be granted.
  66. if (accessGranted) return accessGranted
  67. }
  68. return false
  69. }
  70. export default hasPermission