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

Jacek Kowalski
2020-10-17 58cea4fab65777587fbd9902bc6756f9ed44f3fe
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
65 ■■■■ changed files
integrationTest/suite.sh 54 ●●●● 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)
    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")
    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);