acl.ts (2774B)
1 import { AccessTokenPayload } from '@/lib/security/token' 2 3 export interface ACLItem { 4 cond: string 5 allow: boolean 6 } 7 8 export interface ACL { 9 [action: string]: ACLItem[] 10 } 11 12 export const ACL_ACTION_READ = 'read' 13 export const ACL_ACTION_WRITE = 'write' 14 export const ACL_ACTION_DELETE = 'delete' 15 export const ACL_ACTION_MOVE = 'move' 16 export const ACL_ACTION_MANAGE = 'manage' 17 export const ACL_ACTION_TALK = 'talk' 18 export const ACL_ACTION_CREATE_THREAD = 'create_thread' 19 20 type ArrayOrElement<T> = T | T[] 21 22 export function resolveACL ( 23 token: AccessTokenPayload | null, 24 acl: ArrayOrElement<ACL | null | undefined> | null | undefined, 25 action: string 26 ): boolean { 27 const acls = acl == null 28 ? null 29 : Array.isArray(acl) 30 ? acl 31 : [acl] 32 33 if (acls == null || acls.length === 0) { 34 return true 35 } 36 37 for (const acl of acls) { 38 if (!resolveACLInternal(token, acl, action)) { 39 return false 40 } 41 } 42 43 return true 44 } 45 46 export function resolveACLInternal ( 47 token: AccessTokenPayload | null, 48 acl: ACL | null | undefined, 49 action: string 50 ): boolean { 51 const items = acl?.[action] 52 if (items == null || items.length === 0) { 53 return true 54 } 55 56 let allow = false 57 for (const item of items) { 58 allow = resolveACLItem(token, item) ?? allow 59 } 60 61 return allow 62 } 63 64 function resolveACLItem ( 65 token: AccessTokenPayload | null, 66 item: ACLItem 67 ): boolean | null { 68 const [prefix, suffix] = item.cond.split(':') 69 70 switch (prefix) { 71 case 'special': 72 return resolveSpecialACLItem(token, suffix, item) 73 74 case 'user': 75 return token?.uid === parseInt(suffix) ? item.allow : null 76 77 case 'group': 78 return token?.aclGroup?.includes(suffix) ? item.allow : null 79 80 default: 81 return null 82 } 83 } 84 85 function resolveSpecialACLItem (token: AccessTokenPayload | null, suffix: string, item: ACLItem): boolean | null { 86 switch (suffix) { 87 case 'all': 88 return item.allow 89 case 'member': 90 return token != null ? item.allow : null 91 case 'anon': 92 return token == null ? item.allow : null 93 default: 94 return null 95 } 96 } 97 98 export function validateACL (acl: any): acl is ACL { 99 if (typeof acl !== 'object' || acl == null) { 100 return false 101 } 102 103 for (const key in acl) { 104 if (!validateACLItems(acl[key])) { 105 return false 106 } 107 } 108 109 return true 110 } 111 112 function validateACLItems (aclItems: any): aclItems is ACLItem[] { 113 if (!Array.isArray(aclItems)) { 114 return false 115 } 116 117 for (const aclItem of aclItems) { 118 if (!validateACLItem(aclItem)) { 119 return false 120 } 121 } 122 123 return true 124 } 125 126 function validateACLItem (aclItem: any): aclItem is ACLItem { 127 return typeof aclItem === 'object' && aclItem != null && 128 typeof aclItem.cond === 'string' && 129 typeof aclItem.allow === 'boolean' 130 }