/** * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2007 Sun Microsystems Inc. All Rights Reserved * * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at * https://opensso.dev.java.net/public/CDDLv1.0.html or * opensso/legal/CDDLv1.0.txt * See the License for the specific language governing * permission and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at opensso/legal/CDDLv1.0.txt. * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * $Id: IdentityServicesHandler.java,v 1.7 2008/12/15 19:50:21 arviranga Exp $ * */ /* * Portions Copyrighted 2011-2015 ForgeRock AS. * Portions Copyrighted 2012 Open Source Solution Technology Corporation */ package com.sun.identity.idsvcs.rest; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import com.iplanet.sso.SSOException; import com.iplanet.sso.SSOToken; import com.iplanet.sso.SSOTokenManager; import com.sun.identity.authentication.client.AuthClientUtils; import com.sun.identity.idsvcs.Attribute; import com.sun.identity.idsvcs.GeneralFailure; import com.sun.identity.idsvcs.IdentityDetails; import com.sun.identity.idsvcs.IdentityServicesFactory; import com.sun.identity.idsvcs.IdentityServicesImpl; import com.sun.identity.idsvcs.ListWrapper; import com.sun.identity.idsvcs.ObjectNotFound; import com.sun.identity.idsvcs.Token; import com.sun.identity.idsvcs.UserDetails; import com.sun.identity.shared.debug.Debug; /** * Provides a marshall/unmarshall layer to the Security interface. */ public class IdentityServicesHandler extends HttpServlet { private static final long serialVersionUID = 2774677132209419157L; private static Debug debug = Debug.getInstance("amIdentityServices"); // ======================================================================= // Constants // ======================================================================= private static final String PARAM_PROVIDER = "provider"; private static final Class PROVIDER_DEFAULT = IdentityServicesImpl.class; // ======================================================================= // Fields // ======================================================================= private IdentityServicesFactory factory; private String lbCookieName; private String lbCookieValue; // ======================================================================= // Initialize/Destroy // ======================================================================= /** * Loads the init parameters for use in the HTTP methods. * * @see javax.servlet.GenericServlet#init() */ public void init() throws ServletException { super.init(); // determine if the provider is correct.. try { // get the security provider from the params. String def = PROVIDER_DEFAULT.toString(); String provider = getInitParameter(PARAM_PROVIDER, def); this.factory = IdentityServicesFactory.getInstance(provider); } catch (Exception e) { // wrap in a servlet exception as to not scare the natives.. throw new ServletException(e); } } // ======================================================================= // HTTP Methods // ======================================================================= /** * Determines unmarshalls the request and executes the proper method based * on the request parameters. * * @see javax.servlet.http.HttpServlet#service(HttpServletRequest request, * HttpServletResponse response) */ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // check/init LB cookie names if (lbCookieName == null || lbCookieValue == null) { initLbCookieSettings(); } //set headers before executing the method, so they are set even if exception is being thrown response.addHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"); response.addHeader("Pragma", "no-cache"); IdentityServicesImpl security = this.factory.newInstance(); SecurityMethod.execute(security, request, response); // check/set LB cookie setLbCookie(request, response); } // ======================================================================= // Helper Methods // ======================================================================= /** * Get a consistent behaviour between application servers.. */ String getInitParameter(String param, String def) { String ret = getInitParameter(param); if (isBlank(param)) { ret = def; } return ret; } private static boolean isBlank(String val) { return (val == null) ? true : ((val.trim().length() == 0) ? true : false); } private void initLbCookieSettings() { lbCookieName = AuthClientUtils.getlbCookieName(); lbCookieValue = AuthClientUtils.getlbCookieValue(); } private void setLbCookie(HttpServletRequest request, HttpServletResponse response) { if (lbCookieName == null || lbCookieValue == null) { return; } Cookie[] cookies = request.getCookies(); if (cookies != null) { for (int c = 0; c < cookies.length; c++) { if (cookies[c].getName().equals(lbCookieName)) { return; } } } try { AuthClientUtils.setlbCookie(request, response); } catch (Exception ex) { // unable to set lb cookie } } /** * Enum to get the request parameters and test w/ the SecurityMethods. */ public static class SecurityParameter { public static final SecurityParameter URI = new SecurityParameter("URI"); public static final SecurityParameter ACTION = new SecurityParameter("ACTION"); public static final SecurityParameter USERNAME = new SecurityParameter("USERNAME"); public static final SecurityParameter PASSWORD = new SecurityParameter("PASSWORD"); public static final SecurityParameter SUBJECTID = new SecurityParameter("SUBJECTID", Token.class); public static final SecurityParameter IDENTITY = new SecurityParameter("IDENTITY", IdentityDetails.class); public static final SecurityParameter ATTRIBUTENAMES = new SecurityParameter("ATTRIBUTENAMES", (new String[1]).getClass()); public static final SecurityParameter LOGNAME = new SecurityParameter("LOGNAME"); public static final SecurityParameter MESSAGE = new SecurityParameter("MESSAGE"); public static final SecurityParameter APPID = new SecurityParameter("APPID", Token.class); public static final SecurityParameter ADMIN = new SecurityParameter("ADMIN", Token.class); public static final SecurityParameter NAME = new SecurityParameter("NAME"); public static final SecurityParameter FILTER = new SecurityParameter("FILTER"); public static final SecurityParameter ATTRIBUTES = new SecurityParameter("ATTRIBUTES", Attribute[].class); public static final SecurityParameter REFRESH = new SecurityParameter("REFRESH", Boolean.class); // =================================================================== // Fields // =================================================================== final Class type; final String name; SecurityParameter(String name) { this.name = name; this.type = String.class; } SecurityParameter(String name, Class type) { this.name = name; this.type = type; } String name() { return name; } Object getValue(ServletRequest request) { Object ret = null; if (this.type == Token.class) { ret = getToken(request); } else if (this.type == List.class) { ret = getList(request); } else if (this.type == String[].class) { ret = getArray(request); } else if (this.type == Attribute[].class) { ret = getAttributeArray(request); } else if (this.type == IdentityDetails.class) { ret = getIdentityDetails(request); } else if (type == Boolean.class) { ret = getBoolean(request); } else { ret = getString(request); } return ret; } public Boolean getBoolean(ServletRequest request) { String name = name().toLowerCase(); return Boolean.valueOf(request.getParameter(name)); } public String getString(ServletRequest request) { String name = name().toLowerCase(); String ret = request.getParameter(name); if (isBlank(ret)) { ret = null; } return ret; } public Token getToken(ServletRequest request) { Token ret = null; String n = name().toLowerCase(); String id = request.getParameter(n); if (isBlank(id)) { try { // Check the cookie value "iPlanetDirectoryPro" SSOTokenManager mgr = SSOTokenManager.getInstance(); SSOToken token = mgr.createSSOToken( (HttpServletRequest) request); if (token != null) { id = token.getTokenID().toString(); } } catch (SSOException ex) { // Ignore the exception, and no valid token } } if (!isBlank(id)) { ret = new Token(); ret.setId(id); } return ret; } public List getList(ServletRequest request) { List ret = null; String n = name().toLowerCase(); String[] values = request.getParameterValues(n); if (values != null) { ret = new ArrayList(); for (int i = 0; i < values.length; i++) { String value = values[i]; if (!isBlank(value)) { ret.add(value); } } } return ret; } public String[] getArray(ServletRequest request) { String[] ret = null; List valuesList = getList(request); if ((valuesList != null) && (valuesList.size() > 0)) { ret = new String[valuesList.size()]; valuesList.toArray(ret); } return ret; } public Attribute[] getAttributeArray(ServletRequest request) { Attribute[] ret = null; List attributeList = null; String n = name().toLowerCase(); String[] attrNames = request.getParameterValues(n + "_names"); if (attrNames != null) { for (int i = 0; i < attrNames.length; i++) { String attrName = attrNames[i]; if (isBlank(attrName)) { break; } String attrValues[] = request.getParameterValues(n + "_values_" + attrName); if ((attrValues != null) && (attrValues.length > 0)) { List attrValueList = new ArrayList(); for (int j = 0; j < attrValues.length; j++) { String attrValue = attrValues[j]; if (!isBlank(attrValue)) { attrValueList.add(attrValue); } } String[] attrValuesArray = new String[attrValueList.size()]; attrValueList.toArray(attrValuesArray); Attribute attribute = new Attribute(); attribute.setName(attrName); attribute.setValues(attrValuesArray); if (attributeList == null) { attributeList = new ArrayList(); } attributeList.add(attribute); } else { // Add empyt attribute Attribute attribute = new Attribute(); attribute.setName(attrName); if (attributeList == null) { attributeList = new ArrayList(); } attributeList.add(attribute); } } } if ((attributeList != null) && (attributeList.size() > 0)) { ret = new Attribute[attributeList.size()]; attributeList.toArray(ret); } return ret; } public IdentityDetails getIdentityDetails(ServletRequest request) { IdentityDetails rv = null; String n = name().toLowerCase(); String identityName = request.getParameter(n + "_name"); if (!isBlank(identityName)) { rv = new IdentityDetails(); rv.setName(identityName); String objType = request.getParameter(n + "_type"); if (!isBlank(objType)) { rv.setType(objType); } String realm = request.getParameter(n + "_realm"); if (!isBlank(realm)) { rv.setRealm(realm); } String[] roles = request.getParameterValues(n + "_roles"); if (roles != null) { List rolesList = new ArrayList(); for (int i = 0; i < roles.length; i++) { String role = roles[i]; if (!isBlank(role)) { rolesList.add(role); } } String[] rolesArray = new String[rolesList.size()]; rolesList.toArray(rolesArray); rv.setRoleList(new ListWrapper(rolesArray)); } String[] groups = request.getParameterValues(n + "_groups"); if (groups != null) { List groupsList = new ArrayList(); for (int i = 0; i < groups.length; i++) { String group = groups[i]; if (!isBlank(group)) { groupsList.add(group); } } String[] groupsArray = new String[groupsList.size()]; groupsList.toArray(groupsArray); rv.setGroupList(new ListWrapper(groupsArray)); } String[] members = request.getParameterValues(n + "_members"); if (members != null) { List membersList = new ArrayList(); for (int i = 0; i < members.length; i++) { String member = members[i]; if (!isBlank(member)) { membersList.add(member); } } String[] membersArray = new String[membersList.size()]; membersList.toArray(membersArray); rv.setMemberList(new ListWrapper(membersArray)); } List attrList = new ArrayList(); String[] attrNames = request.getParameterValues(n + "_attribute_names"); if (attrNames != null) { for (int i = 0; i < attrNames.length; i++) { String attrName = attrNames[i]; if (isBlank(attrName)) { break; } Attribute attribute = null; String attrValues[] = request.getParameterValues(n + "_attribute_values_" + attrName); if (attrValues != null) { List attrValueList = new ArrayList(); for (int j = 0; j < attrValues.length; j++) { String attrValue = attrValues[j]; if (!isBlank(attrValue)) { attrValueList.add(attrValue); } } String[] attrValuesArray = new String[attrValueList.size()]; attrValueList.toArray(attrValuesArray); attribute = new Attribute(); attribute.setName(attrName); attribute.setValues(attrValuesArray); attrList.add(attribute); } else { attribute = new Attribute(); attribute.setName(attrName); attrList.add(attribute); } if (attrList.size() > 0) { Attribute[] attrArray = new Attribute[attrList.size()]; attrList.toArray(attrArray); rv.setAttributes(attrArray); } } } } return rv; } } /** * Defined by the interface 'com.sun.identity.idsvcs.Security'. */ public static class SecurityMethod { public static final SecurityMethod TOKENCOOKIE = new SecurityMethod( "GETCOOKIENAMEFORTOKEN", String.class, (SecurityParameter[]) null); public static final SecurityMethod ALLCOOKIES = new SecurityMethod( "GETCOOKIENAMESTOFORWARD", String[].class, (SecurityParameter[]) null); public static final SecurityMethod ATTRIBUTES = new SecurityMethod( "ATTRIBUTES", UserDetails.class, SecurityParameter.ATTRIBUTENAMES, SecurityParameter.SUBJECTID, SecurityParameter.REFRESH); public static final SecurityMethod LOG = new SecurityMethod( "LOG", Void.class, new SecurityParameter[] {SecurityParameter.APPID, SecurityParameter.SUBJECTID, SecurityParameter.LOGNAME, SecurityParameter.MESSAGE}); public static final SecurityMethod READ = new SecurityMethod( "READ", IdentityDetails.class, new SecurityParameter[] {SecurityParameter.NAME, SecurityParameter.ATTRIBUTES, SecurityParameter.ADMIN}); // =================================================================== // Constructors // =================================================================== private SecurityMethod(String name, Class clazz, SecurityParameter[] params) { final Method[] SECURITY_METHODS = IdentityServicesImpl.class.getMethods(); // find the method Method imethod = null; String lname = name.toLowerCase(); for (int i = 0; i < SECURITY_METHODS.length; i++) { Method m = SECURITY_METHODS[i]; // found the method by name.. String mname = m.getName().toLowerCase(); if (mname.equals(lname)) { // lets check based on parameters.. imethod = m; break; } } // need to throw if we can't find it.. if (imethod == null) { throw new IllegalArgumentException(); } // set the internal fields this.type = clazz; this.method = imethod; this.parameters = params; } private SecurityMethod(String name, Class clazz, SecurityParameter param1) { this(name, clazz, new SecurityParameter[]{param1}); } private SecurityMethod(String name, Class clazz, SecurityParameter param1, SecurityParameter param2) { this(name, clazz, new SecurityParameter[]{param1, param2}); } private SecurityMethod(String name, Class clazz, SecurityParameter param1, SecurityParameter param2, SecurityParameter param3) { this(name, clazz, new SecurityParameter[]{param1, param2, param3}); } // =================================================================== // Fields // =================================================================== final Class type; final Method method; final SecurityParameter[] parameters; public static void execute(IdentityServicesImpl security, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // find the security method from the path.. response.setCharacterEncoding("UTF-8"); Writer wrt = response.getWriter(); StringWriter sw = null; String path = request.getPathInfo(); MarshallerFactory mar = getMarshaller(path); // Set the respone content type if (mar.getProtocol().equalsIgnoreCase("XML")) { response.setContentType("text/xml"); } else if (mar.getProtocol().equals("JSON")) { response.setContentType("application/json"); } else { response.setContentType("text/plain"); } path = path.substring(path.lastIndexOf('/') + 1).toUpperCase(); SecurityMethod method = null; if (path.equals("ATTRIBUTES")) { method = SecurityMethod.ATTRIBUTES; } else if (path.equals("LOG")) { method = SecurityMethod.LOG; } else if (path.equals("READ")) { method = SecurityMethod.READ; } else if (path.equals("GETCOOKIENAMEFORTOKEN")) { method = SecurityMethod.TOKENCOOKIE; } else if (path.equals("GETCOOKIENAMESTOFORWARD")) { method = SecurityMethod.ALLCOOKIES; } try { if (method == null) { // Throw Unsupported Operation Exception response.setStatus(501); mar.newInstance(Throwable.class).marshall(wrt, new UnsupportedOperationException(path)); return; } // execute the method w/ the parameters.. Object value = method.invoke(security, request); // marshall the response.. if (method.type != Void.class && value != null) { mar.newInstance(method.type).marshall(wrt, value); } else { response.setContentType("text/plain"); if (value == null) { wrt.write("NULL"); } } } catch (ObjectNotFound ex) { // write out the proper ObjectNotFound exception. // set the response error code if(debug.messageEnabled()) { debug.message("An objecjNotFound exception has been caught; details: ", ex); } try { mar.newInstance(ObjectNotFound.class).marshall(wrt, ex); response.setStatus(401); } catch (Exception e) { // something really went wrong so just give up.. throw new ServletException(e); } } catch (GeneralFailure ex) { // write out the proper security based exception.. if(debug.messageEnabled()) { debug.message("A generalFailure exception has been caught; details: ", ex); } try { mar.newInstance(GeneralFailure.class).marshall(wrt, ex); response.setStatus(500); } catch (Exception e) { // something really went wrong so just give up.. throw new ServletException(e); } } catch (Throwable e) { if(debug.messageEnabled()) { debug.message("An unknown exception has been caught; details: ", e); } try { // something really went wrong so just give up.. mar.newInstance(Throwable.class).marshall(wrt, e); if (e instanceof UnsupportedOperationException) { response.setStatus(501); } else if (e instanceof InvocationTargetException) { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } else { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); } } catch (Exception ex) { throw new ServletException(ex); } } finally { if (sw != null) { sw.close(); } } } /** * Gets the realm being used for this request by parsing the query parameters on the URI parameter (if present). * This matches the logic used in opensso/IdentityServicesImpl. Defaults to "/". */ private static String getRealm(HttpServletRequest request) { String realm = "/"; final String uri = SecurityParameter.URI.getString(request); if (uri != null) { for (String param : uri.split("&")) { String[] parts = param.split("="); if (parts.length == 2 && "realm".equals(parts[0])) { realm = parts[1]; break; } } } return realm; } /** * If both exist on the path then return JSON, XML, and then Properites * in that order. */ private static MarshallerFactory getMarshaller(String path) { // default is properties format boolean xml = path.indexOf("xml/") != -1; boolean json = path.indexOf("json/") != -1; return (json) ? MarshallerFactory.JSON : (xml) ? MarshallerFactory.XML : MarshallerFactory.PROPS; } private Object invoke(IdentityServicesImpl security, ServletRequest request) throws Throwable { // find the value for each parameter.. Object[] params = null; if (parameters != null) { params = new Object[this.parameters.length]; for (int i = 0; i < this.parameters.length; i++) { SecurityParameter param = this.parameters[i]; params[i] = param.getValue(request); } } try { // invoke the actual security param.. return method.invoke(security, params); } catch (IllegalArgumentException e) { throw new GeneralFailure(e.getMessage()); } catch (IllegalAccessException e) { throw new GeneralFailure(e.getMessage()); } catch (InvocationTargetException e) { if (debug.warningEnabled()) { debug.warning("Exception during invocation", e); } throw e.getTargetException(); } } } }