/*
* Copyright 2015 Red Hat, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.vertx.ext.auth.oauth2.rbac.impl;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.oauth2.AccessToken;
import io.vertx.ext.auth.oauth2.OAuth2Options;
import io.vertx.ext.auth.oauth2.OAuth2RBAC;
import java.util.Collections;
public class KeycloakRBACImpl implements OAuth2RBAC {
private static final JsonObject EMPTY_JSON = new JsonObject(Collections.EMPTY_MAP);
private static final JsonArray EMPTY_ARRAY = new JsonArray(Collections.EMPTY_LIST);
private final OAuth2Options options;
public KeycloakRBACImpl(OAuth2Options options) {
if (options == null) {
throw new IllegalArgumentException("options is a required argument");
}
this.options = options;
}
Determine if this token has an associated role.
This method is only functional if the token is constructed
with a `clientId` parameter.
The parameter matches a role specification using the following rules:
- If the name contains no colons, then the name is taken as the entire
name of a role within the current application, as specified via
`clientId`.
- If the name starts with the literal `realm:`, the subsequent portion
is taken as the name of a _realm-level_ role.
- Otherwise, the name is split at the colon, with the first portion being
taken as the name of an arbitrary application, and the subsequent portion
as the name of a role with that app.
Params: - authority – The role name specifier.
- handler – `true` if this token has the specified role, otherwise `false`.
/**
* Determine if this token has an associated role.
* <p>
* This method is only functional if the token is constructed
* with a `clientId` parameter.
* <p>
* The parameter matches a role specification using the following rules:
* <p>
* - If the name contains no colons, then the name is taken as the entire
* name of a role within the current application, as specified via
* `clientId`.
* - If the name starts with the literal `realm:`, the subsequent portion
* is taken as the name of a _realm-level_ role.
* - Otherwise, the name is split at the colon, with the first portion being
* taken as the name of an arbitrary application, and the subsequent portion
* as the name of a role with that app.
*
* @param authority The role name specifier.
* @param handler `true` if this token has the specified role, otherwise `false`.
*/
@Override
public void isAuthorized(AccessToken user, String authority, Handler<AsyncResult<Boolean>> handler) {
JsonObject accessToken = user.accessToken();
if (accessToken == null) {
handler.handle(Future.failedFuture("AccessToken is not a valid JWT"));
return;
}
String[] parts = authority.split(":");
if (parts.length == 1) {
handler.handle(Future.succeededFuture(hasApplicationRole(accessToken, options.getClientID(), parts[0])));
return;
}
if ("realm".equals(parts[0])) {
handler.handle(Future.succeededFuture(hasRealmRole(accessToken, parts[1])));
return;
}
handler.handle(Future.succeededFuture(hasApplicationRole(accessToken, parts[0], parts[1])));
}
Determine if this token has an associated specific application role.
Even if `clientId` is not set, this method may be used to explicitly test
roles for any given application.
Params: - appName – The identifier of the application to test.
- roleName – The name of the role within that application to test.
Returns: `true` if this token has the specified role, otherwise `false`.
/**
* Determine if this token has an associated specific application role.
* <p>
* Even if `clientId` is not set, this method may be used to explicitly test
* roles for any given application.
*
* @param appName The identifier of the application to test.
* @param roleName The name of the role within that application to test.
* @return `true` if this token has the specified role, otherwise `false`.
*/
private boolean hasApplicationRole(JsonObject accessToken, String appName, String roleName) {
if (accessToken == null) {
return false;
}
JsonObject appRoles = accessToken
.getJsonObject("resource_access", EMPTY_JSON)
.getJsonObject(appName);
if (appRoles == null) {
return false;
}
return appRoles
.getJsonArray("roles", EMPTY_ARRAY)
.contains(roleName);
}
Determine if this token has an associated specific realm-level role.
Even if `clientId` is not set, this method may be used to explicitly test
roles for the realm.
Params: - roleName – The name of the role within that application to test.
Returns: `true` if this token has the specified role, otherwise `false`.
/**
* Determine if this token has an associated specific realm-level role.
* <p>
* Even if `clientId` is not set, this method may be used to explicitly test
* roles for the realm.
*
* @param roleName The name of the role within that application to test.
* @return `true` if this token has the specified role, otherwise `false`.
*/
private boolean hasRealmRole(JsonObject accessToken, String roleName) {
if (accessToken == null) {
return false;
}
return accessToken
.getJsonObject("realm_access", EMPTY_JSON)
.getJsonArray("roles", EMPTY_ARRAY)
.contains(roleName);
}
}