    001/*
    002    Licensed to the Apache Software Foundation (ASF) under one
    003    or more contributor license agreements.  See the NOTICE file
    004    distributed with this work for additional information
    005    regarding copyright ownership.  The ASF licenses this file
    006    to you under the Apache License, Version 2.0 (the
    007    "License"); you may not use this file except in compliance
    008    with the License.  You may obtain a copy of the License at
    009
    010       http://www.apache.org/licenses/LICENSE-2.0
    011
    012    Unless required by applicable law or agreed to in writing,
    013    software distributed under the License is distributed on an
    014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015    KIND, either express or implied.  See the License for the
    016    specific language governing permissions and limitations
    017    under the License.
    018 */
    019package org.apache.wiki.auth.user;
    020
    021import org.apache.commons.lang3.StringUtils;
    022
    023import jakarta.servlet.http.HttpServletRequest;
    024import java.io.Serializable;
    025import java.util.ArrayList;
    026import java.util.Date;
    027import java.util.HashMap;
    028import java.util.List;
    029import java.util.Map;
    030
    031
    032/**
    033 * Default implementation for representing wiki user information, such as the login name, full name, wiki name, and e-mail address.
    034 *
    035 * @since 2.3
    036 */
    037public final class DefaultUserProfile implements UserProfile {
    038
    039    private static final long serialVersionUID = -5600466893735300647L;
    040    private static final String EMPTY_STRING = "";
    041    private static final String WHITESPACE = "\\s";
    042
    043    private final Map< String, Serializable > attributes = new HashMap<>();
    044    private Date created;
    045    private String email;
    046    private String fullname;
    047    private Date lockExpiry;
    048    private String loginName;
    049    private Date modified;
    050    private String password;
    051    private String uid;
    052    private String wikiname;
    053    //oldest should be in position zero, size bounded via configuration
    054    private List<String> previousHashedCredentials = new ArrayList<>();
    055
    056    public List<String> getPreviousHashedCredentials() {
    057        return previousHashedCredentials;
    058    }
    059
    060    /**
    061     * Package constructor to allow direct instantiation only from package related classes (i.e., AbstractUserDatabase).
    062     */
    063    DefaultUserProfile() {}
    064
    065    /**
    066     * {@inheritDoc}
    067     */
    068    @Override
    069    public boolean equals( final Object o ) {
    070        if ( o instanceof UserProfile ) {
    071            final DefaultUserProfile u = ( DefaultUserProfile )o;
    072            return  same( fullname, u.fullname ) &&
    073                    //same( password, u.password ) &&
    074                    //Note: this used to compare the password for some reason, but that was causing
    075                    //issues when the user wanted to change their password. since this is called
    076                    //from DefaultUserManager#setProfile
    077                    same( loginName, u.loginName ) &&
    078                    same( StringUtils.lowerCase( email ), StringUtils.lowerCase( u.email ) ) &&
    079                    same( wikiname, u.wikiname );
    080        }
    081
    082        return false;
    083    }
    084
    085    @Override
    086    public int hashCode() {
    087        return ( fullname  != null ? fullname.hashCode()  : 0 ) ^
    088               ( password  != null ? password.hashCode()  : 0 ) ^
    089               ( loginName != null ? loginName.hashCode() : 0 ) ^
    090               ( wikiname  != null ? wikiname.hashCode()  : 0 ) ^
    091               ( email     != null ? StringUtils.lowerCase( email ).hashCode() : 0 );
    092    }
    093
    094    /**
    095     * Returns the creation date
    096     *
    097     * @return the creation date
    098     * @see org.apache.wiki.auth.user.UserProfile#getCreated()
    099     */
    100    @Override
    101    public Date getCreated()
    102    {
    103        return created;
    104    }
    105
    106    /**
    107     * Returns the user's e-mail address.
    108     *
    109     * @return the e-mail address
    110     */
    111    @Override
    112    public String getEmail()
    113    {
    114        return email;
    115    }
    116
    117    /**
    118     * Returns the user's full name.
    119     *
    120     * @return the full name
    121     */
    122    @Override
    123    public String getFullname()
    124    {
    125        return fullname;
    126    }
    127
    128    /**
    129     * Returns the last-modified date.
    130     *
    131     * @return the last-modified date
    132     * @see org.apache.wiki.auth.user.UserProfile#getLastModified()
    133     */
    134    @Override
    135    public Date getLastModified()
    136    {
    137        return modified;
    138    }
    139
    140    /**
    141     * Returns the user's login name.
    142     * @return the login name
    143     */
    144    @Override
    145    public String getLoginName()
    146    {
    147        return loginName;
    148    }
    149
    150    /**
    151     * Returns the user password for use with custom authentication. Note that the password field is not meaningful for container
    152     * authentication; the user's private credentials are generally stored elsewhere. While it depends on the {@link UserDatabase}
    153     * implementation, in most cases the value returned by this method will be a password hash, not the password itself.
    154     *
    155     * @return the password
    156     */
    157    @Override
    158    public String getPassword()
    159    {
    160        return password;
    161    }
    162
    163    /**
    164     * Returns the user's wiki name.
    165     *
    166     * @return the wiki name.
    167     */
    168    @Override
    169    public String getWikiName()
    170    {
    171        return wikiname;
    172    }
    173
    174    /**
    175     * Returns <code>true</code> if the user profile is new. This implementation checks whether {@link #getLastModified()} returns
    176     * <code>null</code> to determine the status.
    177     *
    178     * @see org.apache.wiki.auth.user.UserProfile#isNew()
    179     */
    180    @Override
    181    public boolean isNew()
    182    {
    183        return  modified == null;
    184    }
    185
    186    /**
    187     * @param date the creation date
    188     * @see org.apache.wiki.auth.user.UserProfile#setCreated(java.util.Date)
    189     */
    190    @Override
    191    public void setCreated( final Date date )
    192    {
    193        created = date;
    194    }
    195
    196    /**
    197     * Sets the user's e-mail address.
    198     *
    199     * @param email the e-mail address
    200     */
    201    @Override
    202    public void setEmail( final String email )
    203    {
    204        this.email = email;
    205    }
    206
    207    /**
    208     * Sets the user's full name. For example, "Janne Jalkanen."
    209     *
    210     * @param arg the full name
    211     */
    212    @Override
    213    public void setFullname( final String arg ) {
    214        fullname = arg;
    215
    216        // Compute wiki name
    217        if ( fullname != null ) {
    218            wikiname = fullname.replaceAll( WHITESPACE, EMPTY_STRING );
    219        }
    220    }
    221
    222    /**
    223     * Sets the last-modified date.
    224     *
    225     * @param date the last-modified date
    226     * @see org.apache.wiki.auth.user.UserProfile#setLastModified(java.util.Date)
    227     */
    228    @Override
    229    public void setLastModified( final Date date )
    230    {
    231        modified = date;
    232    }
    233
    234    /**
    235     * Sets the name by which the user logs in. The login name is used as the username for custom authentication (see
    236     * {@link org.apache.wiki.auth.AuthenticationManager#login(org.apache.wiki.api.core.Session,HttpServletRequest, String, String)}).
    237     * The login name is typically a short name ("jannej"). In contrast, the wiki name is typically of type
    238     * FirstnameLastName ("JanneJalkanen").
    239     *
    240     * @param name the login name
    241     */
    242    @Override
    243    public void setLoginName( final String name )
    244    {
    245        loginName = name;
    246    }
    247
    248    /**
    249     * Sets the user's password for use with custom authentication. It is <em>not</em> the responsibility of implementing classes to hash
    250     * the password; that responsibility is borne by the UserDatabase implementation during save operations (see
    251     * {@link UserDatabase#save(UserProfile)}). Note that the password field is not meaningful for container authentication; the user's
    252     * private credentials are generally stored elsewhere.
    253     *
    254     * @param arg the password
    255     */
    256    @Override
    257    public void setPassword( final String arg )
    258    {
    259        password = arg;
    260    }
    261
    262    /**
    263     * Returns a string representation of this user profile.
    264     *
    265     * @return the string
    266     */
    267    @Override
    268    public String toString()
    269    {
    270        return "[DefaultUserProfile: '" + getFullname() + "']";
    271    }
    272
    273    /**
    274     * Private method that compares two objects and determines whether they are equal. Two nulls are considered equal.
    275     *
    276     * @param arg1 the first object
    277     * @param arg2 the second object
    278     * @return the result of the comparison
    279     */
    280    private boolean same( final Object arg1, final Object arg2 ) {
    281        if( arg1 == null && arg2 == null ) {
    282            return true;
    283        }
    284        if( arg1 == null || arg2 == null ) {
    285            return false;
    286        }
    287        return arg1.equals( arg2 );
    288    }
    289
    290    //--------------------------- Attribute and lock interface implementations ---------------------------
    291
    292    /**
    293     * {@inheritDoc}
    294     */
    295    @Override
    296    public Map< String, Serializable > getAttributes()
    297    {
    298        return attributes;
    299    }
    300
    301    /**
    302     * {@inheritDoc}
    303     */
    304    @Override
    305    public Date getLockExpiry()
    306    {
    307        return isLocked() ? lockExpiry : null;
    308    }
    309
    310    /**
    311     * {@inheritDoc}
    312     */
    313    @Override
    314    public String getUid()
    315    {
    316        return uid;
    317    }
    318
    319    /**
    320     * {@inheritDoc}
    321     */
    322    @Override
    323    public boolean isLocked() {
    324        final boolean locked = lockExpiry != null && System.currentTimeMillis() < lockExpiry.getTime();
    325        // Clear the lock if it's expired already
    326        if( !locked && lockExpiry != null ) {
    327            lockExpiry = null;
    328        }
    329        return locked;
    330
    331    }
    332
    333    /**
    334     * {@inheritDoc}
    335     */
    336    @Override
    337    public void setLockExpiry( final Date expiry )
    338    {
    339        this.lockExpiry = expiry;
    340    }
    341
    342    /**
    343     * {@inheritDoc}
    344     */
    345    @Override
    346    public void setUid( final String uid )
    347    {
    348        this.uid = uid;
    349    }
    350
    351}