/**
 * OWASP Enterprise Security API (ESAPI)
 * 
 * This file is part of the Open Web Application Security Project (OWASP)
 * Enterprise Security API (ESAPI) project. For details, please see
 * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
 *
 * Copyright (c) 2007 - The OWASP Foundation
 * 
 * The ESAPI is published by OWASP under the BSD license. You should read and accept the
 * LICENSE before you use, modify, and/or redistribute this software.
 * 
 * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
 * @created 2007
 */
package org.owasp.esapi;

import org.owasp.esapi.errors.AuthenticationException;
import org.owasp.esapi.errors.AuthenticationHostException;
import org.owasp.esapi.errors.EncryptionException;

import javax.servlet.http.HttpSession;
import java.io.Serializable;
import java.security.Principal;
import java.util.*;

/**
 * The User interface represents an application user or user account. There is quite a lot of information that an
 * application must store for each user in order to enforce security properly. There are also many rules that govern
 * authentication and identity management.
 * <P>
 * A user account can be in one of several states. When first created, a User should be disabled, not expired, and
 * unlocked. To start using the account, an administrator should enable the account. The account can be locked for a
 * number of reasons, most commonly because they have failed login for too many times. Finally, the account can expire
 * after the expiration date has been reached. The User must be enabled, not expired, and unlocked in order to pass
 * authentication.
 * 
 * @author <a href="mailto:jeff.williams@aspectsecurity.com?subject=ESAPI question">Jeff Williams</a> at <a href="http://www.aspectsecurity.com">Aspect Security</a>
 * @author Chris Schmidt (chrisisbeef .at. gmail.com) <a href="http://www.digital-ritual.com">Digital Ritual Software</a>
 * @since June 1, 2007
 */

public interface User extends Principal, Serializable {
	/**
	 * @return the locale
	 */
	Locale getLocale();

	/**
	 * @param locale the locale to set
	 */
	void setLocale(Locale locale);

    /**
     * Adds a role to this user's account.
     * 
     * @param role 
     * 		the role to add
     * 
     * @throws AuthenticationException 
     * 		the authentication exception
     */
    void addRole(String role) throws AuthenticationException;

    /**
     * Adds a set of roles to this user's account.
     * 
     * @param newRoles 
     * 		the new roles to add
     * 
     * @throws AuthenticationException 
     * 		the authentication exception
     */
    void addRoles(Set<String> newRoles) throws AuthenticationException;

    /**
     * Sets the user's password, performing a verification of the user's old password, the equality of the two new
     * passwords, and the strength of the new password.
     * 
     * @param oldPassword 
     * 		the old password
     * @param newPassword1 
     * 		the new password
     * @param newPassword2 
     * 		the new password - used to verify that the new password was typed correctly
     * 
     * @throws AuthenticationException 
     * 		if newPassword1 does not match newPassword2, if oldPassword does not match the stored old password, or if the new password does not meet complexity requirements 
     * @throws EncryptionException 
     */
    void changePassword(String oldPassword, String newPassword1, String newPassword2) throws AuthenticationException, EncryptionException;

    /**
     * Disable this user's account.
     */
    void disable();

    /**
     * Enable this user's account.
     */
    void enable();

    /**
     * Gets this user's account id number.
     * 
     * @return the account id
     */
    long getAccountId();
    
    /**
     * Gets this user's account name.
     * 
     * @return the account name
     */
    String getAccountName();

    /**
     * Gets the CSRF token for this user's current sessions.
     * 
     * @return the CSRF token
     */
    String getCSRFToken();

    /**
     * Returns the date that this user's account will expire.
     *
     * @return Date representing the account expiration time.
     */
    Date getExpirationTime();

    /**
     * Returns the number of failed login attempts since the last successful login for an account. This method is
     * intended to be used as a part of the account lockout feature, to help protect against brute force attacks.
     * However, the implementor should be aware that lockouts can be used to prevent access to an application by a
     * legitimate user, and should consider the risk of denial of service.
     * 
     * @return the number of failed login attempts since the last successful login
     */
    int getFailedLoginCount();

