/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.jmx.adaptor.html; import java.io.IOException; import java.lang.reflect.Method; import java.security.Principal; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.StringTokenizer; import javax.security.auth.Subject; import javax.security.jacc.PolicyContext; import javax.security.jacc.PolicyContextException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import org.jboss.logging.Logger; import org.jboss.security.SimpleGroup; //$Id: JMXOpsAccessControlFilter.java 81038 2008-11-14 13:43:27Z dimitris@jboss.org $ /** * JBAS-3311: Access Control on JMX Operations in the JMX Console. * Filter that allows Role Based Authorization of the various * JMX Operations. The actions that come as part of the request are: * displayMBeans * inspectMBean * updateAttributes - Operations that involve updation of jmx attributes * invokeOp - Operations that involve "invoke" * invokeOpByName * @author <a href="mailto:Anil.Saldhana@jboss.org">Anil Saldhana</a> * @since Jun 12, 2006 * @version $Revision: 81038 $ */ public class JMXOpsAccessControlFilter implements Filter { private static Logger log = Logger.getLogger(JMXOpsAccessControlFilter.class); private boolean trace = log.isTraceEnabled(); private static final String ACTION_PARAM = "action"; private static final String DISPLAY_MBEANS_ACTION = "displayMBeans"; private static final String INSPECT_MBEAN_ACTION = "inspectMBean"; private static final String UPDATE_ATTRIBUTES_ACTION = "updateAttributes"; private static final String INVOKE_OP_ACTION = "invokeOp"; private static final String INVOKE_OP_BY_NAME_ACTION = "invokeOpByName"; private List updateAttributesRoles = null; private List invokeOpRoles = null; //Rare usecase private List invokeMBeanRoles = null; //An authorization delegate that the user can plug in which can do the //authorization decisions - when deeper access control usecases arise //The Authorization Delegate should have a method //public Boolean authorize(ServletRequest,ServletResponse,List) private Object authorizationDelegate = null; /** * @see Filter#init(javax.servlet.FilterConfig) */ public void init(FilterConfig filterConfig) throws ServletException { String updateAttributesStr = filterConfig.getInitParameter("updateAttributes"); if(updateAttributesStr != null && updateAttributesStr.length() > 0) updateAttributesRoles = this.getRoles(updateAttributesStr); String invokeOpStr = filterConfig.getInitParameter("invokeOp"); if(invokeOpStr != null && invokeOpStr.length() > 0) invokeOpRoles = this.getRoles(invokeOpStr); String inspectMBeanStr = filterConfig.getInitParameter("inspectMBean"); if(inspectMBeanStr != null && inspectMBeanStr.length() > 0) invokeMBeanRoles = this.getRoles(inspectMBeanStr); //Optional - Authorization Delegate String delegateStr = filterConfig.getInitParameter("authorizationDelegate"); if(delegateStr != null && delegateStr.length() > 0) authorizationDelegate = this.instantiate(delegateStr); } /** * @see Filter#doFilter(javax.servlet.ServletRequest, * javax.servlet.ServletResponse, javax.servlet.FilterChain) */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { boolean passThrough = true; String action = request.getParameter(ACTION_PARAM); if( action == null ) action = DISPLAY_MBEANS_ACTION; if( action.equals(UPDATE_ATTRIBUTES_ACTION)) passThrough = authorize(request, response, updateAttributesRoles); else if( action.equals(INVOKE_OP_ACTION) || action.equals(INVOKE_OP_BY_NAME_ACTION)) passThrough = authorize(request, response,invokeOpRoles); else if( action.equals(INSPECT_MBEAN_ACTION)) passThrough = authorize(request, response,invokeMBeanRoles); if(!passThrough) ((HttpServletResponse)response).setStatus(HttpServletResponse.SC_FORBIDDEN); else chain.doFilter(request, response); } /** * @see Filter#destroy() */ public void destroy() { } /** * Authorize the JMX Operations * If there is an Authorization Delegate plugged in, it will * be consulted for access control * @param request * @param response * @param listToCheck * @return */ private boolean authorize(ServletRequest request, ServletResponse response, List listToCheck) { //Check if there is an authorization delegate if(authorizationDelegate != null) return checkWithDelegate(request,response,listToCheck); if(listToCheck == null || listToCheck.size() == 0) return true; ArrayList subjectRoles = getSubjectRoles(); boolean result = false; int len = subjectRoles.size(); for(int i = 0; i < len; i++) { String subjectRole = (String)subjectRoles.get(i); result = listToCheck.contains(subjectRole); if(result) break; } return result; } private boolean checkWithDelegate(ServletRequest request, ServletResponse response, List listToCheck) { Boolean result = Boolean.FALSE; String name = "authorize"; Class[] args = new Class[] {ServletRequest.class, ServletResponse.class, List.class}; try { Method meth = authorizationDelegate.getClass().getMethod(name,args); result = (Boolean)meth.invoke(authorizationDelegate, new Object[]{request,response,listToCheck}); } catch ( Exception e) { if(trace) log.error("Error invoking AuthorizationDelegate:",e); } return result.booleanValue(); } /** * Get a list of roles from the string that is comma-delimited * @param commaSeperatedRoles * @return */ private List getRoles(String commaSeperatedRoles) { StringTokenizer st = new StringTokenizer(commaSeperatedRoles,","); int numTokens = st.countTokens(); String[] strArr = new String[numTokens]; for(int i=0; i < numTokens; i++) { strArr[i] = st.nextToken(); } return Arrays.asList(strArr); } /** * Get a list of roles from the authenticated subject * @return */ private ArrayList getSubjectRoles() { ArrayList alist = new ArrayList(); String SUBJECT_CONTEXT_KEY = "javax.security.auth.Subject.container"; try { Subject caller = (Subject) PolicyContext.getContext(SUBJECT_CONTEXT_KEY); Iterator iter = caller.getPrincipals().iterator(); while(iter != null && iter.hasNext()) { Principal p = (Principal)iter.next(); if(p instanceof SimpleGroup) { SimpleGroup sg = (SimpleGroup)p; String name = sg.getName(); if("Roles".equals(name)) { Enumeration en = sg.members(); while(en.hasMoreElements()) { String role = en.nextElement().toString(); if(role != null) alist.add(role); } } } } } catch (PolicyContextException e) { if(trace) log.trace("Error obtaining authenticated subject:",e); } if(trace) log.trace("Subject Roles="+alist); return alist; } /** * Instantiate The Authorization Delegate * @param delegateStr * @return */ public Object instantiate(String delegateStr) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); Object obj = null; try { Class clazz = cl.loadClass(delegateStr); obj = clazz.newInstance(); } catch (Exception e) { if(trace) log.error("Error instantiating AuthorizationDelegate:",e); } return obj; } }