001: /* Copyright 2006 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal.security.provider.cas;
007:
008: import java.io.IOException;
009: import java.net.URLEncoder;
010:
011: import org.jasig.portal.security.PortalSecurityException;
012: import org.jasig.portal.security.provider.ChainingSecurityContext;
013:
014: import edu.yale.its.tp.cas.client.ServiceTicketValidator;
015: import edu.yale.its.tp.cas.proxy.ProxyTicketReceptor;
016:
017: /**
018: * <p>
019: * A SecurityContext using the Central Authentication Service.
020: * </p>
021: * An ICasSecurityContext implementation in keeping with the traditional approach of
022: * performing actual authentication inside this security context. This security
023: * context is suitable for cases where your login URL (the "portalServiceUrl")
024: * is a constant.
025: *
026: * This class is based on edu.yale.its.tp.portal.security.YaleCasContext as
027: * distributed in the Yale uPortal CAS security provider version 3.0.0.
028: *
029: * @version $Revision$ $Date$
030: */
031: public class CasSecurityContext extends ChainingSecurityContext
032: implements ICasSecurityContext {
033:
034: private static final long serialVersionUID = 1L;
035:
036: /**
037: * The URL of the uPortal Login servlet to which service tickets will
038: * authenticate users.
039: */
040: private final String portalServiceUrl;
041:
042: /**
043: * The https: URL at which CAS offers its ticket validation service.
044: */
045: private final String casValidateUrl;
046:
047: /**
048: * The https: URL at which CAS is to call back the uPortal with Proxy
049: * Granting Tickets.
050: */
051: private final String casProxyCallbackUrl;
052:
053: /**
054: * The pgtIou which keys to the Proxy Granting Ticket associated with this
055: * authenticated security context, if any.
056: */
057: private String pgtIou = null;
058:
059: /**
060: * Instantiate a YaleCasContext given a constant URL to which CAS service
061: * tickets will authenticate users, a URL at which to validate those
062: * tickets, and a callback URL at which to ask CAS to deliver Proxy Granting
063: * Tickets.
064: *
065: * @param portalServiceUrl -
066: * the constant URL to which service tickets authenticate users
067: * @param casValidateUrl -
068: * the https: URL at which CAS offers its ticket validation
069: * service
070: * @param casProxyCallbackUrl -
071: * the https: URL to which CAS should send proxy granting
072: * tickets.
073: */
074: public CasSecurityContext(String portalServiceUrl,
075: String casValidateUrl, String casProxyCallbackUrl) {
076: super ();
077: log.trace("entering YaleCasContext(" + portalServiceUrl + ","
078: + casValidateUrl + "," + casProxyCallbackUrl + ")");
079:
080: if (portalServiceUrl == null) {
081: throw new IllegalArgumentException(
082: "Cannot instantiate a YaleCasContext with a null portalServiceUrl.");
083: }
084: if (casValidateUrl == null) {
085: throw new IllegalArgumentException(
086: "Cannot instantiate a YaleCasContext with a null casValidateUrl.");
087: }
088: this .casProxyCallbackUrl = casProxyCallbackUrl;
089: this .portalServiceUrl = portalServiceUrl;
090: this .casValidateUrl = casValidateUrl;
091:
092: }
093:
094: /**
095: * Instantiate a YaleCasContext given a constant URL to which CAS service
096: * tickets will authenticate users, a URL at which to validate those
097: * tickets.
098: *
099: * @param portalServiceUrl -
100: * the constant URL to which service tickets authenticate users
101: * @param casValidateUrl -
102: * the https: URL at which CAS offers its ticket validation
103: * service
104: */
105: public CasSecurityContext(String portalServiceUrl,
106: String casValidateUrl) {
107: this (portalServiceUrl, casValidateUrl, null);
108: }
109:
110: public int getAuthType() {
111: return ICasSecurityContext.CAS_AUTHTYPE;
112: }
113:
114: public synchronized void authenticate()
115: throws PortalSecurityException {
116: if (log.isTraceEnabled()) {
117: log.trace("entering authenticate()");
118: }
119: String serviceTicket = new String(
120: this .myOpaqueCredentials.credentialstring);
121:
122: this .isauth = false;
123: try {
124: ServiceTicketValidator sv = new ServiceTicketValidator();
125:
126: sv.setCasValidateUrl(this .casValidateUrl);
127: if (this .casProxyCallbackUrl != null) {
128: sv.setProxyCallbackUrl(this .casProxyCallbackUrl);
129: }
130: sv.setService(URLEncoder.encode(this .portalServiceUrl,
131: "UTF-8"));
132: sv.setServiceTicket(serviceTicket);
133: log.debug("authenticate(): Validating ServiceTicket: ["
134: + serviceTicket + "]");
135: sv.validate();
136: log.debug("authenticate(): got response:["
137: + sv.getResponse() + "]");
138:
139: if (sv.isAuthenticationSuccesful()) {
140: this .myPrincipal.setUID(sv.getUser());
141:
142: // We keep the pgtIOU around as the key to reference the Proxy
143: // granting ticket
144: // that will be used to obtain future Proxy Tickets.
145: // A channel has
146: // access to this securityContext and can request a Proxy ticket
147: // via
148: // the public getCasServiceToken method
149: this .pgtIou = sv.getPgtIou();
150: this .isauth = true;
151: log.debug("CASContext authenticated ["
152: + this .myPrincipal.getUID() + "]");
153: }
154: //else {
155: // throw new PortalSecurityException("error code: " +
156: // sv.getErrorCode()+"\n error message: "+sv.getErrorMessage());
157: // }
158: } catch (Throwable t) {
159: log.error(t);
160: throw new PortalSecurityException(
161: "Error in CAS Authentication: " + t.getMessage());
162: /*
163: * if PortalSecurityException supported it, it would be nice to
164: * chain here: throw new PortalSecurityException("Error in CAS
165: * Authentication:", t);
166: */
167: }
168: this .myAdditionalDescriptor = null; //no additional
169: // descriptor from CAS
170: super .authenticate();
171: if (log.isTraceEnabled()) {
172: log.trace("returning from authenticate()");
173: }
174: return;
175: }
176:
177: public String getCasServiceToken(String target)
178: throws CasProxyTicketAcquisitionException {
179: if (log.isTraceEnabled()) {
180: log.trace("entering getCasServiceToken(" + target
181: + "), previously cached pgtIou=[" + this .pgtIou
182: + "]");
183: }
184: String proxyTicket;
185: try {
186: proxyTicket = ProxyTicketReceptor.getProxyTicket(
187: this .pgtIou, target);
188: } catch (IOException e) {
189: throw new CasProxyTicketAcquisitionException(target,
190: this .pgtIou, e);
191: }
192: if (log.isTraceEnabled()) {
193: log
194: .trace("returning from getCasServiceToken() with return value ["
195: + proxyTicket + "]");
196: }
197: return proxyTicket;
198: }
199:
200: public String toString() {
201: StringBuffer sb = new StringBuffer();
202: sb.append(this .getClass().getName());
203: sb.append(" portalServiceUrl=[").append(this .portalServiceUrl)
204: .append("]");
205: sb.append(" casValidateUrl=[").append(this .casValidateUrl)
206: .append("]");
207: sb.append(" casProxyCallbackUrl=[").append(
208: this .casProxyCallbackUrl).append("]");
209: sb.append(" pgtIou=[").append(this .pgtIou).append("]");
210: return sb.toString();
211: }
212: }
|