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 */ 017package org.apache.activemq.jaas; 018 019import java.io.IOException; 020import java.security.Principal; 021import java.util.HashSet; 022import java.util.Map; 023import java.util.Properties; 024import java.util.Set; 025 026import javax.security.auth.Subject; 027import javax.security.auth.callback.Callback; 028import javax.security.auth.callback.CallbackHandler; 029import javax.security.auth.callback.NameCallback; 030import javax.security.auth.callback.PasswordCallback; 031import javax.security.auth.callback.UnsupportedCallbackException; 032import javax.security.auth.login.FailedLoginException; 033import javax.security.auth.login.LoginException; 034import javax.security.auth.spi.LoginModule; 035 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039public class PropertiesLoginModule extends PropertiesLoader implements LoginModule { 040 041 private static final String USER_FILE_PROP_NAME = "org.apache.activemq.jaas.properties.user"; 042 private static final String GROUP_FILE_PROP_NAME = "org.apache.activemq.jaas.properties.group"; 043 044 private static final Logger LOG = LoggerFactory.getLogger(PropertiesLoginModule.class); 045 046 private Subject subject; 047 private CallbackHandler callbackHandler; 048 049 private Properties users; 050 private Map<String,Set<String>> groups; 051 private String user; 052 private final Set<Principal> principals = new HashSet<Principal>(); 053 054 /** the authentication status*/ 055 private boolean succeeded = false; 056 private boolean commitSucceeded = false; 057 058 @Override 059 public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { 060 this.subject = subject; 061 this.callbackHandler = callbackHandler; 062 succeeded = false; 063 init(options); 064 users = load(USER_FILE_PROP_NAME, "user", options).getProps(); 065 groups = load(GROUP_FILE_PROP_NAME, "group", options).invertedPropertiesValuesMap(); 066 } 067 068 @Override 069 public boolean login() throws LoginException { 070 Callback[] callbacks = new Callback[2]; 071 072 callbacks[0] = new NameCallback("Username: "); 073 callbacks[1] = new PasswordCallback("Password: ", false); 074 try { 075 callbackHandler.handle(callbacks); 076 } catch (IOException ioe) { 077 throw new LoginException(ioe.getMessage()); 078 } catch (UnsupportedCallbackException uce) { 079 throw new LoginException(uce.getMessage() + " not available to obtain information from user"); 080 } 081 user = ((NameCallback) callbacks[0]).getName(); 082 char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword(); 083 if (tmpPassword == null) { 084 tmpPassword = new char[0]; 085 } 086 if (user == null) { 087 throw new FailedLoginException("user name is null"); 088 } 089 String password = users.getProperty(user); 090 091 if (password == null) { 092 throw new FailedLoginException("User does exist"); 093 } 094 if (!password.equals(new String(tmpPassword))) { 095 throw new FailedLoginException("Password does not match"); 096 } 097 succeeded = true; 098 099 if (debug) { 100 LOG.debug("login " + user); 101 } 102 return succeeded; 103 } 104 105 @Override 106 public boolean commit() throws LoginException { 107 if (!succeeded) { 108 clear(); 109 if (debug) { 110 LOG.debug("commit, result: false"); 111 } 112 return false; 113 } 114 115 principals.add(new UserPrincipal(user)); 116 117 Set<String> matchedGroups = groups.get(user); 118 if (matchedGroups != null) { 119 for (String entry : matchedGroups) { 120 principals.add(new GroupPrincipal(entry)); 121 } 122 } 123 124 subject.getPrincipals().addAll(principals); 125 126 if (debug) { 127 LOG.debug("commit, result: true"); 128 } 129 130 commitSucceeded = true; 131 return true; 132 } 133 134 @Override 135 public boolean abort() throws LoginException { 136 if (debug) { 137 LOG.debug("abort"); 138 } 139 if (!succeeded) { 140 return false; 141 } else if (succeeded && commitSucceeded) { 142 // we succeeded, but another required module failed 143 logout(); 144 } else { 145 // our commit failed 146 clear(); 147 succeeded = false; 148 } 149 return true; 150 } 151 152 @Override 153 public boolean logout() throws LoginException { 154 subject.getPrincipals().removeAll(principals); 155 clear(); 156 if (debug) { 157 LOG.debug("logout"); 158 } 159 160 succeeded = false; 161 commitSucceeded = false; 162 return true; 163 } 164 165 private void clear() { 166 user = null; 167 principals.clear(); 168 } 169 170}