Merge pull request #831 from patriot1burke/master
principal token attribute mapping
This commit is contained in:
commit
380f5dfb4e
13 changed files with 188 additions and 8 deletions
|
@ -18,7 +18,7 @@ import org.codehaus.jackson.annotate.JsonPropertyOrder;
|
|||
"allow-any-hostname", "disable-trust-manager", "truststore", "truststore-password",
|
||||
"client-keystore", "client-keystore-password", "client-key-password",
|
||||
"auth-server-url-for-backend-requests", "always-refresh-token",
|
||||
"register-node-at-startup", "register-node-period", "token-store"
|
||||
"register-node-at-startup", "register-node-period", "token-store", "principal-attribute"
|
||||
})
|
||||
public class AdapterConfig extends BaseAdapterConfig {
|
||||
|
||||
|
@ -48,6 +48,8 @@ public class AdapterConfig extends BaseAdapterConfig {
|
|||
protected int registerNodePeriod = -1;
|
||||
@JsonProperty("token-store")
|
||||
protected String tokenStore;
|
||||
@JsonProperty("principal-attribute")
|
||||
protected String principalAttribute;
|
||||
|
||||
public boolean isAllowAnyHostname() {
|
||||
return allowAnyHostname;
|
||||
|
@ -152,4 +154,12 @@ public class AdapterConfig extends BaseAdapterConfig {
|
|||
public void setTokenStore(String tokenStore) {
|
||||
this.tokenStore = tokenStore;
|
||||
}
|
||||
|
||||
public String getPrincipalAttribute() {
|
||||
return principalAttribute;
|
||||
}
|
||||
|
||||
public void setPrincipalAttribute(String principalAttribute) {
|
||||
this.principalAttribute = principalAttribute;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -336,6 +336,15 @@
|
|||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>principal-attribute</term>
|
||||
<listitem>
|
||||
<para>
|
||||
OpenID Connection ID Token attribute to populate the UserPrincipal name with. If token attribute is null, defaults to <literal>sub</literal>
|
||||
Possible values are <literal>sub</literal>, <literal>preferred_username</literal>, <literal>email</literal>, <literal>name</literal>, <literal>nickname</literal>, <literal>given_name</literal>, <literal>family_name</literal>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</para>
|
||||
</section>
|
||||
|
|
23
integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterUtils.java
Normal file → Executable file
23
integration/adapter-core/src/main/java/org/keycloak/adapters/AdapterUtils.java
Normal file → Executable file
|
@ -59,4 +59,27 @@ public class AdapterUtils {
|
|||
}
|
||||
return roles;
|
||||
}
|
||||
|
||||
public static String getPrincipalName(KeycloakDeployment deployment, AccessToken token) {
|
||||
String attr = "sub";
|
||||
if (deployment.getPrincipalAttribute() != null) attr = deployment.getPrincipalAttribute();
|
||||
String name = null;
|
||||
if ("sub".equals(attr)) {
|
||||
name = token.getSubject();
|
||||
} else if ("email".equals(attr)) {
|
||||
name = token.getEmail();
|
||||
} else if ("preferred_username".equals(attr)) {
|
||||
name = token.getPreferredUsername();
|
||||
} else if ("name".equals(attr)) {
|
||||
name = token.getName();
|
||||
} else if ("given_name".equals(attr)) {
|
||||
name = token.getGivenName();
|
||||
} else if ("family_name".equals(attr)) {
|
||||
name = token.getFamilyName();
|
||||
} else if ("nickname".equals(attr)) {
|
||||
name = token.getNickName();
|
||||
}
|
||||
if (name == null) name = token.getSubject();
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ public class CookieTokenStore {
|
|||
|
||||
log.debug("Token Verification succeeded!");
|
||||
RefreshableKeycloakSecurityContext secContext = new RefreshableKeycloakSecurityContext(deployment, tokenStore, accessTokenString, accessToken, idTokenString, idToken, refreshTokenString);
|
||||
return new KeycloakPrincipal<RefreshableKeycloakSecurityContext>(accessToken.getSubject(), secContext);
|
||||
return new KeycloakPrincipal<RefreshableKeycloakSecurityContext>(AdapterUtils.getPrincipalName(deployment, accessToken), secContext);
|
||||
} catch (VerificationException ve) {
|
||||
log.warn("Failed verify token", ve);
|
||||
return null;
|
||||
|
|
|
@ -34,6 +34,7 @@ public class KeycloakDeployment {
|
|||
protected String accountUrl;
|
||||
protected String registerNodeUrl;
|
||||
protected String unregisterNodeUrl;
|
||||
protected String principalAttribute = "sub";
|
||||
|
||||
protected String resourceName;
|
||||
protected boolean bearerOnly;
|
||||
|
@ -333,4 +334,12 @@ public class KeycloakDeployment {
|
|||
public void setRegisterNodePeriod(int registerNodePeriod) {
|
||||
this.registerNodePeriod = registerNodePeriod;
|
||||
}
|
||||
|
||||
public String getPrincipalAttribute() {
|
||||
return principalAttribute;
|
||||
}
|
||||
|
||||
public void setPrincipalAttribute(String principalAttribute) {
|
||||
this.principalAttribute = principalAttribute;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ public class KeycloakDeploymentBuilder {
|
|||
} else {
|
||||
deployment.setTokenStore(TokenStore.SESSION);
|
||||
}
|
||||
if (adapterConfig.getPrincipalAttribute() != null) deployment.setPrincipalAttribute(adapterConfig.getPrincipalAttribute());
|
||||
deployment.setResourceCredentials(adapterConfig.getCredentials());
|
||||
deployment.setPublicClient(adapterConfig.isPublicClient());
|
||||
deployment.setUseResourceRoleMappings(adapterConfig.isUseResourceRoleMappings());
|
||||
|
|
|
@ -106,7 +106,7 @@ public abstract class RequestAuthenticator {
|
|||
|
||||
protected void completeAuthentication(OAuthRequestAuthenticator oauth) {
|
||||
RefreshableKeycloakSecurityContext session = new RefreshableKeycloakSecurityContext(deployment, tokenStore, oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), oauth.getRefreshToken());
|
||||
final KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = new KeycloakPrincipal<RefreshableKeycloakSecurityContext>(oauth.getToken().getSubject(), session);
|
||||
final KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = new KeycloakPrincipal<RefreshableKeycloakSecurityContext>(AdapterUtils.getPrincipalName(deployment, oauth.getToken()), session);
|
||||
completeOAuthAuthentication(principal);
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ public abstract class RequestAuthenticator {
|
|||
|
||||
protected void completeAuthentication(BearerTokenRequestAuthenticator bearer) {
|
||||
RefreshableKeycloakSecurityContext session = new RefreshableKeycloakSecurityContext(deployment, null, bearer.getTokenString(), bearer.getToken(), null, null, null);
|
||||
final KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = new KeycloakPrincipal<RefreshableKeycloakSecurityContext>(bearer.getToken().getSubject(), session);
|
||||
final KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = new KeycloakPrincipal<RefreshableKeycloakSecurityContext>(AdapterUtils.getPrincipalName(deployment, bearer.getToken()), session);
|
||||
completeBearerAuthentication(principal);
|
||||
}
|
||||
|
||||
|
|
|
@ -130,6 +130,42 @@ public class SharedAttributeDefinitons {
|
|||
.setAllowExpression(true)
|
||||
.setDefaultValue(new ModelNode(false))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition AUTH_SERVER_URL_FOR_BACKEND_REQUESTS =
|
||||
new SimpleAttributeDefinitionBuilder("auth-server-url-for-backend-requests", ModelType.STRING, true)
|
||||
.setXmlName("auth-server-url-for-backend-requests")
|
||||
.setAllowExpression(true)
|
||||
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition ALWAYS_REFRESH_TOKEN =
|
||||
new SimpleAttributeDefinitionBuilder("always-refresh-token", ModelType.BOOLEAN, true)
|
||||
.setXmlName("always-refresh-token")
|
||||
.setAllowExpression(true)
|
||||
.setDefaultValue(new ModelNode(false))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition REGISTER_NODE_AT_STARTUP =
|
||||
new SimpleAttributeDefinitionBuilder("register-node-at-startup", ModelType.BOOLEAN, true)
|
||||
.setXmlName("register-node-at-startup")
|
||||
.setAllowExpression(true)
|
||||
.setDefaultValue(new ModelNode(false))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition REGISTER_NODE_PERIOD =
|
||||
new SimpleAttributeDefinitionBuilder("register-node-period", ModelType.INT, true)
|
||||
.setXmlName("register-node-period")
|
||||
.setAllowExpression(true)
|
||||
.setValidator(new IntRangeValidator(-1, true))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition TOKEN_STORE =
|
||||
new SimpleAttributeDefinitionBuilder("token-store", ModelType.STRING, true)
|
||||
.setXmlName("token-store")
|
||||
.setAllowExpression(true)
|
||||
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition PRINCIPAL_ATTRIBUTE =
|
||||
new SimpleAttributeDefinitionBuilder("principal-attribute", ModelType.STRING, true)
|
||||
.setXmlName("principal-attribute")
|
||||
.setAllowExpression(true)
|
||||
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
|
||||
.build();
|
||||
|
||||
|
||||
protected static final List<SimpleAttributeDefinition> ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
|
||||
|
@ -150,6 +186,12 @@ public class SharedAttributeDefinitons {
|
|||
ATTRIBUTES.add(CORS_ALLOWED_HEADERS);
|
||||
ATTRIBUTES.add(CORS_ALLOWED_METHODS);
|
||||
ATTRIBUTES.add(EXPOSE_TOKEN);
|
||||
ATTRIBUTES.add(AUTH_SERVER_URL_FOR_BACKEND_REQUESTS);
|
||||
ATTRIBUTES.add(ALWAYS_REFRESH_TOKEN);
|
||||
ATTRIBUTES.add(REGISTER_NODE_AT_STARTUP);
|
||||
ATTRIBUTES.add(REGISTER_NODE_PERIOD);
|
||||
ATTRIBUTES.add(TOKEN_STORE);
|
||||
ATTRIBUTES.add(PRINCIPAL_ATTRIBUTE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,6 +23,12 @@ keycloak.realm.cors-max-age=CORS max-age header
|
|||
keycloak.realm.cors-allowed-headers=CORS allowed headers
|
||||
keycloak.realm.cors-allowed-methods=CORS allowed methods
|
||||
keycloak.realm.expose-token=Enable secure URL that exposes access token
|
||||
keycloak.realm.auth-server-url-for-backend-requests=URL to use to make background calls to auth server
|
||||
keycloak.realm.always-refresh-token=Refresh token on every single web request
|
||||
keycloak.realm.register-node-at-startup=Cluster setting
|
||||
keycloak.realm.register-node-period=how often to re-register node
|
||||
keycloak.realm.token-store=cookie or session storage for auth session data
|
||||
keycloak.realm.principal-attribute=token attribute to use to set Principal name
|
||||
|
||||
keycloak.secure-deployment=A deployment secured by Keycloak
|
||||
keycloak.secure-deployment.add=Add a deployment to be secured by Keycloak
|
||||
|
@ -49,6 +55,12 @@ keycloak.secure-deployment.cors-max-age=CORS max-age header
|
|||
keycloak.secure-deployment.cors-allowed-headers=CORS allowed headers
|
||||
keycloak.secure-deployment.cors-allowed-methods=CORS allowed methods
|
||||
keycloak.secure-deployment.expose-token=Enable secure URL that exposes access token
|
||||
keycloak.secure-deployment.auth-server-url-for-backend-requests=URL to use to make background calls to auth server
|
||||
keycloak.secure-deployment.always-refresh-token=Refresh token on every single web request
|
||||
keycloak.secure-deployment.register-node-at-startup=Cluster setting
|
||||
keycloak.secure-deployment.register-node-period=how often to re-register node
|
||||
keycloak.secure-deployment.token-store=cookie or session storage for auth session data
|
||||
keycloak.secure-deployment.principal-attribute=token attribute to use to set Principal name
|
||||
|
||||
keycloak.secure-deployment.credential=Credential value
|
||||
|
||||
|
|
|
@ -83,6 +83,12 @@
|
|||
<xs:element name="cors-allowed-headers" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="cors-allowed-methods" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="expose-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="auth-server-url-for-backend-requests" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="always-refresh-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="register-node-at-startup" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="register-node-period" type="xs:integer" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="token-store" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="principal-attribute" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:complexType>
|
||||
|
||||
</xs:schema>
|
||||
|
|
|
@ -126,10 +126,47 @@ public class SharedAttributeDefinitons {
|
|||
.build();
|
||||
protected static final SimpleAttributeDefinition EXPOSE_TOKEN =
|
||||
new SimpleAttributeDefinitionBuilder("expose-token", ModelType.BOOLEAN, true)
|
||||
.setXmlName("expose-token")
|
||||
.setAllowExpression(true)
|
||||
.setDefaultValue(new ModelNode(false))
|
||||
.build();
|
||||
.setXmlName("expose-token")
|
||||
.setAllowExpression(true)
|
||||
.setDefaultValue(new ModelNode(false))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition AUTH_SERVER_URL_FOR_BACKEND_REQUESTS =
|
||||
new SimpleAttributeDefinitionBuilder("auth-server-url-for-backend-requests", ModelType.STRING, true)
|
||||
.setXmlName("auth-server-url-for-backend-requests")
|
||||
.setAllowExpression(true)
|
||||
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition ALWAYS_REFRESH_TOKEN =
|
||||
new SimpleAttributeDefinitionBuilder("always-refresh-token", ModelType.BOOLEAN, true)
|
||||
.setXmlName("always-refresh-token")
|
||||
.setAllowExpression(true)
|
||||
.setDefaultValue(new ModelNode(false))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition REGISTER_NODE_AT_STARTUP =
|
||||
new SimpleAttributeDefinitionBuilder("register-node-at-startup", ModelType.BOOLEAN, true)
|
||||
.setXmlName("register-node-at-startup")
|
||||
.setAllowExpression(true)
|
||||
.setDefaultValue(new ModelNode(false))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition REGISTER_NODE_PERIOD =
|
||||
new SimpleAttributeDefinitionBuilder("register-node-period", ModelType.INT, true)
|
||||
.setXmlName("register-node-period")
|
||||
.setAllowExpression(true)
|
||||
.setValidator(new IntRangeValidator(-1, true))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition TOKEN_STORE =
|
||||
new SimpleAttributeDefinitionBuilder("token-store", ModelType.STRING, true)
|
||||
.setXmlName("token-store")
|
||||
.setAllowExpression(true)
|
||||
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
|
||||
.build();
|
||||
protected static final SimpleAttributeDefinition PRINCIPAL_ATTRIBUTE =
|
||||
new SimpleAttributeDefinitionBuilder("principal-attribute", ModelType.STRING, true)
|
||||
.setXmlName("principal-attribute")
|
||||
.setAllowExpression(true)
|
||||
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
|
||||
.build();
|
||||
|
||||
|
||||
|
||||
protected static final List<SimpleAttributeDefinition> ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
|
||||
|
@ -150,6 +187,12 @@ public class SharedAttributeDefinitons {
|
|||
ATTRIBUTES.add(CORS_ALLOWED_HEADERS);
|
||||
ATTRIBUTES.add(CORS_ALLOWED_METHODS);
|
||||
ATTRIBUTES.add(EXPOSE_TOKEN);
|
||||
ATTRIBUTES.add(AUTH_SERVER_URL_FOR_BACKEND_REQUESTS);
|
||||
ATTRIBUTES.add(ALWAYS_REFRESH_TOKEN);
|
||||
ATTRIBUTES.add(REGISTER_NODE_AT_STARTUP);
|
||||
ATTRIBUTES.add(REGISTER_NODE_PERIOD);
|
||||
ATTRIBUTES.add(TOKEN_STORE);
|
||||
ATTRIBUTES.add(PRINCIPAL_ATTRIBUTE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,6 +23,13 @@ keycloak.realm.cors-max-age=CORS max-age header
|
|||
keycloak.realm.cors-allowed-headers=CORS allowed headers
|
||||
keycloak.realm.cors-allowed-methods=CORS allowed methods
|
||||
keycloak.realm.expose-token=Enable secure URL that exposes access token
|
||||
keycloak.realm.auth-server-url-for-backend-requests=URL to use to make background calls to auth server
|
||||
keycloak.realm.always-refresh-token=Refresh token on every single web request
|
||||
keycloak.realm.register-node-at-startup=Cluster setting
|
||||
keycloak.realm.register-node-period=how often to re-register node
|
||||
keycloak.realm.token-store=cookie or session storage for auth session data
|
||||
keycloak.realm.principal-attribute=token attribute to use to set Principal name
|
||||
|
||||
|
||||
keycloak.secure-deployment=A deployment secured by Keycloak
|
||||
keycloak.secure-deployment.add=Add a deployment to be secured by Keycloak
|
||||
|
@ -49,6 +56,12 @@ keycloak.secure-deployment.cors-max-age=CORS max-age header
|
|||
keycloak.secure-deployment.cors-allowed-headers=CORS allowed headers
|
||||
keycloak.secure-deployment.cors-allowed-methods=CORS allowed methods
|
||||
keycloak.secure-deployment.expose-token=Enable secure URL that exposes access token
|
||||
keycloak.secure-deployment.auth-server-url-for-backend-requests=URL to use to make background calls to auth server
|
||||
keycloak.secure-deployment.always-refresh-token=Refresh token on every single web request
|
||||
keycloak.secure-deployment.register-node-at-startup=Cluster setting
|
||||
keycloak.secure-deployment.register-node-period=how often to re-register node
|
||||
keycloak.secure-deployment.token-store=cookie or session storage for auth session data
|
||||
keycloak.secure-deployment.principal-attribute=token attribute to use to set Principal name
|
||||
|
||||
keycloak.secure-deployment.credential=Credential value
|
||||
|
||||
|
|
|
@ -42,6 +42,12 @@
|
|||
<xs:element name="ssl-required" type="xs:string" minOccurs="0" maxOccurs="1" />
|
||||
<xs:element name="cors-allowed-methods" type="xs:string" minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element name="realm-public-key" type="xs:string" minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element name="auth-server-url-for-backend-requests" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="always-refresh-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="register-node-at-startup" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="register-node-period" type="xs:integer" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="token-store" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="principal-attribute" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:all>
|
||||
<xs:attribute name="name" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
|
@ -74,6 +80,12 @@
|
|||
<xs:element name="ssl-required" type="xs:string" minOccurs="0" maxOccurs="1" />
|
||||
<xs:element name="realm-public-key" type="xs:string" minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element name="credential" type="credential-type" minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element name="auth-server-url-for-backend-requests" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="always-refresh-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="register-node-at-startup" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="register-node-period" type="xs:integer" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="token-store" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="principal-attribute" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
||||
</xs:all>
|
||||
<xs:attribute name="name" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
|
|
Loading…
Reference in a new issue