Skip to content

Commit

Permalink
Import changes from jacekkow#49
Browse files Browse the repository at this point in the history
  • Loading branch information
XSmeets committed Apr 4, 2024
1 parent 47b0411 commit 65af2d1
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 2 deletions.
6 changes: 6 additions & 0 deletions src/main/java/org/keycloak/protocol/cas/CASLoginProtocol.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.keycloak.models.*;
import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.cas.utils.LogoutHelper;
import org.keycloak.protocol.cas.utils.UsernameMapperHelper;
import org.keycloak.protocol.oidc.utils.OAuth2Code;
import org.keycloak.protocol.oidc.utils.OAuth2CodeParser;
import org.keycloak.services.ErrorPage;
Expand Down Expand Up @@ -95,6 +96,11 @@ public CASLoginProtocol setEventBuilder(EventBuilder event) {
public Response authenticated(AuthenticationSessionModel authSession, UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
AuthenticatedClientSessionModel clientSession = clientSessionCtx.getClientSession();

// Verify that if a username mapper is set - there is a username being returned
if(UsernameMapperHelper.getMappedUsername(session, clientSession) == null) {
return ErrorPage.error(session, authSession, Response.Status.INTERNAL_SERVER_ERROR, "Unable to map username for CAS client");
}

String service = authSession.getRedirectUri();
//TODO validate service

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.keycloak.protocol.cas.representations.CASErrorCode;
import org.keycloak.protocol.cas.representations.SamlResponseHelper;
import org.keycloak.protocol.cas.utils.CASValidationException;
import org.keycloak.protocol.cas.utils.UsernameMapperHelper;
import org.keycloak.services.Urls;
import org.xml.sax.InputSource;

Expand Down Expand Up @@ -61,7 +62,7 @@ public Response validate(String input) {

Map<String, Object> attributes = getUserAttributes();

SAML11ResponseType response = SamlResponseHelper.successResponse(issuer, user.getUsername(), attributes);
SAML11ResponseType response = SamlResponseHelper.successResponse(issuer, UsernameMapperHelper.getMappedUsername(session,clientSession), attributes);

return Response.ok(SamlResponseHelper.soap(response)).build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.keycloak.protocol.cas.utils.CASValidationException;
import org.keycloak.protocol.cas.utils.ContentTypeHelper;
import org.keycloak.protocol.cas.utils.ServiceResponseHelper;
import org.keycloak.protocol.cas.utils.UsernameMapperHelper;

import java.util.Map;

Expand All @@ -22,7 +23,7 @@ public ServiceValidateEndpoint(KeycloakSession session, RealmModel realm, EventB
protected Response successResponse() {
UserSessionModel userSession = clientSession.getUserSession();
Map<String, Object> attributes = getUserAttributes();
CASServiceResponse serviceResponse = ServiceResponseHelper.createSuccess(userSession.getUser().getUsername(), attributes);
CASServiceResponse serviceResponse = ServiceResponseHelper.createSuccess(UsernameMapperHelper.getMappedUsername(session,clientSession), attributes);
return prepare(Response.Status.OK, serviceResponse);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.keycloak.protocol.cas.mappers;

import org.keycloak.models.*;
import org.keycloak.protocol.ProtocolMapper;

public interface CASUsernameMapper extends ProtocolMapper {

String getMappedUsername(ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, AuthenticatedClientSessionModel clientSession);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.keycloak.protocol.cas.mappers;

import org.keycloak.Config;
import org.keycloak.models.*;
import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.cas.CASLoginProtocol;
import org.keycloak.provider.ProviderConfigProperty;

import java.util.ArrayList;
import java.util.List;

public class UserAttributeCasUsernameMapper extends AbstractCASProtocolMapper implements CASUsernameMapper {
public static final String PROVIDER_ID = "cas-usermodel-username-mapper";
public static final String USERNAME_MAPPER_CATEGORY = "CAS Username Mapper";
private static final String CONF_FALLBACK_TO_USERNAME_IF_NULL = "username_fallback";

private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty();
property.setName(ProtocolMapperUtils.USER_ATTRIBUTE);
property.setLabel(ProtocolMapperUtils.USER_MODEL_PROPERTY_LABEL);
property.setType(ProviderConfigProperty.STRING_TYPE);
property.setHelpText(ProtocolMapperUtils.USER_MODEL_PROPERTY_HELP_TEXT);
configProperties.add(property);

property = new ProviderConfigProperty();
property.setName(CONF_FALLBACK_TO_USERNAME_IF_NULL);
property.setLabel("Use username if attribute is missing");
property.setHelpText("Should the User's username be used if the specified attribute is blank?");
property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
property.setDefaultValue(false);
configProperties.add(property);


}

@Override
public final String getDisplayCategory() {
return USERNAME_MAPPER_CATEGORY;
}

@Override
public final String getId() {
return PROVIDER_ID;
}

@Override
public String getDisplayType() {
return "User Attribute Mapper For CAS Username";
}

@Override
public String getHelpText() {
return "Maps a user attribute to CAS Username value.";
}

@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
}

@Override
public String getMappedUsername(ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {

boolean defaultIfNull = Boolean.parseBoolean(mappingModel.getConfig().get(CONF_FALLBACK_TO_USERNAME_IF_NULL));
UserModel user = userSession.getUser();
String mappedUsername = user.getFirstAttribute(mappingModel.getConfig().get(ProtocolMapperUtils.USER_ATTRIBUTE));

if(mappedUsername == null && defaultIfNull) {
mappedUsername = user.getUsername();
}
return mappedUsername;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.keycloak.protocol.cas.utils;

import org.keycloak.models.*;
import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.cas.mappers.CASUsernameMapper;
import org.keycloak.services.util.DefaultClientSessionContext;

import java.util.Map;

public class UsernameMapperHelper {
public static String getMappedUsername(KeycloakSession session, AuthenticatedClientSessionModel clientSession) {
// CAS protocol does not support scopes, so pass null scopeParam
ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndScopeParameter(clientSession, null, session);
UserSessionModel userSession = clientSession.getUserSession();


Map.Entry<ProtocolMapperModel, ProtocolMapper> mapperPair = ProtocolMapperUtils.getSortedProtocolMappers(session,clientSessionCtx)
.filter(e -> e.getValue() instanceof CASUsernameMapper)
.findFirst()
.orElse(null);

String mappedUsername = userSession.getUser().getUsername();

if(mapperPair != null) {
ProtocolMapperModel mapping = mapperPair.getKey();
CASUsernameMapper casUsernameMapper = (CASUsernameMapper) mapperPair.getValue();
mappedUsername = casUsernameMapper.getMappedUsername(mapping, session, userSession, clientSession);
}
return mappedUsername;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ org.keycloak.protocol.cas.mappers.UserClientRoleMappingMapper
org.keycloak.protocol.cas.mappers.UserPropertyMapper
org.keycloak.protocol.cas.mappers.UserRealmRoleMappingMapper
org.keycloak.protocol.cas.mappers.UserSessionNoteMapper
org.keycloak.protocol.cas.mappers.UserAttributeCasUsernameMapper

0 comments on commit 65af2d1

Please sign in to comment.