/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/urm/org/apache/slide/urm/authenticator/userdb/impl/URMUserImpl.java,v 1.4 2005/03/02 10:53:34 eckehard Exp $
 * $Revision: 1.4 $
 * $Date: 2005/03/02 10:53:34 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * Licensed 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
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.apache.slide.urm.authenticator.userdb.impl;

import java.util.Iterator;
import java.util.Set;
import java.util.Properties;
import java.util.HashSet;

import org.apache.slide.urm.authenticator.URMAuthenticator;
import org.apache.slide.urm.authenticator.URMSubject;
import org.apache.slide.urm.authenticator.rolemanager.URMRoleManagerSpi;
import org.apache.slide.urm.authenticator.rolemanager.impl.URMRoleImpl;
import org.apache.slide.urm.authenticator.userdb.URMGroup;
import org.apache.slide.urm.authenticator.userdb.URMMetadataPropsSpi;
import org.apache.slide.urm.authenticator.userdb.URMUser;
import org.apache.slide.urm.authenticator.userdb.URMUserDBManagerSpi;
import org.apache.slide.urm.URMNotImplementedException;
import org.apache.slide.urm.URMForbiddenException;
import org.apache.slide.urm.common.URMDeleteException;
import org.apache.slide.urm.common.URMInsertException;
import org.apache.slide.urm.common.URMInternalServerException;
import org.apache.slide.urm.common.URMUpdateException;
import org.apache.slide.urm.utils.messagelogger.MessageLogger;
import java.util.Map;
import java.util.Hashtable;

/**
 * Represents an userdb user.
 *
 * @author eckehard.hermann@softwareag.com
 * @author dieter.kessler@softwareag.com
 * @author zsolt.sasvarie@softwareag.com
 */
public class URMUserImpl implements URMUser {
    
    private static org.apache.log4j.Logger msLogger =
        org.apache.log4j.Logger.getLogger(URMUserImpl.class.getName());

    protected String mDomain = null;
    protected String mUserName = null;
    protected URMUserDBManagerSpi mUserDBManagerSpi = null;
    protected URMMetadataPropsSpi mMetadataPropsSpi = null;
    protected URMRoleManagerSpi mRoleManagerSpi = null;
    
    private int mHashCode;
    
    /*
     * Constructor: create new URMUser instance
     *
     * @param user name
     * @param domain name
     * @param URMUserDBManagerSpi
     */
    public URMUserImpl( String inUsername, String inDomain,
                        URMUserDBManagerSpi userDbManagerSpi,
                        URMMetadataPropsSpi metadataPropsSpi,
                        URMRoleManagerSpi roleManagerSpi) {

    	if ((inDomain != null) && !URMAuthenticator.isCaseSensitive()) mDomain = inDomain.toUpperCase();
    	else mDomain = inDomain;
    	// check if case sensitive    	
    	if ((inUsername != null) && !URMAuthenticator.isCaseSensitive()) mUserName = inUsername.toUpperCase();
    	else mUserName = inUsername;
    	
        mUserDBManagerSpi = userDbManagerSpi;
        mMetadataPropsSpi = metadataPropsSpi;
        mRoleManagerSpi = roleManagerSpi;
        mHashCode = ((mDomain != null ? (mDomain + "/") : "") + (mUserName != null ? mUserName : "")).hashCode();
    }

    public String getName() {
        return mUserName;
    }

    public int getType() {
        return org.apache.slide.urm.common.URMConstants.USER;
    }
    
    /** Returns the users domain or null if no domain is defined
     *
     * @return domain
     */
    public String getDomain(){
        return mDomain;
    }
    
    /**
     * Returns an Set over all groups where the user is a member of
     *
     * @return Set over all groups where the user is a member of
     *
     * @exception URMNotImplementedException if the method is not
     *            implemented by the user database.
     * @exception URMForbiddenException if the user has not the permission
     *            to invoke the method.
     * @exception URMException if an unpredictable internal error occurs
     */
    public Set getGroups()
            throws URMNotImplementedException, URMForbiddenException,
                URMInternalServerException{
        return URMGroupImpl.getURMGroupObjectSetFromStringSet(
                mUserDBManagerSpi.getGroupsOfUser(mUserName, mDomain),
                mDomain, mUserDBManagerSpi, mMetadataPropsSpi, mRoleManagerSpi);
    }
    
    /**
     * Returns an Set over all groups where the user is a member of
     * and the groups where these groups are membgers recursively.
     *
     * @return Set over all groups where the user is a member of
     *
     * @exception URMNotImplementedException if the method is not
     *            implemented by the user database.
     * @exception URMForbiddenException if the user has not the permission
     *            to invoke the method.
     * @exception URMException if an unpredictable internal error occurs
     */
    public Set getAllGroups()
            throws URMNotImplementedException, URMForbiddenException, URMInternalServerException {
        Set all_grps = new HashSet();
        Set grps = this.getGroups();
        all_grps.addAll(grps);
        for (Iterator iter = grps.iterator(); iter.hasNext(); )
            all_grps.addAll(((URMGroup)iter.next()).getAllGroupsMemberOf());
        return all_grps;
    }
    
    /**
     * Retrieves user specific properties
     *
     * @return Properties, properties of the passed user
     *
     * @exception URMNotImplementedException if the method is not
     *            implemented by the user database.
     * @exception URMForbiddenException if the user has not the permission
     *            to invoke the method.
     * @exception URMException if an unpredictable internal error occurs
     */
    public Properties getProperties()
            throws URMNotImplementedException, URMForbiddenException,
                URMInternalServerException {
        Properties props = new Properties();
        Properties uprs = mUserDBManagerSpi.getUserProperties(mUserName, mDomain);
        if (uprs != null)
            props.putAll(uprs);
        if (mMetadataPropsSpi != null)
            props.putAll(mMetadataPropsSpi.getProperties(mUserName, mDomain, true));
        return props;
    }
            
    /**
     * Retrieves a specific property
     *
     * @param key property key
     * @return property value or null if the property has not been found
     *
     * @exception URMNotImplementedException if the method is not
     *            implemented by the user database.
     * @exception URMForbiddenException if the user has not the permission
     *            to invoke the method.
     * @exception URMException if an unpredictable internal error occurs
     */
    public Object getProperty(String key)throws URMNotImplementedException, URMForbiddenException,URMInternalServerException {
        //Properties props = mUserDBManagerSpi.getUserProperties(mUserName, mDomain);
        Properties props = getProperties();
        return props.getProperty(key);
    }
            
    /**
     * Retrieves a aggregated set of values of a specific property. The result set
     * contains the values of the property defined for the ancestors (groups) of the subject.
     *
     * @param key property key
     * @return aggregated property value
     *
     * @exception URMNotImplementedException if the method is not
     *            implemented by the user database.
     * @exception URMForbiddenException if the user has not the permission
     *            to invoke the method.
     * @exception URMException if an unpredictable internal error occurs
     */
    public Set getAggregatedProperty(String key) throws URMNotImplementedException, URMForbiddenException, URMInternalServerException {
        HashSet allProps = new HashSet();
        Object prop = this.getProperty(key);
        if (prop != null)
            allProps.add(prop);
        for (Iterator iter = this.getAllGroups().iterator(); iter.hasNext(); ) {
            URMGroup currentGroup = (URMGroup) iter.next();
            prop = currentGroup.getProperty((String) key);
            if (prop != null)
                allProps.add(prop);
        }
        
        return allProps;
    }
                            
    /** Returns all the roles, mapped to the subject and where the subject can take this roles
     *
     * @return Set of URMRole Object, associated with the group
     *
     * @exception URMNotImplementedException if the method is not
     *            implemented by the user database.
     * @exception URMForbiddenException if the user has not the permission
     *            to invoke the method.
     * @exception URMException if an unpredictable internal error occurs
     */
    public Set getRoleMapping()
            throws URMNotImplementedException, URMForbiddenException,
                URMInternalServerException {
        return URMRoleImpl.getURMRoleObjectSetFromSubjectSet(
                mRoleManagerSpi.getRoleMappingOfUser(mUserName, mDomain), mRoleManagerSpi,
                                                mUserDBManagerSpi, mMetadataPropsSpi);
    }
    
    /** Returns all the roles (inclusive all inherited roles,
     *  mapped to the subject and where the subject can take this roles
     *
     * @return Set of URMRole Object, associated with the user
     *
     * @exception URMNotImplementedException if the method is not
     *            implemented by the user database.
     * @exception URMForbiddenException if the user has not the permission
     *            to invoke the method.
     * @exception URMException if an unpredictable internal error occurs
     */
    public Set getAllRoleMapping()
            throws URMNotImplementedException, URMForbiddenException,
                URMInternalServerException {
        return URMRoleImpl.getURMRoleObjectSetFromSubjectSet(
                mRoleManagerSpi.getAllRoleMappingOfUser(mUserName, mDomain), mRoleManagerSpi,
                                                mUserDBManagerSpi, mMetadataPropsSpi);
    }
    
    public boolean equals(Object obj) {
        if (obj == null)
            return false;
        //if (obj instanceof String)
        //    return mUserName.equals((String)obj);
        if (obj instanceof URMUser) {
            URMUser usr = (URMUser)obj;
            String dom = usr.getDomain();
            if (dom != null)
                if (mDomain != null)
                    return mUserName.equals(usr.getName()) && mDomain.equals(dom);
                else
                    return false;
            else
                if (mDomain == null)
                    return mUserName.equals(usr.getName());
                else
                    return false;
        }
        return false;
    }

    public int hashCode() {
        return mHashCode;
    }
    
    public static Set getURMUserObjectSetFromStringSet(Set usernames, String domain,
                                                    URMUserDBManagerSpi userDbManagerSpi,
                                                    URMMetadataPropsSpi metadataPropsSpi,
                                                    URMRoleManagerSpi roleManagerSpi) {
        if (usernames == null)
            return null;
        Iterator iter = usernames.iterator();
        HashSet retusrs = new HashSet();
        while (iter.hasNext())
            retusrs.add(new URMUserImpl((String)iter.next(), domain,
                                        userDbManagerSpi, metadataPropsSpi, roleManagerSpi));
        return retusrs;
    }
    
    public static Set getURMUserObjectSetFromSubjectSet(Set subjects,
                                                    URMUserDBManagerSpi userDbManagerSpi,
                                                    URMMetadataPropsSpi metadataPropsSpi,
                                                    URMRoleManagerSpi roleManagerSpi) {
        if (subjects == null)
            return null;
        Iterator iter = subjects.iterator();
        HashSet retusrs = new HashSet();
        while (iter.hasNext()) {
            URMSubject subj = (URMSubject)iter.next();
            retusrs.add(new URMUserImpl(subj.getName(), subj.getDomain(),
                                        userDbManagerSpi, metadataPropsSpi, roleManagerSpi));
        }
        return retusrs;
    }
    
    public static boolean contains(Set users, String user, String domain) {
        if (users == null || user == null)
            return false;
        
        URMUser trole = new URMUserImpl(user, domain, null, null, null);
        return users.contains(trole);
    }
  
    /**
     * Retrieves user specific properties
     *
     * @param prop properties to set them
     *
     * @exception URMNotImplementedException if the method is not
     *            implemented by the user database.
     * @exception URMForbiddenException if the user has not the permission
     *            to invoke the method.
     * @exception URMException if an unpredictable internal error occurs
     */
    public void setProperties(Properties prop)
            throws URMNotImplementedException, URMForbiddenException,
                URMUpdateException, URMDeleteException, URMInternalServerException {
        throw new URMForbiddenException(MessageLogger.getAndLogMessage(msLogger, "URMCOE0027"));
    }
    
    /**
     * Changes the password of a user in the user database
     *
     * @param oldpassword the user's current password
     *
     * @param newpassword the desired password of the userd
     *
     * @exception URMNotImplementedException if the method is not
     *            implemented by the user database.
     * @exception URMForbiddenException if the user has not the permission
     *            to invoke the method.
     * @exception URMException if an unpredictable internal error occurs
     */
    public void setPassword(char[] oldpassword, char[] newpassword)
            throws URMNotImplementedException, URMForbiddenException, URMUpdateException {
        throw new URMForbiddenException(MessageLogger.getAndLogMessage(msLogger, "URMCOE0027"));
    }

    /** Mapps this subject to a role, so that the subject can take this role
     *
     * @param role name of the role.
     *
     * @exception URMNotImplementedException if the method is not
     *            implemented by the user database.
     * @exception URMForbiddenException if the user has not the permission
     *            to invoke the method.
     * @exception URMException if an unpredictable internal error occurs
     */
    public void linkToRole(String role)
            throws URMNotImplementedException, URMForbiddenException, URMInsertException, URMInternalServerException {
        throw new URMForbiddenException(MessageLogger.getAndLogMessage(msLogger, "URMCOE0027"));
    }

    /** Delets a role link to the subject. The subject is no longer able to take this role
     *
     * @param role name the ancestor that should be removed from the role
     *
     * @exception URMNotImplementedException if the method is not
     *            implemented by the user database.
     * @exception URMForbiddenException if the user has not the permission
     *            to invoke the method.
     * @exception URMException if an unpredictable internal error occurs
     */
    public void deleteRoleLink(String role)
            throws URMNotImplementedException, URMForbiddenException, URMDeleteException, URMInternalServerException {
        throw new URMForbiddenException(MessageLogger.getAndLogMessage(msLogger, "URMCOE0027"));
    }

    /**
     * Sets new subject specific properties
     *
     * @param key property key
     * @param prop property value
     *
     * @exception URMNotImplementedException if the method is not
     *            implemented by the user database.
     * @exception URMForbiddenException if the user has not the permission
     *            to invoke the method.
     * @exception URMException if an unpredictable internal error occurs
     */
    public void setProperty(String key, Object prop) throws URMNotImplementedException, URMForbiddenException, URMUpdateException, URMDeleteException, URMInternalServerException{
        throw new URMForbiddenException(MessageLogger.getAndLogMessage(msLogger, "URMCOE0027"));
    }
    
    public Map getAvailablePropertyNames() {
        Hashtable map = new Hashtable();
        Object obj = null;
        // property names from userdb spi
        Set props = mUserDBManagerSpi.getWriteablePropKeys(true);
        for (Iterator iter = props.iterator(); iter.hasNext();)
            map.put(iter.next(), Boolean.TRUE);
        props = mUserDBManagerSpi.getAvailablePropKeys(true);
        for (Iterator iter = props.iterator(); iter.hasNext();)
            if (!map.containsKey(obj = iter.next()))
                map.put(obj, Boolean.FALSE);
        // property names from metaprops spi
        props = mMetadataPropsSpi.getWriteableUserPropKeys();
        for (Iterator iter = props.iterator(); iter.hasNext();)
            map.put(iter.next(), Boolean.TRUE);
        props = mMetadataPropsSpi.getAvailableUserPropKeys();
        for (Iterator iter = props.iterator(); iter.hasNext();)
            if (!map.containsKey(obj = iter.next()))
                map.put(obj, Boolean.FALSE);
         return map;
    }
}