    /**
     * Returns the last host address used by the user. This will be used in any log messages generated by the processing
     * of this request.
     * 
     * @return the last host address used by the user
     */
    String getLastHostAddress();

	/**
     * Returns the date of the last failed login time for a user. This date should be used in a message to users after a
     * successful login, to notify them of potential attack activity on their account.
     * 
     * @return date of the last failed login
     * 
     * @throws AuthenticationException 
     * 		the authentication exception
     */
    Date getLastFailedLoginTime() throws AuthenticationException;

    /**
     * Returns the date of the last successful login time for a user. This date should be used in a message to users
     * after a successful login, to notify them of potential attack activity on their account.
     * 
     * @return date of the last successful login
     */
    Date getLastLoginTime();

    /**
     * Gets the date of user's last password change.
     * 
     * @return the date of last password change
     */
    Date getLastPasswordChangeTime();

    /**
     * Gets the roles assigned to a particular account.
     * 
     * @return an immutable set of roles
     */
    Set<String> getRoles();

    /**
     * Gets the screen name (alias) for the current user.
     * 
     * @return the screen name
     */
    String getScreenName();

    /**
     * Adds a session for this User.
     * 
     * @param s
     * 			The session to associate with this user.
     */
    void addSession( HttpSession s );
    
    /**
     * Removes a session for this User.
     * 
     * @param s
     * 			The session to remove from being associated with this user.
     */
    void removeSession( HttpSession s );
    
    /**
     * Returns a Set containing the sessions associated with this User.
     * @return The Set of sessions for this User.
     */
    Set getSessions();
    
    /**
     * Increment failed login count.
     */
    void incrementFailedLoginCount();

    /**
     * Checks if user is anonymous.
     * 
     * @return true, if user is anonymous
     */
    boolean isAnonymous();

    /**
     * Checks if this user's account is currently enabled.
     * 
     * @return true, if account is enabled 
     */
    boolean isEnabled();

    /**
     * Checks if this user's account is expired.
     * 
     * @return true, if account is expired
     */
    boolean isExpired();

    /**
     * Checks if this user's account is assigned a particular role.
     * 
     * @param role 
     * 		the role for which to check
     * 
     * @return true, if role has been assigned to user
     */
    boolean isInRole(String role);

    /**
     * Checks if this user's account is locked.
     * 
     * @return true, if account is locked
     */
    boolean isLocked();

    /**
     * Tests to see if the user is currently logged in.
     * 
     * @return true, if the user is logged in
     */
    boolean isLoggedIn();

    /**
     * Tests to see if this user's session has exceeded the absolute time out based 
      * on ESAPI's configuration settings.
     * 
     * @return true, if user's session has exceeded the absolute time out
     */
    boolean isSessionAbsoluteTimeout();

    /**
      * Tests to see if the user's session has timed out from inactivity based 
      * on ESAPI's configuration settings.
      * 
      * A session may timeout prior to ESAPI's configuration setting due to 
      * the servlet container setting for session-timeout in web.xml. The 
      * following is an example of a web.xml session-timeout set for one hour. 	
      *
      * <session-config>
      *   <session-timeout>60</session-timeout> 
      * </session-config>
      * 
      * @return true, if user's session has timed out from inactivity based 
      *               on ESAPI configuration
      */
     boolean isSessionTimeout();

    /**
     * Lock this user's account.
     */
    void lock();

    /**
     * Login with password.
     * 
     * @param password 
     * 		the password
     * @throws AuthenticationException 
     * 		if login fails
     */
    void loginWithPassword(String password) throws AuthenticationException;

    /**
     * Logout this user.
     */
    void logout();

    /**
     * Removes a role from this user's account.
     * 
     * @param role 
     * 		the role to remove
     * @throws AuthenticationException 
     * 		the authentication exception
     */
    void removeRole(String role) throws AuthenticationException;

    /**
     * Returns a token to be used as a prevention against CSRF attacks. This token should be added to all links and
     * forms. The application should verify that all requests contain the token, or they may have been generated by a
     * CSRF attack. It is generally best to perform the check in a centralized location, either a filter or controller.
     * See the verifyCSRFToken method.
     * 
     * @return the new CSRF token
     * 
     * @throws AuthenticationException 
     * 		the authentication exception
     */
    String resetCSRFToken() throws AuthenticationException;

