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

Matthias Piepkorn
2017-02-24 4a6620008abf71df354840cd0efe1319ef5ded9c
commit | author | age
7f7e0c 1 package org.keycloak.protocol.cas;
MP 2
57a6c1 3 import org.apache.http.HttpEntity;
MP 4 import org.jboss.logging.Logger;
7f7e0c 5 import org.keycloak.common.util.KeycloakUriBuilder;
MP 6 import org.keycloak.events.EventBuilder;
7 import org.keycloak.events.EventType;
4a6620 8 import org.keycloak.forms.login.LoginFormsProvider;
7f7e0c 9 import org.keycloak.models.*;
MP 10 import org.keycloak.protocol.LoginProtocol;
57a6c1 11 import org.keycloak.protocol.cas.utils.LogoutHelper;
7f7e0c 12 import org.keycloak.services.managers.ClientSessionCode;
MP 13 import org.keycloak.services.managers.ResourceAdminManager;
14
15 import javax.ws.rs.core.HttpHeaders;
16 import javax.ws.rs.core.Response;
17 import javax.ws.rs.core.UriInfo;
57a6c1 18 import java.io.IOException;
7f7e0c 19 import java.net.URI;
MP 20
21 public class CASLoginProtocol implements LoginProtocol {
57a6c1 22     private static final Logger logger = Logger.getLogger(CASLoginProtocol.class);
MP 23
7f7e0c 24     public static final String LOGIN_PROTOCOL = "cas";
MP 25
26     public static final String SERVICE_PARAM = "service";
27     public static final String RENEW_PARAM = "renew";
28     public static final String GATEWAY_PARAM = "gateway";
29     public static final String TICKET_PARAM = "ticket";
30     public static final String FORMAT_PARAM = "format";
31
32     public static final String TICKET_RESPONSE_PARAM = "ticket";
33
34     public static final String SERVICE_TICKET_PREFIX = "ST-";
57a6c1 35     public static final String SESSION_SERVICE_TICKET = "service_ticket";
4a6620 36
MP 37     public static final String LOGOUT_REDIRECT_URI = "CAS_LOGOUT_REDIRECT_URI";
7f7e0c 38
MP 39     protected KeycloakSession session;
40     protected RealmModel realm;
41     protected UriInfo uriInfo;
42     protected HttpHeaders headers;
43     protected EventBuilder event;
44
7124d2 45     public CASLoginProtocol(KeycloakSession session, RealmModel realm, UriInfo uriInfo, HttpHeaders headers, EventBuilder event) {
7f7e0c 46         this.session = session;
MP 47         this.realm = realm;
48         this.uriInfo = uriInfo;
49         this.headers = headers;
50         this.event = event;
51     }
52
53     public CASLoginProtocol() {
54     }
55
56     @Override
57     public CASLoginProtocol setSession(KeycloakSession session) {
58         this.session = session;
59         return this;
60     }
61
62     @Override
63     public CASLoginProtocol setRealm(RealmModel realm) {
64         this.realm = realm;
65         return this;
66     }
67
68     @Override
69     public CASLoginProtocol setUriInfo(UriInfo uriInfo) {
70         this.uriInfo = uriInfo;
71         return this;
72     }
73
74     @Override
75     public CASLoginProtocol setHttpHeaders(HttpHeaders headers) {
76         this.headers = headers;
77         return this;
78     }
79
80     @Override
81     public CASLoginProtocol setEventBuilder(EventBuilder event) {
82         this.event = event;
83         return this;
84     }
85
86     @Override
87     public Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode) {
88         ClientSessionModel clientSession = accessCode.getClientSession();
89
90         String service = clientSession.getRedirectUri();
91         //TODO validate service
92         accessCode.setAction(ClientSessionModel.Action.CODE_TO_TOKEN.name());
93         KeycloakUriBuilder uriBuilder = KeycloakUriBuilder.fromUri(service);
94         uriBuilder.queryParam(TICKET_RESPONSE_PARAM, SERVICE_TICKET_PREFIX + accessCode.getCode());
95
96         URI redirectUri = uriBuilder.build();
97
98         Response.ResponseBuilder location = Response.status(302).location(redirectUri);
99         return location.build();
100     }
101
102     @Override
103     public Response sendError(ClientSessionModel clientSession, Error error) {
104         return Response.serverError().entity(error).build();
105     }
106
107     @Override
108     public void backchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) {
57a6c1 109         String logoutUrl = clientSession.getRedirectUri();
MP 110         String serviceTicket = clientSession.getNote(CASLoginProtocol.SESSION_SERVICE_TICKET);
111         //check if session is fully authenticated (i.e. serviceValidate has been called)
112         if (serviceTicket != null && !serviceTicket.isEmpty()) {
113             sendSingleLogoutRequest(logoutUrl, serviceTicket);
114         }
7f7e0c 115         ClientModel client = clientSession.getClient();
MP 116         new ResourceAdminManager(session).logoutClientSession(uriInfo.getRequestUri(), realm, client, clientSession);
117     }
118
57a6c1 119     private void sendSingleLogoutRequest(String logoutUrl, String serviceTicket) {
MP 120         HttpEntity requestEntity = LogoutHelper.buildSingleLogoutRequest(serviceTicket);
121         try {
122             LogoutHelper.postWithRedirect(session, logoutUrl, requestEntity);
123             logger.debug("Sent CAS single logout for service " + logoutUrl);
124         } catch (IOException e) {
125             logger.warn("Failed to call CAS service for logout: " + logoutUrl, e);
126         }
127     }
128
7f7e0c 129     @Override
MP 130     public Response frontchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) {
131         // todo oidc redirect support
132         throw new RuntimeException("NOT IMPLEMENTED");
133     }
134
135     @Override
136     public Response finishLogout(UserSessionModel userSession) {
4a6620 137         String redirectUri = userSession.getNote(CASLoginProtocol.LOGOUT_REDIRECT_URI);
MP 138
7f7e0c 139         event.event(EventType.LOGOUT);
MP 140         event.user(userSession.getUser()).session(userSession).success();
4a6620 141         LoginFormsProvider infoPage = session.getProvider(LoginFormsProvider.class).setSuccess("Logout successful");
MP 142         if (redirectUri != null) {
143             infoPage.setAttribute("pageRedirectUri", redirectUri);
144         } else {
145             infoPage.setAttribute("skipLink", true);
146         }
147         return infoPage.createInfoPage();
7f7e0c 148     }
MP 149
150     @Override
151     public boolean requireReauthentication(UserSessionModel userSession, ClientSessionModel clientSession) {
7124d2 152         return "true".equals(clientSession.getNote(CASLoginProtocol.RENEW_PARAM));
7f7e0c 153     }
MP 154
155     @Override
156     public void close() {
157
158     }
159 }