View Javadoc
1   /*
2    * Licensed under the Apache License, Version 2.0 (the "License");
3    * you may not use this file except in compliance with the License.
4    * You may obtain a copy of the License at
5    *
6    * http://www.apache.org/licenses/LICENSE-2.0
7    *
8    * Unless required by applicable law or agreed to in writing, software
9    * distributed under the License is distributed on an "AS IS" BASIS,
10   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11   * See the License for the specific language governing permissions and
12   * limitations under the License.
13   *
14   */
15  
16  package org.esigate.cas;
17  
18  import java.security.Principal;
19  import java.util.Properties;
20  
21  import org.apache.http.Header;
22  import org.apache.http.HttpResponse;
23  import org.apache.http.HttpStatus;
24  import org.apache.http.client.methods.CloseableHttpResponse;
25  import org.apache.http.util.EntityUtils;
26  import org.esigate.Driver;
27  import org.esigate.HttpErrorPage;
28  import org.esigate.events.Event;
29  import org.esigate.events.EventDefinition;
30  import org.esigate.events.EventManager;
31  import org.esigate.events.IEventListener;
32  import org.esigate.events.impl.FragmentEvent;
33  import org.esigate.extension.Extension;
34  import org.esigate.http.IncomingRequest;
35  import org.esigate.http.OutgoingRequest;
36  import org.esigate.util.Parameter;
37  import org.esigate.util.ParameterString;
38  import org.jasig.cas.client.authentication.AttributePrincipal;
39  import org.slf4j.Logger;
40  import org.slf4j.LoggerFactory;
41  
42  public class CasAuthenticationHandler implements IEventListener, Extension {
43      // Configuration properties names
44      public static final Parameter<String> CAS_LOGIN_URL = new ParameterString("casLoginUrl", "/login");
45      private static final Logger LOG = LoggerFactory.getLogger(CasAuthenticationHandler.class);
46      private Driver driver;
47      private String loginUrl;
48  
49      private void addCasAuthentication(OutgoingRequest outgoingRequest, IncomingRequest incomingRequest) {
50          String location = outgoingRequest.getRequestLine().getUri();
51          String resultLocation = location;
52          AttributePrincipal principal = getCasAuthentication(incomingRequest);
53          if (principal != null) {
54              LOG.debug("User logged in CAS as: " + principal.getName());
55              String casProxyTicket = principal.getProxyTicketFor(resultLocation);
56              LOG.debug("Proxy ticket retrieved: " + principal.getName() + " for service: " + location + " : "
57                      + casProxyTicket);
58              if (casProxyTicket != null) {
59                  if (resultLocation.indexOf("?") > 0) {
60                      resultLocation = resultLocation + "&ticket=" + casProxyTicket;
61                  } else {
62                      resultLocation = resultLocation + "?ticket=" + casProxyTicket;
63                  }
64              }
65          }
66          outgoingRequest.setUri(resultLocation);
67      }
68  
69      @Override
70      public boolean event(EventDefinition id, Event event) {
71          if (EventManager.EVENT_FRAGMENT_POST.equals(id)) {
72              FragmentEvent e = (FragmentEvent) event;
73              IncomingRequest incomingRequest = e.getOriginalRequest();
74              CloseableHttpResponse httpResponse = e.getHttpResponse();
75              if (isRedirectToCasServer(httpResponse)) {
76                  if (getCasAuthentication(incomingRequest) != null) {
77                      LOG.debug("CAS authentication required for {}", e);
78                      EntityUtils.consumeQuietly(e.getHttpResponse().getEntity());
79                      e.setHttpResponse(null);
80                      addCasAuthentication(e.getHttpRequest(), e.getOriginalRequest());
81                      try {
82                          LOG.debug("Sending new request {}", e);
83                          e.setHttpResponse(this.driver.getRequestExecutor().execute(e.getHttpRequest()));
84                      } catch (HttpErrorPage e1) {
85                          e.setHttpResponse(e1.getHttpResponse());
86                      }
87                  } else {
88                      LOG.debug("CAS authentication required but we are not authenticated for {}", e);
89                      e.setHttpResponse(HttpErrorPage.generateHttpResponse(HttpStatus.SC_UNAUTHORIZED,
90                              "CAS authentication required"));
91                  }
92              }
93          }
94          return true;
95      }
96  
97      private AttributePrincipal getCasAuthentication(IncomingRequest incomingRequest) {
98          Principal principal = incomingRequest.getUserPrincipal();
99          if (principal != null && principal instanceof AttributePrincipal) {
100             return (AttributePrincipal) principal;
101         }
102         return null;
103     }
104 
105     /*
106      * (non-Javadoc)
107      * 
108      * @see org.esigate.extension.Extension#init(org.esigate.Driver, java.util.Properties)
109      */
110     @Override
111     public final void init(Driver d, Properties properties) {
112         this.driver = d;
113         this.driver.getEventManager().register(EventManager.EVENT_FRAGMENT_PRE, this);
114         this.driver.getEventManager().register(EventManager.EVENT_FRAGMENT_POST, this);
115         loginUrl = CAS_LOGIN_URL.getValue(properties);
116     }
117 
118     private boolean isRedirectToCasServer(HttpResponse httpResponse) {
119         Header locationHeader = httpResponse.getFirstHeader("Location");
120         if (locationHeader != null) {
121             String locationHeaderValue = locationHeader.getValue();
122             if (locationHeaderValue != null && locationHeaderValue.contains(loginUrl)) {
123                 return true;
124             }
125         }
126         return false;
127     }
128 
129 }