/*
* Copyright 2015 OmniFaces.
*
* 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.
*/
package org.omnifaces.security.jaspic.wrappers;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import static javax.security.auth.message.AuthStatus.SUCCESS;
import static org.omnifaces.security.jaspic.core.Jaspic.LOGGEDIN_ROLES;
import static org.omnifaces.security.jaspic.core.Jaspic.LOGGEDIN_USERNAME;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.message.AuthException;
import javax.security.auth.message.AuthStatus;
import javax.security.auth.message.MessageInfo;
import javax.security.auth.message.MessagePolicy;
import javax.security.auth.message.module.ServerAuthModule;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.omnifaces.security.jaspic.core.HttpMsgContext;
import org.omnifaces.security.jaspic.core.ServerAuthModuleWrapper;
public class AutoRegisterSessionWrapper extends ServerAuthModuleWrapper {
private static final String AUTHENTICATOR_SESSION_NAME = "org.omnifaces.security.jaspic.Authenticator";
private CallbackHandler handler;
private Map<String, String> options;
public AutoRegisterSessionWrapper(ServerAuthModule serverAuthModule) {
super(serverAuthModule);
}
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler, Map options) throws AuthException {
super.initialize(requestPolicy, responsePolicy, handler, options);
this.handler = handler;
this.options = options;
}
@Override
public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException {
HttpMsgContext msgContext = new HttpMsgContext(handler, options, messageInfo, clientSubject);
// Check to see if we're already authenticated.
//
// With JASPIC 1.0MR1, the container doesn't remember authentication data between requests and we thus have to
// re-authenticate before every request. It's important to skip this step if authentication is explicitly requested, otherwise
// we risk re-authenticating instead of processing a new login request.
//
// With JASPIC 1.1, the container partially takes care of this detail if so requested.
if (!msgContext.isAuthenticationRequest() && canReAuthenticate(msgContext)) {
return null;
}
AuthStatus authstatus = super.validateRequest(messageInfo, clientSubject, serviceSubject);
if (authstatus == SUCCESS) {
saveAuthentication(msgContext.getRequest());
}
return authstatus;
}
private boolean canReAuthenticate(HttpMsgContext msgContext) {
AuthenticationData authenticationData = getAuthenticationDataFromSession(msgContext);
if (authenticationData != null) {
msgContext.notifyContainerAboutLogin(authenticationData.getUserName(), authenticationData.getApplicationRoles());
return true;
}
return false;
}
@SuppressWarnings("unchecked")
private void saveAuthentication(HttpServletRequest request) {
if (request.getAttribute(LOGGEDIN_USERNAME) != null) {
request.getSession().setAttribute(
AUTHENTICATOR_SESSION_NAME,
new AuthenticationData(
(String) request.getAttribute(LOGGEDIN_USERNAME),
(List<String>) request.getAttribute(LOGGEDIN_ROLES)
)
);
}
}
private AuthenticationData getAuthenticationDataFromSession(HttpMsgContext msgContext) {
HttpSession session = msgContext.getRequest().getSession(false);
if (session != null) {
return (AuthenticationData) session.getAttribute(AUTHENTICATOR_SESSION_NAME);
}
return null;
}
private class AuthenticationData {
private final String username;
private final List<String> applicationRoles;
public AuthenticationData(String username, List<String> applicationRoles) {
this.username = username;
if (applicationRoles != null) {
this.applicationRoles = unmodifiableList(new ArrayList<>(applicationRoles));
} else {
this.applicationRoles = emptyList();
}
}
public String getUserName() {
return username;
}
public List<String> getApplicationRoles() {
return applicationRoles;
}
}
}