/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package net.rrm.ehour.ui.common.session;
import com.google.common.base.Optional;
import net.rrm.ehour.audit.service.AuditService;
import net.rrm.ehour.config.EhourConfig;
import net.rrm.ehour.config.EhourConfigCache;
import net.rrm.ehour.domain.Audit;
import net.rrm.ehour.domain.AuditActionType;
import net.rrm.ehour.domain.User;
import net.rrm.ehour.domain.UserRole;
import net.rrm.ehour.report.criteria.UserSelectedCriteria;
import net.rrm.ehour.security.SecurityRules;
import net.rrm.ehour.ui.EhourWebApplication;
import net.rrm.ehour.ui.common.authorization.AuthUser;
import net.rrm.ehour.ui.common.util.WebUtils;
import net.rrm.ehour.util.DateUtil;
import org.apache.log4j.Logger;
import org.apache.wicket.Session;
import org.apache.wicket.authroles.authentication.AuthenticatedWebSession;
import org.apache.wicket.authroles.authorization.strategies.role.Roles;
import org.apache.wicket.request.Request;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Set;
/**
* Ehour Web session
*/
public class EhourWebSession extends AuthenticatedWebSession {
@SpringBean
private EhourConfig unCachedEhourConfig;
private EhourConfig ehourConfig;
@SpringBean
private AuditService auditService;
private Calendar navCalendar;
private UserSelectedCriteria userSelectedCriteria;
private Boolean hideInactiveSelections = true;
private Optional<AuthUser> impersonatingAuthUser = Optional.absent();
private static final Logger LOGGER = Logger.getLogger(EhourWebSession.class);
public EhourWebSession(Request req) {
super(req);
reloadConfig();
}
public static EhourWebSession getSession() {
return (EhourWebSession) Session.get();
}
public static User getUser() {
EhourWebSession session = EhourWebSession.getSession();
AuthUser authUser = session.getAuthUser();
return (authUser != null) ? authUser.getUser() : null;
}
public final void reloadConfig() {
WebUtils.springInjection(this);
ehourConfig = new EhourConfigCache(unCachedEhourConfig);
if (!ehourConfig.isDontForceLanguage()) {
setLocale(ehourConfig.getLanguageLocale());
}
}
public Boolean getHideInactiveSelections() {
return hideInactiveSelections;
}
public void setHideInactiveSelections(Boolean hideInactiveSelections) {
this.hideInactiveSelections = hideInactiveSelections;
}
public Boolean toggleHideInactiveSelections() {
this.hideInactiveSelections = !this.hideInactiveSelections;
return this.hideInactiveSelections;
}
public static EhourConfig getEhourConfig() {
return EhourWebSession.getSession().ehourConfig;
}
public Calendar getNavCalendar() {
if (navCalendar == null) {
navCalendar = DateUtil.getCalendar(ehourConfig);
}
return (Calendar) navCalendar.clone();
}
public void setNavCalendar(Calendar navCalendar) {
this.navCalendar = navCalendar;
}
/**
* Get authenticated user
*/
public AuthUser getAuthUser() {
AuthUser authUser = null;
if (isSignedIn()) {
if (impersonatingAuthUser.isPresent()) {
authUser = impersonatingAuthUser.get();
} else {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
authUser = (AuthUser) authentication.getPrincipal();
}
}
}
return authUser;
}
/**
* Authenticate based on username/pass
*/
@Override
public boolean authenticate(String username, String password) {
String u = username == null ? "" : username;
String p = password == null ? "" : password;
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(u, p);
// Attempt authentication.
try {
AuthenticationManager authenticationManager = ((EhourWebApplication) getApplication()).getAuthenticationManager();
if (authenticationManager == null) {
throw new AuthenticationServiceException("no authentication manager defined");
}
Authentication authResult = authenticationManager.authenticate(authRequest);
setAuthentication(authResult);
User user = ((AuthUser) authResult.getPrincipal()).getUser();
auditService.doAudit(new Audit()
.setAuditActionType(AuditActionType.LOGIN)
.setUser(user)
.setUserFullName(user.getFullName())
.setDate(new Date())
.setSuccess(Boolean.TRUE));
LOGGER.info("Login by user '" + username + "'.");
return true;
} catch (BadCredentialsException e) {
LOGGER.info("Failed to login for" +
" user '" + username + "': " + e.getMessage());
setAuthentication(null);
return false;
} catch (AuthenticationException e) {
LOGGER.info("Could not authenticate a user", e);
setAuthentication(null);
throw e;
} catch (RuntimeException e) {
LOGGER.info("Unexpected exception while authenticating a user", e);
setAuthentication(null);
throw e;
}
}
@Override
public Roles getRoles() {
if (isSignedIn()) {
if (impersonatingAuthUser.isPresent()) {
Roles roles = new Roles();
Set<UserRole> userRoles = getAuthUser().getUser().getUserRoles();
for (UserRole userRole : userRoles) {
roles.add(userRole.getRole());
}
return roles;
}
return getRolesForSignedInUser();
}
return null;
}
private Roles getRolesForSignedInUser() {
// Retrieve the granted authorities from the current authentication. These correspond one on
// one with user roles.
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
Roles roles = new Roles();
Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
for (GrantedAuthority grantedAuthority : authorities) {
roles.add(grantedAuthority.getAuthority());
}
if (roles.isEmpty()) {
LOGGER.warn("User " + auth.getPrincipal() + " logged in but no roles could be found!");
}
return roles;
} else {
LOGGER.warn("User is signed in but authentication is not set!");
return null;
}
}
public boolean isReporter() {
return SecurityRules.isWithReportRole(getRoles());
}
public boolean isProjectManager() {
return SecurityRules.isWithPmRole(getRoles());
}
public boolean isAdmin() {
return SecurityRules.isWithAdminRole(getRoles());
}
public boolean isManager() {
return SecurityRules.isWithManagerRole(getRoles());
}
/**
* Invalidate authenticated user
*/
public void signOut() {
AuthUser user = getAuthUser();
getSession().clear();
setAuthentication(null);
setUserSelectedCriteria(null);
super.signOut();
auditService.doAudit(new Audit()
.setAuditActionType(AuditActionType.LOGOUT)
.setUser(((user != null) ? user.getUser() : null))
.setUserFullName(((user != null) ? user.getUser().getFullName() : "N/A"))
.setDate(new Date())
.setSuccess(Boolean.TRUE));
Session.get().replaceSession();
}
public void impersonateUser(User userToImpersonate) throws UnauthorizedToImpersonateException {
boolean allowedToImpersonate = allowedToImpersonate(userToImpersonate);
if (!allowedToImpersonate) {
throw new UnauthorizedToImpersonateException();
}
User originalUser = getUser();
impersonatingAuthUser = Optional.of(new AuthUser(userToImpersonate));
clearUserSelectedReportCriteria();
logAndAuditImpersonation(originalUser);
}
protected boolean allowedToImpersonate(User userToImpersonate) {
return SecurityRules.allowedToModify(getUser(), userToImpersonate, ehourConfig.isSplitAdminRole());
}
private void logAndAuditImpersonation(User originalUser) {
StringBuilder auditMsg = new StringBuilder((originalUser != null) ? originalUser.getFullName() : "N/A");
auditMsg.append(" started impersonating as ");
auditMsg.append(impersonatingAuthUser.get().getUser().getFullName());
LOGGER.info(auditMsg.toString());
auditService.doAudit(new Audit()
.setAuditActionType(AuditActionType.IMPERSONATE)
.setUser(originalUser)
.setUserFullName(auditMsg.toString())
.setDate(new Date())
.setSuccess(true));
}
public void stopImpersonating() {
if (impersonatingAuthUser.isPresent()) {
User impUser = impersonatingAuthUser.get().getUser();
User originalUser = getUser();
logAndAuditStopImpersonation(originalUser, impUser);
impersonatingAuthUser = Optional.absent();
}
clearUserSelectedReportCriteria();
}
private void clearUserSelectedReportCriteria() {
userSelectedCriteria = null;
}
public boolean isImpersonating() {
return impersonatingAuthUser.isPresent();
}
private void logAndAuditStopImpersonation(User originalUser, User impUser) {
StringBuilder auditMsg = new StringBuilder((originalUser != null) ? originalUser.getFullName() : "N/A");
auditMsg.append(" stopped impersonating as ");
auditMsg.append(impUser.getFullName());
LOGGER.info(auditMsg.toString());
auditService.doAudit(new Audit()
.setAuditActionType(AuditActionType.STOP_IMPERSONATE)
.setUser(originalUser)
.setUserFullName(auditMsg.toString())
.setDate(new Date())
.setSuccess(true));
}
private void setAuthentication(Authentication authentication) {
SecurityContextHolder.getContext().setAuthentication(authentication);
}
public UserSelectedCriteria getUserSelectedCriteria() {
return userSelectedCriteria;
}
public void setUserSelectedCriteria(UserSelectedCriteria userSelectedCriteria) {
this.userSelectedCriteria = userSelectedCriteria;
}
private static final long serialVersionUID = 93189812483240412L;
}