package org.cloudgraph.web.model.profile;
import java.io.Serializable;
import java.security.AccessController;
import java.security.Principal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;
import javax.faces.validator.ValidatorException;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.cloudgraph.web.AppMessageUtils;
import org.cloudgraph.web.WebConstants;
import org.cloudgraph.web.component.ChartType;
import org.cloudgraph.web.config.web.ComponentName;
import org.cloudgraph.web.config.web.PropertyName;
import org.cloudgraph.web.jaas.LoginCallbackHandler;
import org.cloudgraph.web.jaas.RolePrincipal;
import org.cloudgraph.web.jaas.UserPrincipal;
import org.cloudgraph.web.sdo.personalization.Element;
import org.cloudgraph.web.sdo.personalization.ElementType;
import org.cloudgraph.web.sdo.personalization.Person;
import org.cloudgraph.web.sdo.personalization.Profile;
import org.cloudgraph.web.sdo.personalization.ProfileElementSetting;
import org.cloudgraph.web.sdo.personalization.Role;
import org.cloudgraph.web.sdo.personalization.RoleName;
import org.cloudgraph.web.sdo.personalization.Setting;
import org.cloudgraph.web.sdo.personalization.User;
import org.cloudgraph.web.sdo.personalization.UserRole;
import org.plasma.sdo.PlasmaDataGraph;
import org.plasma.sdo.PlasmaDataGraphVisitor;
import org.plasma.sdo.PlasmaDataObject;
import org.plasma.sdo.access.client.SDODataAccessClient;
import org.plasma.sdo.helper.PlasmaCopyHelper;
import org.plasma.sdo.helper.PlasmaDataFactory;
import org.plasma.sdo.helper.PlasmaTypeHelper;
import commonj.sdo.DataGraph;
import commonj.sdo.DataObject;
import commonj.sdo.Type;
@ManagedBean(name="UserBean")
@SessionScoped
public class UserBean implements Serializable {
private static final long serialVersionUID = 1L;
private static Log log = LogFactory.getLog(UserBean.class);
private String defaultUserName = "anonymous"; // fallback user for Tomcat or other non-auth testing
private String name = defaultUserName;
private RoleName roleName = RoleName.ANONYMOUS; // fallback role name in the event we cannot even lookup a Role from DB
private Role role;
private Profile profile;
private User user;
private Element applicationDefaultSettings;
private Map<String, Element> elements;
private String stagingUsername;
private String stagingPassword;
boolean authenticated = false;
/**
* Maps elements (component names) to a map of
* name/value setting structures mapped by the
* setting name. Setting names are restricted on the
* client side only using an enum. See (web dashboard
* PropertyName enum defined in ApplicationConfig.xsd)
*/
private Map<String, Map<String, Setting>> settings;
public UserBean() {
try {
AppMessageUtils.setMessageBundle(this.getBundleName());
} catch (Throwable t) {
log.error(t.getMessage(), t);
//throw new RuntimeException("could not load bundle");
}
SDODataAccessClient service = new SDODataAccessClient();
// if we have an authenticated principal, override default name
FacesContext context = FacesContext.getCurrentInstance();
Principal principal = context.getExternalContext().getUserPrincipal();
if (principal != null &&
principal.getName() != null &&
principal.getName().length() > 0) {
name = principal.getName();
this.authenticated = true;
}
if (this.authenticated) {
loadAuthenticatedUser();
}
else {
DataGraph dataGraph = PlasmaDataFactory.INSTANCE.createDataGraph();
dataGraph.getChangeSummary().beginLogging(); // log changes from this point
Type rootType = PlasmaTypeHelper.INSTANCE.getType(User.class);
this.user = (User)dataGraph.createRootObject(rootType);
String ip = getClientIpAddr();
this.user.setIpAddress(ip);
this.user.setUsername(this.defaultUserName);
this.user.setPassword(this.defaultUserName);
this.user.setExternalId(UUID.randomUUID().toString());
UserRole userRole = this.user.createUserRole();
Role role = getRole(RoleName.ANONYMOUS, service);
Role roleCopy = (Role)PlasmaCopyHelper.INSTANCE.copyShallow(role);
this.role = roleCopy;
userRole.setRole(this.role);
this.profile = user.createProfile();
try {
service.commit(dataGraph, ip);
} catch (Throwable t) {
log.error(t.getMessage(), t);
}
}
}
public boolean getIsAuthenticated() {
return authenticated;
}
public String getPrincipalName()
{
FacesContext context = FacesContext.getCurrentInstance();
Principal principal = context.getExternalContext().getUserPrincipal();
if (principal != null &&
principal.getName() != null &&
principal.getName().length() > 0) {
this.name = principal.getName();
loadAuthenticatedUser();
return this.name;
}
return null;
}
private void loadAuthenticatedUser() {
SDODataAccessClient service = new SDODataAccessClient();
try {
this.user = this.getUser(service);
this.profile = this.user.getProfile(0);
this.role = this.findRole(service);
if (this.role == null)
this.role = createDefaultRole(this.user, service);
this.roleName = findRoleNameEnum(this.role.getName());
/*
this.applicationDefaultSettings = this.getDefaultSettings(service);
SettingCollector collector = new SettingCollector(this.role.getName());
((PlasmaDataObject)this.applicationDefaultSettings).accept(collector);
((PlasmaDataObject)this.applicationDefaultSettings).accept(new GraphRemover());
// merge in any profile specific settings overwriting
// defaults
if (this.profile != null)
((PlasmaDataObject)this.profile).accept(collector);
this.settings = collector.getSettings();
this.elements = collector.getElements();
*/
} catch (Throwable t) {
log.error(t.getMessage(), t);
}
}
private String getIpAddress()
{
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
String ipAddress = request.getHeader("X-FORWARDED-FOR");
if (ipAddress == null) {
ipAddress = request.getRemoteAddr();
return ipAddress;
}
return null;
}
private String getClientIpAddr() {
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
private RoleName findRoleNameEnum(String roleName)
{
for (RoleName name : RoleName.values())
if (name.getInstanceName().equals(roleName))
return name;
return null;
}
public String getRoleName() {
return this.roleName.name();
}
public String getStagingUsername() {
return stagingUsername;
}
public void setStagingUsername(String stagingUsername) {
this.stagingUsername = stagingUsername;
}
public void validateStagingUsername(FacesContext facesContext,
UIComponent component, Object value) {
String name = null;
if (value == null || ((String)value).trim().length() == 0) {
return;
}
else
name = ((String)value).trim();
this.stagingUsername = name;
// don't validate, just wait around for the password validator
}
public String getStagingPassword() {
return stagingPassword;
}
public void setStagingPassword(String stagingPassword) {
this.stagingPassword = stagingPassword;
}
public void validateStagingPassword(FacesContext facesContext,
UIComponent component, Object value) {
String pwd = null;
if (value == null || ((String)value).trim().length() == 0) {
return;
}
else
pwd = ((String)value).trim();
this.stagingPassword = pwd;
LoginContext lc = null;
try {
lc = new LoginContext("Jaas",
new LoginCallbackHandler(this.stagingUsername,
this.stagingPassword));
} catch (LoginException le) {
log.error(le.getMessage(), le);
}
try {
lc.login();
} catch (LoginException le) {
String msg = "Invalid username or password";
throw new ValidatorException(
new FacesMessage(msg, msg));
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
private Profile findProfile(SDODataAccessClient service) {
Profile result = null;
DataGraph[] results = service.find(ProfileQuery.createProfileQuery(this.getName()));
if (results != null && results.length > 0) {
if (results.length > 1)
log.warn("found multiple profile trees for user, "
+ this.getName());
result = (Profile)results[0].getRootObject();
if (log.isDebugEnabled())
log.debug("profile: " + ((PlasmaDataObject)result).dump());
}
return result;
}
private User getUser(SDODataAccessClient service) {
DataGraph[] results = service.find(UserQuery.createProfileGraphQuery(this.getName()));
if (results == null || results.length == 0)
throw new IllegalStateException("cannot find person information for username, "
+ this.getName());
if (results.length == 0)
throw new IllegalStateException("no person information found for username, "
+ this.getName());
if (results.length > 1)
throw new IllegalStateException("multiple person information found for username, "
+ this.getName());
User result = (User)results[0].getRootObject();
return result;
}
private Role findRole(SDODataAccessClient service) {
DataGraph[] results = service.find(RoleQuery.createQueryByUserName(this.getName()));
if (results == null || results.length == 0)
return null;
if (results.length > 1)
log.warn("multiple role information found for user, "
+ this.getName());
Role result = (Role)results[0].getRootObject();
return result;
}
private Role getRole(RoleName roleName,
SDODataAccessClient service) {
DataGraph[] results = service.find(RoleQuery.createQueryByRoleName(
roleName));
if (results == null || results.length == 0)
throw new IllegalStateException("no role information found for role, "
+ roleName.getInstanceName());
if (results.length > 1)
log.warn("multiple role information found for role, "
+ roleName.getInstanceName());
Role result = (Role)results[0].getRootObject();
return result;
}
private Role createDefaultRole(User user,
SDODataAccessClient service) {
DataGraph dataGraph = PlasmaDataFactory.INSTANCE.createDataGraph();
dataGraph.getChangeSummary().beginLogging(); // log changes from this point
Type rootType = PlasmaTypeHelper.INSTANCE.getType(UserRole.class);
UserRole userRole = (UserRole)dataGraph.createRootObject(rootType);
Role defaultRole = getRole(RoleName.USER, service);
userRole.setRole(defaultRole);
User userCopy = (User)PlasmaCopyHelper.INSTANCE.copyShallow(user);
userRole.setUser(userCopy);
service.commit(dataGraph, this.getName());
((PlasmaDataObject)defaultRole).setDataGraph(null); // so we can use it in other graphs
return defaultRole;
}
private Element getDefaultSettings(SDODataAccessClient service) {
Element result = null;
DataGraph[] results = service.find(ProfileQuery.createDefaultSettingQuery(roleName));
if (results == null || results.length == 0)
throw new IllegalStateException("cannot find default setting information for role, "
+ this.roleName.toString());
result = (Element)results[0].getRootObject();
if (log.isDebugEnabled())
log.debug(((PlasmaDataObject)result).dump());
return result;
}
public String getBundleName() {
return WebConstants.BUNDLE_BASENAME;
}
public String getName() {
return name;
}
public Profile getProfile() {
return profile;
}
public User getUser() {
return this.user;
}
public String getUsername() {
return this.user.getUsername();
}
public void setUsername(String name) {
if (name == null) {
if (this.user.isSetUsername())
this.user.unsetUsername();
}
else
this.user.setUsername(name);
}
public String getPassword() {
return this.user.getPassword();
}
public void setPassword(String passwd) {
if (passwd == null) {
if (this.user.isSetPassword())
this.user.unsetPassword();
}
else
this.user.setPassword(passwd);
}
public Person getPerson() {
return this.user.getPerson(0);
}
public String getFirstName() {
if (this.user.getPersonCount() > 0)
return this.user.getPerson(0).getFirstName();
else
return null;
}
public void setFirstName(String firstName) {
if (this.user.getPersonCount() == 0)
this.user.createPerson();
if (firstName == null) {
if (this.user.getPerson(0).isSetFirstName())
this.user.getPerson(0).unsetFirstName();
}
else
this.user.getPerson(0).setFirstName(firstName);
}
public String getLastName() {
if (this.user.getPersonCount() > 0)
return this.user.getPerson(0).getLastName();
else
return null;
}
public void setLastName(String lastName) {
if (this.user.getPersonCount() == 0)
this.user.createPerson();
if (lastName == null) {
if (this.user.getPerson(0).isSetLastName())
this.user.getPerson(0).unsetLastName();
}
else
this.user.getPerson(0).setLastName(lastName);
}
public String getEmailAddress() {
if (this.user.getPersonCount() > 0)
return this.user.getPerson(0).getEmailAddress();
else
return null;
}
public void setEmailAddress(String addr) {
if (this.user.getPersonCount() == 0)
this.user.createPerson();
if (addr == null) {
if (this.user.getPerson(0).isSetEmailAddress())
this.user.getPerson(0).unsetEmailAddress();
}
else
this.user.getPerson(0).setEmailAddress(addr);
}
public void initializeProfile(ActionEvent event) {
initializeProfile();
}
public Profile initializeProfile() {
if (this.profile != null)
throw new IllegalStateException("profile already exists");
PlasmaDataGraph profileDataGraph = PlasmaDataFactory.INSTANCE.createDataGraph();
profileDataGraph.getChangeSummary().beginLogging(); // log changes from this point
Type rootType = PlasmaTypeHelper.INSTANCE.getType(Profile.class);
this.profile = (Profile)profileDataGraph.createRootObject(rootType);
this.profile.setUser(this.user);
return this.profile;
}
public void commitProfile(ActionEvent event) {
commitProfile();
}
public String commitProfile() {
try {
if (this.profile == null)
throw new IllegalStateException("no profile found");
SDODataAccessClient service = new SDODataAccessClient();
this.role = getRole(RoleName.USER, service);
this.roleName = RoleName.USER;
this.name = this.user.getUsername();
Role roleCopy = (Role)PlasmaCopyHelper.INSTANCE.copyShallow(this.role);
this.user.getUserRole(0).setRole(roleCopy);
service.commit(profile.getDataGraph(), this.name);
} catch (Throwable t) {
log.error(t.getMessage(), t);
}
return null;
}
public String cancelCommitProfile() {
return null;
}
public void login(ActionEvent event) {
login();
}
public String login() {
if (this.profile == null)
throw new IllegalStateException("no profile found");
// set new subject into session
FacesContext context = FacesContext.getCurrentInstance();
HttpServletRequest request = (HttpServletRequest)context.getExternalContext().getRequest();
HttpSession httpSession = request.getSession(false);
Subject subject = (Subject)httpSession.getAttribute("javax.security.auth.subject");
Subject acSubject = Subject.getSubject(AccessController.getContext());
String remoteUser = request.getRemoteUser();
if (subject == null) {
subject = new Subject();
httpSession.setAttribute("javax.security.auth.subject", subject);
}
LoginContext lc = null;
try {
lc = new LoginContext("Jaas",
new LoginCallbackHandler(this.stagingUsername,
this.stagingPassword));
} catch (LoginException le) {
log.error(le.getMessage(), le);
}
try {
lc.login();
} catch (LoginException le) {
log.error(le.getMessage(), le);
String msg = "Invalid username or password";
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(msg));
return null;
} finally {
this.stagingUsername = null;
this.stagingPassword = null;
}
try {
subject = lc.getSubject();
// set new subject into session
httpSession.setAttribute("javax.security.auth.subject", subject);
for (Principal principal : subject.getPrincipals()) {
if (principal instanceof UserPrincipal) {
this.name = principal.getName();
loadAuthenticatedUser();
SDODataAccessClient service = new SDODataAccessClient();
service.commit(profile.getDataGraph(), this.name);
this.authenticated = true;
}
else if (principal instanceof RolePrincipal) {
// do something
}
}
} catch (Throwable t) {
log.error(t.getMessage(), t);
}
return null;
}
public String cancelLogin() {
return null;
}
public Element getAppSettings() {
return applicationDefaultSettings;
}
public Setting findComponentSetting(ComponentName componentName,
PropertyName propertyName) {
Map<String, Setting> compSettings = this.settings.get(componentName.value());
if (compSettings != null)
return compSettings.get(propertyName.value());
else
return null;
}
/**
* Sets the profile setting for the given property, for the component to the
* name of another component.
* @param componentName the component who's setting to update
* @param elementType the element type for the setting
* @param propertyName the property name for the setting
* @param component the target componet name value
*/
public void updateProfileSetting(ComponentName componentName,
ElementType elementType,
PropertyName propertyName, ComponentName component) {
updateProfileSetting(componentName, elementType, propertyName,
component.value());
// note use the 'value()' from these JAXB unums as the
// Enum.valueOf(String) only works with these
}
/**
* Sets the profile setting for the given property, for the component to the
* given value.
* @param componentName the component who's setting to update
* @param elementType the element type for the setting
* @param propertyName the property name for the setting
* @param value the value
*/
public void updateProfileSetting(ComponentName componentName,
ElementType elementType,
PropertyName propertyName, String value) {
if (this.profile == null)
initializeProfile();
Map<String, Setting> compSettings = this.settings.get(componentName.value());
if (compSettings == null) {
ProfileElementSetting profileSetting = this.profile.createProfileElementSetting();
profileSetting.setRole(this.role);
profileSetting.setProfile(this.profile);
// Create a new element to "mirror" this component
// on the back end. Make it a child of the app element.
// FIXME - what's its real parent
Element elem = profileSetting.createElement();
elem.setParent(applicationDefaultSettings);
elem.setName(componentName.value());
elem.setElementType(elementType.getInstanceName());
Setting setting = profileSetting.createSetting();
setting.setName(propertyName.value());
setting.setValue(String.valueOf(value));
// map the new setting
compSettings = new HashMap<String, Setting>();
this.settings.put(componentName.value(), compSettings);
compSettings.put(propertyName.value(), setting);
}
else {
Setting compSetting = compSettings.get(propertyName.value());
if (compSetting != null) {
if (compSetting.getProfileElementSettingCount() == 1) {
// profile setting exists update it
ProfileElementSetting profileSetting = compSetting.getProfileElementSetting(0);
if (profileSetting.getProfile().getSeqId() != this.profile.getSeqId())
throw new IllegalStateException("found profile setting(s) for profile seqId:"
+ profileSetting.getProfile().getSeqId() + " within profile seqId:"
+ this.profile.getSeqId());
compSetting.setValue(String.valueOf(value));
}
else if (compSetting.getProfileElementSettingCount() == 0) {
// there's a default role setting, add a profile specific one
Element elem = this.elements.get(componentName.value());
ProfileElementSetting profileSetting = this.profile.createProfileElementSetting();
profileSetting.setRole(this.role);
profileSetting.setProfile(this.profile);
profileSetting.setElement(elem);
Setting setting = profileSetting.createSetting();
setting.setName(propertyName.value());
setting.setValue(String.valueOf(value));
compSettings.put(propertyName.value(), setting);
}
else
throw new IllegalStateException("found multiple profile element settings for property, "
+ propertyName.value());
}
else {
ProfileElementSetting profileSetting = this.profile.createProfileElementSetting();
profileSetting.setRole(this.role);
profileSetting.setProfile(this.profile);
Setting setting = profileSetting.createSetting();
setting.setName(propertyName.value());
setting.setValue(String.valueOf(value));
Element elem = this.elements.get(componentName.value());
if (elem == null) {
elem = profileSetting.createElement(); // auto links it
elem.setParent(applicationDefaultSettings);
elem.setName(componentName.value());
elem.setElementType(elementType.getInstanceName());
}
else
profileSetting.setElement(elem);
compSettings.put(propertyName.value(), setting);
}
}
}
public List<SelectItem> getChartTypeItems() {
List<SelectItem> result = new ArrayList<SelectItem>();
result.add(new SelectItem(-1, "--none selected--"));
for (ChartType type : ChartType.values()) {
result.add(new SelectItem(type.ordinal(), type.name()));
}
return result;
}
public int getChartType() {
Setting setting = findComponentSetting(ComponentName.PAGE___DASHBOARD,
PropertyName.CHART___TYPE);
if (setting != null) {
return ChartType.valueOf(setting.getValue()).ordinal();
}
else
return -1;
}
public void setChartType(int type) {
if (type != -1) {
ChartType selected = null;
for (ChartType chartType : ChartType.values())
if (chartType.ordinal() == type)
selected = chartType;
updateProfileSetting(ComponentName.PAGE___DASHBOARD,
ElementType.PAGE,
PropertyName.CHART___TYPE,
selected.name());
}
}
class GraphRemover implements PlasmaDataGraphVisitor {
public void visit(DataObject target, DataObject source,
String sourceKey, int level) {
((PlasmaDataObject)target).setDataGraph(null);
}
}
}