import org.opensaml.saml2.core.AuthnRequest;
/**
- * Authentication handlers are responsible for authenticating a user using a particular authentication context class and
- * logging users out for that same mechanism.
+ * Authentication handlers are responsible for authenticating a user using a
+ * particular authentication context class and logging users out for that same
+ * mechanism.
*
- * When this handler is invoked to log a user in the incomg will contain a {@link AuthnRequest} attribute registered
- * under the name <strong>AuthnRequest</strong>. If the authentication request coming into the IdP is not a SAML 2
- * request the receiving profile handler will translate the incoming details into a {@link AuthnRequest}.
+ * When this handler is invoked to log a user in the incoming request will contain a
+ * {@link AuthnRequest} attribute registered under the name
+ * <strong>AuthnRequest</strong>. If the authentication request coming into the
+ * IdP is not a SAML 2 request the receiving profile handler will translate the
+ * incoming details into a {@link AuthnRequest}.
*
- * Upon successfull authentication the handler <strong>must</strong> set a request attribute called <strong>principal</strong>
- * with the principal name of the authenticated user. It must then forward the request/response to the provided return
- * location by means of the
- * {@link RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)} method.
+ * Upon successfull authentication the handler <strong>must</strong> set a
+ * request attribute called <strong>principal</strong> with the principal name
+ * of the authenticated user. It must then forward the request/response to the
+ * provided return location by means of the
+ * {@link RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)}
+ * method.
*
- * When this handler is invoked to log a user out of the particular authentication source the handler may perform any
- * operation necessary to log a user out. When finished it must then forward the request/response to the provided return
- * location by means of the
- * {@link RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)} method. This call will
- * occur only after SAML log out requests have been sent to all services supporting such requests.
+ * When this handler is invoked to log a user out of the particular
+ * authentication source the handler may perform any operation necessary to
+ * log a user out. When finished it must then forward the request/response to
+ * the provided return location by means of the
+ * {@link RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)}
+ * method. This call will occur before SAML logout requests have been sent to all services
+ * supporting such requests.
*
- * AuthentcationHandlers <strong>MUST NOT</strong> change or add any data to the user's {@link HttpSession} that
- * persists past the process of authenticating the user, that is no additional session data may be added and no existing
- * session data may be changed when the handler redirects back to the return location.
+ * AuthentcationHandlers <strong>MUST NOT</strong> change or add any data to the
+ * user's {@link HttpSession} that persists past the process of authenticating
+ * the user, that is no additional session data may be added and no existing
+ * session data may be changed when the handler redirects back to the return
+ * location.
*/
public interface AuthenticationHandler {
+ /** Key contiaing in login()'s request parameter */
+ public static final String AUTHN_REQUEST_KEY = "AuthnRequest";
+
+ /** Key containing principal name in login()'s response parameter */
+ public static final String PRINCIPAL_KEY = "principal";
+
+
/**
* Authenticates the user making the request.
*
* @param passive whether the authentication must be passive
* @param force whether the handler must force an authentication
*/
- public void login(HttpServletRequest request, HttpServletResponse response, boolean passive, boolean force);
+ public void login(HttpServletRequest request, HttpServletResponse response,
+ boolean passive, boolean force);
+
/**
- * Logs out the given user from the authentication mechansim represented by this handler.
+ * Logs out the given user from the authentication
+ * mechanism represented by this handler.
*
* @param request user request
* @param response response to user
* @param principal principal named as returned during authentication
*/
- public void logout(HttpServletRequest request, HttpServletResponse response, String principal);
+ public void logout(HttpServletRequest request, HttpServletResponse response,
+ String principal);
+
/**
* Gets whether this handler supports passive authentication.
*/
public boolean supportsPassive();
+
/**
- * Gets whether this handler supports the ability to force a user's to authenticate.
+ * Returns if this handler supports the ability
+ * to force a user to (re-)authenticate.
*
- * @return whether this handler supports the ability to force a user's to authenticate
+ * @return if this handler can force a user to (re-)authenticate.
*/
public boolean supportsForceAuthentication();
+
/**
* Sets the location to return the user to once authenticated.
*
package edu.internet2.middleware.shibboleth.idp.authn;
-import org.springframework.web.servlet.HttpServletBean;
+import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
+import edu.internet2.middleware.shibboleth.idp.session.impl.AuthenticationMethodInformationImpl;
+import java.io.IOException;
+import java.util.List;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import edu.internet2.middleware.shibboleth.idp.authn.AuthenticationHandler;
+import edu.internet2.middleware.shibboleth.idp.authn.impl.AuthenticationHandlerInfo;
+import edu.internet2.middleware.shibboleth.idp.session.Session;
import edu.internet2.middleware.shibboleth.idp.session.SessionManager;
+import javolution.util.FastList;
+import javolution.util.FastMap;
+
+import org.apache.log4j.Logger;
+
+import org.opensaml.saml2.core.AuthnContextClassRef;
+import org.opensaml.saml2.core.AuthnContextDeclRef;
+import org.opensaml.saml2.core.AuthnContextComparisonTypeEnumeration;
+import org.opensaml.saml2.core.RequestedAuthnContext;
+
+import org.opensaml.saml2.core.AuthnContextClassRef;
+import org.opensaml.saml2.core.AuthnContextDeclRef;
+import org.opensaml.saml2.core.AuthnContextComparisonTypeEnumeration;
+import org.opensaml.saml2.core.AuthnRequest;
+import org.opensaml.saml2.core.RequestedAuthnContext;
+
+import org.springframework.web.servlet.HttpServletBean;
+
+
/**
- * Manager responsible for handling an authentication requests.
+ * Manager responsible for handling authentication requests.
*/
public class AuthenticationManager extends HttpServletBean {
-
- /** Session manager to be used by this servlet */
- private SessionManager sessionMgr;
+
+ private static final Logger log =
+ Logger.getLogger(AuthenticationManager.class.getName());
+
+ /** SessionManager to be used */
+ private SessionManager sessionMgr;
+
+ /** Map of URIs onto AuthenticationHandlerInfo */
+ private FastMap<String, AuthenticationHandlerInfo> handlerMap
+ = new FastMap<String, AuthenticationHandlerInfo>();
+
+ /** The default AuthenticationHandler */
+ private AuthenticationHandlerInfo defaultHandlerInfo;
+
+
+ /**
+ * Gets the session manager to be used
+ *
+ * @return session manager to be used
+ */
+ public SessionManager getSessionManager() {
+ return this.sessionMgr;
+ }
+
+
+ /**
+ * Sets the session manager to be used.
+ *
+ * @param manager session manager to be used.
+ */
+ public void setSessionManager(final SessionManager manager) {
+ this.sessionMgr = manager;
+ }
+
+
+ /**
+ * Add a <String:AuthenticationHandler> mapping to the
+ * AuthenticationManager's table. If a mapping for the URI
+ * already exists, it will be overwritten.
+ *
+ * The URI SHOULD be from the saml-autn-context-2.0-os
+ *
+ * @param uri A URI identifying the authentcation method.
+ * @param handlerInfo Informarmation about the handler.
+ *
+ * @throws IllegalArgumentExcetption if <code>handlerInfo.getUri()</code> returns </code>null</code>
+ */
+ public void addHandlerMapping(String uri, AuthenticationHandlerInfo handlerInfo) {
+
+ if (uri == null || handlerInfo == null) {
+ return;
+ }
+
+ log.debug("registering " + handlerInfo.getHandler().getClass().getName()
+ + " for " + uri);
+
+ this.handlerMap.put(uri, handlerInfo);
+ }
+
/**
- * Gets the session manager to be used by this servlet.
- *
- * @return session manager to be used by this servlet
+ * Register the default {@link AuthenticationHandler}
+ *
+ * @param handlerInfo Information about the handler.
*/
- public SessionManager getSessionManager(){
- return sessionMgr;
+ public void addDefaultHandler(AuthenticationHandlerInfo handlerInfo) {
+
+ log.debug("Registering default handler "
+ + handlerInfo.getHandler().getClass().getName());
+
+ this.defaultHandlerInfo = handlerInfo;
}
+
+ /**
+ * Remove a <String:AuthenticationHandler> mapping from the
+ * AuthenticationManager's table.
+ *
+ * The URI SHOULD be from the saml-authn-context-2.0-os
+ *
+ * @param URI A URI identifying the authentcation method.
+ */
+ public void removeHandlerMapping(String uri) {
+
+ if (uri == null) {
+ return;
+ }
+
+ log.debug("unregistering handler for " + uri);
+
+ this.handlerMap.remove(uri);
+ }
+
+
+
+ /**
+ * Primary entrypoint for the AuthnManager
+ */
+ public void doPost(HttpServletRequest req,
+ HttpServletResponse resp) throws ServletException, IOException {
+
+ if (req == null || resp == null) {
+ log.error("Invalid parameters in AuthenticationManager's doPost().");
+ return;
+ }
+
+ HttpSession httpSession = req.getSession();
+ if (httpSession == null) {
+ log.error("Unable to retrieve HttpSession from request.");
+ return;
+ }
+ Object o = httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
+ if (! (o instanceof LoginContext)) {
+ log.error("Invalid login context object -- object is not an instance of LoginContext.");
+ return;
+ }
+ LoginContext loginContext = (LoginContext)o;
+
+ // If authentication has been attempted, don't try it again.
+ if (loginContext.getAuthenticationAttempted()) {
+ this.handleNewAuthnRequest(loginContext, req, resp);
+ } else {
+ this.finishAuthnRequest(loginContext, req, resp);
+ }
+
+
+ }
+
+
+
+ /**
+ * Handle a new {@link AuthnRequest}s
+ *
+ */
+ private void handleNewAuthnRequest(final LoginContext loginContext,
+ final HttpServletRequest servletRequest,
+ final HttpServletResponse servletResponse) throws ServletException, IOException {
+
+ boolean forceAuthN = loginContext.getForceAuth();
+ boolean isPassive = loginContext.getPassiveAuth();
+
+ RequestedAuthnContext authnCtx = null;
+
+ // if loginContext is really a Saml2LoginContext, extract the
+ // (possibly null) RequestedAuthnContext.
+ if (loginContext instanceof Saml2LoginContext) {
+ Saml2LoginContext samlLoginContext = (Saml2LoginContext)loginContext;
+ authnCtx = samlLoginContext.getRequestedAuthnContext();
+ }
+
+ AuthenticationHandler handler = this.getHandler(authnCtx, forceAuthN, isPassive);
+ if (handler == null) {
+ loginContext.setAuthenticationAttempted();
+ loginContext.setAuthnOK(false);
+ loginContext.setAuthnFailureMessage("No installed AuthenticationHandlers can satisfy the authentication request.");
+
+ RequestDispatcher dispatcher =
+ servletRequest.getRequestDispatcher(loginContext.getProfileHandlerURL());
+ dispatcher.forward(servletRequest, servletResponse);
+ }
+
+ // forward control to the authenticationhandler
+ ServletContext servletContext = this.getServletContext();
+ String saml2handlerPath = servletContext.getRealPath(servletRequest.getServletPath());
+ loginContext.setAuthnManagerURL(servletRequest.getPathInfo());
+ }
+
+
+ /**
+ * Handle the "return leg" of an authentication request
+ * (i.e. clean up after an authentication handler has run).
+ *
+ */
+ private void finishAuthnRequest(final LoginContext loginContext,
+ final HttpServletRequest servletRequest,
+ final HttpServletResponse servletResponse) throws ServletException, IOException {
+
+ // if authentication was successful, the authentication handler should
+ // have updated the LoginContext with additional information. Use that
+ // info to create a Session.
+ if (loginContext.getAuthnOK()) {
+
+ AuthenticationMethodInformationImpl authMethodInfo =
+ new AuthenticationMethodInformationImpl(loginContext.getAuthenticationMethod(),
+ loginContext.getAuthenticationInstant(), loginContext.getAuthenticationDuration());
+
+ Session shibSession = this.getSessionManager().createSession();
+ List<AuthenticationMethodInformation> authMethods = shibSession.getAuthenticationMethods();
+ authMethods.add(authMethodInfo);
+ loginContext.setSessionID(shibSession.getSessionID());
+ }
+
+ RequestDispatcher dispatcher =
+ servletRequest.getRequestDispatcher(loginContext.getProfileHandlerURL());
+ dispatcher.forward(servletRequest, servletResponse);
+ }
+
+
+ /**
+ * "Stub" method for handling LogoutRequest
+ */
+ private void handleLogoutRequest(final HttpServletRequest servletRequest,
+ final HttpServletResponse servletResponse) throws ServletException, IOException {
+
+ }
+
+
+ /**
+ * Examine an {@link RequestedAuthnContext} against a list of installed
+ * {@link AuthenticationHandler}s. If an acceptable handler is found, return
+ * a reference to it. Otherwise return <code>null</code>
+ *
+ * @param ctx A {@link RequestedAuthnContext}
+ * @param forceAuthN Should authentication be forced.
+ * @param passiveAuthN Must authentication happen without UI control.
+ *
+ * @return A reference to an {@link AuthenticationHandler} or <code>null</code>.
+ */
+ private AuthenticationHandler getHandler(final RequestedAuthnContext ctx,
+ boolean forceAuthN, boolean passiveAuthN) {
+
+ // if no context is specified, evaluate the default handler
+ if (ctx == null) {
+ return this.evaluateHandler(this.defaultHandlerInfo, "default", forceAuthN, passiveAuthN);
+ }
+
+
+ // For the immediate future, we only support the "exact" comparator.
+ AuthnContextComparisonTypeEnumeration comparator = ctx.getComparison();
+ if (comparator != null && comparator != AuthnContextComparisonTypeEnumeration.EXACT) {
+ log.error("Unsupported comparision operator ( " + comparator
+ + ") in RequestedAuthnContext. Only exact comparisions are supported.");
+ return (null);
+ }
+
+ // build a list of all requested authn classes and declrefs
+ List<String> requestedAuthnMethods = new FastList<String>();
+ List<AuthnContextClassRef> authnClasses = ctx.getAuthnContextClassRefs();
+ List<AuthnContextDeclRef> authnDeclRefs = ctx.getAuthnContextDeclRefs();
+
+ if (authnClasses != null) {
+ for (AuthnContextClassRef classRef : authnClasses) {
+ if (classRef != null) {
+ String s = classRef.getAuthnContextClassRef();
+ if (s != null) {
+ requestedAuthnMethods.add(s);
+ }
+ }
+ }
+ }
+
+ if (authnDeclRefs != null) {
+ for (AuthnContextDeclRef declRef : authnDeclRefs) {
+ if (declRef != null) {
+ String s = declRef.getAuthnContextDeclRef();
+ if (s != null) {
+ requestedAuthnMethods.add(s);
+ }
+ }
+ }
+ }
+
+
+ // if no AuthnContextClasses or AuthnContextDeclRefs were actually specified,
+ // evaluate the default handler
+ if (requestedAuthnMethods.size() == 0) {
+ return this.evaluateHandler(this.defaultHandlerInfo, "default", forceAuthN, passiveAuthN);
+ }
+
+
+ // evaluate all requested authn methods until we find a match.
+ AuthenticationHandler handler = null;
+ for (String s : requestedAuthnMethods) {
+
+ AuthenticationHandlerInfo handlerInfo = this.handlerMap.get(s);
+ if (handlerInfo == null) {
+ log.debug("No registered authentication handlers can satisfy the "
+ + " requested authentication method " + s);
+ continue;
+ }
+
+ handler = this.evaluateHandler(handlerInfo, s, forceAuthN, passiveAuthN);
+
+ if (handler != null) {
+ // we found a match. stop iterating.
+ log.info("Using authentication handler " + handlerInfo.getHandler().getClass().getName()
+ + " for authentication method " + s);
+ break;
+ }
+ }
+
+ if (handler == null) {
+ log.error("No registered authentication handlers could satisify any requested "
+ + "authentication methods. Unable to process authentication request.");
+ }
+
+ return (handler);
+ }
+
+
/**
- * Sets the session manager to be used by this servlet.
- *
- * @param manager session manager to be used by this servlet
+ * Evaluate an authenticationhandler against a set of evaluation criteria.
+ *
+ * @param handlerInfo Handler metadata
+ * @param forceAuthN Is (re)authentication forced?
+ * @param passiveAuthN Can the AuthenticationHandler take control of the UI
+ * @param description A description of the handler
+ *
+ * @return A reference to an {@link AuthenticationHandler} or <code>null</code>.
*/
- public void setSessionManager(SessionManager manager){
- sessionMgr = manager;
+ private AuthenticationHandler evaluateHandler(final AuthenticationHandlerInfo handlerInfo,
+ String description, boolean forceAuthN, boolean passiveAuthN) {
+
+ if (handlerInfo == null) {
+ return (null);
+ }
+
+ if (forceAuthN && !handlerInfo.supportsForce()) {
+ log.debug("The RequestedAuthnContext required forced authentication, "
+ + "but the " + description + " handler does not support that feature.");
+ return (null);
+ }
+
+ if (passiveAuthN && !handlerInfo.supportsPassive()) {
+ log.debug("The RequestedAuthnContext required passive authentication, "
+ + "but the " + description + " handler does not support that feature.");
+ return (null);
+ }
+
+ return handlerInfo.getHandler();
}
-}
\ No newline at end of file
+}
--- /dev/null
+/*
+ * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.internet2.middleware.shibboleth.idp.authn;
+
+
+import java.util.Map;
+import javolution.util.FastMap;
+
+import org.joda.time.DateTime;
+
+
+/**
+ * Login context created by a profile handler and interpreted
+ * by the authentication package.
+ *
+ * Two properties are tracked by default:
+ *
+ * <code>forceAuth</code> - Should user authentiation be forced.
+ * <code>passiveAuth</code> - Should user authentication not control the UI.
+ *
+ * A Map<String, Object> is provided to store other properties.
+ * Alternatively, a profile handler may create a subclass of LoginContext with
+ * extra fields.
+ *
+ * LoginContexts should be created by a profile handler when authentication is needed.
+ * Once control has returned to the profile handler, it should remove the LoginContext
+ * from the HttpSession.
+ *
+ * The {@link AuthenticationManager} or an {@link AuthenticationHandler} should set the
+ * {@link LoginContext#setAuthenticationAttempted()}, {@link LoginContext#setAuthnOK(boolean)},
+ * {@link LoginContext#setAuthnFailure(String)} appropriately.
+ *
+ */
+public class LoginContext {
+
+ /** the key in a HttpSession where login contexts are stored */
+ public static final String LOGIN_CONTEXT_KEY = "shib2.logincontext";
+
+
+ /** Should user authentication be forced */
+ private boolean forceAuth = false;
+
+ /** Must authentication not interact with the UI */
+ private boolean passiveAuth = false;
+
+ /** a catch-all map for other properties */
+ private Map<String, Object> propsMap = new FastMap<String, Object>();
+
+ /** The ProfileHandler URL */
+ private String profileHandlerURL;
+
+ /** The AuthenticationManager's URL */
+ private String authnManagerURL;
+
+ /** has authentication been attempted yet */
+ private boolean authnAttempted = false;
+
+ /** The id of the authenticated user */
+ private String userID;
+
+ /** Did authentication succceed? */
+ private boolean authenticationOK;
+
+ /** Optional failure message */
+ private String authnFailureMessage;
+
+ /** The instant of authentication */
+ private DateTime authnInstant;
+
+ /** The duration of authentication */
+ private long authnDuration;
+
+ /** The method used to authenticate the user */
+ private String authnMethod;
+
+ /** The session id */
+ private String sessionID;
+
+
+ /** Creates a new instance of LoginContext */
+ public LoginContext() {
+ }
+
+
+ /**
+ * Creates a new instance of LoginContext
+ *
+ * @param forceAuth if the authentication manager must reauth the user.
+ * @param passiveAuth if the authentication manager must not interact with the users UI.
+ */
+ public LoginContext(boolean forceAuth, boolean passiveAuth) {
+
+ this.forceAuth = forceAuth;
+ this.passiveAuth = passiveAuth;
+ }
+
+
+ /**
+ * Returns if authentication must be forced.
+ *
+ * @return <code>true</code> if the authentication manager must reauth the user.
+ */
+ public boolean getForceAuth() {
+ return this.forceAuth;
+ }
+
+
+ /**
+ * Returns if authentication must be passive.
+ *
+ * @return <code>true</code> if the authentication manager must not interact with the users UI.
+ */
+ public boolean getPassiveAuth() {
+ return this.passiveAuth;
+ }
+
+
+ /**
+ * Sets if authentication must be forced.
+ *
+ * @param forceAuth if the authentication manager must reauth the user.
+ */
+ public void setForceAuth(boolean forceAuth) {
+ this.forceAuth = forceAuth;
+ }
+
+
+ /**
+ * Sets if authentication must be passive.
+ *
+ * @param passiveAuth if the authentication manager must not interact with the users UI.
+ */
+ public void setPassiveAuth(boolean passiveAuth) {
+ this.passiveAuth = passiveAuth;
+ }
+
+
+ /**
+ * Get an optional property object.
+ *
+ * @param key The key in the properites Map.
+ *
+ * @return The object, or <code>null</code> is no object exists for the key.
+ */
+ public Object getProperty(String key) {
+ return this.propsMap.get(key);
+ }
+
+
+ /**
+ * Sets an optional property object.
+ *
+ * If an object is already associated with key, it will be overwritten.
+ *
+ * @param key The key to set.
+ * @param obj The object to associate with key.
+ */
+ public void setProperty(String key, final Object obj) {
+ this.propsMap.put(key, obj);
+ }
+
+
+ /**
+ * Sets if authentication succeeded.
+ *
+ * @param authnOK if authentication succeeded;
+ */
+ public void setAuthnOK(boolean authnOK) {
+ this.authenticationOK = authnOK;
+ }
+
+
+ /**
+ * Returns if authentication succeeded.
+ *
+ * @return <code>true</code> is the user was successfully authenticated.
+ */
+ public boolean getAuthnOK() {
+ return this.authenticationOK;
+ }
+
+
+ /** Sets the optional authentication failure message.
+ *
+ * @param failureMessage A description of why authN failed.
+ */
+ public void setAuthnFailureMessage(String failureMessage) {
+ this.authnFailureMessage = failureMessage;
+ }
+
+
+ /**
+ * Returns the optional authentication failure message.
+ *
+ * @return The failure message, or <code>null</code> is none was set.
+ */
+ public String getAuthnFailureMessage() {
+ return this.authnFailureMessage;
+ }
+
+
+ /**
+ * Set if authentication has been attempted.
+ *
+ * This method should be called by an {@link AuthenticationHandler}
+ * while processing a request.
+ */
+ public void setAuthenticationAttempted() {
+ this.authnAttempted = true;
+ }
+
+
+ /**
+ * Returns if authentication has been attempted for this user.
+ */
+ public boolean getAuthenticationAttempted() {
+ return this.authnAttempted;
+ }
+
+
+ /**
+ * Sets the ID of the authenticated user.
+ *
+ * @param userID The userid.
+ */
+ public void setUserID(String userID) {
+ this.userID = userID;
+ }
+
+
+ /**
+ * Returns the ID of the authenticated user.
+ *
+ * @return the ID of the user, or <code>null</code> if authentication failed.
+ */
+ public String getUserID() {
+ return this.userID;
+ }
+
+
+ /**
+ * Gets the ProfileHandler URL.
+ *
+ * @return the URL of the profile handler that is invoking the Authentication Manager.
+ */
+ public String getProfileHandlerURL() {
+ return this.profileHandlerURL;
+ }
+
+
+ /**
+ * Sets the ProfileHandler URL.
+ *
+ * @param profileHandlerURL The URL of the profile handler that invoked the AuthenticationManager/
+ */
+ public void setProfileHandlerURL(String profileHandlerURL) {
+ this.profileHandlerURL = profileHandlerURL;
+ }
+
+
+ /**
+ * Gets the AuthenticationManager URL.
+ *
+ * @return the URL of the AuthenticationManager.
+ */
+ public String getAuthnManagerURL() {
+ return this.authnManagerURL;
+ }
+
+
+ /**
+ * Sets the AuthenticationManager's URL.
+ *
+ * @param authnManagerURL the URL of the AuthenticationManager.
+ */
+ public void setAuthnManagerURL(String authnManagerURL) {
+ this.authnManagerURL = authnManagerURL;
+ }
+
+
+ /**
+ * Gets the authentication instant.
+ *
+ * @return The instant of authentication, or <code>null</code> if none was set.
+ */
+ public DateTime getAuthenticationInstant() {
+ return this.authnInstant;
+ }
+
+
+ /**
+ * Sets the authentication instant.
+ *
+ * @param authnInstant The instant of authentication.
+ */
+ public void setAuthenticationInstant(final DateTime authnInstant) {
+ this.authnInstant = authnInstant;
+ }
+
+
+ /**
+ * Gets the duration of authentication.
+ *
+ * @return The duration of authentication, or zero if none was set.
+ */
+ public long getAuthenticationDuration() {
+ return this.authnDuration;
+ }
+
+
+ /**
+ * Sets the duration of authentication.
+ *
+ * @param authnDuration The duration of authentication.
+ */
+ public void setAuthenticationDuration(long authnDuration) {
+ this.authnDuration = authnDuration;
+ }
+
+
+ /**
+ * Gets the method used to authenticate the user.
+ *
+ * @return The method used to authenticate the user.
+ */
+ public String getAuthenticationMethod() {
+ return this.authnMethod;
+ }
+
+
+ /**
+ * Sets the method used to authenticate the user.
+ *
+ * @param authnMethod The method used to authenticate the user.
+ */
+ public void setAuthenticationMethod(String authnMethod) {
+ this.authnMethod = authnMethod;
+ }
+
+
+ /**
+ * Gets the {@link Session} ID
+ *
+ * @return the Session id.
+ */
+ public String getSessionID() {
+ return this.sessionID;
+ }
+
+
+ /**
+ * Sets the {@link Session} ID
+ *
+ * @param sessionID the Session ID
+ */
+ public void setSessionID(String sessionID) {
+ this.sessionID = sessionID;
+ }
+}
--- /dev/null
+/*
+ * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.internet2.middleware.shibboleth.idp.authn;
+
+
+import bsh.This;
+import java.util.Map;
+import javolution.util.FastMap;
+
+import org.opensaml.saml2.core.RequestedAuthnContext;
+
+/**
+ * Login context created by a profile handler and interpreted
+ * by the authentication package.
+ *
+ * Two properties are tracked by default:
+ *
+ * <code>forceAuth</code> - Should user authentiation be forced.
+ * <code>passiveAuth</code> - Should user authentication not control the UI.
+ *
+ * A Map<String, Object> is provided to store other properties.
+ * Alternatively, a profile handler may create a subclass of LoginContext with
+ * extra fields.
+ */
+public class Saml2LoginContext extends LoginContext {
+
+ /** The {@link RequestedAuthnContext} */
+ private RequestedAuthnContext ctx;
+
+
+ /** Creates a new instance of LoginContext */
+ public Saml2LoginContext() {
+ }
+
+
+ /**
+ * Creates a new instance of LoginContext
+ *
+ * @param forceAuth if the authentication manager must reauth the user.
+ * @param passiveAuth if the authentication manager must not interact with the users UI.
+ * @param ctx The requested login context.
+ */
+ public Saml2LoginContext(boolean forceAuth, boolean passiveAuth, final RequestedAuthnContext ctx) {
+
+ super(forceAuth, passiveAuth);
+
+ this.ctx = ctx;
+ }
+
+
+ /**
+ * Set the requested authentication context.
+ *
+ * @param ctx The requested authN context.
+ */
+ public void setRequestedAuthnContext(RequestedAuthnContext ctx) {
+ this.ctx = ctx;
+ }
+
+
+ /**
+ * Returns the requested authentication context.
+ *
+ * @return the RequestedAuthnContext, or <code>null</code> if none was set.
+ */
+ public RequestedAuthnContext getRequestedAuthnContext() {
+ return this.ctx;
+ }
+
+
+}
--- /dev/null
+/*
+ * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.internet2.middleware.shibboleth.idp.authn.impl;
+
+
+import edu.internet2.middleware.shibboleth.idp.authn.AuthenticationHandler;
+
+
+/**
+ * Wrapper class around {@link AuthenticationHandler} metadata.
+ *
+ * This class wraps three pieces of metadata:
+ * <code>supportsPassive</code>, a boolean indicating if the handler supports passive authN
+ * <code>supportsForce</code>, a boolean indicating if the handler supports forced authN
+ * <code>handler</code>, a reference to an {@link AuthenticationHandler} servlet
+ */
+public class AuthenticationHandlerInfo {
+
+ /** does the handler support passive authN */
+ private boolean supportsPassive;
+
+ /** does the handler support forced authN */
+ private boolean supportsForce;
+
+ /** the {@link AuthenticationHandler itself */
+ private AuthenticationHandler handler;
+
+
+ /**
+ * constructor.
+ *
+ * @param handler The {@link AuthenticationHandler} reference.
+ * @param supportsPassive does the handler supports passive authN
+ * @param supportsForce does the handler supports forced authN
+ *
+ * @throws IllegalArgumentException if handler is <code>null</code>
+ */
+ public AuthenticationHandlerInfo(AuthenticationHandler handler,
+ boolean supportsPassive, boolean supportsForce) throws IllegalArgumentException {
+
+ if (handler == null) {
+ throw new IllegalArgumentException("handler is null");
+ }
+
+
+ this.supportsPassive = supportsPassive;
+ this.supportsForce = supportsForce;
+ }
+
+
+ /**
+ * Getter for the {@link AuthenticationHandler} itself.
+ *
+ * @return the AuthenticationHandler reference.
+ */
+ public AuthenticationHandler getHandler() {
+ return this.handler;
+ }
+
+
+ /**
+ * Getter for supportsPassive.
+ *
+ * @return if the handler supports passive authN.
+ */
+ public boolean supportsPassive() {
+ return this.supportsPassive;
+ }
+
+
+ /**
+ * Getter for supportsForce
+ *
+ * @return if the handler supports forced authN.
+ */
+ public boolean supportsForce() {
+ return this.supportsForce;
+ }
+}
\ No newline at end of file
*/
package edu.internet2.middleware.shibboleth.idp.profile.saml2;
+import java.io.InputStream;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.List;
+
+
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import edu.internet2.middleware.shibboleth.common.profile.ProfileHandler;
+import edu.internet2.middleware.shibboleth.idp.authn.AuthenticationManager;
+
+import javolution.util.FastList;
+
+import org.apache.log4j.Logger;
+
+import org.opensaml.Configuration;
+import org.opensaml.DefaultBootstrap;
+import org.opensaml.common.xml.ParserPoolManager;
+import org.opensaml.saml2.core.AuthnContextClassRef;
+import org.opensaml.saml2.core.AuthnContextDeclRef;
+import org.opensaml.saml2.core.AuthnContextComparisonTypeEnumeration;
+import org.opensaml.saml2.core.AuthnRequest;
+import org.opensaml.saml2.core.GetComplete;
+import org.opensaml.saml2.core.IDPEntry;
+import org.opensaml.saml2.core.IDPList;
+import org.opensaml.saml2.core.LogoutRequest;
+import org.opensaml.saml2.core.RequestedAuthnContext;
+import org.opensaml.saml2.core.Scoping;
+import org.opensaml.xml.ConfigurationException;
+import org.opensaml.xml.io.Unmarshaller;
+import org.opensaml.xml.io.UnmarshallingException;
+import org.opensaml.xml.parse.XMLParserException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.xml.sax.InputSource;
+
/**
* SAML 2.0 Authentication Request profile handler
*/
public class AuthenticationRequest implements ProfileHandler {
-
+
+ private static final Logger log =
+ Logger.getLogger(AuthenticationRequest.class.getName());
+
+
+ /** AuthenticationManager to be used */
+ private AuthenticationManager authnMgr;
+
+
/** {@inheritDoc} */
public boolean processRequest(ServletRequest request, ServletResponse response) throws ServletException {
- // TODO Auto-generated method stub
+
+
+// // Check if we are in scope to handle this AuthnRequest
+// // XXX: How do we get the current IdP's relying party uri (if we're in multiple feds?)
+// boolean scopeOK = this.checkScope(authnRequest, "");
+// if (!scopeOK) {
+// log.error("AuthnRequest contains a Scoping element which "
+// + "does not contain a providerID registered with this IdP.");
+// }
+
+
return false;
}
-}
\ No newline at end of file
+
+
+ /**
+ * Check if an {@link AuthnRequest} contains a {@link Scoping} element.
+ * If so, check if the specified IdP is in the {@link IDPList} element.
+ * If no Scoping element is present, this method returns <code>true</code>.
+ *
+ * @param authnRequest The {@link AuthnRequest} element to check.
+ * @param providerId The IdP's ProviderID
+ *
+ * @return <code>true</code>if idp is in the IDPList, otherwise <code>false</code>
+ */
+ private boolean checkScope(final AuthnRequest authnRequest, String providerId) {
+
+ List<String> idpEntries = new FastList<String>();
+
+ if (authnRequest == null) {
+ return (false);
+ }
+
+ if (providerId == null) {
+ return (false);
+ }
+
+ Scoping scoping = authnRequest.getScoping();
+ if (scoping == null) {
+ return (true);
+ }
+
+ // process all of the explicitly listed idp provider ids
+ IDPList idpList = scoping.getIDPList();
+ if (idpList == null) {
+ return (true);
+ }
+
+ List<IDPEntry> explicitIDPEntries = idpList.getIDPEntrys();
+ if (explicitIDPEntries != null) {
+ for (IDPEntry entry : explicitIDPEntries) {
+ String s = entry.getProviderID();
+ if (s != null) {
+ idpEntries.add(s);
+ }
+ }
+ }
+
+
+ // If the IDPList is incomplete, retrieve the complete list
+ // and add the entries to idpEntries.
+ GetComplete getComplete = idpList.getGetComplete();
+ IDPList referencedIdPs = this.getCompleteIDPList(getComplete);
+ if (referencedIdPs != null) {
+ List<IDPEntry> referencedIDPEntries = referencedIdPs.getIDPEntrys();
+ if (referencedIDPEntries != null) {
+ for (IDPEntry entry : referencedIDPEntries) {
+ String s = entry.getProviderID();
+ if (s != null) {
+ idpEntries.add(s);
+ }
+ }
+ }
+ }
+
+
+ // iterate over all the IDPEntries we've gathered,
+ // and check if we're in scope.
+ boolean found = false;
+ for (String requestProviderId : idpEntries) {
+ if (providerId.equals(requestProviderId)) {
+ found = true;
+ log.debug("Found Scoping match for IdP: ("
+ + providerId + ")");
+ break;
+ }
+ }
+
+ return (found);
+ }
+
+
+ /**
+ * Retrieve an incomplete IDPlist.
+ *
+ * This only handles URL-based <GetComplete/> references.
+ *
+ * @param getComplete The (possibly <code>null</code>) <GetComplete/> element
+ *
+ * @return an {@link IDPList} or <code>null</code> if the uri can't be dereferenced.
+ */
+ private IDPList getCompleteIDPList(GetComplete getComplete) {
+
+ // XXX: enhance this method to cache the url and last-modified-header
+
+ if (getComplete == null) {
+ return (null);
+ }
+
+ String uri = getComplete.getGetComplete();
+ if (uri != null) {
+ return (null);
+ }
+
+
+ IDPList idpList = null;
+ InputStream istream = null;
+
+ try {
+ URL url = new URL(uri);
+ URLConnection conn = url.openConnection();
+ istream = conn.getInputStream();
+
+ // convert the raw data into an XML object
+ DefaultBootstrap.bootstrap();
+ ParserPoolManager parserMgr = ParserPoolManager.getInstance();
+ Document doc = parserMgr.parse(new InputSource(istream));
+ Element docElement = doc.getDocumentElement();
+ Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory().getUnmarshaller(docElement);
+ idpList = (IDPList) unmarshaller.unmarshall(docElement);
+
+ } catch (MalformedURLException ex) {
+ log.error("Unable to retrieve GetComplete IDPList. Unsupported URI: " + uri);
+ } catch (IOException ex) {
+ log.error("IO Error while retreieving GetComplete IDPList from " + uri, ex);
+ } catch (ConfigurationException ex) {
+ log.error("Internal OpenSAML error while parsing GetComplete IDPList from " + uri, ex);
+ } catch (XMLParserException ex) {
+ log.error("Internal OpenSAML error while parsing GetComplete IDPList from " + uri, ex);
+ } catch (UnmarshallingException ex) {
+ log.error("Internal OpenSAML error while unmarshalling GetComplete IDPList from " + uri, ex);
+ } finally {
+ if (istream != null) {
+ try {
+ istream.close();
+ } catch (IOException ex) {
+ // nothing to do here.
+ }
+ }
+ }
+
+ return idpList;
+ }
+}
*
* @return time the user authenticated with this member
*/
- public DateTime getAuthenticationInstance();
+ public DateTime getAuthenticationInstant();
/**
* Gets the duration of this authentication method.
--- /dev/null
+/*
+ * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.internet2.middleware.shibboleth.idp.session.impl;
+
+import org.joda.time.DateTime;
+
+import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
+
+
+/**
+ * Information about an authentication method employed by a user.
+ */
+public class AuthenticationMethodInformationImpl implements AuthenticationMethodInformation {
+
+ /** The authentication method (a URI) */
+ private String authenticationMethod;
+
+ /** The timestamp at which authentication occurred */
+ private DateTime authenticationInstant;
+
+ /** The lifetime of the authentication method */
+ private long authenticationDuration;
+
+
+ /**
+ * Default constructor
+ *
+ * @param authenticationMethod The unique identifier for the authentication method.
+ * @param authenticationInstant The time the user authenticated with this member.
+ * @param authenticationDuration The duration of this authentication method.
+ */
+ public AuthenticationMethodInformationImpl(final String authenticationMethod,
+ final DateTime authenticationInstant, long authenticationDuration) {
+
+ if (authenticationMethod == null || authenticationInstant == null
+ || authenticationDuration < 0) {
+ return;
+ }
+
+ this.authenticationMethod = authenticationMethod;
+ this.authenticationInstant = authenticationInstant;
+ this.authenticationDuration = authenticationDuration;
+ }
+
+
+ /**
+ * "Cloning" constructor.
+ *
+ * @param methodInfo The {@link AuthenticationMethodInfo} to duplicate.
+ */
+ public AuthenticationMethodInformationImpl(final AuthenticationMethodInformation methodInfo) {
+
+ if (methodInfo == null) {
+ return;
+ }
+
+ this.authenticationMethod = methodInfo.getAuthenticationMethod();
+ this.authenticationInstant = methodInfo.getAuthenticationInstant();
+ this.authenticationDuration = methodInfo.getAuthenticationDuration();
+ }
+
+
+ /** {@inheritDoc} */
+ public String getAuthenticationMethod() {
+ return this.authenticationMethod;
+ }
+
+
+ /** {@inheritDoc} */
+ public DateTime getAuthenticationInstant() {
+ return this.authenticationInstant;
+ }
+
+
+ /** {@inheritDoc} */
+ public long getAuthenticationDuration() {
+ return this.authenticationDuration;
+ }
+
+
+ /** {@inheritDoc} */
+ public boolean equals(Object obj) {
+
+ if (!(obj instanceof AuthenticationMethodInformation)) {
+ return false;
+ }
+
+ AuthenticationMethodInformation amInfo = (AuthenticationMethodInformation)obj;
+
+ if (this.getAuthenticationMethod().equals(amInfo.getAuthenticationMethod())
+ && this.getAuthenticationInstant().equals(amInfo.getAuthenticationInstant())
+ && this.getAuthenticationDuration() == amInfo.getAuthenticationDuration()) {
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.internet2.middleware.shibboleth.idp.session.impl;
+
+import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
+import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
+
+import org.joda.time.DateTime;
+import org.opensaml.saml2.core.NameID;
+
+
+/**
+ * Information about a service a user has logged in to.
+ */
+public class ServiceInformationImpl implements ServiceInformation {
+
+ private String entityID;
+
+ private DateTime authenticationInstant;
+
+ private AuthenticationMethodInformation methodInfo;
+
+ private NameID nameId;
+
+
+ /**
+ * Default constructor.
+ *
+ * @param entityID The unique identifier for the service.
+ * @param authenticationInstant The time the user authenticated to the service.
+ * @param methodInfo The authentication method used to log into the service.
+ * @param nameId The {@link NameID} used for the subject/user with this service.
+ *
+ */
+ public ServiceInformationImpl(String entityID, DateTime authenticationInstant,
+ AuthenticationMethodInfo methodInfo, final NameID nameId) {
+
+ if (entityID == null || authenticationInstant == null
+ || methodInfo == null || nameId == null) {
+
+ return;
+ }
+
+ this.entityID = entityID;
+ this.authenticationInstant = authenticationInstant;
+ this.methodInfo = methodInfo;
+ this.nameId = nameId; // XXX: Don't we need to defensively clone nameId? It's mutable.
+ }
+
+
+ /**
+ * Cloning constructor.
+ *
+ * @param serviceInfo The ServiceInformation instance to duplicate.
+ */
+ public ServiceInformationImpl(final ServiceInformation serviceInfo) {
+
+ if (serviceInfo == null) {
+ return;
+ }
+
+ this.entityID = serviceInfo.getEntityID();
+ this.authenticationInstant = serviceInfo.getAuthenticationInstant();
+ this.methodInfo = serviceInfo.getAuthenticationMethod();
+ this.nameId = serviceInfo.getSubjectNameID();
+ }
+
+
+ /** {@inheritDoc} */
+ public String getEntityID() {
+ return this.entityID;
+ }
+
+
+ /** {@inheritDoc} */
+ public DateTime getAuthenticationInstant() {
+ return this.authenticationInstant;
+ }
+
+
+ /** {@inheritDoc} */
+ public AuthenticationMethodInformation getAuthenticationMethod() {
+ return this.methodInfo;
+ }
+
+
+ /** {@inheritDoc} */
+ public NameID getSubjectNameID() {
+ return this.nameId;
+ }
+
+
+ /** {@inheritDoc} */
+ public boolean equals(Object obj) {
+
+ if (! obj instanceof ServiceInformation) {
+ return false;
+ }
+
+ ServiceInformation si = (ServiceInformation)obj;
+
+ if (this.getEntityID().equals(si.getEntityID())
+ && this.getAuthenticationInstant().equals(si.getAuthenticationInstant())
+ && this.getSubjectNameID().equals(si.getSubjecTNameID())) {
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.internet2.middleware.shibboleth.idp.session.impl;
+
+import java.util.List;
+
+import javolution.util.FastList;
+
+import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
+import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
+import edu.internet2.middleware.shibboleth.idp.session.Session;
+
+// implementation note:
+// pay attention to package names in this file!
+//
+// this class is shib.idp.session.impl.SessionImpl. It implements the shib.idp.session.Session
+// interface. that interface, in turn, extends shib.common.session.Session, which is implemented
+// in shib.common.session.impl.SessionImpl.
+
+
+/**
+ * Session information for user logged into the IdP.
+ */
+public class SessionImpl
+ extends edu.internet2.middleware.shibboleth.common.session.impl.SessionImpl
+ implements Session {
+
+ /** The list of methods used to authentictate the user */
+ private List<AuthenticationMethodInformation> authnMethods =
+ new FastList<AuthenticationMethodInformation>();
+
+ /** The list of services to which the user has logged in */
+ private List<ServiceInformation> servicesInformation =
+ new FastList<ServiceInformation>();
+
+
+ /**
+ * Default constructor.
+ *
+ * @param principalID The principal ID of the user
+ */
+ public SessionImpl(String principalID) {
+
+ super(principalID);
+ }
+
+
+ /** {@inheritDoc} */
+ public List<AuthenticationMethodInformation> getAuthenticationMethods() {
+
+ // XXX : This is suspect. One should not return
+ // a reference to a private mutable object. The Session
+ // interface should have methods for adding and removing
+ // AuthenticationMethodInformation and ServicesInformation
+ // entries. Further, the Session interface assumes that
+ // the implementation will return a thread-safe List. Not
+ // all List implementations are thread-safe.
+
+ return this.authnMethods;
+ }
+
+
+ /** {@inheritDoc} */
+ public List<ServiceInformation> getServicesInformation() {
+
+ // XXX: warning: Potentially dangerous. see above note.
+
+ return this.servicesInformation;
+ }
+
+}