# Accessing user roles in progressively decoupled blocks

**URL:** <https://farmos.discourse.group/t/accessing-user-roles-in-progressively-decoupled-blocks/1815>\
**Category:** Development\
**Tags:** feature-request, support-request\
**Created:** [December 13, 2023, 4:08pm UTC](https://farmos.discourse.group/t/accessing-user-roles-in-progressively-decoupled-blocks/1815 "2023-12-13T16:08:07Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![braught](https://yyz2.discourse-cdn.com/free1/user_avatar/farmos.discourse.group/braught/32/294_2.png) [@braught](https://farmos.discourse.group/u/braught)\
**Post date:** [December 13, 2023, 4:08pm UTC](https://farmos.discourse.group/t/accessing-user-roles-in-progressively-decoupled-blocks/1815/1 "2023-12-13T16:08:07Z")

</div>

I am building a module for farmOS that uses Vue.js to render pages that interact with farmOS via the API using the farmOS.js library. Overall this is working like a charm so far 🙂

One thing I would like to be able to do is to change some the available UI elements based on the role (or permissions) of the currently logged in user (e.g. a button to create a new plant type, or a new location, etc).

The challenge is that there does not seem to be a standard way to obtain role/permission information about the currently logged user via API. The `admin` user can get role/permission information about their self (and others). However any other user cannot. Note: I only want the currently logged in user to get information about themselves.

I spent some time experimenting with trying to create a view that exposed an API endpoint showing just information about the currently logged user (including their roles and permissions). The effect is the same, the `admin` user receives a populated `roles` array in the response, while any other user does not. Apart from granting “User Administer…” permissions to other roles (clearly not a good idea), I could not see any standard way to allow a non-admin user to access their own role or permission information via the API.

Some of my thoughts at this point are:

- Inject the role/permission information for the currently logged in user into the html page as global variables in the Drupal `controller` (though I’ve not tried it so not sure if it would work either).
- Have the Vue app probe farmOS paths and construct its own permission based on which paths are available or not available. (E.g. try to access the `http://farmos/admin/structure/taxonomy/manage/crop_family/add` page or try to actually create a `crop_family` via API and see if it is allowed or not.)

I’m hoping others may be able to point out something I am missing or have other thoughts on how this type of functionality could be implemented. Thanks!

---

<div class="post-metadata">

**Author:** ![braught](https://yyz2.discourse-cdn.com/free1/user_avatar/farmos.discourse.group/braught/32/294_2.png) [@braught](https://farmos.discourse.group/u/braught)\
**Post date:** [December 13, 2023, 9:05pm UTC](https://farmos.discourse.group/t/accessing-user-roles-in-progressively-decoupled-blocks/1815/2 "2023-12-13T21:05:50Z")

</div>

Any chance that there could be a permission that would allow a non-admin user to retrieve just their own list of roles/permissions via API?

Would this be a farmOS thing or a JSON:API module thing?

---

<div class="post-metadata">

**Author:** ![mstenta](https://yyz2.discourse-cdn.com/free1/user_avatar/farmos.discourse.group/mstenta/32/4_2.png) [@mstenta](https://farmos.discourse.group/u/mstenta)\
**Post date:** [December 14, 2023, 2:46pm UTC](https://farmos.discourse.group/t/accessing-user-roles-in-progressively-decoupled-blocks/1815/3 "2023-12-14T14:46:40Z")

</div>

Great question @braught - and not something I’ve dealt with myself, but I might be able to make some educated guesses based on my understanding…

> [@braught](#):
>
> The challenge is that there does not seem to be a standard way to obtain role/permission information about the currently logged user via API.

Yea I don’t think there is any built-in way to do this in Drupal. Just like there is no way for a Drupal user to see what roles/permissions they have via the UI (unless they have admin level access to configure those things themselves). These are simply not exposed to users.

Instead, the expectation is that the pages built by Drupal will perform the necessary permission checking and build themselves accordingly.

JSON:API _does_ filter out resources that the user does not have access to. And it will prevent the user from creating resources if they don’t have permission.

That doesn’t help if you are trying to build a UI that shows/hides things based on permission though…

(Also just an aside: best practice is to only build your modules around _permissions_ - not _roles_. Because roles may differ from one instance to another, and unless your module is the one providing the roles, and is designed to ONLY work with those roles, then there’s no guarantee they will exist. So it’s best to only check if a user has a permission - not a role.)

> [@braught](#):
>
> One thing I would like to be able to do is to change some the available UI elements based on the role (or permissions) of the currently logged in user (e.g. a button to create a new plant type, or a new location, etc).

I think the best solution might be to do something along the lines of what you suggested:

> [@braught](#):
>
> Inject the role/permission information for the currently logged in user into the html page as global variables in the Drupal `controller` (though I’ve not tried it so not sure if it would work either).

It’s possible to add variables to the global Javascript `Drupal.settings` variable on the page, which you can then access from your Vue app.

This gives an example using `hook_preprocess_page()` but you can do something similar within your page controller too:

> **[Adding assets (CSS, JS) to a Drupal theme via \*.libraries.yml](https://www.drupal.org/docs/develop/theming-drupal/adding-assets-css-js-to-a-drupal-theme-via-librariesyml#attaching-configurable-javascript)**
>
> This documentation is for themes. For modules, see Adding stylesheets (CSS) and JavaScript (JS) to a Drupal module. In Drupal, CSS stylesheets (CSS) and JavaScript (JS) are loaded through the same system for modules and themes as asset libraries....

This is a simple example that you can use in your controller PHP (assuming the build array is `$build`):

`$build['#attached']['drupalSettings']['foo'] = 'bar';`

With that you can then run the following in the browser console:

`console.log(Drupal.settings.foo)`

I probably wouldn’t recommend sticking the full list of user permissions into the page’s JavaScript, though. Something about that feels wrong (and maybe risky from a security perspective). Instead, the best approach might be to set flags for your own app specifically based on permissions. So for example… based on what you described…

> [@braught](#):
>
> a button to create a new plant type

Maybe in your controller you could check if they have the necessary permission, and set something like this:

`$build['#attached']['drupalSettings']['farmdata2']['create_plant_type'] = $user->hasPermission('create terms in plant_type');` (I didn’t test this so I might have the permission wrong…)

Then you can check for that boolean in your Vue code.

That’s probably how I would go about this! Of course, it’s easier when you are also building the controller, which I think you are in FarmData2.

In completely decoupled apps, where you are not building the page in Drupal, but rendering it on an external URL, it’s a little trickier (eg: Field Kit) because you can’t know the permissions ahead of time.

---

<div class="post-metadata">

**Author:** ![braught](https://yyz2.discourse-cdn.com/free1/user_avatar/farmos.discourse.group/braught/32/294_2.png) [@braught](https://farmos.discourse.group/u/braught)\
**Post date:** [December 14, 2023, 3:02pm UTC](https://farmos.discourse.group/t/accessing-user-roles-in-progressively-decoupled-blocks/1815/4 "2023-12-14T15:02:45Z")

</div>

Thanks for this detailed reply! I will give this a go and report back.

---

<div class="post-metadata">

**Author:** ![braught](https://yyz2.discourse-cdn.com/free1/user_avatar/farmos.discourse.group/braught/32/294_2.png) [@braught](https://farmos.discourse.group/u/braught)\
**Post date:** [December 21, 2023, 3:08pm UTC](https://farmos.discourse.group/t/accessing-user-roles-in-progressively-decoupled-blocks/1815/5 "2023-12-21T15:08:09Z")

</div>

In the end I decided to have the front end probe for permissions instead of having Drupal embed them into the pages. The main reason for this was to support Vue component testing for elements of the FarmData2 application. Some of the components contain elements that need to be shown/hidden based on the user’s permissions. However, the component tests run outside of farmOS and thus would not have access to the permissions embedded by in the page by Drupal. This is a little less efficient because it requires some additional API calls on page load to check permissions, but doesn’t seem too bad. If necessary, performance can be improved in the future by caching the permissions the first time they are fetched.

So basically, I added a function that probes for each required permission by trying to create an asset/log/quantity/etc… and then checking if it was successful and cleaning up if necessary. The code for one such function is here for reference:

```JavaScript
/**
 * Check if the current user has permission to create a new crop (i.e. `taxonomy_term--plant_type`).
 *
 * @return {boolean} true if the current user has permission to create a new crop.
 *
 * @category Permissions
 */
export async function canCreatePlantType() {
  const farm = await getFarmOSInstance();
  const testCrop = farm.term.create({
    type: 'taxonomy_term--plant_type',
    attributes: {
      name: 'Permission Check',
    },
  });

  try {
    await farm.term.send(testCrop);
    await farm.term.delete('plant_type', testCrop.id);
    return true;
  } catch (err) {
    return false;
  }
}

```

---

<div class="post-metadata">

**Author:** ![braught](https://yyz2.discourse-cdn.com/free1/user_avatar/farmos.discourse.group/braught/32/294_2.png) [@braught](https://farmos.discourse.group/u/braught)\
**Post date:** [December 22, 2023, 3:27pm UTC](https://farmos.discourse.group/t/accessing-user-roles-in-progressively-decoupled-blocks/1815/6 "2023-12-22T15:27:47Z")

</div>

Following conversations during the last dev call (thanks @mstenta, @paul121) a better solution was to create a API endpiont in the farmdata2 module. This turned out to be relatively easy (despite my limited PHP/Drupal experience).

In the module’s `routing.yml` file:

```yml
farm.fd2_permissions_api:
  path: '/api/permissions'
  defaults:
    _controller: '\Drupal\farm_fd2\Controller\FD2_Controller::permissions'
    _title: 'permissions'
  methods: [GET]
  requirements:
    _user_is_logged_in: 'TRUE'

```

In the `Controller`:

```php
...
use Symfony\Component\HttpFoundation\JsonResponse;

...

public function permissions() {
    // List each permissions to be checked here.
    $perms = [
      'create land asset',
      'create plant asset',
      'create structure asset',
      'create terms in tray_size',
    ];

    foreach($perms as $perm) {
      $perm_name = str_replace(' ', '-', $perm);
      $result[$perm_name] = \Drupal::currentUser()->hasPermission($perm);
    }

    return new JsonResponse(['permissions' => $result, 'method' => 'GET', 'status'=> 200]);
  }

```

Can then use a `farmOS.js` instance to request the permissions:

```JavaScript
const resp = await farm.remote.request.get('http://farmos/api/permissions');
console.dir(resp.data.permissions);

```

Returns a nice JSON structure:

```JSON
{
      "permissions": {
            "create-land-asset": true,
            "create-plant-asset": true,
            "create-structure-asset": true,
            "create-terms-in-tray_size": true
      },
      "method": "GET",
      "status": 200
}

```

---

<div class="post-metadata">

**Author:** ![mstenta](https://yyz2.discourse-cdn.com/free1/user_avatar/farmos.discourse.group/mstenta/32/4_2.png) [@mstenta](https://farmos.discourse.group/u/mstenta)\
**Post date:** [December 22, 2023, 4:05pm UTC](https://farmos.discourse.group/t/accessing-user-roles-in-progressively-decoupled-blocks/1815/7 "2023-12-22T16:05:30Z")

</div>

@braught Nice!

This definitely seems like it’s going to be a common need, if we expect more decouple JS app development to happen.

For apps that connect via OAuth, scopes may be able to provide similar possibilities, but for cases like yours (where it is using the existing cookie authentication) it makes sense that apps will need to be able to ask what permissions are available in order to render UIs accordingly. This might be worth a dedicated forum topic to discuss the possibilities, as well as implications (security and other).
