/** * */ package com.thinkbiganalytics.auth.jaas; /*- * #%L * thinkbig-security-auth * %% * Copyright (C) 2017 ThinkBig Analytics * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ import com.thinkbiganalytics.security.GroupPrincipal; import com.thinkbiganalytics.security.UsernamePrincipal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.security.Principal; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; /** * */ public abstract class AbstractLoginModule implements LoginModule { private static final Logger log = LoggerFactory.getLogger(AbstractLoginModule.class); private Subject subject; private CallbackHandler callbackHandler; private Map<String, ?> sharedState; private Map<String, ?> options; private boolean loginSucceeded = false; private boolean commitSucceeded = false; private Principal userPrincipal; private Set<Principal> principals = new HashSet<>(); /* (non-Javadoc) * @see javax.security.auth.spi.LoginModule#initialize(javax.security.auth.Subject, javax.security.auth.callback.CallbackHandler, java.util.Map, java.util.Map) */ @Override public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) { log.debug("Initialize - subject: {}, callback handler: {}, options: {}", subject, callbackHandler, options); this.subject = subject; this.callbackHandler = callbackHandler; this.sharedState = sharedState; this.options = options; } /* (non-Javadoc) * @see javax.security.auth.spi.LoginModule#login() */ @Override public boolean login() throws LoginException { try { boolean active = doLogin(); setLoginSucceeded(true); return active && isLoginSucceeded(); } catch (LoginException e) { log.debug("Login exception", e); setLoginSucceeded(false); throw e; } catch (Exception e) { log.debug("Login exception", e); setLoginSucceeded(false); throw new LoginException("Login failure: " + (e.getMessage() == null ? e.toString() : e.getMessage())); } } /* (non-Javadoc) * @see javax.security.auth.spi.LoginModule#commit() */ @Override public boolean commit() throws LoginException { if (isLoginSucceeded()) { try { boolean active = doCommit(); setCommitSucceeded(true); return active && isCommitSucceeded(); } catch (LoginException e) { log.debug("Login commit exception", e); setCommitSucceeded(false); throw e; } catch (Exception e) { log.debug("Login commit exception", e); setCommitSucceeded(false); throw new LoginException("Login commit failure: " + e.getMessage()); } } else { return isCommitSucceeded(); } } /* (non-Javadoc) * @see javax.security.auth.spi.LoginModule#abort() */ @Override public boolean abort() throws LoginException { if (!isLoginSucceeded()) { return false; } else if (isCommitSucceeded()) { try { boolean active = doAbort(); return active && isCommitSucceeded(); } catch (LoginException e) { log.debug("Login abort exception", e); setLoginSucceeded(false); throw e; } catch (Exception e) { log.debug("Login abort exception", e); setLoginSucceeded(false); throw new LoginException("Login abort failure: " + e.getMessage()); } } else { return false; } } /* (non-Javadoc) * @see javax.security.auth.spi.LoginModule#logout() */ @Override public boolean logout() throws LoginException { try { boolean active = doLogout(); return active; } catch (LoginException e) { log.debug("Logout exception", e); setLoginSucceeded(false); throw e; } catch (Exception e) { log.debug("Logout exception", e); setLoginSucceeded(false); throw new LoginException("Login logout failure: " + e.getMessage()); } finally { this.loginSucceeded = false; this.commitSucceeded = false; this.sharedState = null; this.options = null; } } protected abstract boolean doLogin() throws Exception; protected abstract boolean doCommit() throws Exception; protected abstract boolean doAbort() throws Exception; protected abstract boolean doLogout() throws Exception; public Map<String, ?> getSharedState() { return sharedState; } public Map<String, ?> getOptions() { return options; } protected boolean isLoginSucceeded() { return loginSucceeded; } protected void setLoginSucceeded(boolean loginSucceeded) { this.loginSucceeded = loginSucceeded; } protected boolean isCommitSucceeded() { return commitSucceeded; } protected void setCommitSucceeded(boolean commitSucceeded) { this.commitSucceeded = commitSucceeded; } protected Set<Principal> getPrincipals() { return principals; } /** * @return the user principal (if any) and all ather princials in a combined set. */ protected Set<Principal> getAllPrincipals() { return getUserPrincipal() == null ? getPrincipals() : Stream.concat(Stream.of(getUserPrincipal()), getPrincipals().stream()).collect(Collectors.toSet()); } protected Principal getUserPrincipal() { return userPrincipal; } protected void setUserPrincipal(Principal userPrincipal) { this.userPrincipal = userPrincipal; addPrincipal(this.userPrincipal); } protected UsernamePrincipal addNewUserPrincipal(String username) { UsernamePrincipal user = new UsernamePrincipal(username); this.userPrincipal = user; addPrincipal(user); return user; } protected boolean clearUserPrincipal() { Principal principal = this.userPrincipal; this.userPrincipal = null; return principal != null ? removePrincipal(principal) : false; } protected GroupPrincipal addNewGroupPrincipal(String name) { GroupPrincipal group = new GroupPrincipal(name); addPrincipal(group); return group; } protected boolean addPrincipal(Principal principal) { return this.principals.add(principal); } protected boolean addAllPrincipals(Collection<? extends Principal> principals) { return this.principals.addAll(principals); } protected boolean removePrincipal(Principal principal) { return this.principals.remove(principal); } protected void clearAllPrincipals() { this.userPrincipal = null; this.principals.clear(); } protected Subject getSubject() { return subject; } protected CallbackHandler getCallbackHandler() { return callbackHandler; } protected void handle(Callback... callbacks) throws LoginException { try { getCallbackHandler().handle(callbacks); } catch (IOException e) { log.warn("I/O failure attempting to handle callback among: {}", e); throw new LoginException("Login failure attempting to retrieve required login information: " + e.getMessage()); } catch (UnsupportedCallbackException e) { log.error("Unsupported callback among: {}", e); throw new LoginException("Login failure attempting to retrieve required login information: " + e.getMessage()); } } @SuppressWarnings("unchecked") protected Optional<Object> getOption(String name) { return Optional.ofNullable(this.options.get(name)); } }