001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.geronimo.jmxremoting;
018
019 import java.util.Map;
020 import java.util.Collections;
021 import java.util.HashMap;
022 import javax.management.remote.JMXAuthenticator;
023 import javax.management.remote.JMXConnectionNotification;
024 import javax.management.NotificationListener;
025 import javax.management.Notification;
026 import javax.security.auth.Subject;
027 import javax.security.auth.login.LoginContext;
028 import javax.security.auth.login.LoginException;
029
030 /**
031 * JMX Authenticator that checks the Credentials by logging in via JAAS.
032 *
033 * @version $Rev: 565912 $ $Date: 2007-08-14 17:03:11 -0400 (Tue, 14 Aug 2007) $
034 */
035 public class Authenticator implements JMXAuthenticator, NotificationListener {
036 private final String configName;
037 private final ClassLoader cl;
038 private ThreadLocal<LoginContext> threadContext = new ThreadLocal<LoginContext>();
039 private Map<String, LoginContext> contextMap = Collections.synchronizedMap(new HashMap<String, LoginContext>());
040
041 /**
042 * Constructor indicating which JAAS Application Configuration Entry to use.
043 * @param configName the JAAS config name
044 * @param cl classloader to use as TCCL for operations
045 */
046 public Authenticator(String configName, ClassLoader cl) {
047 this.configName = configName;
048 this.cl = cl;
049 }
050
051 public Subject authenticate(Object o) throws SecurityException {
052 if (!(o instanceof String[])) {
053 throw new IllegalArgumentException("Expected String[2], got " + o == null ? null : o.getClass().getName());
054 }
055 String[] params = (String[]) o;
056 if (params.length != 2) {
057 throw new IllegalArgumentException("Expected String[2] but length was " + params.length);
058 }
059
060 Thread thread = Thread.currentThread();
061 ClassLoader oldCL = thread.getContextClassLoader();
062 Credentials credentials = new Credentials(params[0], params[1]);
063 try {
064 thread.setContextClassLoader(cl);
065 //TODO consider using ContextManager for login and checking a permission against the ACC
066 //to do e.g. deployments.
067 LoginContext context = new LoginContext(configName, credentials);
068 context.login();
069 threadContext.set(context);
070 return context.getSubject();
071 } catch (LoginException e) {
072 // do not propogate cause - we don't know what information is may contain
073 throw new SecurityException("Invalid login");
074 } finally {
075 credentials.clear();
076 thread.setContextClassLoader(oldCL);
077 }
078 }
079
080 public void handleNotification(Notification notification, Object o) {
081 if (notification instanceof JMXConnectionNotification) {
082 JMXConnectionNotification cxNotification = (JMXConnectionNotification) notification;
083 String type = cxNotification.getType();
084 String connectionId = cxNotification.getConnectionId();
085 if (JMXConnectionNotification.OPENED.equals(type)) {
086 LoginContext context = threadContext.get();
087 threadContext.set(null);
088 contextMap.put(connectionId, context);
089 } else {
090 LoginContext context = contextMap.remove(connectionId);
091 if (context != null) {
092 try {
093 context.logout();
094 } catch (LoginException e) {
095 //nothing we can do here...
096 }
097 }
098 }
099 }
100 }
101 }