# Using Painless stored script in role mapping

**URL:** <https://discuss.elastic.co/t/using-painless-stored-script-in-role-mapping/362530>\
**Category:** Elasticsearch\
**Tags:** painless\
**Created:** [July 4, 2024, 8:51am UTC](https://discuss.elastic.co/t/using-painless-stored-script-in-role-mapping/362530 "2024-07-04T08:51:47Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![jonaslb](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/jonaslb/32/135794_2.png) [@jonaslb](https://discuss.elastic.co/u/jonaslb)\
**Post date:** [July 4, 2024, 8:51am UTC](https://discuss.elastic.co/t/using-painless-stored-script-in-role-mapping/362530/1 "2024-07-04T08:51:47Z")

</div>

The Kibana interface suggests that it should be possible to use a painless stored script to perform role mapping:

 ![role-mapping-stored-script-kibana](https://us1.discourse-cdn.com/elastic/original/3X/c/5/c58f0eedd69e258853f793c58e6034cdf5fc1790.png)

I would like to use this feature to put logic around which roles a given SSO realm is allowed to assign (so the examplemoustache template `{{#json}}groups{{/json}}`, which is used by default for elastic cloud logon, would be fine except i want to have a whitelist and prefix-based filter of the groups).

I have not been able to find any documentation about this feature, however. Does anyone know (a) which parameters are available in a painless script in the role mapping context and (b) what is the script supposed to return in this context?

---

<div class="post-metadata">

**Author:** ![TimV](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/timv/32/13162_2.png) [@TimV](https://discuss.elastic.co/u/TimV)\
**Post date:** [July 5, 2024, 7:28am UTC](https://discuss.elastic.co/t/using-painless-stored-script-in-role-mapping/362530/2 "2024-07-05T07:28:57Z")

</div>

Painless scripts are not officially supported (not all stored scripts are painless - you can have stored mustache scripts as well).

It can be made to work with painless, but there's no docs because it's not officially supported.

---

<div class="post-metadata">

**Author:** ![jonaslb](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/jonaslb/32/135794_2.png) [@jonaslb](https://discuss.elastic.co/u/jonaslb)\
**Post date:** [July 5, 2024, 8:45am UTC](https://discuss.elastic.co/t/using-painless-stored-script-in-role-mapping/362530/3 "2024-07-05T08:45:31Z")

</div>

Thanks Tim. Yes, the Kibana interface does say "painless or mustache script", but mustache has very limited "scriptability" - they pride themselves on being "logic-less templates". But I will accept that Painless is not officially supported for this purpose and so avoid it.

An alternative approach to this is to perform prefixing on the roles based on the realm using a Mustache template like `[{{#groups}}"myprefix-{{.}}",{{/groups}}]`. Unfortunately, it only _almost_ works, because this isn't valid JSON: There's a trailing comma. Perhaps that's for another topic, but do you know of a way to handle this with Mustache in Elasticsearch?

---

<div class="post-metadata">

**Author:** ![TimV](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/timv/32/13162_2.png) [@TimV](https://discuss.elastic.co/u/TimV)\
**Post date:** [July 5, 2024, 8:59am UTC](https://discuss.elastic.co/t/using-painless-stored-script-in-role-mapping/362530/4 "2024-07-05T08:59:55Z")

</div>

Can you provide a concrete example of what you're trying to do?

Mustache is limited, but I'm happy to try and help if you can give me an example of what you need.

(Also, you really shouldn't be modifying the `elastic-cloud-sso-kibana-do-not-change` mapping)

---

<div class="post-metadata">

**Author:** ![jonaslb](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/jonaslb/32/135794_2.png) [@jonaslb](https://discuss.elastic.co/u/jonaslb)\
**Post date:** [July 5, 2024, 9:32am UTC](https://discuss.elastic.co/t/using-painless-stored-script-in-role-mapping/362530/5 "2024-07-05T09:32:08Z")

</div>

Right, the screenshot only shows the default role mapping because I was exploring the UI, i have no intention of editing it 🙂

We want to integrate an id-provider from a third party. We actually have quite a lot of different roles for users from that third party, and we're fine (even thankful) to let them decide for themselves who should have which roles, so that we don't have to manage it for them. So far, a role mapping similar to that used in the default mapping (`{{#tojson}}groups{{/tojson}}`) might be fine. However, we don't want to let the third party set _any_ group. For example, `superuser` would be out of bounds. So we're just trying to "role-namespace" their realm somehow. It could be by a whitelist or by simply prefixing roles we get from them.

Edit: The workaround is to create an equal number of role mappings that we have roles for them, using the "standard" role mapping method. I have considered that, and we will do it as a fallback. I just thought it would be simpler to have a single role mapping that fulfilled this requirement instead of a lot of them.

---

<div class="post-metadata">

**Author:** ![jonaslb](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/jonaslb/32/135794_2.png) [@jonaslb](https://discuss.elastic.co/u/jonaslb)\
**Post date:** [July 9, 2024, 9:03am UTC](https://discuss.elastic.co/t/using-painless-stored-script-in-role-mapping/362530/6 "2024-07-09T09:03:40Z")

</div>

I think that `claim_patterns` are the closest solution to getting "anything but with whitelist/prefix" right now, it's just impractical that any hypothetical changes would require a reboot.

For example,

```yaml
xpack.security.authc.realms.oidc.oidc1:
  order: 2
  rp: {...}
  op: {...}
  claims:
    groups: groups
  claim_patterns:
    groups: "^(group1|group2|group3|prefix-.*)$"

```

Haven't tested it yet though, but I assume it should work.

---

<div class="post-metadata">

**Author:** ![TimV](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/timv/32/13162_2.png) [@TimV](https://discuss.elastic.co/u/TimV)\
**Post date:** [July 10, 2024, 2:28am UTC](https://discuss.elastic.co/t/using-painless-stored-script-in-role-mapping/362530/7 "2024-07-10T02:28:52Z")

</div>

I'll think about other ways to solve this, but I think you could probably get what want by making the `rules` reject mappings for groups that fall outside of your pattern.

This assumes you can tell your 3rd party IdP that "group" claims must being with a prefix that matches their organization. That is you force the prefixing to happen on the id provider side, not in the role mapping.  
I don't know whether that's true for your case.

If it is, then you can do something that says:

- For the given realm
- Map the "group" names to "role" names
- Unless there is a group that has an invalid pattern

That would be

```auto
{
  "role_templates": [
    {
      "template": { "source": "{{#tojson}}groups{{/tojson}}" }, 
      "format" : "json" 
    }
  ],
  "rules": {
    "all": [
      {
        "field" : { "realm.name" : "oidc-org1" } 
      },
      {
        "except" : {
          "field": { "groups": "/~(org1-.*)/" }
        }
      }
    ]
  },
  "enabled": true
}

```

The key bit is the `except`.

The idea here is that the `rules` will match if `realm.name = "oidc-org1"` **_except_** when `groups matches "/~(org1-.*)/"`

So, the mapping is skipped in cases where one or more of the groups match the regular expression `~(org1-.*)`

That's a Lucene style regular expression that say "match anything except things that start with `org1-`

So, if there is a group that starts with something other than `org1-` then the role mapping will be skipped.

---

<div class="post-metadata">

**Author:** ![TimV](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/timv/32/13162_2.png) [@TimV](https://discuss.elastic.co/u/TimV)\
**Post date:** [July 10, 2024, 2:32am UTC](https://discuss.elastic.co/t/using-painless-stored-script-in-role-mapping/362530/8 "2024-07-10T02:32:54Z")

</div>

If you're willing to write and maintain a Java plugin then this can also be done with a custom realm, but that feels like overkill for what you're asking for.

Also, assuming you're on a paid license (not a trial) then can you raise a support ticket and ask them to submit an enhancement request to have a better solution for this (which might be to officially support painless scripts for role mappings) ?  
You can ask the support engineer to talk to me if they want more details about what to put in the enhancement request.
