mirror of https://github.com/jacekkow/keycloak-protocol-cas

Jacek Kowalski
2020-10-17 3882f0eb56e0b699c071f77bb914b6739b163deb
Fix CAS gateway option handling

Per specification if gateway option is set and no authorization data
is present, then user should be redirected back to the service.
This module responded with 500 Internal Server Error instead.
3 files modified
69 ■■■■ changed files
integrationTest/suite.sh 58 ●●●● patch | view | raw | blame | history
src/main/java/org/keycloak/protocol/cas/CASLoginProtocol.java 8 ●●●● patch | view | raw | blame | history
src/main/java/org/keycloak/protocol/cas/endpoints/AuthorizationEndpoint.java 3 ●●●●● patch | view | raw | blame | history
integrationTest/suite.sh
@@ -1,41 +1,65 @@
#!/bin/bash
set -e
keycloak_cas_url='http://localhost:8080/auth/realms/master/protocol/cas'
action_pattern='action="([^"]+)"'
ticket_pattern='Location: .*\?ticket=(ST-[-A-Za-z0-9_.=]+)'
get_ticket() {
    login_response=$(curl --fail --silent -c /tmp/cookies http://localhost:8080/auth/realms/master/protocol/cas/login?service=http://localhost)
    if [[ !($login_response =~ $action_pattern) ]] ; then
    local cookie_options="-b /tmp/cookies"
    if [ "$1" == "save_cookies" ]; then
      cookie_options="${cookie_options} -c /tmp/cookies"
    fi
    local login_response=$(curl --fail --silent -c /tmp/cookies "${keycloak_cas_url}/login?service=http://localhost")
    if [[ ! ($login_response =~ $action_pattern) ]] ; then
        echo "Could not parse login form in response"
        echo $login_response
        echo "${login_response}"
        exit 1
    fi
    login_url=${BASH_REMATCH[1]//&/&}
    redirect_response=$(curl --fail --silent -D - -b /tmp/cookies --data 'username=admin&password=admin' "$login_url")
    if [[ !($redirect_response =~ $ticket_pattern) ]] ; then
    local login_url=${BASH_REMATCH[1]//&/&}
    local redirect_response=$(curl --fail --silent -D - $cookie_options --data 'username=admin&password=admin' "$login_url")
    if [[ ! ($redirect_response =~ $ticket_pattern) ]] ; then
        echo "No service ticket found in response"
        echo $redirect_response
        echo "${redirect_response}"
        exit 1
    fi
    ticket=${BASH_REMATCH[1]}
    echo $ticket
    echo "${BASH_REMATCH[1]}"
}
get_ticket
curl --fail --silent "http://localhost:8080/auth/realms/master/protocol/cas/validate?service=http://localhost&ticket=$ticket"
# CAS 1.0
ticket=$(get_ticket)
curl --fail --silent "${keycloak_cas_url}/validate?service=http://localhost&ticket=$ticket"
echo
get_ticket
curl --fail --silent "http://localhost:8080/auth/realms/master/protocol/cas/serviceValidate?service=http://localhost&format=XML&ticket=$ticket"
# CAS 2.0
ticket=$(get_ticket)
curl --fail --silent "${keycloak_cas_url}/serviceValidate?service=http://localhost&format=XML&ticket=$ticket"
echo
get_ticket
curl --fail --silent "http://localhost:8080/auth/realms/master/protocol/cas/serviceValidate?service=http://localhost&format=JSON&ticket=$ticket"
ticket=$(get_ticket)
curl --fail --silent "${keycloak_cas_url}/serviceValidate?service=http://localhost&format=JSON&ticket=$ticket"
echo
get_ticket
curl --fail --silent "http://localhost:8080/auth/realms/master/protocol/cas/p3/serviceValidate?service=http://localhost&format=JSON&ticket=$ticket"
# CAS 3.0
ticket=$(get_ticket save_cookies)
curl --fail --silent "${keycloak_cas_url}/p3/serviceValidate?service=http://localhost&format=JSON&ticket=$ticket"
echo
# CAS, gateway option
get_ticket save_cookies
login_response=$(curl --fail --silent -D - -b /tmp/cookies "${keycloak_cas_url}/login?service=http://localhost&gateway=true")
if echo "${login_response}" | grep '^Location: http://localhost\?ticket='; then
    echo "Gateway option did not redirect back to service with ticket"
    echo "${login_response}"
    exit 1
fi
login_response=$(curl --fail --silent -D - "${keycloak_cas_url}/login?service=http://localhost&gateway=true")
if echo "${login_response}" | grep '^Location: http://localhost$'; then
    echo "Gateway option did not redirect back to service without ticket"
    echo "${login_response}"
    exit 1
fi
src/main/java/org/keycloak/protocol/cas/CASLoginProtocol.java
@@ -12,6 +12,7 @@
import org.keycloak.protocol.cas.utils.LogoutHelper;
import org.keycloak.protocol.oidc.utils.OAuth2Code;
import org.keycloak.protocol.oidc.utils.OAuth2CodeParser;
import org.keycloak.services.ErrorPage;
import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.sessions.AuthenticationSessionModel;
@@ -111,7 +112,12 @@
    @Override
    public Response sendError(AuthenticationSessionModel authSession, Error error) {
        return Response.serverError().entity(error).build();
        if (authSession.getClientNotes().containsKey(CASLoginProtocol.GATEWAY_PARAM)) {
            if (error == Error.PASSIVE_INTERACTION_REQUIRED || error == Error.PASSIVE_LOGIN_REQUIRED) {
                return Response.status(302).location(URI.create(authSession.getRedirectUri())).build();
            }
        }
        return ErrorPage.error(session, authSession, Response.Status.INTERNAL_SERVER_ERROR, error.name());
    }
    @Override
src/main/java/org/keycloak/protocol/cas/endpoints/AuthorizationEndpoint.java
@@ -51,6 +51,9 @@
        if (renew) {
            authenticationSession.setClientNote(CASLoginProtocol.RENEW_PARAM, "true");
        }
        if (gateway) {
            authenticationSession.setClientNote(CASLoginProtocol.GATEWAY_PARAM, "true");
        }
        this.event.event(EventType.LOGIN);
        return handleBrowserAuthenticationRequest(authenticationSession, new CASLoginProtocol(session, realm, session.getContext().getUri(), headers, event), gateway, false);