JmxAuthorizeContext.java

package com.ziesemer.utils.jmx.server.authorization;

import java.lang.reflect.Method;
import java.security.AccessController;
import java.util.Set;

import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.remote.JMXPrincipal;
import javax.security.auth.Subject;

/**
 * <p>Used by {@link BaseJmxAuthorizer}.</p>
 * @author Mark A. Ziesemer
 * 	<a href="http://www.ziesemer.com.">&lt;www.ziesemer.com&gt;</a>
 */
public class JmxAuthorizeContext{
	public final MBeanServer mBeanServer;
	public final Object proxy;
	public final Method method;
	public final Object[] args;
	
	public Object customContext;
	
	// These values are cached for performance.
	protected boolean subjectLoaded;
	protected Subject subject;
	protected boolean principalLoaded;
	protected JMXPrincipal principal;
	protected Integer impact;
	
	public JmxAuthorizeContext(final MBeanServer mBeanServer,
			final Object proxy, final Method method, final Object[] args){
		this.mBeanServer = mBeanServer;
		this.proxy = proxy;
		this.method = method;
		this.args = args;
	}
	
	/**
	 * <p>Returns the cached or current {@link Subject} from {@link AccessController#getContext()}.
	 * 	May be null.</p>
	 */
	public Subject getSubject(){
		if(!subjectLoaded){
			subject = Subject.getSubject(AccessController.getContext());
			subjectLoaded = true;
		}
		return subject;
	}
	
	/**
	 * <p>Returns the cached or current {@link JMXPrincipal} from {@link #getSubject()}.
	 * 	May be null.</p>
	 */
	public JMXPrincipal getPrincipal(){
		if(!principalLoaded){
			final Subject subject = getSubject();
			if(subject != null){
				final Set<JMXPrincipal> principals = subject.getPrincipals(JMXPrincipal.class);
				if(principals == null || principals.isEmpty()){
					throw new SecurityException("Access denied (principal)");
				}
				principal = principals.iterator().next();
				principalLoaded = true;
			}
		}
		return principal;
	}
	
	/**
	 * <p>Returns the cached or current {@link MBeanOperationInfo#getImpact()}
	 * 	for the current operation.</p>
	 * @throws UnsupportedOperationException If the current call is not a call to
	 * 	{@link MBeanServer#invoke(ObjectName, String, Object[], String[])}.
	 */
	public int getImpact() throws Exception{
		if(!"invoke".equals(method.getName())){
			throw new UnsupportedOperationException("getImpact only applicable for invoke operations.");
		}
		
		if(impact != null){
			return impact;
		}
		
		final ObjectName on = (ObjectName)args[0];
		final String methodName = (String)args[1];
		// Actual values are stored in [2].
		final String[] methodSig = (String[])args[3];
		final MBeanInfo mbi = mBeanServer.getMBeanInfo(on);
		for(final MBeanOperationInfo oi : mbi.getOperations()){
			if(methodName.equals(oi.getName())){
				final MBeanParameterInfo[] sig = oi.getSignature();
				if(methodSig.length == sig.length){
					boolean match = true;
					for(int i=0; i < methodSig.length && match; i++){
						match = methodSig[i].equals(sig[i].getType());
					}
					if(match){
						return (impact = oi.getImpact());
					}
				}
			}
		}
		return (impact = MBeanOperationInfo.UNKNOWN);
	}
	
	public Object invoke() throws Throwable{
		return method.invoke(mBeanServer, args);
	}
}