    /**
     * Sets this user's account name.
     * 
     * @param accountName the new account name
     */
    void setAccountName(String accountName);

    /**
     * Sets the date and time when this user's account will expire.
     * 
     * @param expirationTime the new expiration time
     */
	void setExpirationTime(Date expirationTime);

	/**
     * Sets the roles for this account.
     * 
     * @param roles 
     * 		the new roles
     * 
     * @throws AuthenticationException 
     * 		the authentication exception
     */
    void setRoles(Set<String> roles) throws AuthenticationException;

    /**
     * Sets the screen name (username alias) for this user.
     * 
     * @param screenName the new screen name
     */
    void setScreenName(String screenName);

    /**
     * Unlock this user's account.
     */
    void unlock();

	/**
	 * Verify that the supplied password matches the password for this user. This method
	 * is typically used for "reauthentication" for the most sensitive functions, such
	 * as transactions, changing email address, and changing other account information.
	 * 
	 * @param password 
	 * 		the password that the user entered
	 * 
	 * @return true, if the password passed in matches the account's password
	 * 
	 * @throws EncryptionException 
	 */
	boolean verifyPassword(String password) throws EncryptionException;

	/**
	 * Set the time of the last failed login for this user.
	 * 
	 * @param lastFailedLoginTime the date and time when the user just failed to login correctly.
	 */
	void setLastFailedLoginTime(Date lastFailedLoginTime);
	
	/**
	 * Set the last remote host address used by this user.
	 * 
	 * @param remoteHost The address of the user's current source host.
	 */
	void setLastHostAddress(String remoteHost) throws AuthenticationHostException;
	
	/**
	 * Set the time of the last successful login for this user.
	 * 
	 * @param lastLoginTime the date and time when the user just successfully logged in.
	 */
	void setLastLoginTime(Date lastLoginTime);
	
	/**
	 * Set the time of the last password change for this user.
	 * 
	 * @param lastPasswordChangeTime the date and time when the user just successfully changed his/her password.
	 */
	void setLastPasswordChangeTime(Date lastPasswordChangeTime);

	/**
	 * Returns the hashmap used to store security events for this user. Used by the
	 * IntrusionDetector.
	 */
	HashMap getEventMap();
	

	/**
	 * The ANONYMOUS user is used to represent an unidentified user. Since there is
	 * always a real user, the ANONYMOUS user is better than using null to represent
	 * this.
	 */
    User ANONYMOUS = new User() {

		private static final long serialVersionUID = -1850916950784965502L;

		private String csrfToken = "";
    	private Set<Object> sessions = new HashSet<Object>();
		private Locale locale = null;
    	
    	/**
         * {@inheritDoc}
         */
        public void addRole(String role) throws AuthenticationException {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }

        /**
         * {@inheritDoc}
         */
        public void addRoles(Set newRoles) throws AuthenticationException {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }

        /**
         * {@inheritDoc}
         */
        public void changePassword(String oldPassword, String newPassword1,
                String newPassword2) throws AuthenticationException,
                EncryptionException {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }

        /**
         * {@inheritDoc}
         */
        public void disable() {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }

        /**
         * {@inheritDoc}
         */
        public void enable() {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }

        /**
         * {@inheritDoc}
         */
        public long getAccountId() {
	        return 0;
        }

        /**
         * {@inheritDoc}
         */
        public String getAccountName() {
	        return "Anonymous";
        }

		/**
		 * Alias method that is equivalent to getAccountName()
		 * 
		 * @return the name of the current user's account
         */
        public String getName() {
        	return getAccountName();
        }
        
        /**
         * {@inheritDoc}
         */
        public String getCSRFToken() {
	        return csrfToken;
        }

        /**
         * {@inheritDoc}
         */
        public Date getExpirationTime() {
	        return null;
        }

        /**
         * {@inheritDoc}
         */
        public int getFailedLoginCount() {
	        return 0;
        }

        /**
         * {@inheritDoc}
         */
        public Date getLastFailedLoginTime() throws AuthenticationException {
	        return null;
        }

        /**
         * {@inheritDoc}
         */
        public String getLastHostAddress() {
	        return "unknown";
        }

        /**
         * {@inheritDoc}
         */
        public Date getLastLoginTime() {
	        return null;
        }

        /**
         * {@inheritDoc}
         */
        public Date getLastPasswordChangeTime() {
	        return null;
        }

        /**
         * {@inheritDoc}
         */
        public Set<String> getRoles() {
	        return new HashSet<String>();
        }

        /**
         * {@inheritDoc}
         */
        public String getScreenName() {
	        return "Anonymous";
        }

        /**
         * {@inheritDoc}
         */
        public void addSession(HttpSession s)  {
        }

        /**
         * {@inheritDoc}
         */
        public void removeSession(HttpSession s)  {
        }

        /**
         * {@inheritDoc}
         */
        public Set getSessions()  {
            return sessions;
        }

        /**
         * {@inheritDoc}
         */
        public void incrementFailedLoginCount() {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }

        /**
         * {@inheritDoc}
         */
        public boolean isAnonymous() {
	        return true;
        }

        /**
         * {@inheritDoc}
         */
        public boolean isEnabled() {
	        return false;
        }

        /**
         * {@inheritDoc}
         */
        public boolean isExpired() {
	        return false;
        }

        /**
         * {@inheritDoc}
         */
        public boolean isInRole(String role) {
	        return false;
        }

        /**
         * {@inheritDoc}
         */
        public boolean isLocked() {
	        return false;
        }

        /**
         * {@inheritDoc}
         */
        public boolean isLoggedIn() {
	        return false;
        }

        /**
         * {@inheritDoc}
         */
        public boolean isSessionAbsoluteTimeout() {
	        return false;
        }

        /**
         * {@inheritDoc}
         */
        public boolean isSessionTimeout() {
	        return false;
        }

        /**
         * {@inheritDoc}
         */
        public void lock() {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }

        /**
         * {@inheritDoc}
         */
        public void loginWithPassword(String password)
                throws AuthenticationException {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }

        /**
         * {@inheritDoc}
         */
        public void logout() {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }

        /**
         * {@inheritDoc}
         */
        public void removeRole(String role) throws AuthenticationException {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }

        /**
         * {@inheritDoc}
         */
        public String resetCSRFToken() throws AuthenticationException {
    		csrfToken = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
    		return csrfToken;
        }

        /**
         * {@inheritDoc}
         */
        public void setAccountName(String accountName) {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }

        /**
         * {@inheritDoc}
         */
    	public void setExpirationTime(Date expirationTime) {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }
        
    	/**
         * {@inheritDoc}
         */
        public void setRoles(Set roles) throws AuthenticationException {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }

        /**
         * {@inheritDoc}
         */
        public void setScreenName(String screenName) {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }

        /**
         * {@inheritDoc}
         */
        public void unlock() {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }

        /**
         * {@inheritDoc}
         */
        public boolean verifyPassword(String password) throws EncryptionException {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }

        /**
         * {@inheritDoc}
         */
        public void setLastFailedLoginTime(Date lastFailedLoginTime) {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }
        
        /**
         * {@inheritDoc}
         */
    	public void setLastLoginTime(Date lastLoginTime) {
    		throw new RuntimeException("Invalid operation for the anonymous user");
    	}

    	/**
         * {@inheritDoc}
         */
     	public void setLastHostAddress(String remoteHost) {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }
                
     	/**
         * {@inheritDoc}
         */
        public void setLastPasswordChangeTime(Date lastPasswordChangeTime) {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }
        
        /**
         *  {@inheritDoc}
         */
        public HashMap getEventMap() {
        	throw new RuntimeException("Invalid operation for the anonymous user");
        }
         /**
    	 * @return the locale
    	 */
    	public Locale getLocale() {
    		return locale;
    	}

    	/**
    	 * @param locale the locale to set
    	 */
    	public void setLocale(Locale locale) {
    		this.locale = locale;
    	}
    };
}
