---
# System prepended metadata

title: RBAC

---

# RBAC
## What is RBAC?
RBAC stands for role-based access control (sometimes referenced as role-based security) is an approach to restricting system access to authorized users. It is an approach to implement mandatory access control or discretionary access control.
## How is RBAC implemented?
RBAC consist of 2 major parts:
* rules
* rules handler (aka CanUser)

### Rules

So far we are enforcing only 2 roles: admin and moderator. The number of roles and the privilages associated with them might vary depending on the complexity of the project or it's goals.

Simply add new object giving it a role name and add as many rules as you wish this role is authorized to do.

```json=
const rules = {
  admin: [
    'basicMessages:create',
    'basicMessages:read',
    'contacts:create',
    'contacts:read',
    'contacts:update',
    'contacts:delete',
    'credentials:issue',
    'credentials:read',
    'credentials:reissue',
    'credentials:revoke',
    'demographics:create',
    'demographics:read',
    'demographics:update',
    'demographics:delete',
    'invitations:create',
    'invitations:delete',
    'roles:read',
    'settings:read',
    'settings:update',
    'users:create',
    'users:read',
    'users:update',
    'users:delete',
    'users:updatePassword',
    'users:updateRoles',
  ],
  moderator: [
    'basicMessages:create',
    'basicMessages:read',
    'contacts:create',
    'contacts:read',
    'contacts:update',
    'credentials:issue',
    'credentials:read',
    'credentials:reissue',
    'credentials:revoke',
    'demographics:create',
    'demographics:read',
    'demographics:update',
    'invitations:create',
  ],
}
```


Note in case the user shares multiple roles duplication of the RBAC rules will be handled and only unique roles will be applied.

```javascript
  // Combine roles, ignore duplicate roles
  for (let i = 0; i < Object.keys(roles).length; i++) {
    permissions = permissions.concat(
      rules[roles[i]].filter((item) => permissions.indexOf(item) < 0)
    )
  }
```

### Rules handler

The rules handler requires 3 main pieces to work:
* rules
* user
* actions

**Rules** are described in the section above and are imported as a file.

**User** is set up on the login and may consist of different information. But what user must have is the roles property. Roles object of the user is an array of roles set up by the admin during the [user creation](http://link.here).

```json=
{"id":3478,"username":"Vic","roles":["admin"]}
```

**Actions** is the list of privileges that the specific function, operation or component must meet to be able to function/render. Formated as a string list.

```javascript=
"contacts:create, contacts:create"
```

### RBAC handler (aka CanUser) for the front-end
```javascript=
import rules from './rbac-rules'

export const check = (rules, user, actions) => {
  // Get user roles
  if (!user) return false

  let roles = user.roles

  // Handle multiple roles by casting roles into array
  roles = roles instanceof Array ? roles : [roles]

  let permissions = []

  // Combine roles, ignore duplicate roles
  for (let i = 0; i < Object.keys(roles).length; i++) {
    permissions = permissions.concat(
      rules[roles[i]].filter((item) => permissions.indexOf(item) < 0)
    )
  }

  if (!permissions) {
    return false
  }

  // Determine all the permissions required by the component
  if (permissions) {
    const actionsList = actions.split(', ')

    // Check if the user has all required permissions
    for (const action in actionsList) {
      if (permissions.includes(actionsList[action])) {
        return true
      }
    }
  }
  return false
}

// Descision maker
export const CanUser = (props) =>
  check(rules, props.user, props.perform, props.data) ? props.yes() : props.no()

CanUser.defaultProps = {
  yes: () => null,
  no: () => null,
}
```

### RBAC handler (aka CanUser) for the back-end
```javascript=
const check = (rules, user, actions) => {
  // Get user roles

  let roles = user.roles

  if (!user) return false

  // Handle multiple roles by casting roles into array
  roles = roles instanceof Array ? roles : [roles]

  let permissions = []

  // Combine roles, discard duplicate roles
  for (let i = 0; i < Object.keys(roles).length; i++) {
    permissions = permissions.concat(
      rules[roles[i]].filter((item) => permissions.indexOf(item) < 0),
    )
  }

  if (!permissions) {
    return false
  }

  // Assign all permissions required by the component
  if (permissions) {
    const actionsList = actions.split(', ')

    // Check if the user has all required permissions
    for (const action in actionsList) {
      if (permissions.includes(actionsList[action])) {
        return true
      }
    }
  }
  return false
}

module.exports = check
```


## Implementation and examples
Since it is potentially possible to compromise the front-end and go around the checks, RBAC is enforsed on both the back- and front-ends of our agents for extra security. The back-end RBAC is enforsed for all the websocket calls only.

### Front-end examples

Allow ContentFlexBox to render if the user has "contacts:create" permissions
```javascript=
<CanUser
    user={localUser}
    perform="contacts:create"
    yes={() => (
      <ContentFlexBox
        onClick={addContact}
      >
        Add Contact
      </ContentFlexBox>
    )}
/>
```

If the privilages fail, don't render any content.
```javascript=
<CanUser
    user={localUser}
    perform="settings:read"
    yes={() => (
      <>
        <Item
          className={pathMatch === '/settings' ? 'active' : undefined}
        >
          <StyledLink to="/settings">Settings</StyledLink>
        </Item>
      </>
    )}
    no={() => ''}
/>
```

Enforce the RBAC on the websocket messages (App.js)
```javascript=
import { check } from './UI/CanUser'
...
if (
  check(rules, loggedInUserState, 'contacts:read', 'demographics:read')
) {
  sendMessage('CONTACTS', 'GET_ALL', {
    additional_tables: ['Demographic', 'Passport'],
  })
  addLoadingProcess('CONTACTS')
}
```

### Back-end examples
Enforce the RBAC on the websocket messages (websockets.js)
```javascript=
// All you need for CanUser to work
const check = require('./canUser')
const rules = require('./rbac-rules')
const cookie = require('cookie')
const cookieParser = require('cookie-parser')
let userCookieParsed = undefined
...

wss.on('connection', (ws, req) => {
  console.log('New Websocket Connection')

  // Getting the user data from the cookie
  try {
    const cookies = cookie.parse(req.headers.cookie)
    const userCookie = cookieParser.signedCookie(cookies['user'])
    userCookieParsed = JSON.parse(userCookie.substring(2))
  } catch (error) { }

...
switch (context) {
      case 'USERS':
        switch (type) {
          case 'GET_ALL':
            if (check(rules, userCookieParsed, 'users:read')) {
              const users = await Users.getAll()
              sendMessage(ws, 'USERS', 'USERS', { users })
            } else {
              sendMessage(ws, 'USERS', 'USER_ERROR', {
                error: 'ERROR: You are not authorized to fetch users.',
              })
            }
            break
...
```