/* * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0 * (the "License"). You may not use this work except in compliance with the License, which is * available at www.apache.org/licenses/LICENSE-2.0 * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied, as more fully set forth in the License. * * See the NOTICE file distributed with this work for information regarding copyright ownership. */ package alluxio.security.login; import alluxio.security.User; import java.security.Principal; import java.util.Map; import java.util.Set; import javax.annotation.concurrent.NotThreadSafe; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; /** * A login module that search the Kerberos or OS user from Subject, and then convert to an Alluxio * user. It does not really authenticate the user in its login method. */ @NotThreadSafe public final class AlluxioLoginModule implements LoginModule { private Subject mSubject; private User mUser; /** * Constructs a new {@link AlluxioLoginModule}. */ public AlluxioLoginModule() {} @Override public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) { mSubject = subject; } /** * Authenticates the user (first phase). * * The implementation does not really authenticate the user here. Always return true. * @return true in all cases * @throws LoginException when the login fails */ @Override public boolean login() throws LoginException { return true; } /** * Aborts the authentication (second phase). * * This method is called if the LoginContext's overall authentication failed. (login failed) * It cleans up any state that was changed in the login and commit methods. * @return true in all cases * @throws LoginException when the abortion fails */ @Override public boolean abort() throws LoginException { logout(); mUser = null; return true; } /** * Commits the authentication (second phase). * * This method is called if the LoginContext's overall authentication succeeded. (login * succeeded) * The implementation searches the Kerberos or OS user in the Subject. If existed, * convert it to an Alluxio user and add into the Subject. * @return true in all cases * @throws LoginException if the user extending a specific Principal is not found */ @Override public boolean commit() throws LoginException { // if there is already an Alluxio user, it's done. if (!mSubject.getPrincipals(User.class).isEmpty()) { return true; } Principal user = null; // TODO(dong): get a Kerberos user if we are using Kerberos. // user = getKerberosUser(); // get a OS user if (user == null) { user = getPrincipalUser(LoginModuleConfigurationUtils.OS_PRINCIPAL_CLASS_NAME); } // if a user is found, convert it to an Alluxio user and save it. if (user != null) { mUser = new User(user.getName()); mSubject.getPrincipals().add(mUser); return true; } throw new LoginException("Cannot find a user"); } /** * Logs out the user. * * The implementation removes the User associated with the Subject. * @return true in all cases * @throws LoginException if logout fails */ @Override public boolean logout() throws LoginException { if (mSubject.isReadOnly()) { throw new LoginException("logout Failed: Subject is Readonly."); } if (mUser != null) { mSubject.getPrincipals().remove(mUser); } return true; } private Principal getPrincipalUser(String className) throws LoginException { // load the principal class ClassLoader loader = Thread.currentThread().getContextClassLoader(); if (loader == null) { loader = ClassLoader.getSystemClassLoader(); } Class<? extends Principal> clazz; try { // Declare a temp variable so that we can suppress warnings locally @SuppressWarnings("unchecked") Class<? extends Principal> tmpClazz = (Class<? extends Principal>) loader.loadClass(className); clazz = tmpClazz; } catch (ClassNotFoundException e) { throw new LoginException("Unable to find JAAS principal class:" + e.getMessage()); } // find corresponding user based on the principal Set<? extends Principal> userSet = mSubject.getPrincipals(clazz); if (!userSet.isEmpty()) { if (userSet.size() == 1) { return userSet.iterator().next(); } throw new LoginException("More than one instance of Principal " + className + " is found"); } return null; } }