npm package: @inventi/acl
Description: [![@inventi/acl](https://img.shields.io/npm/v/@inventi/acl.svg)]()
Author: Adam Bisek
Latest version: 1.1.0

# @inventi/acl 🇩🇪
[![@inventi/acl](https://img.shields.io/npm/v/@inventi/acl.svg)]()


## Example

See tests in source code for more examples!

### Permission definition and plain usage:

```javascript
import Permission, { Permission, PermissionUser, ResourceNameArgument } from '@inventi/acl'

const acl = new Permission()
acl.addRole('guest')
acl.addRole('registered', 'guest') // 'registered' dědí od 'guest'
acl.addRole('admin', 'registered')  // a od něj dědí 'admin'

acl.addResource('article')
acl.addResource('comment')
acl.addResource('poll')

// guest může prohlížet články, komentáře i ankety
acl.allow('guest', ['article', 'comment', 'poll'], 'view')

// administrátor nemůže editovat ankety, to by bylo nedemokratické
acl.deny('admin', 'poll', 'edit')

// volitelný callback = dynamická aserce
acl.allow('guest', 'article', 'poll', (role: string, resource: ResourceNameArgument, operation: string, extraData?: any) => {
  if (typeof resource !== 'string' && resource?.resourceName === 'article' && extraData?.userInfo?.id === 1) return true
  return false
})

expect(acl.isAllowed('guest', 'article', 'view')).toBe(true)
expect(acl.isAllowed('guest', { resourceName: 'article', type: 'baz' }, 'poll', { userInfo: { id: 1 } })).toBe(true)
expect(acl.isAllowed('registered', 'article', 'view')).toBe(true)
expect(acl.isAllowed('admin', 'article', 'edit')).toBe(true)


let user = new PermissionUser(acl)
user.setRole('guest')
expect(user.isAllowed('article', 'view')).toBe(true)
expect(user.isAllowed('article', 'edit')).toBe(false)
expect(user.isAllowed('poll', 'vote')).toBe(true)
expect(user.isAllowed('comment', 'add')).toBe(false)
expect(user.isAllowed({ resourceName: 'article', type: 'baz' }, 'poll', { userInfo: { id: 1 } })).toBe(true)
expect(user.isAllowed({ resourceName: 'bar', type: 'foo' }, 'add', { userInfo: { id: 2 } })).toBe(false)

user = new PermissionUser(acl)
user.setRole('registered')
expect(user.isAllowed('article', 'view')).toBe(true)
```

### Usage in React:

1. Define permissions
```javascript
import { Permission } from '@packages/acl'

const acl = new Permission()
acl.addRole('guest')
acl.addRole('admin')

acl.addResource('article')

acl.allow('guest', ['article', 'comment'], 'view')
acl.allow('admin', acl.ALL, ['view', 'edit', 'add'])

export default acl
```

2. Add a provider to the root of your app
```javascript
import { Provider as AclProvider } from '@packages/acl'

<AclProvider permission={acl} role="guest">
   ...
</AclProvider>
```

3. Query (hooks or Can component) :)
```javascript
import { Can, usePermission } from '@packages/acl'

const MyComponent = () => {
  // the hooks way
  const permision = usePermission()
  console.log('Are you allowed to view a comment?', permision.isAllowed('comment', 'view'))
  return (
  {/* the component way */}
  <Can I="launch" a="atomMissile">
    <h1>You can launch a atom missile!</h1>
  </Can>
  <Can not I="launch" a="atomMissile">
    <h1>You can NOT launch a atom missile!</h1>
  </Can>
  )
}
```

## Disclaimer
This package is not intended for dynamic permissions changes (but it can be done with little effort ;)).

If you make changes like `permission.permission.allow()`, `permission.permission.deny()`, and so on, after Context Provider is created,
context wont catch this changes!