parent
c51060ae34
commit
6112b25648
50 changed files with 1091 additions and 304 deletions
|
@ -40,6 +40,9 @@ import java.util.Map;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class JsonWebToken implements Serializable, Token {
|
public class JsonWebToken implements Serializable, Token {
|
||||||
|
public static final String AZP = "azp";
|
||||||
|
public static final String SUBJECT = "sub";
|
||||||
|
|
||||||
@JsonProperty("jti")
|
@JsonProperty("jti")
|
||||||
protected String id;
|
protected String id;
|
||||||
|
|
||||||
|
@ -53,11 +56,11 @@ public class JsonWebToken implements Serializable, Token {
|
||||||
@JsonSerialize(using = StringOrArraySerializer.class)
|
@JsonSerialize(using = StringOrArraySerializer.class)
|
||||||
@JsonDeserialize(using = StringOrArrayDeserializer.class)
|
@JsonDeserialize(using = StringOrArrayDeserializer.class)
|
||||||
protected String[] audience;
|
protected String[] audience;
|
||||||
@JsonProperty("sub")
|
@JsonProperty(SUBJECT)
|
||||||
protected String subject;
|
protected String subject;
|
||||||
@JsonProperty("typ")
|
@JsonProperty("typ")
|
||||||
protected String type;
|
protected String type;
|
||||||
@JsonProperty("azp")
|
@JsonProperty(AZP)
|
||||||
public String issuedFor;
|
public String issuedFor;
|
||||||
protected Map<String, Object> otherClaims = new HashMap<>();
|
protected Map<String, Object> otherClaims = new HashMap<>();
|
||||||
|
|
||||||
|
@ -184,7 +187,7 @@ public class JsonWebToken implements Serializable, Token {
|
||||||
this.iat = iat;
|
this.iat = iat;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated int will overflow with values after 2038. Use {@link #iat(Long)} ()} instead.
|
* @deprecated int will overflow with values after 2038. Use {@link #iat(Long)} ()} instead.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -3096,6 +3096,10 @@
|
||||||
"label": "Add to userinfo",
|
"label": "Add to userinfo",
|
||||||
"tooltip": "Should the claim be added to the userinfo?"
|
"tooltip": "Should the claim be added to the userinfo?"
|
||||||
},
|
},
|
||||||
|
"includeInIntrospection": {
|
||||||
|
"label": "Add to token introspection",
|
||||||
|
"tooltip": "Should the claim be added to the token introspection?"
|
||||||
|
},
|
||||||
"sectorIdentifierUri": {
|
"sectorIdentifierUri": {
|
||||||
"label": "Sector Identifier URI",
|
"label": "Sector Identifier URI",
|
||||||
"tooltip": "Providers that use pairwise sub values and support Dynamic Client Registration SHOULD use the sector_identifier_uri parameter. It provides a way for a group of websites under common administrative control to have consistent pairwise sub values independent of the individual domain names. It also provides a way for Clients to change redirect_uri domains without having to reregister all their users."
|
"tooltip": "Providers that use pairwise sub values and support Dynamic Client Registration SHOULD use the sector_identifier_uri parameter. It provides a way for a group of websites under common administrative control to have consistent pairwise sub values independent of the individual domain names. It also provides a way for Clients to change redirect_uri domains without having to reregister all their users."
|
||||||
|
|
|
@ -315,6 +315,7 @@ public class UserSessionAdapter implements UserSessionModel {
|
||||||
update(task);
|
update(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public SessionPersistenceState getPersistenceState() {
|
public SessionPersistenceState getPersistenceState() {
|
||||||
return persistenceState;
|
return persistenceState;
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,6 +232,11 @@ public class PersistentUserSessionAdapter implements OfflineUserSessionModel {
|
||||||
return State.valueOf(state);
|
return State.valueOf(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SessionPersistenceState getPersistenceState() {
|
||||||
|
return SessionPersistenceState.PERSISTENT;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setState(State state) {
|
public void setState(State state) {
|
||||||
String stateStr = state==null ? null : state.toString();
|
String stateStr = state==null ? null : state.toString();
|
||||||
|
|
|
@ -23,7 +23,6 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ModelIllegalStateException;
|
import org.keycloak.models.ModelIllegalStateException;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.models.map.common.TimeAdapter;
|
import org.keycloak.models.map.common.TimeAdapter;
|
||||||
|
|
||||||
|
@ -289,4 +288,9 @@ public class MapUserSessionAdapter extends AbstractUserSessionModel {
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return getId().hashCode();
|
return getId().hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SessionPersistenceState getPersistenceState() {
|
||||||
|
return entity.getPersistenceState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ public interface UserSessionModel {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns map where key is ID of the client (its UUID) and value is ID respective {@link AuthenticatedClientSessionModel} object.
|
* Returns map where key is ID of the client (its UUID) and value is ID respective {@link AuthenticatedClientSessionModel} object.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
Map<String, AuthenticatedClientSessionModel> getAuthenticatedClientSessions();
|
Map<String, AuthenticatedClientSessionModel> getAuthenticatedClientSessions();
|
||||||
/**
|
/**
|
||||||
|
@ -135,6 +135,8 @@ public interface UserSessionModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SessionPersistenceState getPersistenceState();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag used when creating user session
|
* Flag used when creating user session
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -24,6 +24,12 @@ import org.keycloak.TokenVerifier;
|
||||||
import org.keycloak.common.VerificationException;
|
import org.keycloak.common.VerificationException;
|
||||||
import org.keycloak.crypto.SignatureProvider;
|
import org.keycloak.crypto.SignatureProvider;
|
||||||
import org.keycloak.crypto.SignatureVerifierContext;
|
import org.keycloak.crypto.SignatureVerifierContext;
|
||||||
|
import org.keycloak.events.Details;
|
||||||
|
import org.keycloak.events.EventBuilder;
|
||||||
|
import org.keycloak.events.EventType;
|
||||||
|
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||||
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.ClientSessionContext;
|
||||||
import org.keycloak.models.ImpersonationSessionNote;
|
import org.keycloak.models.ImpersonationSessionNote;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
@ -31,6 +37,8 @@ import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.services.Urls;
|
import org.keycloak.services.Urls;
|
||||||
|
import org.keycloak.services.util.DefaultClientSessionContext;
|
||||||
|
import org.keycloak.services.util.UserSessionUtil;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
import jakarta.ws.rs.core.MediaType;
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
@ -55,6 +63,7 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
|
||||||
public Response introspect(String token) {
|
public Response introspect(String token) {
|
||||||
try {
|
try {
|
||||||
AccessToken accessToken = verifyAccessToken(token);
|
AccessToken accessToken = verifyAccessToken(token);
|
||||||
|
accessToken = transformAccessToken(accessToken);
|
||||||
ObjectNode tokenMetadata;
|
ObjectNode tokenMetadata;
|
||||||
|
|
||||||
if (accessToken != null) {
|
if (accessToken != null) {
|
||||||
|
@ -106,6 +115,57 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AccessToken transformAccessToken(AccessToken token) {
|
||||||
|
if (token == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientModel client = realm.getClientByClientId(token.getIssuedFor());
|
||||||
|
EventBuilder event = new EventBuilder(realm, session, session.getContext().getConnection())
|
||||||
|
.event(EventType.INTROSPECT_TOKEN)
|
||||||
|
.detail(Details.AUTH_METHOD, Details.VALIDATE_ACCESS_TOKEN);
|
||||||
|
UserSessionModel userSession;
|
||||||
|
try {
|
||||||
|
userSession = UserSessionUtil.findValidSession(session, realm, token, event, client);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.debugf("Can not get user session: %s", e.getMessage());
|
||||||
|
// Backwards compatibility
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
if (userSession.getUser() == null) {
|
||||||
|
logger.debugf("User not found");
|
||||||
|
// Backwards compatibility
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
|
||||||
|
ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionScopeParameter(clientSession, session);
|
||||||
|
AccessToken smallToken = getAccessTokenFromStoredData(token, userSession);
|
||||||
|
return tokenManager.transformIntrospectionAccessToken(session, smallToken, userSession, clientSessionCtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AccessToken getAccessTokenFromStoredData(AccessToken token, UserSessionModel userSession) {
|
||||||
|
// Copy just "basic" claims from the initial token. The same like filled in TokenManager.initToken. The rest should be possibly added by protocol mappers (only if configured for introspection response)
|
||||||
|
AccessToken newToken = new AccessToken();
|
||||||
|
newToken.id(token.getId());
|
||||||
|
newToken.type(token.getType());
|
||||||
|
newToken.subject(token.getSubject() != null ? token.getSubject() : userSession.getUser().getId());
|
||||||
|
newToken.iat(token.getIat());
|
||||||
|
newToken.exp(token.getExp());
|
||||||
|
newToken.issuedFor(token.getIssuedFor());
|
||||||
|
newToken.issuer(token.getIssuer());
|
||||||
|
newToken.setNonce(token.getNonce());
|
||||||
|
newToken.setScope(token.getScope());
|
||||||
|
newToken.setAuth_time(token.getAuth_time());
|
||||||
|
newToken.setSessionState(token.getSessionState());
|
||||||
|
|
||||||
|
// In the case of a refresh token, aud is a basic claim.
|
||||||
|
newToken.audience(token.getAudience());
|
||||||
|
|
||||||
|
// The cnf is not a claim controlled by the protocol mapper.
|
||||||
|
newToken.setConfirmation(token.getConfirmation());
|
||||||
|
return newToken;
|
||||||
|
}
|
||||||
|
|
||||||
protected AccessToken verifyAccessToken(String token) {
|
protected AccessToken verifyAccessToken(String token) {
|
||||||
AccessToken accessToken;
|
AccessToken accessToken;
|
||||||
|
|
||||||
|
|
|
@ -133,29 +133,29 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
||||||
private Map<String, ProtocolMapperModel> builtins = new HashMap<>();
|
private Map<String, ProtocolMapperModel> builtins = new HashMap<>();
|
||||||
|
|
||||||
void initBuiltIns() {
|
void initBuiltIns() {
|
||||||
ProtocolMapperModel model;
|
ProtocolMapperModel model;
|
||||||
model = UserAttributeMapper.createClaimMapper(USERNAME,
|
model = UserAttributeMapper.createClaimMapper(USERNAME,
|
||||||
"username",
|
"username",
|
||||||
"preferred_username", String.class.getSimpleName(),
|
"preferred_username", String.class.getSimpleName(),
|
||||||
true, true);
|
true, true, true);
|
||||||
builtins.put(USERNAME, model);
|
builtins.put(USERNAME, model);
|
||||||
|
|
||||||
model = UserAttributeMapper.createClaimMapper(EMAIL,
|
model = UserAttributeMapper.createClaimMapper(EMAIL,
|
||||||
"email",
|
"email",
|
||||||
"email", "String",
|
"email", "String",
|
||||||
true, true);
|
true, true, true);
|
||||||
builtins.put(EMAIL, model);
|
builtins.put(EMAIL, model);
|
||||||
|
|
||||||
model = UserAttributeMapper.createClaimMapper(GIVEN_NAME,
|
model = UserAttributeMapper.createClaimMapper(GIVEN_NAME,
|
||||||
"firstName",
|
"firstName",
|
||||||
"given_name", "String",
|
"given_name", "String",
|
||||||
true, true);
|
true, true, true);
|
||||||
builtins.put(GIVEN_NAME, model);
|
builtins.put(GIVEN_NAME, model);
|
||||||
|
|
||||||
model = UserAttributeMapper.createClaimMapper(FAMILY_NAME,
|
model = UserAttributeMapper.createClaimMapper(FAMILY_NAME,
|
||||||
"lastName",
|
"lastName",
|
||||||
"family_name", "String",
|
"family_name", "String",
|
||||||
true, true);
|
true, true, true);
|
||||||
builtins.put(FAMILY_NAME, model);
|
builtins.put(FAMILY_NAME, model);
|
||||||
|
|
||||||
createUserAttributeMapper(MIDDLE_NAME, "middleName", IDToken.MIDDLE_NAME, "String");
|
createUserAttributeMapper(MIDDLE_NAME, "middleName", IDToken.MIDDLE_NAME, "String");
|
||||||
|
@ -175,10 +175,10 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
||||||
model = UserPropertyMapper.createClaimMapper(EMAIL_VERIFIED,
|
model = UserPropertyMapper.createClaimMapper(EMAIL_VERIFIED,
|
||||||
"emailVerified",
|
"emailVerified",
|
||||||
"email_verified", "boolean",
|
"email_verified", "boolean",
|
||||||
true, true);
|
true, true, true);
|
||||||
builtins.put(EMAIL_VERIFIED, model);
|
builtins.put(EMAIL_VERIFIED, model);
|
||||||
|
|
||||||
ProtocolMapperModel fullName = FullNameMapper.create(FULL_NAME, true, true, true);
|
ProtocolMapperModel fullName = FullNameMapper.create(FULL_NAME, true, true, true, true);
|
||||||
builtins.put(FULL_NAME, fullName);
|
builtins.put(FULL_NAME, fullName);
|
||||||
|
|
||||||
ProtocolMapperModel address = AddressMapper.createAddressMapper();
|
ProtocolMapperModel address = AddressMapper.createAddressMapper();
|
||||||
|
@ -187,19 +187,19 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
||||||
model = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
|
model = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
|
||||||
KerberosConstants.GSS_DELEGATION_CREDENTIAL,
|
KerberosConstants.GSS_DELEGATION_CREDENTIAL,
|
||||||
KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
|
KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
|
||||||
true, false);
|
true, false, true);
|
||||||
builtins.put(KerberosConstants.GSS_DELEGATION_CREDENTIAL, model);
|
builtins.put(KerberosConstants.GSS_DELEGATION_CREDENTIAL, model);
|
||||||
|
|
||||||
model = UserRealmRoleMappingMapper.create(null, REALM_ROLES, "realm_access.roles", true, false, true);
|
model = UserRealmRoleMappingMapper.create(null, REALM_ROLES, "realm_access.roles", true, false, true, true);
|
||||||
builtins.put(REALM_ROLES, model);
|
builtins.put(REALM_ROLES, model);
|
||||||
|
|
||||||
model = UserClientRoleMappingMapper.create(null, null, CLIENT_ROLES, "resource_access.${client_id}.roles", true, false, true);
|
model = UserClientRoleMappingMapper.create(null, null, CLIENT_ROLES, "resource_access.${client_id}.roles", true, false, true, true);
|
||||||
builtins.put(CLIENT_ROLES, model);
|
builtins.put(CLIENT_ROLES, model);
|
||||||
|
|
||||||
model = AudienceResolveProtocolMapper.createClaimMapper(AUDIENCE_RESOLVE);
|
model = AudienceResolveProtocolMapper.createClaimMapper(AUDIENCE_RESOLVE, true, true);
|
||||||
builtins.put(AUDIENCE_RESOLVE, model);
|
builtins.put(AUDIENCE_RESOLVE, model);
|
||||||
|
|
||||||
model = AllowedWebOriginsProtocolMapper.createClaimMapper(ALLOWED_WEB_ORIGINS);
|
model = AllowedWebOriginsProtocolMapper.createClaimMapper(ALLOWED_WEB_ORIGINS, true, true);
|
||||||
builtins.put(ALLOWED_WEB_ORIGINS, model);
|
builtins.put(ALLOWED_WEB_ORIGINS, model);
|
||||||
|
|
||||||
builtins.put(IMPERSONATOR_ID.getDisplayName(), UserSessionNoteMapper.createUserSessionNoteMapper(IMPERSONATOR_ID));
|
builtins.put(IMPERSONATOR_ID.getDisplayName(), UserSessionNoteMapper.createUserSessionNoteMapper(IMPERSONATOR_ID));
|
||||||
|
@ -207,14 +207,14 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
||||||
|
|
||||||
model = UserAttributeMapper.createClaimMapper(UPN, "username",
|
model = UserAttributeMapper.createClaimMapper(UPN, "username",
|
||||||
"upn", "String",
|
"upn", "String",
|
||||||
true, true);
|
true, true, true);
|
||||||
builtins.put(UPN, model);
|
builtins.put(UPN, model);
|
||||||
|
|
||||||
model = UserRealmRoleMappingMapper.create(null, GROUPS, GROUPS, true, true, true);
|
model = UserRealmRoleMappingMapper.create(null, GROUPS, GROUPS, true, true, true, true);
|
||||||
builtins.put(GROUPS, model);
|
builtins.put(GROUPS, model);
|
||||||
|
|
||||||
if (Profile.isFeatureEnabled(Profile.Feature.STEP_UP_AUTHENTICATION)) {
|
if (Profile.isFeatureEnabled(Profile.Feature.STEP_UP_AUTHENTICATION)) {
|
||||||
model = AcrProtocolMapper.create(ACR, true, true);
|
model = AcrProtocolMapper.create(ACR, true, true, true);
|
||||||
builtins.put(ACR, model);
|
builtins.put(ACR, model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,7 +223,7 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
||||||
ProtocolMapperModel model = UserAttributeMapper.createClaimMapper(name,
|
ProtocolMapperModel model = UserAttributeMapper.createClaimMapper(name,
|
||||||
attrName,
|
attrName,
|
||||||
claimName, type,
|
claimName, type,
|
||||||
true, true, false);
|
true, true, true, false);
|
||||||
builtins.put(name, model);
|
builtins.put(name, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ import org.keycloak.models.utils.SessionExpirationUtils;
|
||||||
import org.keycloak.models.utils.RoleUtils;
|
import org.keycloak.models.utils.RoleUtils;
|
||||||
import org.keycloak.protocol.ProtocolMapper;
|
import org.keycloak.protocol.ProtocolMapper;
|
||||||
import org.keycloak.protocol.ProtocolMapperUtils;
|
import org.keycloak.protocol.ProtocolMapperUtils;
|
||||||
|
import org.keycloak.protocol.oidc.mappers.TokenIntrospectionTokenMapper;
|
||||||
import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper;
|
import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper;
|
||||||
import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenResponseMapper;
|
import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenResponseMapper;
|
||||||
import org.keycloak.protocol.oidc.mappers.OIDCIDTokenMapper;
|
import org.keycloak.protocol.oidc.mappers.OIDCIDTokenMapper;
|
||||||
|
@ -785,6 +786,17 @@ public class TokenManager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AccessToken transformIntrospectionAccessToken(KeycloakSession session, AccessToken token,
|
||||||
|
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||||
|
return ProtocolMapperUtils.getSortedProtocolMappers(session, clientSessionCtx, mapper -> mapper.getValue() instanceof TokenIntrospectionTokenMapper)
|
||||||
|
.collect(new TokenCollector<AccessToken>(token) {
|
||||||
|
@Override
|
||||||
|
protected AccessToken applyMapper(AccessToken token, Map.Entry<ProtocolMapperModel, ProtocolMapper> mapper) {
|
||||||
|
return ((TokenIntrospectionTokenMapper) mapper.getValue()).transformIntrospectionToken(token, mapper.getKey(), session, userSession, clientSessionCtx);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, Object> generateUserInfoClaims(AccessToken userInfo, UserModel userModel) {
|
public Map<String, Object> generateUserInfoClaims(AccessToken userInfo, UserModel userModel) {
|
||||||
Map<String, Object> claims = new HashMap<>();
|
Map<String, Object> claims = new HashMap<>();
|
||||||
claims.put("sub", userInfo.getSubject() == null? userModel.getId() : userInfo.getSubject());
|
claims.put("sub", userInfo.getSubject() == null? userModel.getId() : userInfo.getSubject());
|
||||||
|
|
|
@ -24,7 +24,6 @@ import org.keycloak.TokenVerifier;
|
||||||
import org.keycloak.common.ClientConnection;
|
import org.keycloak.common.ClientConnection;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.common.VerificationException;
|
import org.keycloak.common.VerificationException;
|
||||||
import org.keycloak.common.constants.ServiceAccountConstants;
|
|
||||||
import org.keycloak.crypto.ContentEncryptionProvider;
|
import org.keycloak.crypto.ContentEncryptionProvider;
|
||||||
import org.keycloak.crypto.CekManagementProvider;
|
import org.keycloak.crypto.CekManagementProvider;
|
||||||
import org.keycloak.crypto.KeyWrapper;
|
import org.keycloak.crypto.KeyWrapper;
|
||||||
|
@ -48,7 +47,6 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
|
||||||
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
|
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.protocol.oidc.TokenManager;
|
import org.keycloak.protocol.oidc.TokenManager;
|
||||||
|
@ -59,15 +57,11 @@ import org.keycloak.services.Urls;
|
||||||
import org.keycloak.services.clientpolicy.ClientPolicyException;
|
import org.keycloak.services.clientpolicy.ClientPolicyException;
|
||||||
import org.keycloak.services.clientpolicy.context.UserInfoRequestContext;
|
import org.keycloak.services.clientpolicy.context.UserInfoRequestContext;
|
||||||
import org.keycloak.services.managers.AppAuthManager;
|
import org.keycloak.services.managers.AppAuthManager;
|
||||||
import org.keycloak.services.managers.AuthenticationManager;
|
|
||||||
import org.keycloak.services.managers.UserSessionCrossDCManager;
|
|
||||||
import org.keycloak.services.managers.UserSessionManager;
|
|
||||||
import org.keycloak.services.resources.Cors;
|
import org.keycloak.services.resources.Cors;
|
||||||
import org.keycloak.services.util.DPoPUtil;
|
import org.keycloak.services.util.DPoPUtil;
|
||||||
import org.keycloak.services.util.DefaultClientSessionContext;
|
import org.keycloak.services.util.DefaultClientSessionContext;
|
||||||
import org.keycloak.services.util.MtlsHoKTokenUtil;
|
import org.keycloak.services.util.MtlsHoKTokenUtil;
|
||||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
import org.keycloak.services.util.UserSessionUtil;
|
||||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
import org.keycloak.util.TokenUtil;
|
import org.keycloak.util.TokenUtil;
|
||||||
import org.keycloak.utils.MediaType;
|
import org.keycloak.utils.MediaType;
|
||||||
|
@ -145,15 +139,15 @@ public class UserInfoEndpoint {
|
||||||
authorization(accessToken);
|
authorization(accessToken);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
String contentType = headers.getHeaderString(HttpHeaders.CONTENT_TYPE);
|
String contentType = headers.getHeaderString(HttpHeaders.CONTENT_TYPE);
|
||||||
jakarta.ws.rs.core.MediaType mediaType = jakarta.ws.rs.core.MediaType.valueOf(contentType);
|
jakarta.ws.rs.core.MediaType mediaType = jakarta.ws.rs.core.MediaType.valueOf(contentType);
|
||||||
|
|
||||||
if (jakarta.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED_TYPE.isCompatible(mediaType)) {
|
if (jakarta.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED_TYPE.isCompatible(mediaType)) {
|
||||||
MultivaluedMap<String, String> formParams = request.getDecodedFormParameters();
|
MultivaluedMap<String, String> formParams = request.getDecodedFormParameters();
|
||||||
checkAccessTokenDuplicated(formParams);
|
checkAccessTokenDuplicated(formParams);
|
||||||
accessToken = formParams.getFirst(OAuth2Constants.ACCESS_TOKEN);
|
accessToken = formParams.getFirst(OAuth2Constants.ACCESS_TOKEN);
|
||||||
authorization(accessToken);
|
authorization(accessToken);
|
||||||
}
|
}
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
// not application/x-www-form-urlencoded, ignore
|
// not application/x-www-form-urlencoded, ignore
|
||||||
|
@ -229,7 +223,7 @@ public class UserInfoEndpoint {
|
||||||
throw error.invalidToken("Client disabled");
|
throw error.invalidToken("Client disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
UserSessionModel userSession = findValidSession(token, event, clientModel);
|
UserSessionModel userSession = UserSessionUtil.findValidSession(session, realm, token, event, clientModel);
|
||||||
|
|
||||||
UserModel userModel = userSession.getUser();
|
UserModel userModel = userSession.getUser();
|
||||||
if (userModel == null) {
|
if (userModel == null) {
|
||||||
|
@ -350,73 +344,6 @@ public class UserInfoEndpoint {
|
||||||
return encryptedToken;
|
return encryptedToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserSessionModel createTransientSessionForClient(AccessToken token, ClientModel client) {
|
|
||||||
// create a transient session
|
|
||||||
UserModel user = TokenManager.lookupUserFromStatelessToken(session, realm, token);
|
|
||||||
if (user == null) {
|
|
||||||
throw error.invalidToken("User not found");
|
|
||||||
}
|
|
||||||
UserSessionModel userSession = new UserSessionManager(session).createUserSession(KeycloakModelUtils.generateId(), realm, user, user.getUsername(), clientConnection.getRemoteAddr(),
|
|
||||||
ServiceAccountConstants.CLIENT_AUTH, false, null, null, UserSessionModel.SessionPersistenceState.TRANSIENT);
|
|
||||||
// attach an auth session for the client
|
|
||||||
RootAuthenticationSessionModel rootAuthSession = session.authenticationSessions().createRootAuthenticationSession(realm);
|
|
||||||
AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(client);
|
|
||||||
authSession.setAuthenticatedUser(userSession.getUser());
|
|
||||||
authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
|
||||||
authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
|
|
||||||
AuthenticationManager.setClientScopesInSession(authSession);
|
|
||||||
TokenManager.attachAuthenticationSession(session, userSession, authSession);
|
|
||||||
return userSession;
|
|
||||||
}
|
|
||||||
|
|
||||||
private UserSessionModel findValidSession(AccessToken token, EventBuilder event, ClientModel client) {
|
|
||||||
if (token.getSessionState() == null) {
|
|
||||||
return createTransientSessionForClient(token, client);
|
|
||||||
}
|
|
||||||
|
|
||||||
UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), false, client.getId());
|
|
||||||
UserSessionModel offlineUserSession = null;
|
|
||||||
if (AuthenticationManager.isSessionValid(realm, userSession)) {
|
|
||||||
checkTokenIssuedAt(token, userSession, event, client);
|
|
||||||
event.session(userSession);
|
|
||||||
return userSession;
|
|
||||||
} else {
|
|
||||||
offlineUserSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), true, client.getId());
|
|
||||||
if (AuthenticationManager.isOfflineSessionValid(realm, offlineUserSession)) {
|
|
||||||
checkTokenIssuedAt(token, offlineUserSession, event, client);
|
|
||||||
event.session(offlineUserSession);
|
|
||||||
return offlineUserSession;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userSession == null && offlineUserSession == null) {
|
|
||||||
event.error(Errors.USER_SESSION_NOT_FOUND);
|
|
||||||
throw error.invalidToken("User session not found or doesn't have client attached on it");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userSession != null) {
|
|
||||||
event.session(userSession);
|
|
||||||
} else {
|
|
||||||
event.session(offlineUserSession);
|
|
||||||
}
|
|
||||||
|
|
||||||
event.error(Errors.SESSION_EXPIRED);
|
|
||||||
throw error.invalidToken("Session expired");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkTokenIssuedAt(AccessToken token, UserSessionModel userSession, EventBuilder event, ClientModel client) {
|
|
||||||
if (token.isIssuedBeforeSessionStart(userSession.getStarted())) {
|
|
||||||
event.error(Errors.INVALID_TOKEN);
|
|
||||||
throw error.invalidToken("Stale token");
|
|
||||||
}
|
|
||||||
|
|
||||||
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
|
|
||||||
if (token.isIssuedBeforeSessionStart(clientSession.getStarted())) {
|
|
||||||
event.error(Errors.INVALID_TOKEN);
|
|
||||||
throw error.invalidToken("Stale token");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkAccessTokenDuplicated(MultivaluedMap<String, String> formParams) {
|
private void checkAccessTokenDuplicated(MultivaluedMap<String, String> formParams) {
|
||||||
// If access_token is not provided, error is thrown in issueUserInfo().
|
// If access_token is not provided, error is thrown in issueUserInfo().
|
||||||
// Only checks duplication of access token parameter in this function.
|
// Only checks duplication of access token parameter in this function.
|
||||||
|
|
|
@ -106,6 +106,17 @@ public abstract class AbstractOIDCProtocolMapper implements ProtocolMapper {
|
||||||
return accessTokenResponse;
|
return accessTokenResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
|
||||||
|
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||||
|
|
||||||
|
if (!OIDCAttributeMapperHelper.includeInIntrospection(mappingModel)){
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
setClaim(token, mappingModel, userSession, session, clientSessionCtx);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Intended to be overridden in {@link ProtocolMapper} implementations to add claims to an token.
|
* Intended to be overridden in {@link ProtocolMapper} implementations to add claims to an token.
|
||||||
* @param token
|
* @param token
|
||||||
|
|
|
@ -14,6 +14,7 @@ import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.IDToken;
|
import org.keycloak.representations.IDToken;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ import java.util.List;
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:martin.hardselius@gmail.com">Martin Hardselius</a>
|
* @author <a href="mailto:martin.hardselius@gmail.com">Martin Hardselius</a>
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractPairwiseSubMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
|
public abstract class AbstractPairwiseSubMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
|
||||||
public static final String PROVIDER_ID_SUFFIX = "-pairwise-sub-mapper";
|
public static final String PROVIDER_ID_SUFFIX = "-pairwise-sub-mapper";
|
||||||
|
|
||||||
public abstract String getIdPrefix();
|
public abstract String getIdPrefix();
|
||||||
|
@ -75,6 +76,12 @@ public abstract class AbstractPairwiseSubMapper extends AbstractOIDCProtocolMapp
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||||
|
setAccessTokenSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSessionCtx.getClientSession().getClient(), mappingModel), userSession.getUser().getId()));
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AccessToken transformUserInfoToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
public AccessToken transformUserInfoToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||||
setUserInfoTokenSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSessionCtx.getClientSession().getClient(), mappingModel), userSession.getUser().getId()));
|
setUserInfoTokenSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSessionCtx.getClientSession().getClient(), mappingModel), userSession.getUser().getId()));
|
||||||
|
|
|
@ -38,7 +38,7 @@ import java.util.stream.Collectors;
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
|
* @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
|
||||||
*/
|
*/
|
||||||
abstract class AbstractUserRoleMappingMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
|
abstract class AbstractUserRoleMappingMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPriority() {
|
public int getPriority() {
|
||||||
|
|
|
@ -42,7 +42,7 @@ import org.keycloak.services.managers.AuthenticationManager;
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
public class AcrProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, EnvironmentDependentProviderFactory {
|
public class AcrProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, TokenIntrospectionTokenMapper, EnvironmentDependentProviderFactory {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(AcrProtocolMapper.class);
|
private static final Logger logger = Logger.getLogger(AcrProtocolMapper.class);
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ public class AcrProtocolMapper extends AbstractOIDCProtocolMapper implements OID
|
||||||
token.setAcr(acr);
|
token.setAcr(acr);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolMapperModel create(String name, boolean accessToken, boolean idToken) {
|
public static ProtocolMapperModel create(String name, boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
|
||||||
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
||||||
mapper.setName(name);
|
mapper.setName(name);
|
||||||
mapper.setProtocolMapper(PROVIDER_ID);
|
mapper.setProtocolMapper(PROVIDER_ID);
|
||||||
|
@ -95,6 +95,7 @@ public class AcrProtocolMapper extends AbstractOIDCProtocolMapper implements OID
|
||||||
Map<String, String> config = new HashMap<>();
|
Map<String, String> config = new HashMap<>();
|
||||||
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
|
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
|
||||||
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
|
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
|
||||||
|
if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true");
|
||||||
mapper.setConfig(config);
|
mapper.setConfig(config);
|
||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ import java.util.Map;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
|
public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
|
||||||
|
|
||||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||||
|
|
||||||
|
@ -69,10 +69,10 @@ public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAcc
|
||||||
public static final String PROVIDER_ID = "oidc-address-mapper";
|
public static final String PROVIDER_ID = "oidc-address-mapper";
|
||||||
|
|
||||||
public static ProtocolMapperModel createAddressMapper() {
|
public static ProtocolMapperModel createAddressMapper() {
|
||||||
return createAddressMapper(true, true, true);
|
return createAddressMapper(true, true, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolMapperModel createAddressMapper(boolean idToken, boolean accessToken, boolean userInfo) {
|
public static ProtocolMapperModel createAddressMapper(boolean idToken, boolean accessToken, boolean userInfo, boolean introspectionEndpoint) {
|
||||||
Map<String, String> config;
|
Map<String, String> config;
|
||||||
ProtocolMapperModel address = new ProtocolMapperModel();
|
ProtocolMapperModel address = new ProtocolMapperModel();
|
||||||
address.setName("address");
|
address.setName("address");
|
||||||
|
@ -82,6 +82,7 @@ public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAcc
|
||||||
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, Boolean.toString(accessToken));
|
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, Boolean.toString(accessToken));
|
||||||
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, Boolean.toString(idToken));
|
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, Boolean.toString(idToken));
|
||||||
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, Boolean.toString(userInfo));
|
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, Boolean.toString(userInfo));
|
||||||
|
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, Boolean.toString(introspectionEndpoint));
|
||||||
|
|
||||||
config.put(getModelPropertyName(STREET), STREET);
|
config.put(getModelPropertyName(STREET), STREET);
|
||||||
config.put(getModelPropertyName(AddressClaimSet.LOCALITY), AddressClaimSet.LOCALITY);
|
config.put(getModelPropertyName(AddressClaimSet.LOCALITY), AddressClaimSet.LOCALITY);
|
||||||
|
|
|
@ -19,7 +19,9 @@ package org.keycloak.protocol.oidc.mappers;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
@ -32,18 +34,24 @@ import org.keycloak.protocol.oidc.utils.WebOriginsUtils;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
|
|
||||||
|
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN;
|
||||||
|
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Protocol mapper to add allowed web origins to the access token to the 'allowed-origins' claim
|
* Protocol mapper to add allowed web origins to the access token to the 'allowed-origins' claim
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
public class AllowedWebOriginsProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
|
public class AllowedWebOriginsProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, TokenIntrospectionTokenMapper {
|
||||||
|
|
||||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||||
|
|
||||||
|
|
||||||
public static final String PROVIDER_ID = "oidc-allowed-origins-mapper";
|
public static final String PROVIDER_ID = "oidc-allowed-origins-mapper";
|
||||||
|
|
||||||
|
static {
|
||||||
|
OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, AllowedWebOriginsProtocolMapper.class);
|
||||||
|
}
|
||||||
|
|
||||||
public List<ProviderConfigProperty> getConfigProperties() {
|
public List<ProviderConfigProperty> getConfigProperties() {
|
||||||
return configProperties;
|
return configProperties;
|
||||||
|
@ -72,23 +80,72 @@ public class AllowedWebOriginsProtocolMapper extends AbstractOIDCProtocolMapper
|
||||||
@Override
|
@Override
|
||||||
public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
|
public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
|
||||||
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||||
|
|
||||||
|
if (!includeInAccessToken(mappingModel)){
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
setWebOrigin(token, session, clientSessionCtx);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean includeInAccessToken(ProtocolMapperModel mappingModel) {
|
||||||
|
String includeInAccessToken = mappingModel.getConfig().get(INCLUDE_IN_ACCESS_TOKEN);
|
||||||
|
|
||||||
|
// Backwards compatibility
|
||||||
|
if (includeInAccessToken == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "true".equals(includeInAccessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
|
||||||
|
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||||
|
if (!includeInIntrospection(mappingModel)) {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
setWebOrigin(token, session, clientSessionCtx);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean includeInIntrospection(ProtocolMapperModel mappingModel) {
|
||||||
|
String includeInIntrospection = mappingModel.getConfig().get(INCLUDE_IN_INTROSPECTION);
|
||||||
|
|
||||||
|
// Backwards compatibility
|
||||||
|
if (includeInIntrospection == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "true".equals(includeInIntrospection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setWebOrigin(AccessToken token, KeycloakSession session, ClientSessionContext clientSessionCtx) {
|
||||||
ClientModel client = clientSessionCtx.getClientSession().getClient();
|
ClientModel client = clientSessionCtx.getClientSession().getClient();
|
||||||
|
|
||||||
Set<String> allowedOrigins = client.getWebOrigins();
|
Set<String> allowedOrigins = client.getWebOrigins();
|
||||||
if (allowedOrigins != null && !allowedOrigins.isEmpty()) {
|
if (allowedOrigins != null && !allowedOrigins.isEmpty()) {
|
||||||
token.setAllowedOrigins(WebOriginsUtils.resolveValidWebOrigins(session, client));
|
token.setAllowedOrigins(WebOriginsUtils.resolveValidWebOrigins(session, client));
|
||||||
}
|
}
|
||||||
|
|
||||||
return token;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ProtocolMapperModel createClaimMapper(String name, boolean accessToken, boolean introspectionEndpoint) {
|
||||||
public static ProtocolMapperModel createClaimMapper(String name) {
|
|
||||||
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
||||||
mapper.setName(name);
|
mapper.setName(name);
|
||||||
mapper.setProtocolMapper(PROVIDER_ID);
|
mapper.setProtocolMapper(PROVIDER_ID);
|
||||||
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
mapper.setConfig(Collections.emptyMap());
|
Map<String, String> config = new HashMap<>();
|
||||||
|
if (accessToken) {
|
||||||
|
config.put(INCLUDE_IN_ACCESS_TOKEN, "true");
|
||||||
|
} else {
|
||||||
|
config.put(INCLUDE_IN_ACCESS_TOKEN, "false");
|
||||||
|
}
|
||||||
|
if (introspectionEndpoint) {
|
||||||
|
config.put(INCLUDE_IN_INTROSPECTION, "true");
|
||||||
|
} else {
|
||||||
|
config.put(INCLUDE_IN_INTROSPECTION, "false");
|
||||||
|
}
|
||||||
|
mapper.setConfig(config);
|
||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ import org.keycloak.representations.IDToken;
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
public class AudienceProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper {
|
public class AudienceProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, TokenIntrospectionTokenMapper {
|
||||||
|
|
||||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
|
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ public class AudienceProtocolMapper extends AbstractOIDCProtocolMapper implement
|
||||||
public static ProtocolMapperModel createClaimMapper(String name,
|
public static ProtocolMapperModel createClaimMapper(String name,
|
||||||
String includedClientAudience,
|
String includedClientAudience,
|
||||||
String includedCustomAudience,
|
String includedCustomAudience,
|
||||||
boolean accessToken, boolean idToken) {
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
|
||||||
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
||||||
mapper.setName(name);
|
mapper.setName(name);
|
||||||
mapper.setProtocolMapper(PROVIDER_ID);
|
mapper.setProtocolMapper(PROVIDER_ID);
|
||||||
|
@ -131,6 +131,7 @@ public class AudienceProtocolMapper extends AbstractOIDCProtocolMapper implement
|
||||||
|
|
||||||
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
|
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
|
||||||
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
|
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
|
||||||
|
if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true");
|
||||||
mapper.setConfig(config);
|
mapper.setConfig(config);
|
||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.protocol.oidc.mappers;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -32,16 +33,21 @@ import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.utils.RoleResolveUtil;
|
import org.keycloak.utils.RoleResolveUtil;
|
||||||
|
|
||||||
|
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN;
|
||||||
|
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Protocol mapper, which adds all client_ids of "allowed" clients to the audience field of the token. Allowed client means the client
|
* Protocol mapper, which adds all client_ids of "allowed" clients to the audience field of the token. Allowed client means the client
|
||||||
* for which user has at least one client role
|
* for which user has at least one client role
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
public class AudienceResolveProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper {
|
public class AudienceResolveProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, TokenIntrospectionTokenMapper {
|
||||||
|
|
||||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||||
|
static {
|
||||||
|
OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, AudienceResolveProtocolMapper.class);
|
||||||
|
}
|
||||||
|
|
||||||
public static final String PROVIDER_ID = "oidc-audience-resolve-mapper";
|
public static final String PROVIDER_ID = "oidc-audience-resolve-mapper";
|
||||||
|
|
||||||
|
@ -79,6 +85,46 @@ public class AudienceResolveProtocolMapper extends AbstractOIDCProtocolMapper im
|
||||||
@Override
|
@Override
|
||||||
public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
|
public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
|
||||||
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||||
|
if (!includeInAccessToken(mappingModel)){
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
setAudience(token, clientSessionCtx, session);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean includeInAccessToken(ProtocolMapperModel mappingModel) {
|
||||||
|
String includeInAccessToken = mappingModel.getConfig().get(INCLUDE_IN_ACCESS_TOKEN);
|
||||||
|
|
||||||
|
// Backwards compatibility
|
||||||
|
if (includeInAccessToken == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "true".equals(includeInAccessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
|
||||||
|
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||||
|
if (!includeInIntrospection(mappingModel)) {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
setAudience(token, clientSessionCtx, session);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean includeInIntrospection(ProtocolMapperModel mappingModel) {
|
||||||
|
String includeInIntrospection = mappingModel.getConfig().get(INCLUDE_IN_INTROSPECTION);
|
||||||
|
|
||||||
|
// Backwards compatibility
|
||||||
|
if (includeInIntrospection == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "true".equals(includeInIntrospection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setAudience(AccessToken token, ClientSessionContext clientSessionCtx, KeycloakSession session) {
|
||||||
String clientId = clientSessionCtx.getClientSession().getClient().getClientId();
|
String clientId = clientSessionCtx.getClientSession().getClient().getClientId();
|
||||||
|
|
||||||
for (Map.Entry<String, AccessToken.Access> entry : RoleResolveUtil.getAllResolvedClientRoles(session, clientSessionCtx).entrySet()) {
|
for (Map.Entry<String, AccessToken.Access> entry : RoleResolveUtil.getAllResolvedClientRoles(session, clientSessionCtx).entrySet()) {
|
||||||
|
@ -92,16 +138,25 @@ public class AudienceResolveProtocolMapper extends AbstractOIDCProtocolMapper im
|
||||||
token.addAudience(entry.getKey());
|
token.addAudience(entry.getKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return token;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolMapperModel createClaimMapper(String name) {
|
public static ProtocolMapperModel createClaimMapper(String name, boolean accessToken, boolean introspectionEndpoint) {
|
||||||
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
||||||
mapper.setName(name);
|
mapper.setName(name);
|
||||||
mapper.setProtocolMapper(PROVIDER_ID);
|
mapper.setProtocolMapper(PROVIDER_ID);
|
||||||
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
mapper.setConfig(Collections.emptyMap());
|
Map<String, String> config = new HashMap<>();
|
||||||
|
if (accessToken) {
|
||||||
|
config.put(INCLUDE_IN_ACCESS_TOKEN, "true");
|
||||||
|
} else {
|
||||||
|
config.put(INCLUDE_IN_ACCESS_TOKEN, "false");
|
||||||
|
}
|
||||||
|
if (introspectionEndpoint) {
|
||||||
|
config.put(INCLUDE_IN_INTROSPECTION, "true");
|
||||||
|
} else {
|
||||||
|
config.put(INCLUDE_IN_INTROSPECTION, "false");
|
||||||
|
}
|
||||||
|
mapper.setConfig(config);
|
||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,16 +110,16 @@ public class ClaimsParameterTokenMapper extends AbstractOIDCProtocolMapper imple
|
||||||
fullNameMapper.setClaim(token, mappingModel, userSession);
|
fullNameMapper.setClaim(token, mappingModel, userSession);
|
||||||
} else if (i.equals(IDToken.GIVEN_NAME)) {
|
} else if (i.equals(IDToken.GIVEN_NAME)) {
|
||||||
UserAttributeMapper userPropertyMapper = new UserAttributeMapper();
|
UserAttributeMapper userPropertyMapper = new UserAttributeMapper();
|
||||||
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested firstName", "firstName", IDToken.GIVEN_NAME, "String", false, true), userSession);
|
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested firstName", "firstName", IDToken.GIVEN_NAME, "String", false, true, false), userSession);
|
||||||
} else if (i.equals(IDToken.FAMILY_NAME)) {
|
} else if (i.equals(IDToken.FAMILY_NAME)) {
|
||||||
UserAttributeMapper userPropertyMapper = new UserAttributeMapper();
|
UserAttributeMapper userPropertyMapper = new UserAttributeMapper();
|
||||||
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested lastName", "lastName", IDToken.FAMILY_NAME, "String", false, true), userSession);
|
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested lastName", "lastName", IDToken.FAMILY_NAME, "String", false, true, false), userSession);
|
||||||
} else if (i.equals(IDToken.PREFERRED_USERNAME)) {
|
} else if (i.equals(IDToken.PREFERRED_USERNAME)) {
|
||||||
UserAttributeMapper userPropertyMapper = new UserAttributeMapper();
|
UserAttributeMapper userPropertyMapper = new UserAttributeMapper();
|
||||||
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested username", "username", IDToken.PREFERRED_USERNAME, "String", false, true), userSession);
|
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested username", "username", IDToken.PREFERRED_USERNAME, "String", false, true, false), userSession);
|
||||||
} else if (i.equals(IDToken.EMAIL)) {
|
} else if (i.equals(IDToken.EMAIL)) {
|
||||||
UserAttributeMapper userPropertyMapper = new UserAttributeMapper();
|
UserAttributeMapper userPropertyMapper = new UserAttributeMapper();
|
||||||
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested email", "email", IDToken.EMAIL, "String", false, true), userSession);
|
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested email", "email", IDToken.EMAIL, "String", false, true, false), userSession);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,7 @@ public class ClaimsParameterWithValueIdTokenMapper extends AbstractOIDCProtocolM
|
||||||
}
|
}
|
||||||
|
|
||||||
HardcodedClaim hardcodedClaimMapper = new HardcodedClaim();
|
HardcodedClaim hardcodedClaimMapper = new HardcodedClaim();
|
||||||
hardcodedClaimMapper.setClaim(token, HardcodedClaim.create("hard", claimName, claim, "String", false, true), userSession);
|
hardcodedClaimMapper.setClaim(token, HardcodedClaim.create("hard", claimName, claim, "String", false, true, false), userSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolMapperModel createMapper(String name, String attributeValue, boolean idToken) {
|
public static ProtocolMapperModel createMapper(String name, String attributeValue, boolean idToken) {
|
||||||
|
|
|
@ -37,7 +37,7 @@ import java.util.Optional;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
|
public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
|
||||||
|
|
||||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
|
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolMapperModel create(String name, boolean accessToken, boolean idToken, boolean userInfo) {
|
public static ProtocolMapperModel create(String name, boolean accessToken, boolean idToken, boolean userInfo, boolean introspectionEndpoint) {
|
||||||
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
||||||
mapper.setName(name);
|
mapper.setName(name);
|
||||||
mapper.setProtocolMapper(PROVIDER_ID);
|
mapper.setProtocolMapper(PROVIDER_ID);
|
||||||
|
@ -92,6 +92,7 @@ public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc
|
||||||
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
|
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
|
||||||
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
|
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
|
||||||
if (userInfo) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
|
if (userInfo) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
|
||||||
|
if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true");
|
||||||
mapper.setConfig(config);
|
mapper.setConfig(config);
|
||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ import java.util.stream.Collectors;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
|
public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
|
||||||
|
|
||||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||||
|
|
||||||
|
@ -105,9 +105,9 @@ public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolMapperModel create(String name,
|
public static ProtocolMapperModel create(String name,
|
||||||
String tokenClaimName,
|
String tokenClaimName,
|
||||||
boolean consentRequired, String consentText,
|
boolean consentRequired, String consentText,
|
||||||
boolean accessToken, boolean idToken) {
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
|
||||||
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
||||||
mapper.setName(name);
|
mapper.setName(name);
|
||||||
mapper.setProtocolMapper(PROVIDER_ID);
|
mapper.setProtocolMapper(PROVIDER_ID);
|
||||||
|
@ -116,8 +116,9 @@ public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements
|
||||||
config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, tokenClaimName);
|
config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, tokenClaimName);
|
||||||
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
|
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
|
||||||
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
|
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
|
||||||
|
if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true");
|
||||||
mapper.setConfig(config);
|
mapper.setConfig(config);
|
||||||
|
|
||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ import java.util.Map;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper,
|
public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper,
|
||||||
OIDCAccessTokenResponseMapper {
|
OIDCAccessTokenResponseMapper, TokenIntrospectionTokenMapper {
|
||||||
|
|
||||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAc
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setClaim(AccessTokenResponse accessTokenResponse, ProtocolMapperModel mappingModel, UserSessionModel userSession,
|
protected void setClaim(AccessTokenResponse accessTokenResponse, ProtocolMapperModel mappingModel, UserSessionModel userSession,
|
||||||
KeycloakSession keycloakSession, ClientSessionContext clientSessionCtx) {
|
KeycloakSession keycloakSession, ClientSessionContext clientSessionCtx) {
|
||||||
|
|
||||||
String attributeValue = mappingModel.getConfig().get(CLAIM_VALUE);
|
String attributeValue = mappingModel.getConfig().get(CLAIM_VALUE);
|
||||||
if (attributeValue == null) return;
|
if (attributeValue == null) return;
|
||||||
|
@ -102,9 +102,9 @@ public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAc
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolMapperModel create(String name,
|
public static ProtocolMapperModel create(String name,
|
||||||
String hardcodedName,
|
String hardcodedName,
|
||||||
String hardcodedValue, String claimType,
|
String hardcodedValue, String claimType,
|
||||||
boolean accessToken, boolean idToken) {
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
|
||||||
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
||||||
mapper.setName(name);
|
mapper.setName(name);
|
||||||
mapper.setProtocolMapper(PROVIDER_ID);
|
mapper.setProtocolMapper(PROVIDER_ID);
|
||||||
|
@ -115,6 +115,7 @@ public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAc
|
||||||
config.put(OIDCAttributeMapperHelper.JSON_TYPE, claimType);
|
config.put(OIDCAttributeMapperHelper.JSON_TYPE, claimType);
|
||||||
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
|
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
|
||||||
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
|
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
|
||||||
|
if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true");
|
||||||
mapper.setConfig(config);
|
mapper.setConfig(config);
|
||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ import java.util.Map;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class HardcodedRole extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, UserInfoTokenMapper {
|
public class HardcodedRole extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
|
||||||
|
|
||||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
|
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -104,6 +104,14 @@ public class HardcodedRole extends AbstractOIDCProtocolMapper implements OIDCAcc
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
|
||||||
|
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||||
|
// the mapper is always executed and then other role mappers decide if the claims are really set to the token
|
||||||
|
setClaim(token, mappingModel, userSession, session, clientSessionCtx);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession session,
|
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession session,
|
||||||
ClientSessionContext clientSessionCtx) {
|
ClientSessionContext clientSessionCtx) {
|
||||||
|
|
|
@ -31,10 +31,17 @@ import org.keycloak.representations.IDToken;
|
||||||
import org.keycloak.services.ServicesLogger;
|
import org.keycloak.services.ServicesLogger;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
|
@ -60,6 +67,10 @@ public class OIDCAttributeMapperHelper {
|
||||||
public static final String INCLUDE_IN_USERINFO_LABEL = "includeInUserInfo.label";
|
public static final String INCLUDE_IN_USERINFO_LABEL = "includeInUserInfo.label";
|
||||||
public static final String INCLUDE_IN_USERINFO_HELP_TEXT = "includeInUserInfo.tooltip";
|
public static final String INCLUDE_IN_USERINFO_HELP_TEXT = "includeInUserInfo.tooltip";
|
||||||
|
|
||||||
|
public static final String INCLUDE_IN_INTROSPECTION = "introspection.token.claim";
|
||||||
|
public static final String INCLUDE_IN_INTROSPECTION_LABEL = "includeInIntrospection.label";
|
||||||
|
public static final String INCLUDE_IN_INTROSPECTION_HELP_TEXT = "includeInIntrospection.tooltip";
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(OIDCAttributeMapperHelper.class);
|
private static final Logger logger = Logger.getLogger(OIDCAttributeMapperHelper.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -230,7 +241,7 @@ public class OIDCAttributeMapperHelper {
|
||||||
if (attributeValue instanceof String) return Boolean.valueOf((String) attributeValue);
|
if (attributeValue instanceof String) return Boolean.valueOf((String) attributeValue);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JsonNode getJsonNode(Object attributeValue) {
|
private static JsonNode getJsonNode(Object attributeValue) {
|
||||||
if (attributeValue instanceof JsonNode){
|
if (attributeValue instanceof JsonNode){
|
||||||
return (JsonNode) attributeValue;
|
return (JsonNode) attributeValue;
|
||||||
|
@ -259,7 +270,7 @@ public class OIDCAttributeMapperHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> void mapClaim(T token, ProtocolMapperModel mappingModel, Object attributeValue,
|
private static <T> void mapClaim(T token, ProtocolMapperModel mappingModel, Object attributeValue,
|
||||||
Map<String, PropertySetter<T>> setters, Map<String, Object> jsonObject) {
|
Map<String, PropertySetter<T>> setters, Map<String, Object> jsonObject) {
|
||||||
attributeValue = mapAttributeValue(mappingModel, attributeValue);
|
attributeValue = mapAttributeValue(mappingModel, attributeValue);
|
||||||
if (attributeValue == null) {
|
if (attributeValue == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -317,16 +328,16 @@ public class OIDCAttributeMapperHelper {
|
||||||
public static ProtocolMapperModel createClaimMapper(String name,
|
public static ProtocolMapperModel createClaimMapper(String name,
|
||||||
String userAttribute,
|
String userAttribute,
|
||||||
String tokenClaimName, String claimType,
|
String tokenClaimName, String claimType,
|
||||||
boolean accessToken, boolean idToken,
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint,
|
||||||
String mapperId) {
|
String mapperId) {
|
||||||
return createClaimMapper(name, userAttribute,tokenClaimName, claimType, accessToken, idToken, true, mapperId);
|
return createClaimMapper(name, userAttribute, tokenClaimName, claimType, accessToken, idToken, true, introspectionEndpoint, mapperId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolMapperModel createClaimMapper(String name,
|
public static ProtocolMapperModel createClaimMapper(String name,
|
||||||
String userAttribute,
|
String userAttribute,
|
||||||
String tokenClaimName, String claimType,
|
String tokenClaimName, String claimType,
|
||||||
boolean accessToken, boolean idToken, boolean userinfo,
|
boolean accessToken, boolean idToken, boolean userinfo, boolean introspectionEndpoint,
|
||||||
String mapperId) {
|
String mapperId) {
|
||||||
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
||||||
mapper.setName(name);
|
mapper.setName(name);
|
||||||
mapper.setProtocolMapper(mapperId);
|
mapper.setProtocolMapper(mapperId);
|
||||||
|
@ -338,6 +349,7 @@ public class OIDCAttributeMapperHelper {
|
||||||
if (accessToken) config.put(INCLUDE_IN_ACCESS_TOKEN, "true");
|
if (accessToken) config.put(INCLUDE_IN_ACCESS_TOKEN, "true");
|
||||||
if (idToken) config.put(INCLUDE_IN_ID_TOKEN, "true");
|
if (idToken) config.put(INCLUDE_IN_ID_TOKEN, "true");
|
||||||
if (userinfo) config.put(INCLUDE_IN_USERINFO, "true");
|
if (userinfo) config.put(INCLUDE_IN_USERINFO, "true");
|
||||||
|
if (introspectionEndpoint) config.put(INCLUDE_IN_INTROSPECTION, "true");
|
||||||
mapper.setConfig(config);
|
mapper.setConfig(config);
|
||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
@ -369,6 +381,17 @@ public class OIDCAttributeMapperHelper {
|
||||||
return "true".equals(includeInUserInfo);
|
return "true".equals(includeInUserInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean includeInIntrospection(ProtocolMapperModel mappingModel) {
|
||||||
|
String includeInIntrospection = mappingModel.getConfig().get(INCLUDE_IN_INTROSPECTION);
|
||||||
|
|
||||||
|
// Backwards compatibility
|
||||||
|
if (includeInIntrospection == null && includeInAccessToken(mappingModel)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "true".equals(includeInIntrospection);
|
||||||
|
}
|
||||||
|
|
||||||
public static void addAttributeConfig(List<ProviderConfigProperty> configProperties, Class<? extends ProtocolMapper> protocolMapperClass) {
|
public static void addAttributeConfig(List<ProviderConfigProperty> configProperties, Class<? extends ProtocolMapper> protocolMapperClass) {
|
||||||
addTokenClaimNameConfig(configProperties);
|
addTokenClaimNameConfig(configProperties);
|
||||||
addJsonTypeConfig(configProperties);
|
addJsonTypeConfig(configProperties);
|
||||||
|
@ -441,5 +464,15 @@ public class OIDCAttributeMapperHelper {
|
||||||
property.setHelpText(INCLUDE_IN_ACCESS_TOKEN_RESPONSE_HELP_TEXT);
|
property.setHelpText(INCLUDE_IN_ACCESS_TOKEN_RESPONSE_HELP_TEXT);
|
||||||
configProperties.add(property);
|
configProperties.add(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TokenIntrospectionTokenMapper.class.isAssignableFrom(protocolMapperClass)) {
|
||||||
|
ProviderConfigProperty property = new ProviderConfigProperty();
|
||||||
|
property.setName(INCLUDE_IN_INTROSPECTION);
|
||||||
|
property.setLabel(INCLUDE_IN_INTROSPECTION_LABEL);
|
||||||
|
property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
|
||||||
|
property.setDefaultValue("true");
|
||||||
|
property.setHelpText(INCLUDE_IN_INTROSPECTION_HELP_TEXT);
|
||||||
|
configProperties.add(property);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ import java.util.Map;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, UserInfoTokenMapper {
|
public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
|
||||||
|
|
||||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
|
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -111,6 +111,14 @@ public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
|
||||||
|
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||||
|
// the mapper is always executed and then other role mappers decide if the claims are really set to the token
|
||||||
|
setClaim(token, mappingModel, userSession, session, clientSessionCtx);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession session,
|
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession session,
|
||||||
ClientSessionContext clientSessionCtx) {
|
ClientSessionContext clientSessionCtx) {
|
||||||
|
|
|
@ -46,7 +46,7 @@ import java.util.List;
|
||||||
* @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
|
* @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
|
||||||
*/
|
*/
|
||||||
public class ScriptBasedOIDCProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper,
|
public class ScriptBasedOIDCProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper,
|
||||||
OIDCAccessTokenResponseMapper, EnvironmentDependentProviderFactory {
|
OIDCAccessTokenResponseMapper, TokenIntrospectionTokenMapper, EnvironmentDependentProviderFactory {
|
||||||
|
|
||||||
public static final String PROVIDER_ID = "oidc-script-based-protocol-mapper";
|
public static final String PROVIDER_ID = "oidc-script-based-protocol-mapper";
|
||||||
|
|
||||||
|
@ -197,14 +197,14 @@ public class ScriptBasedOIDCProtocolMapper extends AbstractOIDCProtocolMapper im
|
||||||
public static ProtocolMapperModel create(String name,
|
public static ProtocolMapperModel create(String name,
|
||||||
String userAttribute,
|
String userAttribute,
|
||||||
String tokenClaimName, String claimType,
|
String tokenClaimName, String claimType,
|
||||||
boolean accessToken, boolean idToken, String script, boolean multiValued) {
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint, String script, boolean multiValued) {
|
||||||
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute,
|
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute,
|
||||||
tokenClaimName, claimType,
|
tokenClaimName, claimType,
|
||||||
accessToken, idToken,
|
accessToken, idToken, introspectionEndpoint,
|
||||||
script);
|
script);
|
||||||
|
|
||||||
mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, String.valueOf(multiValued));
|
mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, String.valueOf(multiValued));
|
||||||
|
|
||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
package org.keycloak.protocol.oidc.mappers;
|
||||||
|
|
||||||
|
import org.keycloak.models.ClientSessionContext;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.ProtocolMapperModel;
|
||||||
|
import org.keycloak.models.UserSessionModel;
|
||||||
|
import org.keycloak.representations.AccessToken;
|
||||||
|
|
||||||
|
public interface TokenIntrospectionTokenMapper {
|
||||||
|
AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
|
||||||
|
UserSessionModel userSession, ClientSessionContext clientSessionCtx);
|
||||||
|
}
|
|
@ -37,7 +37,7 @@ import java.util.List;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
|
public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
|
||||||
|
|
||||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||||
|
|
||||||
|
@ -106,19 +106,19 @@ public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements O
|
||||||
public static ProtocolMapperModel createClaimMapper(String name,
|
public static ProtocolMapperModel createClaimMapper(String name,
|
||||||
String userAttribute,
|
String userAttribute,
|
||||||
String tokenClaimName, String claimType,
|
String tokenClaimName, String claimType,
|
||||||
boolean accessToken, boolean idToken, boolean multivalued) {
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint, boolean multivalued) {
|
||||||
return createClaimMapper(name, userAttribute, tokenClaimName, claimType,
|
return createClaimMapper(name, userAttribute, tokenClaimName, claimType,
|
||||||
accessToken, idToken, multivalued, false);
|
accessToken, idToken, introspectionEndpoint, multivalued, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolMapperModel createClaimMapper(String name,
|
public static ProtocolMapperModel createClaimMapper(String name,
|
||||||
String userAttribute,
|
String userAttribute,
|
||||||
String tokenClaimName, String claimType,
|
String tokenClaimName, String claimType,
|
||||||
boolean accessToken, boolean idToken,
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint,
|
||||||
boolean multivalued, boolean aggregateAttrs) {
|
boolean multivalued, boolean aggregateAttrs) {
|
||||||
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute,
|
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute,
|
||||||
tokenClaimName, claimType,
|
tokenClaimName, claimType,
|
||||||
accessToken, idToken,
|
accessToken, idToken, introspectionEndpoint,
|
||||||
PROVIDER_ID);
|
PROVIDER_ID);
|
||||||
|
|
||||||
if (multivalued) {
|
if (multivalued) {
|
||||||
|
@ -134,8 +134,8 @@ public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements O
|
||||||
public static ProtocolMapperModel createClaimMapper(String name,
|
public static ProtocolMapperModel createClaimMapper(String name,
|
||||||
String userAttribute,
|
String userAttribute,
|
||||||
String tokenClaimName, String claimType,
|
String tokenClaimName, String claimType,
|
||||||
boolean accessToken, boolean idToken) {
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
|
||||||
return createClaimMapper(name, userAttribute, tokenClaimName, claimType,
|
return createClaimMapper(name, userAttribute, tokenClaimName, claimType,
|
||||||
accessToken, idToken, false, false);
|
accessToken, idToken, introspectionEndpoint, false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,18 +135,18 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper {
|
||||||
public static ProtocolMapperModel create(String clientId, String clientRolePrefix,
|
public static ProtocolMapperModel create(String clientId, String clientRolePrefix,
|
||||||
String name,
|
String name,
|
||||||
String tokenClaimName,
|
String tokenClaimName,
|
||||||
boolean accessToken, boolean idToken) {
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
|
||||||
return create(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, false);
|
return create(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, introspectionEndpoint, false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolMapperModel create(String clientId, String clientRolePrefix,
|
public static ProtocolMapperModel create(String clientId, String clientRolePrefix,
|
||||||
String name,
|
String name,
|
||||||
String tokenClaimName,
|
String tokenClaimName,
|
||||||
boolean accessToken, boolean idToken, boolean multiValued) {
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint, boolean multiValued) {
|
||||||
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo",
|
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo",
|
||||||
tokenClaimName, "String",
|
tokenClaimName, "String",
|
||||||
accessToken, idToken, false,
|
accessToken, idToken, false, introspectionEndpoint,
|
||||||
PROVIDER_ID);
|
PROVIDER_ID);
|
||||||
|
|
||||||
mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, String.valueOf(multiValued));
|
mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, String.valueOf(multiValued));
|
||||||
|
|
|
@ -35,7 +35,7 @@ import java.util.List;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
|
public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
|
||||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
@ -89,10 +89,10 @@ public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OI
|
||||||
public static ProtocolMapperModel createClaimMapper(String name,
|
public static ProtocolMapperModel createClaimMapper(String name,
|
||||||
String userAttribute,
|
String userAttribute,
|
||||||
String tokenClaimName, String claimType,
|
String tokenClaimName, String claimType,
|
||||||
boolean accessToken, boolean idToken) {
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
|
||||||
return OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute,
|
return OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute,
|
||||||
tokenClaimName, claimType,
|
tokenClaimName, claimType,
|
||||||
accessToken, idToken,
|
accessToken, idToken, introspectionEndpoint,
|
||||||
PROVIDER_ID);
|
PROVIDER_ID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,18 +100,18 @@ public class UserRealmRoleMappingMapper extends AbstractUserRoleMappingMapper {
|
||||||
|
|
||||||
public static ProtocolMapperModel create(String realmRolePrefix,
|
public static ProtocolMapperModel create(String realmRolePrefix,
|
||||||
String name,
|
String name,
|
||||||
String tokenClaimName, boolean accessToken, boolean idToken) {
|
String tokenClaimName, boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
|
||||||
|
|
||||||
return create(realmRolePrefix, name, tokenClaimName, accessToken, idToken, false);
|
return create(realmRolePrefix, name, tokenClaimName, accessToken, idToken, introspectionEndpoint, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolMapperModel create(String realmRolePrefix,
|
public static ProtocolMapperModel create(String realmRolePrefix,
|
||||||
String name,
|
String name,
|
||||||
String tokenClaimName, boolean accessToken, boolean idToken, boolean multiValued) {
|
String tokenClaimName, boolean accessToken, boolean idToken, boolean introspectionEndpoint, boolean multiValued) {
|
||||||
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo",
|
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo",
|
||||||
tokenClaimName, "String",
|
tokenClaimName, "String",
|
||||||
accessToken, idToken, false,
|
accessToken, idToken, false, introspectionEndpoint,
|
||||||
PROVIDER_ID);
|
PROVIDER_ID);
|
||||||
|
|
||||||
mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, String.valueOf(multiValued));
|
mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, String.valueOf(multiValued));
|
||||||
mapper.getConfig().put(ProtocolMapperUtils.USER_MODEL_REALM_ROLE_MAPPING_ROLE_PREFIX, realmRolePrefix);
|
mapper.getConfig().put(ProtocolMapperUtils.USER_MODEL_REALM_ROLE_MAPPING_ROLE_PREFIX, realmRolePrefix);
|
||||||
|
|
|
@ -38,7 +38,7 @@ import java.util.Map;
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, OIDCAccessTokenResponseMapper, UserInfoTokenMapper {
|
public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, OIDCAccessTokenResponseMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
|
||||||
|
|
||||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
|
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setClaim(AccessTokenResponse accessTokenResponse, ProtocolMapperModel mappingModel, UserSessionModel userSession,
|
protected void setClaim(AccessTokenResponse accessTokenResponse, ProtocolMapperModel mappingModel, UserSessionModel userSession,
|
||||||
KeycloakSession keycloakSession, ClientSessionContext clientSessionCtx) {
|
KeycloakSession keycloakSession, ClientSessionContext clientSessionCtx) {
|
||||||
|
|
||||||
String noteName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_SESSION_NOTE);
|
String noteName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_SESSION_NOTE);
|
||||||
String noteValue = userSession.getNote(noteName);
|
String noteValue = userSession.getNote(noteName);
|
||||||
|
@ -101,14 +101,14 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements
|
||||||
public static ProtocolMapperModel createClaimMapper(String name,
|
public static ProtocolMapperModel createClaimMapper(String name,
|
||||||
String userSessionNote,
|
String userSessionNote,
|
||||||
String tokenClaimName, String jsonType,
|
String tokenClaimName, String jsonType,
|
||||||
boolean accessToken, boolean idToken) {
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
|
||||||
return createClaimMapper(name, userSessionNote, tokenClaimName, jsonType, accessToken, idToken, false);
|
return createClaimMapper(name, userSessionNote, tokenClaimName, jsonType, accessToken, idToken, false, introspectionEndpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolMapperModel createClaimMapper(String name,
|
public static ProtocolMapperModel createClaimMapper(String name,
|
||||||
String userSessionNote,
|
String userSessionNote,
|
||||||
String tokenClaimName, String jsonType,
|
String tokenClaimName, String jsonType,
|
||||||
boolean accessToken, boolean idToken, boolean userInfo) {
|
boolean accessToken, boolean idToken, boolean userInfo, boolean introspectionEndpoint) {
|
||||||
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
||||||
mapper.setName(name);
|
mapper.setName(name);
|
||||||
mapper.setProtocolMapper(PROVIDER_ID);
|
mapper.setProtocolMapper(PROVIDER_ID);
|
||||||
|
@ -120,6 +120,7 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements
|
||||||
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
|
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
|
||||||
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
|
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
|
||||||
if (userInfo) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
|
if (userInfo) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
|
||||||
|
if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true");
|
||||||
mapper.setConfig(config);
|
mapper.setConfig(config);
|
||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
@ -135,7 +136,7 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements
|
||||||
userSessionNoteDescriptor.toString(),
|
userSessionNoteDescriptor.toString(),
|
||||||
userSessionNoteDescriptor.getTokenClaim(),
|
userSessionNoteDescriptor.getTokenClaim(),
|
||||||
"String",
|
"String",
|
||||||
true, true
|
true, true, true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -170,7 +170,7 @@ public class ClientManager {
|
||||||
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_ID_PROTOCOL_MAPPER,
|
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_ID_PROTOCOL_MAPPER,
|
||||||
ServiceAccountConstants.CLIENT_ID,
|
ServiceAccountConstants.CLIENT_ID,
|
||||||
ServiceAccountConstants.CLIENT_ID, "String",
|
ServiceAccountConstants.CLIENT_ID, "String",
|
||||||
true, true);
|
true, true, true);
|
||||||
client.addProtocolMapper(protocolMapper);
|
client.addProtocolMapper(protocolMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ public class ClientManager {
|
||||||
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_HOST_PROTOCOL_MAPPER,
|
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_HOST_PROTOCOL_MAPPER,
|
||||||
ServiceAccountConstants.CLIENT_HOST,
|
ServiceAccountConstants.CLIENT_HOST,
|
||||||
ServiceAccountConstants.CLIENT_HOST, "String",
|
ServiceAccountConstants.CLIENT_HOST, "String",
|
||||||
true, true);
|
true, true, true);
|
||||||
client.addProtocolMapper(protocolMapper);
|
client.addProtocolMapper(protocolMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +189,7 @@ public class ClientManager {
|
||||||
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_ADDRESS_PROTOCOL_MAPPER,
|
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_ADDRESS_PROTOCOL_MAPPER,
|
||||||
ServiceAccountConstants.CLIENT_ADDRESS,
|
ServiceAccountConstants.CLIENT_ADDRESS,
|
||||||
ServiceAccountConstants.CLIENT_ADDRESS, "String",
|
ServiceAccountConstants.CLIENT_ADDRESS, "String",
|
||||||
true, true);
|
true, true, true);
|
||||||
client.addProtocolMapper(protocolMapper);
|
client.addProtocolMapper(protocolMapper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
package org.keycloak.services.util;
|
||||||
|
|
||||||
|
import org.keycloak.common.ClientConnection;
|
||||||
|
import org.keycloak.common.constants.ServiceAccountConstants;
|
||||||
|
import org.keycloak.events.Errors;
|
||||||
|
import org.keycloak.events.EventBuilder;
|
||||||
|
import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||||
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.UserSessionModel;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
|
import org.keycloak.protocol.oidc.TokenManager;
|
||||||
|
import org.keycloak.representations.AccessToken;
|
||||||
|
import org.keycloak.services.Urls;
|
||||||
|
import org.keycloak.services.managers.AuthenticationManager;
|
||||||
|
import org.keycloak.services.managers.UserSessionCrossDCManager;
|
||||||
|
import org.keycloak.services.managers.UserSessionManager;
|
||||||
|
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||||
|
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||||
|
import org.keycloak.utils.OAuth2Error;
|
||||||
|
|
||||||
|
public class UserSessionUtil {
|
||||||
|
|
||||||
|
public static UserSessionModel findValidSession(KeycloakSession session, RealmModel realm, AccessToken token, EventBuilder event, ClientModel client) {
|
||||||
|
OAuth2Error error = new OAuth2Error().json(false).realm(realm);
|
||||||
|
if (token.getSessionState() == null) {
|
||||||
|
return createTransientSessionForClient(session, realm, token, client);
|
||||||
|
}
|
||||||
|
|
||||||
|
UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), false, client.getId());
|
||||||
|
UserSessionModel offlineUserSession = null;
|
||||||
|
if (AuthenticationManager.isSessionValid(realm, userSession)) {
|
||||||
|
checkTokenIssuedAt(realm, token, userSession, event, client);
|
||||||
|
event.session(userSession);
|
||||||
|
return userSession;
|
||||||
|
} else {
|
||||||
|
offlineUserSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), true, client.getId());
|
||||||
|
if (AuthenticationManager.isOfflineSessionValid(realm, offlineUserSession)) {
|
||||||
|
checkTokenIssuedAt(realm, token, offlineUserSession, event, client);
|
||||||
|
event.session(offlineUserSession);
|
||||||
|
return offlineUserSession;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userSession == null && offlineUserSession == null) {
|
||||||
|
event.error(Errors.USER_SESSION_NOT_FOUND);
|
||||||
|
throw error.invalidToken("User session not found or doesn't have client attached on it");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userSession != null) {
|
||||||
|
event.session(userSession);
|
||||||
|
} else {
|
||||||
|
event.session(offlineUserSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
event.error(Errors.SESSION_EXPIRED);
|
||||||
|
throw error.invalidToken("Session expired");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UserSessionModel createTransientSessionForClient(KeycloakSession session, RealmModel realm, AccessToken token, ClientModel client) {
|
||||||
|
OAuth2Error error = new OAuth2Error().json(false).realm(realm);
|
||||||
|
// create a transient session
|
||||||
|
UserModel user = TokenManager.lookupUserFromStatelessToken(session, realm, token);
|
||||||
|
if (user == null) {
|
||||||
|
throw error.invalidToken("User not found");
|
||||||
|
}
|
||||||
|
ClientConnection clientConnection = session.getContext().getConnection();
|
||||||
|
UserSessionModel userSession = new UserSessionManager(session).createUserSession(KeycloakModelUtils.generateId(), realm, user, user.getUsername(), clientConnection.getRemoteAddr(),
|
||||||
|
ServiceAccountConstants.CLIENT_AUTH, false, null, null, UserSessionModel.SessionPersistenceState.TRANSIENT);
|
||||||
|
// attach an auth session for the client
|
||||||
|
RootAuthenticationSessionModel rootAuthSession = session.authenticationSessions().createRootAuthenticationSession(realm);
|
||||||
|
AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(client);
|
||||||
|
authSession.setAuthenticatedUser(userSession.getUser());
|
||||||
|
authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
|
authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
|
||||||
|
AuthenticationManager.setClientScopesInSession(authSession);
|
||||||
|
TokenManager.attachAuthenticationSession(session, userSession, authSession);
|
||||||
|
return userSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkTokenIssuedAt(RealmModel realm, AccessToken token, UserSessionModel userSession, EventBuilder event, ClientModel client) {
|
||||||
|
OAuth2Error error = new OAuth2Error().json(false).realm(realm);
|
||||||
|
if (token.isIssuedBeforeSessionStart(userSession.getStarted())) {
|
||||||
|
event.error(Errors.INVALID_TOKEN);
|
||||||
|
throw error.invalidToken("Stale token");
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
|
||||||
|
if (token.isIssuedBeforeSessionStart(clientSession.getStarted())) {
|
||||||
|
event.error(Errors.INVALID_TOKEN);
|
||||||
|
throw error.invalidToken("Stale token");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -817,7 +817,7 @@ public class TestingResourceProvider implements RealmResourceProvider {
|
||||||
clientScopeModel.setIncludeInTokenScope(true);
|
clientScopeModel.setIncludeInTokenScope(true);
|
||||||
|
|
||||||
// Add audience protocol mapper
|
// Add audience protocol mapper
|
||||||
ProtocolMapperModel audienceMapper = AudienceProtocolMapper.createClaimMapper("Audience for " + clientId, clientId, null,true, false);
|
ProtocolMapperModel audienceMapper = AudienceProtocolMapper.createClaimMapper("Audience for " + clientId, clientId, null,true, false, true );
|
||||||
clientScopeModel.addProtocolMapper(audienceMapper);
|
clientScopeModel.addProtocolMapper(audienceMapper);
|
||||||
|
|
||||||
return clientScopeModel.getId();
|
return clientScopeModel.getId();
|
||||||
|
|
|
@ -21,12 +21,12 @@ public class KcOidcBrokerSubMatchIntrospectionTest extends AbstractBrokerTest {
|
||||||
@Override
|
@Override
|
||||||
public List<ClientRepresentation> createConsumerClients() {
|
public List<ClientRepresentation> createConsumerClients() {
|
||||||
List<ClientRepresentation> clients = new ArrayList<>(super.createConsumerClients());
|
List<ClientRepresentation> clients = new ArrayList<>(super.createConsumerClients());
|
||||||
|
|
||||||
clients.add(ClientBuilder.create().clientId("consumer-client")
|
clients.add(ClientBuilder.create().clientId("consumer-client")
|
||||||
.publicClient()
|
.publicClient()
|
||||||
.redirectUris(getConsumerRoot() + "/auth/realms/master/app/auth/*")
|
.redirectUris(getConsumerRoot() + "/auth/realms/master/app/auth/*")
|
||||||
.publicClient().build());
|
.publicClient().build());
|
||||||
|
|
||||||
return clients;
|
return clients;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,14 +36,14 @@ public class KcOidcBrokerSubMatchIntrospectionTest extends AbstractBrokerTest {
|
||||||
List<ProtocolMapperRepresentation> mappers = new ArrayList<>();
|
List<ProtocolMapperRepresentation> mappers = new ArrayList<>();
|
||||||
|
|
||||||
ProtocolMapperRepresentation hardcodedClaim = createHardcodedClaim("sub-override", "sub", "overriden",
|
ProtocolMapperRepresentation hardcodedClaim = createHardcodedClaim("sub-override", "sub", "overriden",
|
||||||
"String", false, false);
|
"String", false, false, false);
|
||||||
|
|
||||||
hardcodedClaim.getConfig().put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, Boolean.TRUE.toString());
|
hardcodedClaim.getConfig().put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, Boolean.TRUE.toString());
|
||||||
|
|
||||||
mappers.add(hardcodedClaim);
|
mappers.add(hardcodedClaim);
|
||||||
|
|
||||||
clients.get(0).setProtocolMappers(mappers);
|
clients.get(0).setProtocolMappers(mappers);
|
||||||
|
|
||||||
return clients;
|
return clients;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -100,7 +100,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
|
||||||
IdentityProviderMapperRepresentation attrMapper2 = new IdentityProviderMapperRepresentation();
|
IdentityProviderMapperRepresentation attrMapper2 = new IdentityProviderMapperRepresentation();
|
||||||
attrMapper2.setName("user-role-mapper");
|
attrMapper2.setName("user-role-mapper");
|
||||||
attrMapper2.setIdentityProviderMapper(ExternalKeycloakRoleToRoleMapper.PROVIDER_ID);
|
attrMapper2.setIdentityProviderMapper(ExternalKeycloakRoleToRoleMapper.PROVIDER_ID);
|
||||||
attrMapper2.setConfig(ImmutableMap.<String,String>builder()
|
attrMapper2.setConfig(ImmutableMap.<String, String>builder()
|
||||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||||
.put("external.role", ROLE_USER)
|
.put("external.role", ROLE_USER)
|
||||||
.put("role", ROLE_USER)
|
.put("role", ROLE_USER)
|
||||||
|
@ -114,7 +114,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
|
||||||
IdentityProviderMapperRepresentation friendlyManagerMapper = new IdentityProviderMapperRepresentation();
|
IdentityProviderMapperRepresentation friendlyManagerMapper = new IdentityProviderMapperRepresentation();
|
||||||
friendlyManagerMapper.setName("friendly-manager-role-mapper");
|
friendlyManagerMapper.setName("friendly-manager-role-mapper");
|
||||||
friendlyManagerMapper.setIdentityProviderMapper(ExternalKeycloakRoleToRoleMapper.PROVIDER_ID);
|
friendlyManagerMapper.setIdentityProviderMapper(ExternalKeycloakRoleToRoleMapper.PROVIDER_ID);
|
||||||
friendlyManagerMapper.setConfig(ImmutableMap.<String,String>builder()
|
friendlyManagerMapper.setConfig(ImmutableMap.<String, String>builder()
|
||||||
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
|
||||||
.put("external.role", ROLE_FRIENDLY_MANAGER)
|
.put("external.role", ROLE_FRIENDLY_MANAGER)
|
||||||
.put("role", ROLE_FRIENDLY_MANAGER)
|
.put("role", ROLE_FRIENDLY_MANAGER)
|
||||||
|
@ -197,7 +197,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
|
||||||
|
|
||||||
logInWithBroker(bc);
|
logInWithBroker(bc);
|
||||||
|
|
||||||
waitForPage(driver, loginIsDenied? "We are sorry..." : "update account information", false);
|
waitForPage(driver, loginIsDenied ? "We are sorry..." : "update account information", false);
|
||||||
if (loginIsDenied) {
|
if (loginIsDenied) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -243,7 +243,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
|
||||||
ClientRepresentation brokerApp = clients.findByClientId("brokerapp").get(0);
|
ClientRepresentation brokerApp = clients.findByClientId("brokerapp").get(0);
|
||||||
IdentityProviderResource identityProviderResource = getIdentityProviderResource();
|
IdentityProviderResource identityProviderResource = getIdentityProviderResource();
|
||||||
|
|
||||||
clients.get(brokerApp.getId()).getProtocolMappers().createMapper(createHardcodedClaim("hard-coded", "hard-coded", "hard-coded", "String", true, true)).close();
|
clients.get(brokerApp.getId()).getProtocolMappers().createMapper(createHardcodedClaim("hard-coded", "hard-coded", "hard-coded", "String", true, true, true)).close();
|
||||||
|
|
||||||
IdentityProviderMapperRepresentation hardCodedSessionNoteMapper = new IdentityProviderMapperRepresentation();
|
IdentityProviderMapperRepresentation hardCodedSessionNoteMapper = new IdentityProviderMapperRepresentation();
|
||||||
|
|
||||||
|
@ -445,7 +445,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
|
||||||
RealmResource realm = adminClient.realm(bc.providerRealmName());
|
RealmResource realm = adminClient.realm(bc.providerRealmName());
|
||||||
ClientRepresentation rep = realm.clients().findByClientId(BrokerTestConstants.CLIENT_ID).get(0);
|
ClientRepresentation rep = realm.clients().findByClientId(BrokerTestConstants.CLIENT_ID).get(0);
|
||||||
ClientResource clientResource = realm.clients().get(rep.getId());
|
ClientResource clientResource = realm.clients().get(rep.getId());
|
||||||
ProtocolMapperRepresentation hardCodedAzp = createHardcodedClaim("hard", "azp", "invalid-azp", ProviderConfigProperty.STRING_TYPE, true, true);
|
ProtocolMapperRepresentation hardCodedAzp = createHardcodedClaim("hard", "azp", "invalid-azp", ProviderConfigProperty.STRING_TYPE, true, true, true);
|
||||||
clientResource.getProtocolMappers().createMapper(hardCodedAzp);
|
clientResource.getProtocolMappers().createMapper(hardCodedAzp);
|
||||||
|
|
||||||
log.debug("Logging in");
|
log.debug("Logging in");
|
||||||
|
@ -469,7 +469,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
|
||||||
RealmResource realm = adminClient.realm(bc.providerRealmName());
|
RealmResource realm = adminClient.realm(bc.providerRealmName());
|
||||||
ClientRepresentation rep = realm.clients().findByClientId(BrokerTestConstants.CLIENT_ID).get(0);
|
ClientRepresentation rep = realm.clients().findByClientId(BrokerTestConstants.CLIENT_ID).get(0);
|
||||||
ClientResource clientResource = realm.clients().get(rep.getId());
|
ClientResource clientResource = realm.clients().get(rep.getId());
|
||||||
ProtocolMapperRepresentation hardCodedAzp = createHardcodedClaim("hard", "aud", "invalid-aud", ProviderConfigProperty.LIST_TYPE, true, true);
|
ProtocolMapperRepresentation hardCodedAzp = createHardcodedClaim("hard", "aud", "invalid-aud", ProviderConfigProperty.LIST_TYPE, true, true, true);
|
||||||
clientResource.getProtocolMappers().createMapper(hardCodedAzp);
|
clientResource.getProtocolMappers().createMapper(hardCodedAzp);
|
||||||
|
|
||||||
log.debug("Logging in");
|
log.debug("Logging in");
|
||||||
|
@ -564,7 +564,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
|
||||||
|
|
||||||
loginFetchingUserFromUserEndpoint(true);
|
loginFetchingUserFromUserEndpoint(true);
|
||||||
Assert.assertEquals("The ID token issued by the identity provider does not match the configured essential claim. Please contact your administrator.",
|
Assert.assertEquals("The ID token issued by the identity provider does not match the configured essential claim. Please contact your administrator.",
|
||||||
loginPage.getInstruction());
|
loginPage.getInstruction());
|
||||||
|
|
||||||
|
|
||||||
List<UserRepresentation> users = realmsResouce().realm(bc.consumerRealmName()).users().search(bc.getUserLogin());
|
List<UserRepresentation> users = realmsResouce().realm(bc.consumerRealmName()).users().search(bc.getUserLogin());
|
||||||
|
@ -572,8 +572,8 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void postInitializeUser(UserRepresentation user) {
|
protected void postInitializeUser(UserRepresentation user) {
|
||||||
user.setAttributes(ImmutableMap.<String, List<String>> builder()
|
user.setAttributes(ImmutableMap.<String, List<String>>builder()
|
||||||
.put(USER_ATTRIBUTE_NAME, ImmutableList.<String> builder().add(USER_ATTRIBUTE_VALUE).build())
|
.put(USER_ATTRIBUTE_NAME, ImmutableList.<String>builder().add(USER_ATTRIBUTE_VALUE).build())
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -585,8 +585,8 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
|
||||||
assertThat(claimFilterValue, Matchers.notNullValue());
|
assertThat(claimFilterValue, Matchers.notNullValue());
|
||||||
|
|
||||||
if (idProvider.getConfig().getOrDefault(IdentityProviderModel.FILTERED_BY_CLAIMS, "false").equals(Boolean.toString(filteredByClaim)) &&
|
if (idProvider.getConfig().getOrDefault(IdentityProviderModel.FILTERED_BY_CLAIMS, "false").equals(Boolean.toString(filteredByClaim)) &&
|
||||||
idProvider.getConfig().getOrDefault(IdentityProviderModel.CLAIM_FILTER_NAME, "").equals(claimFilterName) &&
|
idProvider.getConfig().getOrDefault(IdentityProviderModel.CLAIM_FILTER_NAME, "").equals(claimFilterName) &&
|
||||||
idProvider.getConfig().getOrDefault(IdentityProviderModel.CLAIM_FILTER_VALUE, "").equals(claimFilterValue)
|
idProvider.getConfig().getOrDefault(IdentityProviderModel.CLAIM_FILTER_VALUE, "").equals(claimFilterValue)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -722,7 +722,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateIdPSyncMode(IdentityProviderRepresentation idProvider, IdentityProviderResource idProviderResource,
|
private void updateIdPSyncMode(IdentityProviderRepresentation idProvider, IdentityProviderResource idProviderResource,
|
||||||
IdentityProviderSyncMode syncMode, boolean trustEmail) {
|
IdentityProviderSyncMode syncMode, boolean trustEmail) {
|
||||||
assertThat(idProvider, Matchers.notNullValue());
|
assertThat(idProvider, Matchers.notNullValue());
|
||||||
assertThat(idProviderResource, Matchers.notNullValue());
|
assertThat(idProviderResource, Matchers.notNullValue());
|
||||||
assertThat(syncMode, Matchers.notNullValue());
|
assertThat(syncMode, Matchers.notNullValue());
|
||||||
|
@ -755,6 +755,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final CustomKcOidcBrokerConfiguration BROKER_CONFIG_INSTANCE = new CustomKcOidcBrokerConfiguration();
|
private static final CustomKcOidcBrokerConfiguration BROKER_CONFIG_INSTANCE = new CustomKcOidcBrokerConfiguration();
|
||||||
|
|
||||||
static class CustomKcOidcBrokerConfiguration extends KcOidcBrokerConfiguration {
|
static class CustomKcOidcBrokerConfiguration extends KcOidcBrokerConfiguration {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -766,7 +767,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
|
||||||
userAttrMapper.setName(USER_ATTRIBUTE_NAME);
|
userAttrMapper.setName(USER_ATTRIBUTE_NAME);
|
||||||
userAttrMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
userAttrMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
userAttrMapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID);
|
userAttrMapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID);
|
||||||
|
|
||||||
Map<String, String> userAttrMapperConfig = userAttrMapper.getConfig();
|
Map<String, String> userAttrMapperConfig = userAttrMapper.getConfig();
|
||||||
userAttrMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, USER_ATTRIBUTE_NAME);
|
userAttrMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, USER_ATTRIBUTE_NAME);
|
||||||
userAttrMapperConfig.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, USER_ATTRIBUTE_NAME);
|
userAttrMapperConfig.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, USER_ATTRIBUTE_NAME);
|
||||||
|
@ -781,6 +782,6 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
|
||||||
client.setProtocolMappers(mappers);
|
client.setProtocolMappers(mappers);
|
||||||
|
|
||||||
return clients;
|
return clients;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,18 +50,16 @@ import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
|
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
|
||||||
import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
|
import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
|
||||||
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
|
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeatures;
|
import org.keycloak.testsuite.arquillian.annotation.EnableFeatures;
|
||||||
import org.keycloak.testsuite.util.AdminClientUtil;
|
import org.keycloak.testsuite.util.AdminClientUtil;
|
||||||
import org.keycloak.testsuite.util.ClientBuilder;
|
|
||||||
import org.keycloak.testsuite.util.OAuthClient;
|
import org.keycloak.testsuite.util.OAuthClient;
|
||||||
import org.keycloak.util.BasicAuthHelper;
|
import org.keycloak.util.BasicAuthHelper;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
@EnableFeatures({ @EnableFeature(Profile.Feature.TOKEN_EXCHANGE), @EnableFeature(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ) })
|
@EnableFeatures({@EnableFeature(Profile.Feature.TOKEN_EXCHANGE), @EnableFeature(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ)})
|
||||||
public final class KcOidcBrokerTokenExchangeTest extends AbstractInitializedBaseBrokerTest {
|
public final class KcOidcBrokerTokenExchangeTest extends AbstractInitializedBaseBrokerTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -77,7 +75,7 @@ public final class KcOidcBrokerTokenExchangeTest extends AbstractInitializedBase
|
||||||
brokerApp.setDirectAccessGrantsEnabled(true);
|
brokerApp.setDirectAccessGrantsEnabled(true);
|
||||||
ClientResource brokerAppResource = providerRealm.clients().get(brokerApp.getId());
|
ClientResource brokerAppResource = providerRealm.clients().get(brokerApp.getId());
|
||||||
brokerAppResource.update(brokerApp);
|
brokerAppResource.update(brokerApp);
|
||||||
brokerAppResource.getProtocolMappers().createMapper(createHardcodedClaim("hard-coded", "hard-coded", "hard-coded", "String", true, true)).close();
|
brokerAppResource.getProtocolMappers().createMapper(createHardcodedClaim("hard-coded", "hard-coded", "hard-coded", "String", true, true, true)).close();
|
||||||
|
|
||||||
IdentityProviderMapperRepresentation hardCodedSessionNoteMapper = new IdentityProviderMapperRepresentation();
|
IdentityProviderMapperRepresentation hardCodedSessionNoteMapper = new IdentityProviderMapperRepresentation();
|
||||||
hardCodedSessionNoteMapper.setName("hard-coded");
|
hardCodedSessionNoteMapper.setName("hard-coded");
|
||||||
|
|
|
@ -47,6 +47,7 @@ import org.keycloak.testsuite.util.AccountHelper;
|
||||||
import org.keycloak.testsuite.util.OAuthClient;
|
import org.keycloak.testsuite.util.OAuthClient;
|
||||||
import org.keycloak.testsuite.util.TestAppHelper;
|
import org.keycloak.testsuite.util.TestAppHelper;
|
||||||
|
|
||||||
|
|
||||||
import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId;
|
import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -180,7 +181,7 @@ public abstract class AbstractKerberosSingleRealmTest extends AbstractKerberosTe
|
||||||
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
|
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
|
||||||
KerberosConstants.GSS_DELEGATION_CREDENTIAL,
|
KerberosConstants.GSS_DELEGATION_CREDENTIAL,
|
||||||
KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
|
KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
|
||||||
true, false, true);
|
true, false, true, true);
|
||||||
ProtocolMapperRepresentation protocolMapperRep = ModelToRepresentation.toRepresentation(protocolMapper);
|
ProtocolMapperRepresentation protocolMapperRep = ModelToRepresentation.toRepresentation(protocolMapper);
|
||||||
ClientResource clientResource = findClientByClientId(testRealmResource(), "kerberos-app");
|
ClientResource clientResource = findClientByClientId(testRealmResource(), "kerberos-app");
|
||||||
Response response = clientResource.getProtocolMappers().createMapper(protocolMapperRep);
|
Response response = clientResource.getProtocolMappers().createMapper(protocolMapperRep);
|
||||||
|
|
|
@ -105,8 +105,8 @@ public class LDAPMultipleAttributesTest extends AbstractLDAPTest {
|
||||||
ldapClient.addRedirectUri("/ldap-portal");
|
ldapClient.addRedirectUri("/ldap-portal");
|
||||||
ldapClient.addRedirectUri("/ldap-portal/*");
|
ldapClient.addRedirectUri("/ldap-portal/*");
|
||||||
ldapClient.setManagementUrl("/ldap-portal");
|
ldapClient.setManagementUrl("/ldap-portal");
|
||||||
ldapClient.addProtocolMapper(UserAttributeMapper.createClaimMapper("postalCode", "postal_code", "postal_code", "String", true, true, true));
|
ldapClient.addProtocolMapper(UserAttributeMapper.createClaimMapper("postalCode", "postal_code", "postal_code", "String", true, true, true, true));
|
||||||
ldapClient.addProtocolMapper(UserAttributeMapper.createClaimMapper("street", "street", "street", "String", true, true, false));
|
ldapClient.addProtocolMapper(UserAttributeMapper.createClaimMapper("street", "street", "street", "String", true, true, true, false));
|
||||||
ldapClient.addScopeMapping(appRealm.getRole("user"));
|
ldapClient.addScopeMapping(appRealm.getRole("user"));
|
||||||
ldapClient.setSecret("password");
|
ldapClient.setSecret("password");
|
||||||
});
|
});
|
||||||
|
@ -237,7 +237,6 @@ public class LDAPMultipleAttributesTest extends AbstractLDAPTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -819,7 +819,7 @@ public class AccessTokenTest extends AbstractKeycloakTest {
|
||||||
String clientScopeId = ApiUtil.getCreatedId(response);
|
String clientScopeId = ApiUtil.getCreatedId(response);
|
||||||
response.close();
|
response.close();
|
||||||
ClientScopeResource clientScopeResource = adminClient.proxy(ClientScopeResource.class, scopeUri);
|
ClientScopeResource clientScopeResource = adminClient.proxy(ClientScopeResource.class, scopeUri);
|
||||||
ProtocolMapperModel hard = HardcodedClaim.create("hard", "hard", "coded", "String", true, true);
|
ProtocolMapperModel hard = HardcodedClaim.create("hard", "hard", "coded", "String", true, true, true);
|
||||||
ProtocolMapperRepresentation mapper = ModelToRepresentation.toRepresentation(hard);
|
ProtocolMapperRepresentation mapper = ModelToRepresentation.toRepresentation(hard);
|
||||||
response = clientScopeResource.getProtocolMappers().createMapper(mapper);
|
response = clientScopeResource.getProtocolMappers().createMapper(mapper);
|
||||||
assertEquals(201, response.getStatus());
|
assertEquals(201, response.getStatus());
|
||||||
|
|
|
@ -175,7 +175,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
directPublic.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
directPublic.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
directPublic.setFullScopeAllowed(false);
|
directPublic.setFullScopeAllowed(false);
|
||||||
directPublic.addRedirectUri("*");
|
directPublic.addRedirectUri("*");
|
||||||
directPublic.addProtocolMapper(AudienceProtocolMapper.createClaimMapper("client-exchanger-audience", clientExchanger.getClientId(), null, true, false));
|
directPublic.addProtocolMapper(AudienceProtocolMapper.createClaimMapper("client-exchanger-audience", clientExchanger.getClientId(), null, true, false, true));
|
||||||
|
|
||||||
ClientModel directUntrustedPublic = realm.addClient("direct-public-untrusted");
|
ClientModel directUntrustedPublic = realm.addClient("direct-public-untrusted");
|
||||||
directUntrustedPublic.setClientId("direct-public-untrusted");
|
directUntrustedPublic.setClientId("direct-public-untrusted");
|
||||||
|
@ -186,7 +186,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
directUntrustedPublic.setFullScopeAllowed(false);
|
directUntrustedPublic.setFullScopeAllowed(false);
|
||||||
directUntrustedPublic.addRedirectUri("*");
|
directUntrustedPublic.addRedirectUri("*");
|
||||||
directUntrustedPublic.setAttribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, "+");
|
directUntrustedPublic.setAttribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, "+");
|
||||||
directUntrustedPublic.addProtocolMapper(AudienceProtocolMapper.createClaimMapper("client-exchanger-audience", clientExchanger.getClientId(), null, true, false));
|
directUntrustedPublic.addProtocolMapper(AudienceProtocolMapper.createClaimMapper("client-exchanger-audience", clientExchanger.getClientId(), null, true, false, true));
|
||||||
|
|
||||||
ClientModel directNoSecret = realm.addClient("direct-no-secret");
|
ClientModel directNoSecret = realm.addClient("direct-no-secret");
|
||||||
directNoSecret.setClientId("direct-no-secret");
|
directNoSecret.setClientId("direct-no-secret");
|
||||||
|
@ -538,16 +538,16 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
try (Response response = exchangeUrl.request()
|
try (Response response = exchangeUrl.request()
|
||||||
.header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader("client-exchanger", "secret"))
|
.header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader("client-exchanger", "secret"))
|
||||||
.post(Entity.form(
|
.post(Entity.form(
|
||||||
new Form()
|
new Form()
|
||||||
.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE)
|
.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE)
|
||||||
.param(OAuth2Constants.SUBJECT_TOKEN, accessToken)
|
.param(OAuth2Constants.SUBJECT_TOKEN, accessToken)
|
||||||
.param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE)
|
.param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE)
|
||||||
.param(OAuth2Constants.REQUESTED_SUBJECT, "impersonated-user")
|
.param(OAuth2Constants.REQUESTED_SUBJECT, "impersonated-user")
|
||||||
.param(OAuth2Constants.AUDIENCE, "target")
|
.param(OAuth2Constants.AUDIENCE, "target")
|
||||||
|
|
||||||
))) {
|
))) {
|
||||||
org.junit.Assert.assertEquals(200, response.getStatus());
|
org.junit.Assert.assertEquals(200, response.getStatus());
|
||||||
AccessTokenResponse accessTokenResponse = response.readEntity(AccessTokenResponse.class);
|
AccessTokenResponse accessTokenResponse = response.readEntity(AccessTokenResponse.class);
|
||||||
String exchangedTokenString = accessTokenResponse.getToken();
|
String exchangedTokenString = accessTokenResponse.getToken();
|
||||||
|
|
|
@ -401,7 +401,7 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
|
||||||
String fooScopeId = ApiUtil.getCreatedId(response);
|
String fooScopeId = ApiUtil.getCreatedId(response);
|
||||||
response.close();
|
response.close();
|
||||||
|
|
||||||
ProtocolMapperRepresentation protocolMapper = ProtocolMapperUtil.createAddressMapper(true, true, true);
|
ProtocolMapperRepresentation protocolMapper = ProtocolMapperUtil.createAddressMapper(true, true, true, true);
|
||||||
response = appRealm.clientScopes().get(fooScopeId).getProtocolMappers().createMapper(protocolMapper);
|
response = appRealm.clientScopes().get(fooScopeId).getProtocolMappers().createMapper(protocolMapper);
|
||||||
response.close();
|
response.close();
|
||||||
|
|
||||||
|
|
|
@ -153,11 +153,11 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
reconnectAdminClient();
|
reconnectAdminClient();
|
||||||
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
|
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
|
||||||
|
|
||||||
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper1","computed-via-script", "computed-via-script", "String", true, true, "script-scripts/test-script-mapper1.js", false)).close();
|
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper1","computed-via-script", "computed-via-script", "String", true, true, true,"script-scripts/test-script-mapper1.js", false)).close();
|
||||||
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper2","multiValued-via-script", "multiValued-via-script", "String", true, true, "script-scripts/test-script-mapper2.js", true)).close();
|
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper2","multiValued-via-script", "multiValued-via-script", "String", true, true, true, "script-scripts/test-script-mapper2.js", true)).close();
|
||||||
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3","computed-json-via-script", "computed-json-via-script", "JSON", true, true, "script-scripts/test-script-mapper3.js", false)).close();
|
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3","computed-json-via-script", "computed-json-via-script", "JSON", true, true, true, "script-scripts/test-script-mapper3.js", false)).close();
|
||||||
|
|
||||||
Response response = app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3", "syntax-error-script", "syntax-error-script", "String", true, true, "script-scripts/test-bad-script-mapper3.js", false));
|
Response response = app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3", "syntax-error-script", "syntax-error-script", "String", true, true, true, "script-scripts/test-bad-script-mapper3.js", false));
|
||||||
assertThat(response.getStatusInfo().getFamily(), is(Response.Status.Family.CLIENT_ERROR));
|
assertThat(response.getStatusInfo().getFamily(), is(Response.Status.Family.CLIENT_ERROR));
|
||||||
response.close();
|
response.close();
|
||||||
}
|
}
|
||||||
|
@ -199,31 +199,31 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
|
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
|
||||||
|
|
||||||
ProtocolMapperRepresentation mapper = createAddressMapper(true, true, true);
|
ProtocolMapperRepresentation mapper = createAddressMapper(true, true, true, true);
|
||||||
mapper.getConfig().put(AddressMapper.getModelPropertyName(AddressClaimSet.REGION), "region_some");
|
mapper.getConfig().put(AddressMapper.getModelPropertyName(AddressClaimSet.REGION), "region_some");
|
||||||
mapper.getConfig().put(AddressMapper.getModelPropertyName(AddressClaimSet.COUNTRY), "country_some");
|
mapper.getConfig().put(AddressMapper.getModelPropertyName(AddressClaimSet.COUNTRY), "country_some");
|
||||||
mapper.getConfig().remove(AddressMapper.getModelPropertyName(AddressClaimSet.POSTAL_CODE)); // Even if we remove protocolMapper config property, it should still default to postal_code
|
mapper.getConfig().remove(AddressMapper.getModelPropertyName(AddressClaimSet.POSTAL_CODE)); // Even if we remove protocolMapper config property, it should still default to postal_code
|
||||||
app.getProtocolMappers().createMapper(mapper).close();
|
app.getProtocolMappers().createMapper(mapper).close();
|
||||||
|
|
||||||
ProtocolMapperRepresentation hard = createHardcodedClaim("hard", "hard", "coded", "String", true, true);
|
ProtocolMapperRepresentation hard = createHardcodedClaim("hard", "hard", "coded", "String", true, true, true);
|
||||||
app.getProtocolMappers().createMapper(hard).close();
|
app.getProtocolMappers().createMapper(hard).close();
|
||||||
app.getProtocolMappers().createMapper(createHardcodedClaim("hard-nested", "nested.hard", "coded-nested", "String", true, true)).close();
|
app.getProtocolMappers().createMapper(createHardcodedClaim("hard-nested", "nested.hard", "coded-nested", "String", true, true, true)).close();
|
||||||
app.getProtocolMappers().createMapper(createClaimMapper("custom phone", "phone", "home_phone", "String", true, true, true)).close();
|
app.getProtocolMappers().createMapper(createClaimMapper("custom phone", "phone", "home_phone", "String", true, true, true, true)).close();
|
||||||
app.getProtocolMappers().createMapper(createClaimMapper("nested phone", "phone", "home.phone", "String", true, true, true)).close();
|
app.getProtocolMappers().createMapper(createClaimMapper("nested phone", "phone", "home.phone", "String", true, true, true, true)).close();
|
||||||
app.getProtocolMappers().createMapper(createClaimMapper("dotted phone", "phone", "home\\.phone", "String", true, true, true)).close();
|
app.getProtocolMappers().createMapper(createClaimMapper("dotted phone", "phone", "home\\.phone", "String", true, true, true, true)).close();
|
||||||
app.getProtocolMappers().createMapper(createClaimMapper("departments", "departments", "department", "String", true, true, true)).close();
|
app.getProtocolMappers().createMapper(createClaimMapper("departments", "departments", "department", "String", true, true, true, true)).close();
|
||||||
app.getProtocolMappers().createMapper(createClaimMapper("firstDepartment", "departments", "firstDepartment", "String", true, true, false)).close();
|
app.getProtocolMappers().createMapper(createClaimMapper("firstDepartment", "departments", "firstDepartment", "String", true, true, true,false)).close();
|
||||||
app.getProtocolMappers().createMapper(createHardcodedRole("hard-realm", "hardcoded")).close();
|
app.getProtocolMappers().createMapper(createHardcodedRole("hard-realm", "hardcoded")).close();
|
||||||
app.getProtocolMappers().createMapper(createHardcodedRole("hard-app", "app.hardcoded")).close();
|
app.getProtocolMappers().createMapper(createHardcodedRole("hard-app", "app.hardcoded")).close();
|
||||||
app.getProtocolMappers().createMapper(createRoleNameMapper("rename-app-role", "test-app.customer-user", "realm-user")).close();
|
app.getProtocolMappers().createMapper(createRoleNameMapper("rename-app-role", "test-app.customer-user", "realm-user")).close();
|
||||||
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper1","computed-via-script", "computed-via-script", "String", true, true, "'hello_' + user.username", false)).close();
|
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper1","computed-via-script", "computed-via-script", "String", true, true, true, "'hello_' + user.username", false)).close();
|
||||||
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper2","multiValued-via-script", "multiValued-via-script", "String", true, true, "new java.util.ArrayList(['A','B'])", true)).close();
|
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper2","multiValued-via-script", "multiValued-via-script", "String", true, true, true, "new java.util.ArrayList(['A','B'])", true)).close();
|
||||||
app.getProtocolMappers().createMapper(createClaimMapper("json-attribute-mapper", "json-attribute", "claim-from-json-attribute",
|
app.getProtocolMappers().createMapper(createClaimMapper("json-attribute-mapper", "json-attribute", "claim-from-json-attribute",
|
||||||
"JSON", true, true, false)).close();
|
"JSON", true, true, true, false)).close();
|
||||||
app.getProtocolMappers().createMapper(createClaimMapper("json-attribute-mapper-multi", "json-attribute-multi", "claim-from-json-attribute-multi",
|
app.getProtocolMappers().createMapper(createClaimMapper("json-attribute-mapper-multi", "json-attribute-multi", "claim-from-json-attribute-multi",
|
||||||
"JSON", true, true, true)).close();
|
"JSON", true, true, true, true)).close();
|
||||||
|
|
||||||
Response response = app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3", "syntax-error-script", "syntax-error-script", "String", true, true, "func_tion foo(){ return 'fail';} foo()", false));
|
Response response = app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3", "syntax-error-script", "syntax-error-script", "String", true, true, true, "func_tion foo(){ return 'fail';} foo()", false));
|
||||||
assertThat(response.getStatusInfo().getFamily(), is(Response.Status.Family.CLIENT_ERROR));
|
assertThat(response.getStatusInfo().getFamily(), is(Response.Status.Family.CLIENT_ERROR));
|
||||||
response.close();
|
response.close();
|
||||||
}
|
}
|
||||||
|
@ -360,10 +360,10 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
// create a user attr mapping for some claims that exist as properties in the tokens
|
// create a user attr mapping for some claims that exist as properties in the tokens
|
||||||
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
|
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
|
||||||
app.getProtocolMappers().createMapper(createClaimMapper("userid-as-sub", "userid", "sub", "String", true, true, false)).close();
|
app.getProtocolMappers().createMapper(createClaimMapper("userid-as-sub", "userid", "sub", "String", true, true, true,false)).close();
|
||||||
app.getProtocolMappers().createMapper(createClaimMapper("useraud", "useraud", "aud", "String", true, true, true)).close();
|
app.getProtocolMappers().createMapper(createClaimMapper("useraud", "useraud", "aud", "String", true, true, true, true)).close();
|
||||||
app.getProtocolMappers().createMapper(createHardcodedClaim("website-hardcoded", "website", "http://localhost", "String", true, true)).close();
|
app.getProtocolMappers().createMapper(createHardcodedClaim("website-hardcoded", "website", "http://localhost", "String", true, true, true)).close();
|
||||||
app.getProtocolMappers().createMapper(createHardcodedClaim("iat-hardcoded", "iat", "123", "long", true, false)).close();
|
app.getProtocolMappers().createMapper(createHardcodedClaim("iat-hardcoded", "iat", "123", "long", true, false, true)).close();
|
||||||
|
|
||||||
// login
|
// login
|
||||||
OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password");
|
OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password");
|
||||||
|
@ -432,6 +432,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
"claim-name",
|
"claim-name",
|
||||||
String.class.getSimpleName(),
|
String.class.getSimpleName(),
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
true
|
true
|
||||||
)))) {
|
)))) {
|
||||||
mapperId = getCreatedId(response);
|
mapperId = getCreatedId(response);
|
||||||
|
@ -481,6 +482,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
"claim-name",
|
"claim-name",
|
||||||
String.class.getSimpleName(),
|
String.class.getSimpleName(),
|
||||||
true,
|
true,
|
||||||
|
true,
|
||||||
true
|
true
|
||||||
)))) {
|
)))) {
|
||||||
mapperId = getCreatedId(response);
|
mapperId = getCreatedId(response);
|
||||||
|
@ -519,8 +521,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
userResource.update(user);
|
userResource.update(user);
|
||||||
|
|
||||||
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
|
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
|
||||||
app.getProtocolMappers().createMapper(createClaimMapper("empty", "empty", "empty", "String", true, true, false)).close();
|
app.getProtocolMappers().createMapper(createClaimMapper("empty", "empty", "empty", "String", true, true, true,false)).close();
|
||||||
app.getProtocolMappers().createMapper(createClaimMapper("null", "null", "null", "String", true, true, false)).close();
|
app.getProtocolMappers().createMapper(createClaimMapper("null", "null", "null", "String", true, true, true,false)).close();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -569,8 +571,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
public void testUserRoleToAttributeMappers() throws Exception {
|
public void testUserRoleToAttributeMappers() throws Exception {
|
||||||
// Add mapper for realm roles
|
// Add mapper for realm roles
|
||||||
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true);
|
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
|
||||||
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper("test-app", null, "Client roles mapper", "roles-custom.test-app", true, true);
|
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper("test-app", null, "Client roles mapper", "roles-custom.test-app", true, true, true);
|
||||||
|
|
||||||
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
||||||
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
|
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
|
||||||
|
@ -792,7 +794,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
public void testRoleMapperWithRoleInheritedFromMoreGroups() throws Exception {
|
public void testRoleMapperWithRoleInheritedFromMoreGroups() throws Exception {
|
||||||
// Create client-mapper
|
// Create client-mapper
|
||||||
String clientId = "test-app";
|
String clientId = "test-app";
|
||||||
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom.test-app", true, true);
|
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom.test-app", true, true, true);
|
||||||
|
|
||||||
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
|
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
|
||||||
protocolMappers.createMapper(Arrays.asList(clientMapper));
|
protocolMappers.createMapper(Arrays.asList(clientMapper));
|
||||||
|
@ -825,8 +827,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
public void testUserGroupRoleToAttributeMappers() throws Exception {
|
public void testUserGroupRoleToAttributeMappers() throws Exception {
|
||||||
// Add mapper for realm roles
|
// Add mapper for realm roles
|
||||||
String clientId = "test-app";
|
String clientId = "test-app";
|
||||||
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true);
|
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
|
||||||
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, "ta.", "Client roles mapper", "roles-custom.test-app", true, true);
|
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, "ta.", "Client roles mapper", "roles-custom.test-app", true, true, true);
|
||||||
|
|
||||||
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
|
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
|
||||||
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
|
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
|
||||||
|
@ -861,8 +863,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
@Test
|
@Test
|
||||||
public void testUserGroupRoleToAttributeMappersNotScopedOtherApp() throws Exception {
|
public void testUserGroupRoleToAttributeMappersNotScopedOtherApp() throws Exception {
|
||||||
String clientId = "test-app-authz";
|
String clientId = "test-app-authz";
|
||||||
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true);
|
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
|
||||||
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom." + clientId, true, true);
|
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom." + clientId, true, true, true);
|
||||||
|
|
||||||
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
|
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
|
||||||
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
|
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
|
||||||
|
@ -901,8 +903,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
@Test
|
@Test
|
||||||
public void testUserGroupRoleToAttributeMappersScoped() throws Exception {
|
public void testUserGroupRoleToAttributeMappersScoped() throws Exception {
|
||||||
String clientId = "test-app-scope";
|
String clientId = "test-app-scope";
|
||||||
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true);
|
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
|
||||||
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom.test-app-scope", true, true);
|
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom.test-app-scope", true, true, true);
|
||||||
|
|
||||||
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
|
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
|
||||||
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
|
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
|
||||||
|
@ -935,8 +937,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
@Test
|
@Test
|
||||||
public void testUserGroupRoleToAttributeMappersScopedClientNotSet() throws Exception {
|
public void testUserGroupRoleToAttributeMappersScopedClientNotSet() throws Exception {
|
||||||
String clientId = "test-app-scope";
|
String clientId = "test-app-scope";
|
||||||
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true);
|
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
|
||||||
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(null, null, "Client roles mapper", "roles-custom.test-app-scope", true, true);
|
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(null, null, "Client roles mapper", "roles-custom.test-app-scope", true, true, true);
|
||||||
|
|
||||||
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
|
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
|
||||||
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
|
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
|
||||||
|
@ -971,8 +973,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
@Test
|
@Test
|
||||||
public void testSingleValuedRoleMapping() throws Exception {
|
public void testSingleValuedRoleMapping() throws Exception {
|
||||||
String clientId = "test-app-scope";
|
String clientId = "test-app-scope";
|
||||||
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, false);
|
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true,false);
|
||||||
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(null, null, "Client roles mapper", "roles-custom.test-app-scope", true, true, false);
|
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(null, null, "Client roles mapper", "roles-custom.test-app-scope", true, true, true, false);
|
||||||
|
|
||||||
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
|
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
|
||||||
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
|
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
|
||||||
|
@ -1008,8 +1010,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
final String diffClient = "test-app";
|
final String diffClient = "test-app";
|
||||||
final String realmName = "test";
|
final String realmName = "test";
|
||||||
|
|
||||||
final ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true);
|
final ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
|
||||||
final ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(diffClient, null, "Client roles mapper", "roles-custom.test-app", true, true);
|
final ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(diffClient, null, "Client roles mapper", "roles-custom.test-app", true, true, true);
|
||||||
|
|
||||||
try (ClientAttributeUpdater cau = ClientAttributeUpdater.forClient(adminClient, realmName, clientId).setDirectAccessGrantsEnabled(true);
|
try (ClientAttributeUpdater cau = ClientAttributeUpdater.forClient(adminClient, realmName, clientId).setDirectAccessGrantsEnabled(true);
|
||||||
ProtocolMappersUpdater protocolMappers = new ProtocolMappersUpdater(cau.getResource().getProtocolMappers())) {
|
ProtocolMappersUpdater protocolMappers = new ProtocolMappersUpdater(cau.getResource().getProtocolMappers())) {
|
||||||
|
@ -1057,7 +1059,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
userResource.joinGroup(group1.getId());
|
userResource.joinGroup(group1.getId());
|
||||||
// create the attribute mapper
|
// create the attribute mapper
|
||||||
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
||||||
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, false, false)).close();
|
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true,false, false)).close();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// test it
|
// test it
|
||||||
|
@ -1097,7 +1099,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
userResource.joinGroup(group1.getId());
|
userResource.joinGroup(group1.getId());
|
||||||
// create the attribute mapper
|
// create the attribute mapper
|
||||||
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
||||||
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false)).close();
|
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, false)).close();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// test it
|
// test it
|
||||||
|
@ -1138,7 +1140,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
userResource.joinGroup(group1.getId());
|
userResource.joinGroup(group1.getId());
|
||||||
// create the attribute mapper
|
// create the attribute mapper
|
||||||
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
||||||
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true)).close();
|
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, true)).close();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// test it
|
// test it
|
||||||
|
@ -1177,7 +1179,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
userResource.joinGroup(group1.getId());
|
userResource.joinGroup(group1.getId());
|
||||||
// create the attribute mapper
|
// create the attribute mapper
|
||||||
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
||||||
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, false, false)).close();
|
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false, false)).close();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// test it
|
// test it
|
||||||
|
@ -1212,7 +1214,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
// create the attribute mapper
|
// create the attribute mapper
|
||||||
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
||||||
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false)).close();
|
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, false)).close();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// test it
|
// test it
|
||||||
|
@ -1248,7 +1250,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
// create the attribute mapper
|
// create the attribute mapper
|
||||||
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
||||||
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true)).close();
|
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, true)).close();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// test it
|
// test it
|
||||||
|
@ -1291,7 +1293,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
// create the attribute mapper
|
// create the attribute mapper
|
||||||
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
||||||
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, false, false)).close();
|
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false, false)).close();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// test it
|
// test it
|
||||||
|
@ -1336,7 +1338,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
// create the attribute mapper
|
// create the attribute mapper
|
||||||
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
||||||
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false)).close();
|
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, false)).close();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// test it
|
// test it
|
||||||
|
@ -1383,7 +1385,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
// create the attribute mapper
|
// create the attribute mapper
|
||||||
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
||||||
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true)).close();
|
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, true)).close();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// test it
|
// test it
|
||||||
|
@ -1434,7 +1436,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
// create the attribute mapper
|
// create the attribute mapper
|
||||||
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
||||||
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, false, false)).close();
|
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false, false)).close();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// test it
|
// test it
|
||||||
|
@ -1483,7 +1485,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
// create the attribute mapper
|
// create the attribute mapper
|
||||||
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
||||||
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, false, false)).close();
|
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false, false)).close();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// test it
|
// test it
|
||||||
|
@ -1532,7 +1534,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
// create the attribute mapper
|
// create the attribute mapper
|
||||||
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
||||||
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false)).close();
|
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, false)).close();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// test it
|
// test it
|
||||||
|
@ -1586,7 +1588,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
// create the attribute mapper
|
// create the attribute mapper
|
||||||
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
||||||
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true)).close();
|
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true,true, true)).close();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// test it
|
// test it
|
||||||
|
@ -1776,7 +1778,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
||||||
put(ClientScopeModel.DYNAMIC_SCOPE_REGEXP, "dyn-scope-with-mapper:*");
|
put(ClientScopeModel.DYNAMIC_SCOPE_REGEXP, "dyn-scope-with-mapper:*");
|
||||||
}});
|
}});
|
||||||
// create the attribute mapper
|
// create the attribute mapper
|
||||||
ProtocolMapperRepresentation protocolMapperRepresentation = createHardcodedClaim("dynamic-scope-hardcoded-mapper", "hardcoded-foo", "hardcoded-bar", "String", true, true);
|
ProtocolMapperRepresentation protocolMapperRepresentation = createHardcodedClaim("dynamic-scope-hardcoded-mapper", "hardcoded-foo", "hardcoded-bar", "String", true, true, true);
|
||||||
scopeRep.setProtocolMappers(Collections.singletonList(protocolMapperRepresentation));
|
scopeRep.setProtocolMappers(Collections.singletonList(protocolMapperRepresentation));
|
||||||
|
|
||||||
try (Response resp = adminClient.realm("test").clientScopes().create(scopeRep)) {
|
try (Response resp = adminClient.realm("test").clientScopes().create(scopeRep)) {
|
||||||
|
|
|
@ -119,7 +119,7 @@ public class AudienceTest extends AbstractOIDCScopeTest {
|
||||||
public void testAudienceProtocolMapperWithClientAudience() throws Exception {
|
public void testAudienceProtocolMapperWithClientAudience() throws Exception {
|
||||||
// Add audience protocol mapper to the clientScope "audience-scope"
|
// Add audience protocol mapper to the clientScope "audience-scope"
|
||||||
ProtocolMapperRepresentation audienceMapper = ProtocolMapperUtil.createAudienceMapper("audience mapper", "service-client",
|
ProtocolMapperRepresentation audienceMapper = ProtocolMapperUtil.createAudienceMapper("audience mapper", "service-client",
|
||||||
null, true, false);
|
null, true, false, true);
|
||||||
ClientScopeResource clientScope = ApiUtil.findClientScopeByName(testRealm(), "audience-scope");
|
ClientScopeResource clientScope = ApiUtil.findClientScopeByName(testRealm(), "audience-scope");
|
||||||
Response resp = clientScope.getProtocolMappers().createMapper(audienceMapper);
|
Response resp = clientScope.getProtocolMappers().createMapper(audienceMapper);
|
||||||
String mapperId = ApiUtil.getCreatedId(resp);
|
String mapperId = ApiUtil.getCreatedId(resp);
|
||||||
|
@ -131,7 +131,7 @@ public class AudienceTest extends AbstractOIDCScopeTest {
|
||||||
EventRepresentation loginEvent = events.expectLogin()
|
EventRepresentation loginEvent = events.expectLogin()
|
||||||
.user(userId)
|
.user(userId)
|
||||||
.assertEvent();
|
.assertEvent();
|
||||||
Tokens tokens = sendTokenRequest(loginEvent, userId,"openid profile email audience-scope", "test-app");
|
Tokens tokens = sendTokenRequest(loginEvent, userId, "openid profile email audience-scope", "test-app");
|
||||||
|
|
||||||
assertAudiences(tokens.accessToken, "service-client");
|
assertAudiences(tokens.accessToken, "service-client");
|
||||||
assertAudiences(tokens.idToken, "test-app");
|
assertAudiences(tokens.idToken, "test-app");
|
||||||
|
@ -145,14 +145,14 @@ public class AudienceTest extends AbstractOIDCScopeTest {
|
||||||
public void testAudienceProtocolMapperWithCustomAudience() throws Exception {
|
public void testAudienceProtocolMapperWithCustomAudience() throws Exception {
|
||||||
// Add audience protocol mapper to the clientScope "audience-scope"
|
// Add audience protocol mapper to the clientScope "audience-scope"
|
||||||
ProtocolMapperRepresentation audienceMapper = ProtocolMapperUtil.createAudienceMapper("audience mapper 1", null,
|
ProtocolMapperRepresentation audienceMapper = ProtocolMapperUtil.createAudienceMapper("audience mapper 1", null,
|
||||||
"http://host/service/ctx1", true, false);
|
"http://host/service/ctx1", true, false, true);
|
||||||
ClientScopeResource clientScope = ApiUtil.findClientScopeByName(testRealm(), "audience-scope");
|
ClientScopeResource clientScope = ApiUtil.findClientScopeByName(testRealm(), "audience-scope");
|
||||||
Response resp = clientScope.getProtocolMappers().createMapper(audienceMapper);
|
Response resp = clientScope.getProtocolMappers().createMapper(audienceMapper);
|
||||||
String mapper1Id = ApiUtil.getCreatedId(resp);
|
String mapper1Id = ApiUtil.getCreatedId(resp);
|
||||||
resp.close();
|
resp.close();
|
||||||
|
|
||||||
audienceMapper = ProtocolMapperUtil.createAudienceMapper("audience mapper 2", null,
|
audienceMapper = ProtocolMapperUtil.createAudienceMapper("audience mapper 2", null,
|
||||||
"http://host/service/ctx2", true, true);
|
"http://host/service/ctx2", true, true, true);
|
||||||
resp = clientScope.getProtocolMappers().createMapper(audienceMapper);
|
resp = clientScope.getProtocolMappers().createMapper(audienceMapper);
|
||||||
String mapper2Id = ApiUtil.getCreatedId(resp);
|
String mapper2Id = ApiUtil.getCreatedId(resp);
|
||||||
resp.close();
|
resp.close();
|
||||||
|
@ -163,7 +163,7 @@ public class AudienceTest extends AbstractOIDCScopeTest {
|
||||||
EventRepresentation loginEvent = events.expectLogin()
|
EventRepresentation loginEvent = events.expectLogin()
|
||||||
.user(userId)
|
.user(userId)
|
||||||
.assertEvent();
|
.assertEvent();
|
||||||
Tokens tokens = sendTokenRequest(loginEvent, userId,"openid profile email audience-scope", "test-app");
|
Tokens tokens = sendTokenRequest(loginEvent, userId, "openid profile email audience-scope", "test-app");
|
||||||
|
|
||||||
assertAudiences(tokens.accessToken, "http://host/service/ctx1", "http://host/service/ctx2");
|
assertAudiences(tokens.accessToken, "http://host/service/ctx1", "http://host/service/ctx2");
|
||||||
assertAudiences(tokens.idToken, "test-app", "http://host/service/ctx2");
|
assertAudiences(tokens.idToken, "test-app", "http://host/service/ctx2");
|
||||||
|
|
|
@ -0,0 +1,472 @@
|
||||||
|
package org.keycloak.testsuite.oidc;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.NotFoundException;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.admin.client.resource.ClientScopeResource;
|
||||||
|
import org.keycloak.admin.client.resource.ProtocolMappersResource;
|
||||||
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
|
import org.keycloak.common.Profile;
|
||||||
|
import org.keycloak.models.ProtocolMapperModel;
|
||||||
|
import org.keycloak.models.utils.ModelToRepresentation;
|
||||||
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
|
import org.keycloak.protocol.oidc.mappers.AudienceProtocolMapper;
|
||||||
|
import org.keycloak.protocol.oidc.mappers.GroupMembershipMapper;
|
||||||
|
import org.keycloak.protocol.oidc.mappers.HardcodedClaim;
|
||||||
|
import org.keycloak.protocol.oidc.mappers.HardcodedRole;
|
||||||
|
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
|
||||||
|
import org.keycloak.protocol.oidc.mappers.RoleNameMapper;
|
||||||
|
import org.keycloak.protocol.oidc.mappers.SHA256PairwiseSubMapper;
|
||||||
|
import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
|
||||||
|
import org.keycloak.representations.AccessToken;
|
||||||
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
|
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||||
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||||
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
|
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||||
|
import org.keycloak.testsuite.util.ClientManager;
|
||||||
|
import org.keycloak.testsuite.util.KeycloakModelUtils;
|
||||||
|
import org.keycloak.testsuite.util.OAuthClient;
|
||||||
|
import org.keycloak.testsuite.util.ProtocolMapperUtil;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
import org.wildfly.common.Assert;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.keycloak.protocol.ProtocolMapperUtils.USER_SESSION_NOTE;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.ACR;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.ACR_SCOPE;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.ADDRESS;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.ALLOWED_WEB_ORIGINS;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.AUDIENCE_RESOLVE;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.CLIENT_ROLES;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.EMAIL;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.EMAIL_VERIFIED;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.FAMILY_NAME;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.FULL_NAME;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.GIVEN_NAME;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.PROFILE_CLAIM;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.REALM_ROLES;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.ROLES_SCOPE;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.USERNAME;
|
||||||
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.WEB_ORIGINS_SCOPE;
|
||||||
|
import static org.keycloak.protocol.oidc.mappers.AbstractPairwiseSubMapper.PROVIDER_ID_SUFFIX;
|
||||||
|
import static org.keycloak.protocol.oidc.mappers.AudienceProtocolMapper.INCLUDED_CLIENT_AUDIENCE;
|
||||||
|
import static org.keycloak.protocol.oidc.mappers.HardcodedClaim.CLAIM_VALUE;
|
||||||
|
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN;
|
||||||
|
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION;
|
||||||
|
import static org.keycloak.protocol.oidc.mappers.PairwiseSubMapperHelper.PAIRWISE_SUB_ALGORITHM_SALT;
|
||||||
|
import static org.keycloak.protocol.oidc.mappers.RoleNameMapper.NEW_ROLE_NAME;
|
||||||
|
import static org.keycloak.protocol.oidc.mappers.RoleNameMapper.ROLE_CONFIG;
|
||||||
|
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
|
||||||
|
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
|
||||||
|
|
||||||
|
@EnableFeature(value = Profile.Feature.TOKEN_EXCHANGE, skipRestart = true)
|
||||||
|
public class LightWeightAccessTokenTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void clientConfiguration() {
|
||||||
|
ClientManager.realm(adminClient.realm("test")).clientId("test-app").directAccessGrant(true).setServiceAccountsEnabled(true);
|
||||||
|
ClientManager.realm(adminClient.realm("test")).clientId("resource-server").directAccessGrant(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||||
|
RealmRepresentation realm = loadJson(getClass().getResourceAsStream("/testrealm.json"), RealmRepresentation.class);
|
||||||
|
UserRepresentation user = findUser(realm, "test-user@localhost");
|
||||||
|
Map<String, List<String>> attributes = new HashMap<>(){{
|
||||||
|
put("street", Arrays.asList("1 My Street"));
|
||||||
|
put("locality", Arrays.asList("Cardiff"));
|
||||||
|
put("region", Arrays.asList("Cardiff"));
|
||||||
|
put("postal_code", Arrays.asList("CF104RA"));
|
||||||
|
}};
|
||||||
|
user.setAttributes(attributes);
|
||||||
|
user.setGroups(Arrays.asList("/topGroup/level2group"));
|
||||||
|
ClientRepresentation confApp = KeycloakModelUtils.createClient(realm, "resource-server");
|
||||||
|
confApp.setSecret("password");
|
||||||
|
confApp.setServiceAccountsEnabled(Boolean.TRUE);
|
||||||
|
testRealms.add(realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserRepresentation findUser(RealmRepresentation testRealm, String userName) {
|
||||||
|
for (UserRepresentation user : testRealm.getUsers()) {
|
||||||
|
if (user.getUsername().equals(userName)) return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void accessTokenFalseIntrospectionTrueTest() throws IOException {
|
||||||
|
ProtocolMappersResource protocolMappers = setProtocolMappers(false, true, true);
|
||||||
|
try {
|
||||||
|
oauth.nonce("123456");
|
||||||
|
oauth.scope("address");
|
||||||
|
oauth.clientId("test-app");
|
||||||
|
OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password").tokenResponse;
|
||||||
|
String accessToken = response.getAccessToken();
|
||||||
|
System.out.println("accessToken:" + accessToken);
|
||||||
|
assertAccessToken(oauth.verifyToken(accessToken), true, false);
|
||||||
|
|
||||||
|
oauth.clientId("resource-server");
|
||||||
|
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("resource-server", "password", accessToken);
|
||||||
|
System.out.println("tokenResponse:" + tokenResponse);
|
||||||
|
assertTokenIntrospectionResponse(JsonSerialization.readValue(tokenResponse, AccessToken.class), true, true, false);
|
||||||
|
} finally {
|
||||||
|
deleteProtocolMappers(protocolMappers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void accessTokenTrueIntrospectionFalseTest() throws IOException {
|
||||||
|
ProtocolMappersResource protocolMappers = setProtocolMappers(true, false, true);
|
||||||
|
try {
|
||||||
|
oauth.nonce("123456");
|
||||||
|
oauth.scope("address");
|
||||||
|
oauth.clientId("test-app");
|
||||||
|
OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password").tokenResponse;
|
||||||
|
String accessToken = response.getAccessToken();
|
||||||
|
System.out.println("accessToken:" + accessToken);
|
||||||
|
assertAccessToken(oauth.verifyToken(accessToken), true, true);
|
||||||
|
|
||||||
|
oauth.clientId("resource-server");
|
||||||
|
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("resource-server", "password", accessToken);
|
||||||
|
System.out.println("tokenResponse:" + tokenResponse);
|
||||||
|
// Most of the claims should not be included in introspectionResponse as introspectionMapper was disabled
|
||||||
|
assertTokenIntrospectionResponse(JsonSerialization.readValue(tokenResponse, AccessToken.class), true, false, false);
|
||||||
|
} finally {
|
||||||
|
deleteProtocolMappers(protocolMappers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void accessTokenTrueIntrospectionTrueTest() throws IOException {
|
||||||
|
ProtocolMappersResource protocolMappers = setProtocolMappers(true, true, true);
|
||||||
|
try {
|
||||||
|
oauth.nonce("123456");
|
||||||
|
oauth.scope("address");
|
||||||
|
oauth.clientId("test-app");
|
||||||
|
OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password").tokenResponse;
|
||||||
|
String accessToken = response.getAccessToken();
|
||||||
|
System.out.println("accessToken:" + accessToken);
|
||||||
|
assertAccessToken(oauth.verifyToken(accessToken), true, true);
|
||||||
|
|
||||||
|
oauth.clientId("resource-server");
|
||||||
|
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("resource-server", "password", accessToken);
|
||||||
|
System.out.println("tokenResponse:" + tokenResponse);
|
||||||
|
assertTokenIntrospectionResponse(JsonSerialization.readValue(tokenResponse, AccessToken.class), true, true, false);
|
||||||
|
} finally {
|
||||||
|
deleteProtocolMappers(protocolMappers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void offlineTokenTest() throws IOException {
|
||||||
|
ProtocolMappersResource protocolMappers = setProtocolMappers(false, true, true);
|
||||||
|
try {
|
||||||
|
oauth.nonce("123456");
|
||||||
|
oauth.scope("openid address offline_access");
|
||||||
|
|
||||||
|
oauth.clientId("test-app");
|
||||||
|
TokenResponseContext ctx = browserLogin("password", "test-user@localhost", "password");
|
||||||
|
OAuthClient.AccessTokenResponse response = ctx.tokenResponse;
|
||||||
|
String accessToken = response.getAccessToken();
|
||||||
|
System.out.println("accessToken:" + accessToken);
|
||||||
|
System.out.println("idtoken:" + response.getIdToken());
|
||||||
|
assertAccessToken(oauth.verifyToken(accessToken), true, false);
|
||||||
|
|
||||||
|
oauth.clientId("resource-server");
|
||||||
|
removeSession(ctx.userSessionId);
|
||||||
|
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("resource-server", "password", accessToken);
|
||||||
|
System.out.println("tokenResponse:" + tokenResponse);
|
||||||
|
assertTokenIntrospectionResponse(JsonSerialization.readValue(tokenResponse, AccessToken.class), true, true, false);
|
||||||
|
} finally {
|
||||||
|
deleteProtocolMappers(protocolMappers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeSession(final String sessionId) {
|
||||||
|
testingClient.testing().removeExpired("test");
|
||||||
|
try {
|
||||||
|
testingClient.testing().removeUserSession("test", sessionId);
|
||||||
|
} catch (NotFoundException nfe) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void clientCredentialTest() throws Exception {
|
||||||
|
ProtocolMappersResource protocolMappers = setProtocolMappers(false, true, false);
|
||||||
|
try {
|
||||||
|
oauth.nonce("123456");
|
||||||
|
oauth.scope("address");
|
||||||
|
|
||||||
|
oauth.clientId("test-app");
|
||||||
|
OAuthClient.AccessTokenResponse response = oauth.doClientCredentialsGrantAccessTokenRequest("password");
|
||||||
|
String accessToken = response.getAccessToken();
|
||||||
|
System.out.println("accessToken:" + accessToken);
|
||||||
|
assertAccessToken(oauth.verifyToken(accessToken), false, false);
|
||||||
|
|
||||||
|
oauth.clientId("resource-server");
|
||||||
|
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("resource-server", "password", accessToken);
|
||||||
|
System.out.println("tokenResponse:" + tokenResponse);
|
||||||
|
assertTokenIntrospectionResponse(JsonSerialization.readValue(tokenResponse, AccessToken.class), false);
|
||||||
|
} finally {
|
||||||
|
deleteProtocolMappers(protocolMappers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void exchangeTest() throws Exception {
|
||||||
|
ProtocolMappersResource protocolMappers = setProtocolMappers(false, true, true);
|
||||||
|
try {
|
||||||
|
oauth.nonce("123456");
|
||||||
|
oauth.scope("address");
|
||||||
|
|
||||||
|
oauth.clientId("test-app");
|
||||||
|
OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password").tokenResponse;
|
||||||
|
String accessToken = response.getAccessToken();
|
||||||
|
System.out.println("accessToken:" + accessToken);
|
||||||
|
assertAccessToken(oauth.verifyToken(accessToken), true, false);
|
||||||
|
response = oauth.doTokenExchange(TEST, accessToken, null, "test-app", "password");
|
||||||
|
String exchangedTokenString = response.getAccessToken();
|
||||||
|
System.out.println("exchangedTokenString:" + exchangedTokenString);
|
||||||
|
|
||||||
|
oauth.clientId("resource-server");
|
||||||
|
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("resource-server", "password", exchangedTokenString);
|
||||||
|
System.out.println("tokenResponse:" + tokenResponse);
|
||||||
|
assertTokenIntrospectionResponse(JsonSerialization.readValue(tokenResponse, AccessToken.class), true, true, true);
|
||||||
|
} finally {
|
||||||
|
deleteProtocolMappers(protocolMappers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertMapperClaims(AccessToken token, boolean isAddMapperResponseFlag, boolean isAuthCodeFlow) {
|
||||||
|
if (isAddMapperResponseFlag) {
|
||||||
|
if (isAuthCodeFlow) {
|
||||||
|
Assert.assertNotNull(token.getName());
|
||||||
|
Assert.assertNotNull(token.getGivenName());
|
||||||
|
Assert.assertNotNull(token.getFamilyName());
|
||||||
|
Assert.assertNotNull(token.getAddress());
|
||||||
|
Assert.assertNotNull(token.getEmail());
|
||||||
|
Assert.assertNotNull(token.getOtherClaims().get("user-session-note"));
|
||||||
|
Assert.assertNotNull(token.getOtherClaims().get("test-claim"));
|
||||||
|
Assert.assertNotNull(token.getOtherClaims().get("group-name"));
|
||||||
|
}
|
||||||
|
Assert.assertNotNull(token.getAudience());
|
||||||
|
Assert.assertNotNull(token.getAcr());
|
||||||
|
Assert.assertNotNull(token.getAllowedOrigins());
|
||||||
|
Assert.assertNotNull(token.getRealmAccess());
|
||||||
|
Assert.assertNotNull(token.getResourceAccess());
|
||||||
|
Assert.assertNotNull(token.getEmailVerified());
|
||||||
|
Assert.assertNotNull(token.getPreferredUsername());
|
||||||
|
} else {
|
||||||
|
if (isAuthCodeFlow) {
|
||||||
|
Assert.assertTrue(token.getName() == null);
|
||||||
|
Assert.assertTrue(token.getGivenName() == null);
|
||||||
|
Assert.assertTrue(token.getFamilyName() == null);
|
||||||
|
Assert.assertTrue(token.getAddress() == null);
|
||||||
|
Assert.assertTrue(token.getEmail() == null);
|
||||||
|
Assert.assertTrue(token.getOtherClaims().get("user-session-note") == null);
|
||||||
|
Assert.assertTrue(token.getOtherClaims().get("test-claim") == null);
|
||||||
|
Assert.assertTrue(token.getOtherClaims().get("group-name") == null);
|
||||||
|
}
|
||||||
|
Assert.assertTrue(token.getAcr() == null);
|
||||||
|
Assert.assertTrue(token.getAllowedOrigins() == null);
|
||||||
|
Assert.assertTrue(token.getRealmAccess() == null);
|
||||||
|
Assert.assertTrue(token.getResourceAccess().isEmpty());
|
||||||
|
Assert.assertTrue(token.getEmailVerified() == null);
|
||||||
|
Assert.assertTrue(token.getPreferredUsername() == null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertInitClaims(AccessToken token, boolean isAuthCodeFlow) {
|
||||||
|
Assert.assertNotNull(token.getExp());
|
||||||
|
Assert.assertNotNull(token.getIat());
|
||||||
|
Assert.assertNotNull(token.getId());
|
||||||
|
Assert.assertNotNull(token.getType());
|
||||||
|
if (isAuthCodeFlow) {
|
||||||
|
Assert.assertNotNull(token.getSessionId());
|
||||||
|
Assert.assertNotNull(token.getAuth_time());
|
||||||
|
} else {
|
||||||
|
Assert.assertTrue(token.getSessionId() == null);
|
||||||
|
Assert.assertTrue(token.getAuth_time() == null);
|
||||||
|
}
|
||||||
|
Assert.assertNotNull(token.getIssuedFor());
|
||||||
|
Assert.assertNotNull(token.getScope());
|
||||||
|
Assert.assertNotNull(token.getIssuer());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertIntrospectClaims(AccessToken token) {
|
||||||
|
Assert.assertNotNull(token.getOtherClaims().get("client_id"));
|
||||||
|
Assert.assertNotNull(token.getOtherClaims().get("active"));
|
||||||
|
Assert.assertNotNull(token.getOtherClaims().get("token_type"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNonce(AccessToken token, boolean isAuthCodeFlow, boolean exchangeToken) {
|
||||||
|
if (isAuthCodeFlow && !exchangeToken) {
|
||||||
|
Assert.assertNotNull(token.getNonce());
|
||||||
|
} else {
|
||||||
|
Assert.assertTrue(token.getNonce() == null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertAccessToken(AccessToken token, boolean isAuthCodeFlow, boolean isAddToAccessToken) {
|
||||||
|
assertNonce(token, isAuthCodeFlow, false);
|
||||||
|
assertMapperClaims(token, isAddToAccessToken, isAuthCodeFlow);
|
||||||
|
assertInitClaims(token, isAuthCodeFlow);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertTokenIntrospectionResponse(AccessToken token, boolean isAuthCodeFlow) {
|
||||||
|
assertTokenIntrospectionResponse(token, isAuthCodeFlow, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertTokenIntrospectionResponse(AccessToken token, boolean isAuthCodeFlow, boolean isAddToIntrospect, boolean exchangeToken) {
|
||||||
|
assertNonce(token, isAuthCodeFlow, exchangeToken);
|
||||||
|
assertMapperClaims(token, isAddToIntrospect, isAuthCodeFlow);
|
||||||
|
assertInitClaims(token, isAuthCodeFlow);
|
||||||
|
assertIntrospectClaims(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected RealmResource testRealm() {
|
||||||
|
return adminClient.realm("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setScopeProtocolMappers(boolean isIncludeAccessToken, boolean isIncludeIntrospection) {
|
||||||
|
setScopeProtocolMapper(ACR_SCOPE, ACR, isIncludeAccessToken, isIncludeIntrospection);
|
||||||
|
setScopeProtocolMapper(PROFILE_CLAIM, FULL_NAME, isIncludeAccessToken, isIncludeIntrospection);
|
||||||
|
setScopeProtocolMapper(EMAIL, EMAIL, isIncludeAccessToken, isIncludeIntrospection);
|
||||||
|
setScopeProtocolMapper(EMAIL, EMAIL_VERIFIED, isIncludeAccessToken, isIncludeIntrospection);
|
||||||
|
setScopeProtocolMapper(PROFILE_CLAIM, GIVEN_NAME, isIncludeAccessToken, isIncludeIntrospection);
|
||||||
|
setScopeProtocolMapper(PROFILE_CLAIM, FAMILY_NAME, isIncludeAccessToken, isIncludeIntrospection);
|
||||||
|
setScopeProtocolMapper(PROFILE_CLAIM, USERNAME, isIncludeAccessToken, isIncludeIntrospection);
|
||||||
|
setScopeProtocolMapper(WEB_ORIGINS_SCOPE, ALLOWED_WEB_ORIGINS, isIncludeAccessToken, isIncludeIntrospection);
|
||||||
|
setScopeProtocolMapper(ROLES_SCOPE, REALM_ROLES, isIncludeAccessToken, isIncludeIntrospection);
|
||||||
|
setScopeProtocolMapper(ROLES_SCOPE, CLIENT_ROLES, isIncludeAccessToken, isIncludeIntrospection);
|
||||||
|
setScopeProtocolMapper(ROLES_SCOPE, AUDIENCE_RESOLVE, isIncludeAccessToken, isIncludeIntrospection);
|
||||||
|
setScopeProtocolMapper(ADDRESS, ADDRESS, isIncludeAccessToken, isIncludeIntrospection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setScopeProtocolMapper(String scopeName, String mapperName, boolean isIncludeAccessToken, boolean isIncludeIntrospection) {
|
||||||
|
ClientScopeResource scope = ApiUtil.findClientScopeByName(testRealm(), scopeName);
|
||||||
|
ProtocolMapperRepresentation protocolMapper = ApiUtil.findProtocolMapperByName(scope, mapperName);
|
||||||
|
Map<String, String> config = protocolMapper.getConfig();
|
||||||
|
if (isIncludeAccessToken) {
|
||||||
|
config.put(INCLUDE_IN_ACCESS_TOKEN, "true");
|
||||||
|
} else {
|
||||||
|
config.put(INCLUDE_IN_ACCESS_TOKEN, "false");
|
||||||
|
}
|
||||||
|
if (isIncludeIntrospection) {
|
||||||
|
config.put(INCLUDE_IN_INTROSPECTION, "true");
|
||||||
|
} else {
|
||||||
|
config.put(INCLUDE_IN_INTROSPECTION, "false");
|
||||||
|
}
|
||||||
|
scope.getProtocolMappers().update(protocolMapper.getId(), protocolMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProtocolMappersResource setProtocolMappers(boolean isIncludeAccessToken, boolean isIncludeIntrospection, boolean setPairWise) {
|
||||||
|
setScopeProtocolMappers(isIncludeAccessToken, isIncludeIntrospection);
|
||||||
|
List<ProtocolMapperRepresentation> protocolMapperList = new ArrayList<>();
|
||||||
|
setExistingProtocolMappers(protocolMapperList, isIncludeAccessToken, isIncludeIntrospection, setPairWise);
|
||||||
|
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
|
||||||
|
protocolMappers.createMapper(protocolMapperList);
|
||||||
|
return protocolMappers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setExistingProtocolMappers(List<ProtocolMapperRepresentation> protocolMapperList, boolean isIncludeAccessToken, boolean isIncludeIntrospection, boolean setPairWise) {
|
||||||
|
Map<String, String> config = new HashMap<>();
|
||||||
|
if (isIncludeAccessToken) {
|
||||||
|
config.put(INCLUDE_IN_ACCESS_TOKEN, "true");
|
||||||
|
} else {
|
||||||
|
config.put(INCLUDE_IN_ACCESS_TOKEN, "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isIncludeIntrospection) {
|
||||||
|
config.put(INCLUDE_IN_INTROSPECTION, "true");
|
||||||
|
} else {
|
||||||
|
config.put(INCLUDE_IN_INTROSPECTION, "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtocolMapperRepresentation audienceProtocolMapper = createClaimMapper("audience", AudienceProtocolMapper.PROVIDER_ID, new HashMap<>(config) {{
|
||||||
|
put(INCLUDED_CLIENT_AUDIENCE, "account-console");
|
||||||
|
}});
|
||||||
|
protocolMapperList.add(audienceProtocolMapper);
|
||||||
|
ProtocolMapperRepresentation roleNameMapper = createClaimMapper("role-name", RoleNameMapper.PROVIDER_ID, new HashMap<>(config) {{
|
||||||
|
put(ROLE_CONFIG, "user");
|
||||||
|
put(NEW_ROLE_NAME, "new-role");
|
||||||
|
}});
|
||||||
|
protocolMapperList.add(roleNameMapper);
|
||||||
|
ProtocolMapperRepresentation groupMembershipMapper = createClaimMapper("group-member", GroupMembershipMapper.PROVIDER_ID, new HashMap<>(config) {{
|
||||||
|
put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "group-name");
|
||||||
|
}});
|
||||||
|
protocolMapperList.add(groupMembershipMapper);
|
||||||
|
ProtocolMapperRepresentation hardcodedClaim = createClaimMapper("hardcoded-claim", HardcodedClaim.PROVIDER_ID, new HashMap<>(config) {{
|
||||||
|
put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "test-claim");
|
||||||
|
put(CLAIM_VALUE, "test-value");
|
||||||
|
}});
|
||||||
|
protocolMapperList.add(hardcodedClaim);
|
||||||
|
ProtocolMapperRepresentation hardcodedRole = createClaimMapper("hardcoded-role", HardcodedRole.PROVIDER_ID, new HashMap<>(config) {{
|
||||||
|
put(ROLE_CONFIG, "hardcoded-role");
|
||||||
|
}});
|
||||||
|
protocolMapperList.add(hardcodedRole);
|
||||||
|
ProtocolMapperRepresentation userSessionNoteMapper = createClaimMapper("user-session-note", UserSessionNoteMapper.PROVIDER_ID, new HashMap<>(config) {{
|
||||||
|
put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "user-session-note");
|
||||||
|
put(USER_SESSION_NOTE, "AUTH_TIME");
|
||||||
|
}});
|
||||||
|
protocolMapperList.add(userSessionNoteMapper);
|
||||||
|
if (setPairWise) {
|
||||||
|
ProtocolMapperRepresentation pairwiseSubMapper = createClaimMapper("pairwise-sub-mapper", "oidc-" + SHA256PairwiseSubMapper.PROVIDER_ID + PROVIDER_ID_SUFFIX, new HashMap<>(config) {{
|
||||||
|
put(PAIRWISE_SUB_ALGORITHM_SALT, "abc");
|
||||||
|
}});
|
||||||
|
protocolMapperList.add(pairwiseSubMapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ProtocolMapperRepresentation createClaimMapper(String name, String providerId, Map<String, String> config) {
|
||||||
|
ProtocolMapperModel mapper = new ProtocolMapperModel();
|
||||||
|
mapper.setName(name);
|
||||||
|
mapper.setProtocolMapper(providerId);
|
||||||
|
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
|
mapper.setConfig(config);
|
||||||
|
return ModelToRepresentation.toRepresentation(mapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteProtocolMappers(ProtocolMappersResource protocolMappers) {
|
||||||
|
List<String> mapperNames = new ArrayList<>(Arrays.asList("reference", "audience", "role-name", "group-member", "hardcoded-claim", "hardcoded-role", "user-session-note", "pairwise-sub-mapper"));
|
||||||
|
List<ProtocolMapperRepresentation> mappers = new ArrayList<>();
|
||||||
|
for (String mapperName : mapperNames) {
|
||||||
|
mappers.add(ProtocolMapperUtil.getMapperByNameAndProtocol(protocolMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, mapperName));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ProtocolMapperRepresentation mapper : mappers) {
|
||||||
|
if (mapper != null) {
|
||||||
|
protocolMappers.delete(mapper.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TokenResponseContext browserLogin(String clientSecret, String username, String password) {
|
||||||
|
OAuthClient.AuthorizationEndpointResponse authsEndpointResponse = oauth.doLogin(username, password);
|
||||||
|
String userSessionId = authsEndpointResponse.getSessionState();
|
||||||
|
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(authsEndpointResponse.getCode(), clientSecret);
|
||||||
|
return new TokenResponseContext(userSessionId, tokenResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TokenResponseContext {
|
||||||
|
|
||||||
|
private final String userSessionId;
|
||||||
|
private final OAuthClient.AccessTokenResponse tokenResponse;
|
||||||
|
|
||||||
|
public TokenResponseContext(String userSessionId, OAuthClient.AccessTokenResponse tokenResponse) {
|
||||||
|
this.userSessionId = userSessionId;
|
||||||
|
this.tokenResponse = tokenResponse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -92,7 +92,7 @@ public class DeployedScriptMapperTest extends AbstractTestRealmKeycloakTest {
|
||||||
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
|
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
|
||||||
|
|
||||||
ProtocolMapperRepresentation mapper = createScriptMapper("test-script-mapper1", "computed-via-script",
|
ProtocolMapperRepresentation mapper = createScriptMapper("test-script-mapper1", "computed-via-script",
|
||||||
"computed-via-script", "String", true, true, "'hello_' + user.username", false);
|
"computed-via-script", "String", true, true, true, "'hello_' + user.username", false);
|
||||||
|
|
||||||
mapper.setProtocolMapper("script-mapper-a.js");
|
mapper.setProtocolMapper("script-mapper-a.js");
|
||||||
|
|
||||||
|
|
|
@ -95,7 +95,7 @@ public class UndeployedScriptMapperNotAvailableTest extends AbstractTestRealmKey
|
||||||
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
|
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
|
||||||
{
|
{
|
||||||
ProtocolMapperRepresentation mapper = createScriptMapper("test-script-mapper1", "computed-via-script",
|
ProtocolMapperRepresentation mapper = createScriptMapper("test-script-mapper1", "computed-via-script",
|
||||||
"computed-via-script", "String", true, true, "'hello_' + user.username", false);
|
"computed-via-script", "String", true, true, true, "'hello_' + user.username", false);
|
||||||
|
|
||||||
mapper.setProtocolMapper("script-mapper-a.js");
|
mapper.setProtocolMapper("script-mapper-a.js");
|
||||||
|
|
||||||
|
|
|
@ -47,8 +47,8 @@ public class ProtocolMapperUtil {
|
||||||
* @param accessToken
|
* @param accessToken
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static ProtocolMapperRepresentation createAddressMapper(boolean idToken, boolean accessToken, boolean userInfo) {
|
public static ProtocolMapperRepresentation createAddressMapper(boolean idToken, boolean accessToken, boolean userInfo, boolean introspectionEndpoint) {
|
||||||
return ModelToRepresentation.toRepresentation(AddressMapper.createAddressMapper(idToken, accessToken, userInfo));
|
return ModelToRepresentation.toRepresentation(AddressMapper.createAddressMapper(idToken, accessToken, userInfo, introspectionEndpoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,9 +65,9 @@ public class ProtocolMapperUtil {
|
||||||
public static ProtocolMapperRepresentation createHardcodedClaim(String name,
|
public static ProtocolMapperRepresentation createHardcodedClaim(String name,
|
||||||
String hardcodedName,
|
String hardcodedName,
|
||||||
String hardcodedValue, String claimType,
|
String hardcodedValue, String claimType,
|
||||||
boolean accessToken, boolean idToken) {
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
|
||||||
return ModelToRepresentation.toRepresentation(HardcodedClaim.create(name, hardcodedName, hardcodedValue,
|
return ModelToRepresentation.toRepresentation(HardcodedClaim.create(name, hardcodedName, hardcodedValue,
|
||||||
claimType, accessToken, idToken));
|
claimType, accessToken, idToken, introspectionEndpoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,64 +85,64 @@ public class ProtocolMapperUtil {
|
||||||
public static ProtocolMapperRepresentation createClaimMapper(String name,
|
public static ProtocolMapperRepresentation createClaimMapper(String name,
|
||||||
String userAttribute,
|
String userAttribute,
|
||||||
String tokenClaimName, String claimType,
|
String tokenClaimName, String claimType,
|
||||||
boolean accessToken, boolean idToken, boolean multivalued) {
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint, boolean multivalued) {
|
||||||
return ModelToRepresentation.toRepresentation(UserAttributeMapper.createClaimMapper(name, userAttribute, tokenClaimName,
|
return ModelToRepresentation.toRepresentation(UserAttributeMapper.createClaimMapper(name, userAttribute, tokenClaimName,
|
||||||
claimType, accessToken, idToken, multivalued, false));
|
claimType, accessToken, idToken, introspectionEndpoint, multivalued, false));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolMapperRepresentation createClaimMapper(String name,
|
public static ProtocolMapperRepresentation createClaimMapper(String name,
|
||||||
String userAttribute,
|
String userAttribute,
|
||||||
String tokenClaimName, String claimType,
|
String tokenClaimName, String claimType,
|
||||||
boolean accessToken, boolean idToken,
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint,
|
||||||
boolean multivalued, boolean aggregateAttrs) {
|
boolean multivalued, boolean aggregateAttrs) {
|
||||||
return ModelToRepresentation.toRepresentation(UserAttributeMapper.createClaimMapper(name, userAttribute, tokenClaimName,
|
return ModelToRepresentation.toRepresentation(UserAttributeMapper.createClaimMapper(name, userAttribute, tokenClaimName,
|
||||||
claimType, accessToken, idToken, multivalued, aggregateAttrs));
|
claimType, accessToken, idToken, introspectionEndpoint, multivalued, aggregateAttrs));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolMapperRepresentation createClaimMapper(String name,
|
public static ProtocolMapperRepresentation createClaimMapper(String name,
|
||||||
String userSessionNote,
|
String userSessionNote,
|
||||||
String tokenClaimName, String jsonType,
|
String tokenClaimName, String jsonType,
|
||||||
boolean accessToken, boolean idToken) {
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
|
||||||
|
|
||||||
return ModelToRepresentation.toRepresentation(UserSessionNoteMapper.createClaimMapper(name,
|
return ModelToRepresentation.toRepresentation(UserSessionNoteMapper.createClaimMapper(name,
|
||||||
userSessionNote,
|
userSessionNote,
|
||||||
tokenClaimName, jsonType,
|
tokenClaimName, jsonType,
|
||||||
accessToken, idToken));
|
accessToken, idToken, introspectionEndpoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static ProtocolMapperRepresentation createUserRealmRoleMappingMapper(String realmRolePrefix,
|
public static ProtocolMapperRepresentation createUserRealmRoleMappingMapper(String realmRolePrefix,
|
||||||
String name,
|
String name,
|
||||||
String tokenClaimName,
|
String tokenClaimName,
|
||||||
boolean accessToken, boolean idToken) {
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
|
||||||
|
|
||||||
return createUserRealmRoleMappingMapper(realmRolePrefix, name, tokenClaimName, accessToken, idToken, true);
|
return createUserRealmRoleMappingMapper(realmRolePrefix, name, tokenClaimName, accessToken, idToken, introspectionEndpoint, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolMapperRepresentation createUserRealmRoleMappingMapper(String realmRolePrefix,
|
public static ProtocolMapperRepresentation createUserRealmRoleMappingMapper(String realmRolePrefix,
|
||||||
String name,
|
String name,
|
||||||
String tokenClaimName,
|
String tokenClaimName,
|
||||||
boolean accessToken, boolean idToken, boolean multiValued) {
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint, boolean multiValued) {
|
||||||
|
|
||||||
return ModelToRepresentation.toRepresentation(UserRealmRoleMappingMapper.create(realmRolePrefix, name, tokenClaimName, accessToken, idToken, multiValued));
|
return ModelToRepresentation.toRepresentation(UserRealmRoleMappingMapper.create(realmRolePrefix, name, tokenClaimName, accessToken, idToken, introspectionEndpoint, multiValued));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolMapperRepresentation createUserClientRoleMappingMapper(String clientId, String clientRolePrefix,
|
public static ProtocolMapperRepresentation createUserClientRoleMappingMapper(String clientId, String clientRolePrefix,
|
||||||
String name,
|
String name,
|
||||||
String tokenClaimName,
|
String tokenClaimName,
|
||||||
boolean accessToken, boolean idToken) {
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
|
||||||
|
|
||||||
return createUserClientRoleMappingMapper(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, true);
|
return createUserClientRoleMappingMapper(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, introspectionEndpoint, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolMapperRepresentation createUserClientRoleMappingMapper(String clientId, String clientRolePrefix,
|
public static ProtocolMapperRepresentation createUserClientRoleMappingMapper(String clientId, String clientRolePrefix,
|
||||||
String name,
|
String name,
|
||||||
String tokenClaimName,
|
String tokenClaimName,
|
||||||
boolean accessToken, boolean idToken, boolean multiValued) {
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint, boolean multiValued) {
|
||||||
|
|
||||||
return ModelToRepresentation.toRepresentation(UserClientRoleMappingMapper.create(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, multiValued));
|
return ModelToRepresentation.toRepresentation(UserClientRoleMappingMapper.create(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, introspectionEndpoint, multiValued));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProtocolMapperRepresentation getMapperByNameAndProtocol(ProtocolMappersResource protocolMappers, String protocol, String name) {
|
public static ProtocolMapperRepresentation getMapperByNameAndProtocol(ProtocolMappersResource protocolMappers, String protocol, String name) {
|
||||||
|
@ -160,11 +160,12 @@ public class ProtocolMapperUtil {
|
||||||
String claimType,
|
String claimType,
|
||||||
boolean accessToken,
|
boolean accessToken,
|
||||||
boolean idToken,
|
boolean idToken,
|
||||||
|
boolean introspectionEndpoint,
|
||||||
String script,
|
String script,
|
||||||
boolean multiValued) {
|
boolean multiValued) {
|
||||||
|
|
||||||
return ModelToRepresentation.toRepresentation(
|
return ModelToRepresentation.toRepresentation(
|
||||||
ScriptBasedOIDCProtocolMapper.create(name, userAttribute, tokenClaimName, claimType, accessToken, idToken, script, multiValued)
|
ScriptBasedOIDCProtocolMapper.create(name, userAttribute, tokenClaimName, claimType, accessToken, idToken, introspectionEndpoint, script, multiValued)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,10 +176,10 @@ public class ProtocolMapperUtil {
|
||||||
public static ProtocolMapperRepresentation createAudienceMapper(String name,
|
public static ProtocolMapperRepresentation createAudienceMapper(String name,
|
||||||
String includedClientAudience,
|
String includedClientAudience,
|
||||||
String includedCustomAudience,
|
String includedCustomAudience,
|
||||||
boolean accessToken, boolean idToken) {
|
boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
|
||||||
|
|
||||||
return ModelToRepresentation.toRepresentation(
|
return ModelToRepresentation.toRepresentation(
|
||||||
AudienceProtocolMapper.createClaimMapper(name, includedClientAudience, includedCustomAudience, accessToken, idToken)
|
AudienceProtocolMapper.createClaimMapper(name, includedClientAudience, includedCustomAudience, accessToken, idToken, introspectionEndpoint)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue