/** * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright ownership. Apereo * licenses this file to you 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 the * following location: * * <p>http://www.apache.org/licenses/LICENSE-2.0 * * <p>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.apereo.portal.security.provider; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apereo.portal.EntityIdentifier; import org.apereo.portal.security.IPerson; import org.apereo.portal.security.ISecurityContext; import org.apereo.portal.security.PersonFactory; /** * This is a reference IPerson implementation. * */ public class PersonImpl implements IPerson { private static final long serialVersionUID = 1L; protected ConcurrentMap<String, List<Object>> userAttributes = null; protected String m_FullName = null; protected int m_ID = -1; protected ISecurityContext m_securityContext = null; protected EntityIdentifier m_eid = new EntityIdentifier(null, IPerson.class); @Override public ISecurityContext getSecurityContext() { return m_securityContext; } @Override public void setSecurityContext(ISecurityContext securityContext) { m_securityContext = securityContext; } /** * Returns an attribute for a key. For objects represented as strings, a <code>java.lang.String * </code> will be returned. Binary values will be represented as byte arrays. * * @param key the attribute name. * @return value the attribute value identified by the key. */ @Override public Object getAttribute(String key) { if (userAttributes == null) { return null; } final List<Object> values = userAttributes.get(key); if (values != null && values.size() > 0) { return values.get(0); } return null; } /** * Returns multiple attributes for a key. If only one value exists, it will be returned in an * array of size one. * * @param key the attribute name * @return the array of attribute values identified by the key */ @Override public Object[] getAttributeValues(String key) { if (userAttributes == null) { return null; } final List<Object> values = userAttributes.get(key); if (values != null) { return values.toArray(); } return null; } /** * Provides access to this {@link org.apereo.portal.security.provider.PersonImpl}'s private copy * of the attributes attached to this {@link IPerson}. Changes to the map will affect the * attributes directly. (Perhaps we'd rather do a defensive copy?) */ @Override public Map<String, List<Object>> getAttributeMap() { final Map<String, List<Object>> attrMap = userAttributes; return attrMap; } /** * Sets the specified attribute to a value. * * <p>Reference implementation checks for the setting of the username attribute and updates the * EntityIdentifier accordingly * * @param key Attribute's name * @param value Attribute's value */ @Override public void setAttribute(String key, Object value) { if (value == null) { setAttribute(key, null); } else { setAttribute(key, Collections.singletonList(value)); } } @Override public void setAttribute(String key, List<Object> value) { if (userAttributes == null) { userAttributes = new ConcurrentHashMap<String, List<Object>>(); } if (value != null) { userAttributes.put(key, value); } else { userAttributes.remove(key); } if (key.equals(IPerson.USERNAME)) { final Object userName = value != null && value.size() > 0 ? value.get(0) : null; m_eid = new EntityIdentifier(String.valueOf(userName), IPerson.class); } } /** * Sets the specified attributes. Uses {@link #setAttribute(String, Object)} to set each. * * @see IPerson#setAttributes(java.util.Map) */ @Override public void setAttributes(final Map<String, List<Object>> attrs) { for (final Entry<String, List<Object>> attrEntry : attrs.entrySet()) { final String key = attrEntry.getKey(); final List<Object> value = attrEntry.getValue(); setAttribute(key, value); } /* * This is the method used by Authentication.authenticate() -- and * elsewhere -- to initialize a valid IPerson in the portal. We want * to *fail fast* if there's something wrong with that process. */ validateUsername(); } /** * Returns the user's ID that was used for authentication. Does not correlate to any eduPerson * attribute but is the key for user data in uPortal reference rdbms. * * @return User's ID. */ @Override public int getID() { return m_ID; } /** * Sets the user's ID. * * @param sID User's ID as supplied for authentication Does not correlate to any eduPerson * attribute but is the key for user data in uPortal reference rdbms. */ @Override public void setID(int sID) { m_ID = sID; } /* (non-Javadoc) * @see org.apereo.portal.security.IPerson#getUserName() */ @Override public String getUserName() { return (String) getAttribute(IPerson.USERNAME); } /* (non-Javadoc) * @see org.apereo.portal.security.IPerson#setUserName(java.lang.String) */ @Override public void setUserName(String userName) { setAttribute(IPerson.USERNAME, userName); } /** * Returns the user's name that was established during authentication. Correlates to cn (common * name) in the eduPerson 1.0 specification. * * @return User's name. */ @Override public String getFullName() { return m_FullName; } /** * Sets the user's full name. * * @param sFullName User's name as established during authentication Correlates to cn (common * name) in the eduPerson 1.0 specification. */ @Override public void setFullName(String sFullName) { m_FullName = sFullName; } /** * Determines whether or not this person is a "guest" user. * * <p>This person is a "guest" if both of the following are true: * * <ol> * <li>This person's user name is listed as a guest user account. * <li>This person does not have a live instance ISecurityContext that states he/she has been * successfully authenticated. (It can be either null or unauthenticated.) * </ol> * * @return <code>true</code> If person is a guest, otherwise <code>false</code> */ @Override public boolean isGuest() { String userName = (String) getAttribute(IPerson.USERNAME); boolean isGuestUsername = PersonFactory.GUEST_USERNAMES.contains(userName); boolean isAuthenticated = m_securityContext != null && m_securityContext.isAuthenticated(); return isGuestUsername && !isAuthenticated; } /** * Returns an EntityIdentifier for this person. The key contains the value of the eudPerson * username attribute, or null * * @return EntityIdentifier with attribute 'username' as key. */ @Override public EntityIdentifier getEntityIdentifier() { return m_eid; } /* (non-Javadoc) * @see java.security.Principal#getName() */ @Override public String getName() { return (String) getAttribute(IPerson.USERNAME); } @Override public String toString() { return new ToStringBuilder(this) .append("id", m_ID) .append("fullName", m_FullName) .append("attributes", userAttributes) .append("securityContext", m_securityContext) .append("isGuest", isGuest()) .toString(); } @Override public int hashCode() { int result = new HashCodeBuilder(209348721, -93847839).append(m_ID).toHashCode(); return result; } @Override public boolean equals(Object obj) { if (obj == this) return true; if (!(obj instanceof IPerson)) return false; IPerson other = (IPerson) obj; return new EqualsBuilder().append(getID(), other.getID()).isEquals(); } /* * Implementation */ /** * This method helps the PersonImpl <i>fail fast</i> if it is initialized in an invalid state. * An instance of this class that does not have a value for <code>IPerson.USERNAME</code> cannot * function properly. It would be unusable for groups, permissions, layouts, authenticated * status, and probably a whole host of other things. It's best if we raise the alarm * immediately, otherwise the issue may be much more time consuming to troubleshoot if it slips * downstream. */ private void validateUsername() { final String usernameValue = getUserName(); if (StringUtils.isBlank(usernameValue)) { final String msg = "Username cannot be blank; check configuration " + "of user attributes and their data sources."; throw new IllegalStateException(msg); } } }