Java tutorial
/* * Weblounge: Web Content Management System * Copyright (c) 2003 - 2011 The Weblounge Team * http://entwinemedia.com/weblounge * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package ch.entwine.weblounge.common.impl.security; import ch.entwine.weblounge.common.impl.language.LanguageUtils; import ch.entwine.weblounge.common.impl.util.WebloungeDateFormat; import ch.entwine.weblounge.common.impl.util.config.ConfigurationUtils; import ch.entwine.weblounge.common.impl.util.xml.XPathHelper; import ch.entwine.weblounge.common.language.Language; import ch.entwine.weblounge.common.security.DigestType; import ch.entwine.weblounge.common.security.Password; import ch.entwine.weblounge.common.security.Role; import ch.entwine.weblounge.common.security.WebloungeUser; import ch.entwine.weblounge.common.site.Site; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.text.ParseException; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Set; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathFactory; /** * Default implementation of a weblounge user. */ public class WebloungeUserImpl extends UserImpl implements WebloungeUser { /** Logging facility */ private static final Logger logger = LoggerFactory.getLogger(WebloungeUserImpl.class); /** Enabled flag */ protected boolean enabled = true; /** First name of the person */ protected String firstName = null; /** Family name of the person */ protected String lastName = null; /** E-mail address */ protected String email = null; /** Preferred language */ protected Language language = null; /** Cached initials */ private String initials = null; /** Password challenge */ protected String challenge = null; /** Password challenge response */ protected byte[] response = null; /** Password hash type, either plain or md5 */ protected DigestType responseDigestType = DigestType.plain; /** Additional properties of this user */ protected Map<String, Object> properties = new HashMap<String, Object>(); /** Date of the last login */ protected Date lastLogin = null; /** Source of the last login */ protected String lastLoginSource = null; /** Cached version of automatically generated initials */ private String cachedInitials = null; /** Cached version of automatically generated name */ private String cachedName = null; /** * Creates a user with the given login and initializes it from the weblounge * database. * * @param login * the username * @param realm * the login domain */ public WebloungeUserImpl(String login, String realm) { super(login, realm); } /** * Creates a user with login <code>login</code> and the default realm * {@link #DefaultRealm}. * * @param login * the username */ public WebloungeUserImpl(String login) { this(login, DefaultRealm); } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.security.WebloungeUser#isEnabled() */ public boolean isEnabled() { return enabled; } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.security.WebloungeUser#canLogin() */ public boolean canLogin() { Set<Object> passwords = getPrivateCredentials(Password.class); return isEnabled() && passwords.size() > 0; } /** * Sets the enabled flag. Set it to <code>true</code> to enable the login. * * @param enabled * <code>true</code> to enable this login */ public void setEnabled(boolean enabled) { this.enabled = enabled; } /** * Sets this person's first name. * * @param firstname * the first name */ public void setFirstName(String firstname) { this.firstName = firstname; cachedInitials = null; cachedName = null; } /** * Returns the first name of this person. * * @return the person's first name */ public String getFirstName() { return firstName; } /** * Sets this person's last name. * * @param lastname * the last name */ public void setLastName(String lastname) { this.lastName = lastname; cachedInitials = null; cachedName = null; } /** * Returns the last name of this person. * * @return the person's last name */ public String getLastName() { return lastName; } /** * Returns the name of this user. If possible, the value returned consists of * type <first name><last name>. * * @returns the full user name */ public String getName() { if (name != null) return name; if (cachedName != null) return cachedName; String first = getFirstName(); String last = getLastName(); if (StringUtils.trimToNull(first) == null && StringUtils.trimToNull(last) == null) return null; // Create the name StringBuffer name = new StringBuffer(); if (StringUtils.trimToNull(first) != null) { name.append(first); } if (StringUtils.trimToNull(last) != null) { if (name.length() > 0) name.append(" "); name.append(last); } // Cache for further reference cachedName = name.toString(); return cachedName; } /** * Sets the person's email. * * @param email * the email address */ public void setEmail(String email) { this.email = email; } /** * Returns the email address of this person. * * @return the person's email address */ public String getEmail() { return email; } /** * Sets the person's preferred language. * * @param language * the preferred language */ public void setLanguage(Language language) { this.language = language; } /** * Returns the preferred language of this person. * * @return the person's preferred language */ public Language getLanguage() { return language; } /** * Returns the short version of the persons name, which are constructed from * the first and the last name of the user. * * @return the persons initials */ public String getInitials() { if (initials != null) return initials; if (cachedInitials != null) return cachedInitials; StringBuffer initials = new StringBuffer(); String first = getFirstName(); String last = getLastName(); if (!StringUtils.isBlank(first) && !StringUtils.isBlank(last)) { initials.append(first.substring(0, 1)); initials.append(last.subSequence(0, 1)); cachedInitials = initials.toString().toLowerCase(); return cachedInitials; } return null; } /** * Sets the person's initials. * * @param initials * the person's initials */ public void setInitials(String initials) { this.initials = initials; } /** * Sets the last login date. * * @param date * the login date * @param src * the login source */ public void setLastLogin(Date date, String src) { lastLogin = date; lastLoginSource = src; } /** * Returns the data where the user logged in for the last time. * * @return the last login */ public Date getLastLogin() { return lastLogin; } /** * Returns the last login source. The source can be either an ip address or a * host name. * * @return the source of the last login */ public String getLastLoginFrom() { return lastLoginSource; } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.security.WebloungeUser#getChallenge() */ @Override public String getChallenge() { return challenge; } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.security.WebloungeUser#setChallenge(java.lang.String) */ @Override public void setChallenge(String challenge) { this.challenge = challenge; } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.security.WebloungeUser#getResponse() */ @Override public byte[] getResponse() { return response; } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.security.WebloungeUser#setResponse(java.lang.String) */ @Override public void setResponse(byte[] response, DigestType digest) { this.response = response; this.responseDigestType = digest; } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.security.WebloungeUser#getProperty(java.lang.String) */ public Object getProperty(String name) { return properties.get(name); } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.security.WebloungeUser#removeProperty(java.lang.String) */ public Object removeProperty(String name) { return properties.remove(name); } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.security.WebloungeUser#setProperty(java.lang.String, * java.lang.Object) */ public void setProperty(String name, Object value) { properties.put(name, value); } /** * Initializes this user object by reading all information from the * <code>XML</code> configuration node. * * @param userNode * the <code>XML</code> node containing the user configuration * @param site * the associated site */ public static WebloungeUserImpl fromXml(Node userNode, Site site) throws IllegalStateException { XPath xpath = XPathFactory.newInstance().newXPath(); return fromXml(userNode, site, xpath); } /** * Initializes this user object by reading all information from the * <code>XML</code> configuration node. * * @param userNode * the <code>XML</code> node containing the user configuration * @param site * the associated site * @param xpath * the {@link XPath} processor */ public static WebloungeUserImpl fromXml(Node userNode, Site site, XPath xpath) throws IllegalStateException { if (userNode == null) return null; String login = XPathHelper.valueOf(userNode, "@id", xpath); WebloungeUserImpl user = new WebloungeUserImpl(login); String realm = XPathHelper.valueOf(userNode, "@realm", xpath); if (realm != null) user.realm = realm; user.enabled = ConfigurationUtils.isTrue(XPathHelper.valueOf(userNode, "@enabled", xpath)); String name = XPathHelper.valueOf(userNode, "profile/name", xpath); if (name != null) { user.name = XPathHelper.valueOf(userNode, "profile/name", xpath); } else { user.firstName = XPathHelper.valueOf(userNode, "profile/firstname", xpath); user.lastName = XPathHelper.valueOf(userNode, "profile/lastname", xpath); } user.initials = XPathHelper.valueOf(userNode, "profile/initials", xpath); user.email = XPathHelper.valueOf(userNode, "profile/email", xpath); String language = XPathHelper.valueOf(userNode, "profile/language", xpath); if (language != null) { Language l = LanguageUtils.getLanguage(language); user.language = (l != null) ? l : site.getDefaultLanguage(); } // Password String password = XPathHelper.valueOf(userNode, "security/password", xpath); if (password != null) { String digestType = null; try { digestType = XPathHelper.valueOf(userNode, "security/password/@type", xpath); Password pw = new PasswordImpl(password, DigestType.valueOf(digestType)); user.addPrivateCredentials(pw); } catch (Throwable t) { throw new IllegalStateException("Unknown password digest found: " + digestType); } } // Challenge / Response user.challenge = XPathHelper.valueOf(userNode, "security/challenge", xpath); String response = XPathHelper.valueOf(userNode, "security/response", xpath); if (response != null) { String digestType = null; try { digestType = XPathHelper.valueOf(userNode, "security/response/@type", xpath); user.responseDigestType = DigestType.valueOf(digestType); user.response = response.getBytes(); } catch (Throwable t) { throw new IllegalStateException("Unknown response digest found: " + digestType); } } // Last login String lastLogin = XPathHelper.valueOf(userNode, "security/lastlogin/date", xpath); try { if (lastLogin != null) user.lastLogin = WebloungeDateFormat.parseStatic(lastLogin); user.lastLoginSource = XPathHelper.valueOf(userNode, "security/lastlogin/ip", xpath); } catch (ParseException e) { // It's not important. Let's log and then forget about it logger.error("Unable to parse last login date '{}'", lastLogin, e); } // Properties NodeList properties = XPathHelper.selectList(userNode, "properties/property", xpath); if (properties != null) { for (int i = 0; i < properties.getLength(); i++) { String key = XPathHelper.valueOf(properties.item(i), "name", xpath); String value = XPathHelper.valueOf(properties.item(i), "value", xpath); // TODO: Check for serialized objects or xml nodes if (key != null && value != null) user.properties.put(key, value); } } return user; } /** * Returns an <code>XML</code> representation of this user. * * @return the user as an <code>XML</code> document fragment */ public String toXml() { StringBuffer b = new StringBuffer(); // Add root node b.append("<user id=\"" + login + "\""); if (realm != null) { b.append(" realm=\""); b.append(realm); b.append("\""); } b.append(" enabled=\"" + enabled + "\""); b.append(">"); // // Profile // b.append("<profile>"); // First name if (firstName != null) { b.append("<firstname><![CDATA["); b.append(firstName); b.append("]]></firstname>"); } // Last name if (lastName != null) { b.append("<lastname><![CDATA["); b.append(lastName); b.append("]]></lastname>"); } // Name, if first name and last name were not given if (name != null && firstName == null && lastName == null) { b.append("<name><![CDATA["); b.append(name); b.append("]]></name>"); } // Initials if (getInitials() != null) { b.append("<initials><![CDATA["); b.append(getInitials()); b.append("]]></initials>"); } // Email if (email != null) { b.append("<email>"); b.append(email); b.append("</email>"); } // Language if (language != null) { b.append("<language>"); b.append(language.getIdentifier()); b.append("</language>"); } b.append("</profile>"); // // Security // b.append("<security>"); // Password Set<Object> passwords = getPrivateCredentials(Password.class); for (Object o : passwords) { Password password = (Password) o; b.append("<password type=\""); b.append(password.getDigestType().toString()); b.append("\"><![CDATA["); b.append(password.getPassword()); b.append("]]></password>"); } // Roles Set<Object> roles = getPublicCredentials(Role.class); for (Object r : roles) { Role role = (Role) r; b.append("<role context=\""); b.append(role.getContext()); b.append("\"><![CDATA["); b.append(role.getName()); b.append("]]></password>"); } // challenge - response if (challenge != null) { b.append("<challenge><![CDATA["); b.append(challenge); b.append("]]></challenge>"); } if (response != null) { b.append("<response type=\""); b.append(responseDigestType.toString()); b.append("\"><![CDATA["); b.append(new String(response)); b.append("]]></response>"); } // Last login if (lastLogin != null && lastLoginSource != null) { b.append("<lastlogin>"); b.append("<date>"); b.append(WebloungeDateFormat.formatStatic(lastLogin)); b.append("</date>"); b.append("<ip>"); b.append(lastLoginSource); b.append("</ip>"); b.append("</lastlogin>"); } b.append("</security>"); // properties if (properties != null && properties.size() > 0) { b.append("<properties>"); for (Map.Entry<String, Object> entry : properties.entrySet()) { b.append("<property>"); b.append("<name>"); b.append(entry.getKey()); b.append("</name>"); b.append("<value><![CDATA["); // TODO: Examine object. If XML node or serializable, serialize with // care b.append(entry.getValue().toString()); b.append("]]></value>"); b.append("</property>"); } b.append("</properties>"); } b.append("</user>"); return b.toString(); } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.impl.security.AuthenticatedUserImpl#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { // Overwritten to document that we are using the super impl return super.equals(obj); } /** * {@inheritDoc} * * @see ch.entwine.weblounge.common.impl.security.AuthenticatedUserImpl#hashCode() */ @Override public int hashCode() { // Overwritten to document that we are using the super impl return super.hashCode(); } }