微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 Keycloak Script Mapper 聚合声明中角色的属性

如何解决使用 Keycloak Script Mapper 聚合声明中角色的属性

我们有一个 Keycloak 脚本映射器来将角色的属性添加到 ID 令牌。目标是聚合角色属性中可用的值。映射器看起来像这样:

/**
 * Merge with concatenation for the values of attributes obtained
 * from the token with the attributes obtained from the roles. If for
 * example a group and a role have the same attribute key,the values
 * for that key from the group and role will be concatenated.
 *
 * KNown limitations:
 * When only roles have a certain attribute,and not a group,the 
 * mapper only uses the first role it can find. Bug was difficult to
 * fix because state in the variable currentClaims seems to be
 * persisted over multiple calls.
 * Workaround: Also add this specific attribute to a group with a
 * dummy value.
 * 
 * NOTE: there is no role attribute mapper out-of-the-Box in 
 * Keycloak.
 * 
 * Available variables in script: 
 * user - the current user
 * realm - the current realm
 * token - the current token
 * userSession - the current userSession
 * keycloakSession - the current keycloakSession
 * 
 * Documentation on available variables:
 * https://stackoverflow.com/a/52984849
 */
 
var currentClaims = {};
token.getotherClaims().forEach(function(k,v) {
  currentClaims[k] = v;
});

function isMultiValued(v) {
  // From experience,multivalued attribute values are sometimes 
  // Arrays and sometimes Objects. Thus look for negative case:
  // anything other than a string is multivalued.
  return !(typeof v === 'string' || v instanceof String);
}

function addToList(l,values) {
  for each(var v in values) {
    l.add(v);
  }
  return l;
}

function toStringArray(arr) {
  return Java.to(arr,"java.lang.String[]");
}

user.getRealmRoleMappings().forEach(function(roleModel) {
  roleModel.getAttributes().forEach(function(k,v) {
    var currentValue = currentClaims[k];
    if (k in currentClaims) {
      if (!isMultiValued(currentValue)) {
        v = toStringArray([currentValue].concat(v));
      } else {
        v = addToList(currentValue,v);
      }
    }
    currentClaims[k] = v; // <= to also aggregate over roles!
    token.setotherClaims(k,v); 
  }); 
});

添加的带有 currentClaims[k] = v 的部分是为了聚合角色中可用的值,因此如果两个角色包含相同的属性,它们的值也会聚合。

例如,如果我们有一个用户具有角色 a 和 b,分别具有值为 1 和 2 的属性 foo,我们期望 ID 令牌包含对值为 1 和 2 的 foo 的声明。

user:
  role a foo -> 1
  role b foo -> 2
expected ID token:
  foo -> [1,2]

但是使用当前代码currentClaims 变量似乎在多次调用函数时保持状态。每次检查 ID 令牌时,都会将 2 的更多值添加到令牌中,导致每次检索令牌时都会添加越来越多的 [1,2,...,2] 声明,例如 2。我尝试将整个调用包装在一个函数中,以便可能在调用之间丢弃状态,但无济于事。结果如下:

aggregation

为什么状态会保存在多个调用中?有没有办法同时聚合角色属性的值?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。