/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/security/admin/URMRolesGate.java,v 1.3 2004/07/30 06:52:01 ozeigermann Exp $
 * $Revision: 1.3 $
 * $Date: 2004/07/30 06:52:01 $
 *
 * ====================================================================
 *
 * 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.store.tamino.security.admin;

import java.util.*;

import org.apache.slide.urm.URMException;
import org.apache.slide.urm.URMForbiddenException;
import org.apache.slide.urm.authenticator.URMSubject;
import org.apache.slide.urm.authenticator.rolemanager.URMRole;
import org.apache.slide.urm.common.URMConstants;
import org.apache.slide.urm.common.URMDeleteException;
import org.apache.slide.urm.common.URMInsertException;
import org.apache.slide.urm.common.URMSetRoleException;
import org.apache.slide.store.tamino.common.XForbiddenException;
import org.apache.slide.store.tamino.common.XGlobals;
import org.apache.slide.store.tamino.tools.stores.ActionDeclaration;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.content.NodeProperty;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.macro.ConflictException;
import org.apache.slide.macro.ForbiddenException;
import org.apache.slide.webdav.util.AclConstants;
import org.apache.slide.util.XMLValue;
import org.jdom.Element;


/**
 ** URMRolesGate serves the URMRolesStore using the URM API. It represents a role
 **
 ** @author    josef.haiduk@softwareag.com
 ** @version   $Revision: 1.3 $
 **
 **/

public class URMRolesGate extends URMGate {
    
    String role = null;
    URMRole urmRole = null;
    private boolean roleExists= false;
    private boolean isHome = false;
    private boolean isParentHome = false;
    private Properties roleProperties = null;
    
    /**
     * Creates a new gate to the URM API for serving role requests
     *
     * @param    parent              an URMUserDBStore
     * @param    uriStr              a  String
     *
     * @throws   ServiceAccessException
     *
     */
    public URMRolesGate(URMUserDBStore parent, String uriStr)
        throws ServiceAccessException {
        super(parent, uriStr);
        init();
    }
    
    /**
     * Method init
     *
     * @throws   ServiceAccessException
     *
     */
    private void init()
        throws ServiceAccessException {
		
		String path = URMUserDBStore.getRolesPath();
        if ( uriStr.equals(path)) {
            isHome = true;
            return;
        }
        
        if ( uri.getParent().toString().equals(path) )
            isParentHome = true;
        
        String lastToken = uriStr.substring(path.length()+1);
		if ( lastToken.indexOf("/") != -1 )
			throw new ServiceAccessException(parent,
					new ForbiddenException(uriStr,
						new XForbiddenException( "Operations below a role are forbidden!!!" )));
        try {
            role = new String(lastToken);
            
            if ( urmAdm.isRole(lastToken) ) {
                urmRole = urmAdm.getRole(role);
                roleExists = true;
            }
        }
        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                                             new ForbiddenException(uri.toString(), e));
        }
        
        catch (URMException e) {
            throw new ServiceAccessException(parent, e);
        }
    }
    
    /**
     * isHome returns true if the uri passed to the constructor equals ../administartion/security/userdb/roles
     *
     * @return   a boolean
     *
     */
    public boolean isHome() {
        return isHome;
    }
    
    /**
     * isParentHome returns true if the parent uri of the uri passed to the constructor is
     * ../administartion/security/userdb/roles
     *
     * @return   a boolean
     *
     */
    public boolean isParentHome() {
        return isParentHome;
    }
    
    
    /**
     * objectExists returns true if the role referenced by uri passed to the constructor exists.
     *
     * @return   a boolean
     *
     */
    public boolean objectExists() {
        return roleExists;
    }
    
    /**
     * getId returns the the name of the role
     *
     * @return   a String
     *
     */
    public String getId() {
        return role;
    }
    
    /**
     * getObject returns the URMRole referenced by the uri passed to the constructor
     *
     * @return   an URMAction
     *
     */
    public URMRole getObject() {
        return urmRole;
    }
    
    /**
     * createObject creates the role referenced by the uri passed to the constructor
     *
     * @throws   ServiceAccessException
     *
     */
    public void createObject()
        throws ServiceAccessException {
        try {
            getUrmAdministratorWithAdminRole().createRole(role);
            urmRole = getUrmAdministratorWithAdminRole().getRole(role);
            roleExists = true;
        }
        
        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                                             new ForbiddenException(uri.toString(), e));
        }
        catch (URMInsertException e) {
            throw new ServiceAccessException(parent,
                                             new ForbiddenException(uri.toString(), e));
        }
        catch (URMSetRoleException e) {
            throw new ServiceAccessException(parent,
                                             new ForbiddenException(uri.toString(), e));
        }
        catch (URMException e) {
            throw new ServiceAccessException(parent, e);
        }
    }
    
    
    /**
     * deleteObject deletes the role referenced by the uri passed to the constructor
     *
     * @throws   ServiceAccessException
     *
     */
    public void deleteObject()
        throws ServiceAccessException {
        try {
            getUrmAdministratorWithAdminRole().deleteRole(role);
            roleExists = false;
        }
        
        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                                             new ForbiddenException(uri.toString(), e));
        }
        catch (URMSetRoleException e) {
            throw new ServiceAccessException(parent,
                                             new ForbiddenException(uri.toString(), e));
        }
        catch (URMException e) {
            throw new ServiceAccessException(parent, e);
        }
    }
    
    /**
     * getProperty returns the property value
     *
     * @param    key                 a  String
     *
     * @return   a String
     *
     * @throws   ServiceAccessException
     *
     */
    public String getProperty(String key)
        throws ServiceAccessException {
        return getProperty(urmRole, key);
    }
    
    /**
     * insertMappedProperties puts the mapped properties into the NodeRevisionDescriptor
     *
     * @param    nrd                 a  NodeRevisionDescriptor
     *
     * @throws   ServiceAccessException
     *
     */
    public void insertMappedProperties(NodeRevisionDescriptor nrd)
        throws ServiceAccessException {
        insertMappedProperties(nrd, urmRole);
    }
    
    
    /**
     * getProperties returns the properties of the group
     *
     * @return   a Properties
     *
     * @throws   ServiceAccessException
     *
     */
    public Properties getProperties()
        throws ServiceAccessException {
        try {
            if ( roleProperties == null )
                roleProperties = urmRole.getProperties();
            return roleProperties;
        }
        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                                             new ForbiddenException(uri.toString(), e));
        }
        catch (URMException e) {
            throw new ServiceAccessException(parent, e);
        }
    }
    
    /**
     * getAvailableProperties returns a map containing the property names available for a role
     * and a boolean value which is true if the property can be modified
     *
     * @return   a Map
     *
     * @throws   ServiceAccessException
     *
     */
    public Map getAvailableProperties()
        throws ServiceAccessException {
        return getAvailableProperties(urmRole);
    }
    
    /**
     * getUnmappedProperties returns the unmapped properties. Unmapped properties are properties
     * which are not mapped to standard webdav properties.
     *
     * @return   a Properties
     *
     * @throws   ServiceAccessException
     *
     */
    public Properties getUnmappedProperties()
        throws ServiceAccessException {
        Properties p = (Properties)getProperties().clone();
        for ( int i=0; i<mappedPropertyNames.length; i++ ){
            p.remove(mappedPropertyNames[i]);
        }
        return p;
        
    }
    
    /**
     * getPropertiesAsBytes returns the role properties as a byte stream
     *
     * @return   a byte[]
     *
     * @throws   ServiceAccessException
     *
     */
    public byte[] getPropertiesAsBytes()
        throws ServiceAccessException {
        return getProperties(urmRole);
    }
    /*
     public void setProperties(NodeRevisionDescriptor nrd)
     throws ServiceAccessException
     {
     // group properties
     Properties pr = getPropertiesToModify(nrd);
     Properties newProperties = new Properties();
     Set v = getModifiableProperties(getAvailableProperties());
     Iterator i = v.iterator();
     while (i.hasNext()) {
     String key = (String)i.next();
     NodeProperty p = nrd.getProperty(key, XGlobals.TAMINO_NAMESPACE_URI);
     if ( p != null ) {
     String value = (String)p.getValue();
     if ( value.compareTo("") != 0 )
     newProperties.put(key, value);
     }
     }
     updateProperties(urmRole,newProperties);
     }
     */
    
    
    /**
     * setProperties sets the role properties. It looks for mapped (standard webdav) properties then
     * for tamino properties in the NodeRevisionDescriptor.
     *
     * @param    nrd                 a  NodeRevisionDescriptor
     *
     * @throws   ServiceAccessException
     *
     */
    public void setProperties(NodeRevisionDescriptor nrd)
        throws ServiceAccessException {
        Properties p = getProperties();
        Enumeration en = nrd.enumeratePropertiesName();
        //Enumeration en = nrd.getPropertiesNames();    // it's deprecated
        for ( int i=0; i<mappedPropertyNames.length; i++ ){
            NodeProperty np = nrd.getProperty(mappedPropertyNames[i]);
            if ( np == null )
                continue;
            String value = (String)np.getValue();
            if ( value.compareTo("") != 0 )
                p.put(mappedPropertyNames[i], value);
        }
        while ( en.hasMoreElements() ) {
            String key = (String)en.nextElement();
            NodeProperty np = nrd.getProperty(key, XGlobals.TAMINO_NAMESPACE_URI);
            if  ( np == null )
                continue;
            
            Object value = np.getValue();
            if (value instanceof java.lang.String) {
                if ( ((String) value).compareTo("") != 0 )
                    p.put(key, value);
            } else if (value instanceof XMLValue) {
                p.put(key, ((XMLValue)value).toString());
            }
            
        }
        updateProperties(urmRole,p);
        
    }
    
    /**
     * getUsers returns a set of users with this role
     *
     * @return   a Set
     *
     * @throws   ServiceAccessException
     *
     */
    public Set getUsers()
        throws ServiceAccessException {
        return getSubjects(URMConstants.USER);
    }
    
    /**
     * getGroups returns a set of groups with this role
     *
     * @return   a Set
     *
     * @throws   ServiceAccessException
     *
     */
    public Set getGroups()
        throws ServiceAccessException {
        return getSubjects(URMConstants.GROUP);
    }
    
    /**
     * getRoles returns a set of roles with this role
     *
     * @return   a Set
     *
     * @throws   ServiceAccessException
     *
     */
    public Set getRoles()
        throws ServiceAccessException {
        return getSubjects(URMConstants.ROLE);
    }
    
    /**
     * getActions return a map of actions of this role. The keys represents the
     * the action name the value indicates if the action is granted (true) or denied.
     *
     * @return   a Map
     *
     * @throws   ServiceAccessException
     *
     */
    public Map getActions()
        throws ServiceAccessException {
        try {
            return urmRole.getPredefinedActions();
        }
        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                                             new ForbiddenException(uri.toString(), e));
        }
        catch (URMException e) {
            throw new ServiceAccessException(parent, e);
        }
    }
    
    /**
     * getSubjects a set of user, groups or roles of this role.
     *
     * @param    constant            an int
     *
     * @return   a Set
     *
     * @throws   ServiceAccessException
     *
     */
    private Set getSubjects(int constant)
        throws ServiceAccessException {
        try {
            if ( constant == URMConstants.ROLE )
                return urmRole.getAncestors();
            
            if ( constant == URMConstants.GROUP )
                return urmRole.getGroupMapping();
            
            if ( constant == URMConstants.USER )
                return urmRole.getUserMapping();
        }
        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                                             new ForbiddenException(uri.toString(), e));
        }
        catch (URMException e) {
            throw new ServiceAccessException(parent, e);
        }
        
        return new HashSet();
    }
    
    /**
     * getMembers returns a set of users, groups and roles of this role
     *
     * @return   a Set
     *
     * @throws   ServiceAccessException
     *
     */
    private Set getMembers()
        throws ServiceAccessException {
        try {
            Set memebers = urmRole.getUserMapping();
            memebers.addAll(urmRole.getGroupMapping());
            memebers.addAll(urmRole.getAncestors());
            return memebers;
        }
        catch (Exception e) {
            e.printStackTrace();
            throw new ServiceAccessException(parent,e);
        }
    }
    
    /**
     * getAllRoles returns a set of all existing roles
     *
     * @return   a Set
     *
     * @throws   ServiceAccessException
     *
     */
    private Set getAllRoles()
        throws ServiceAccessException {
        try {
            return urmAdm.getAllRoles();
        }
        catch (Exception e) {
            e.printStackTrace();
            throw new ServiceAccessException(parent,e);
        }
    }
    
    /**
     * enumerateObjects puts all members of this role into the vector
     *
     * @param    v                   a  Vector
     *
     * @throws   ServiceAccessException
     *
     */
    public void enumerateObjects(Vector v)
        throws ServiceAccessException {
        enumerateSubjects(getAllRoles(), v);
    }
    
    /**
     * getParents returns a set of roles where this role belongs to.
     *
     * @return   a Set
     *
     * @throws   ServiceAccessException
     *
     */
    public Set getParents()
        throws ServiceAccessException {
        try {
            return urmRole.getRolesOfAncestor();
        }
        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                                             new ForbiddenException(uri.toString(), e));
        }
        catch (URMException e) {
            throw new ServiceAccessException(parent, e);
        }
    }
    
    /**
     * getNewActions returns a Map of actions to be set from the NodeRevisionDescriptor
     *
     * @param    nrd                 a  NodeRevisionDescriptor
     *
     * @return   a HashMap
     *
     * @throws   ServiceAccessException
     *
     */
    private HashMap getNewActions(NodeRevisionDescriptor nrd)
        throws ServiceAccessException {
        NodeProperty grantSet = nrd.getProperty(URMUserDBStore.GRANT_PRIVILEGE_SET, XGlobals.TAMINO_NAMESPACE_URI);
        NodeProperty denySet = nrd.getProperty(URMUserDBStore.DENY_PRIVILEGE_SET, XGlobals.TAMINO_NAMESPACE_URI);
        HashMap newActions = getNewActions(null, grantSet, true);
        newActions = getNewActions(newActions, denySet, false);
        nrd.removeProperty(URMUserDBStore.GRANT_PRIVILEGE_SET, XGlobals.TAMINO_NAMESPACE_URI);
        nrd.removeProperty(URMUserDBStore.DENY_PRIVILEGE_SET, XGlobals.TAMINO_NAMESPACE_URI);
        return newActions;
    }
    
    private HashMap getNewActions(HashMap map, NodeProperty np, boolean b)
        throws ServiceAccessException {
        if ( map == null )
            map = new HashMap();
        
        if ( np == null )
            return map;
        
        Object o = np.getValue();
        if ( o == null )
            return map;
        if ( !o.getClass().isInstance("") )
            return map;
        try {
            XMLValue xv = new XMLValue((String)o);
            Iterator i = xv.iterator();
            
            while ( i.hasNext() ) {
                Object item = i.next();
                if ((item instanceof Element)
                    && "privilege".equals(((Element)item).getName())) {
                    
                    Element element = (Element)item;
                    if (element.getChildren().size() > 0) {
                        String action = ((Element)element.getChildren().get(0)).getName();
                        if ( urmAclAdm.getAction(action) == null )
                            throw new ConflictException(action, new XForbiddenException( "An action must exist before it can be added or removed to|from a role" ));
                        map.put(action,new Boolean(b));
                    }
                }
            }
            return map;
        }
        catch (Exception e) {
            throw new ServiceAccessException(parent, e);
        }
    }
    
    /**
     * updateActions updates the action of this role
     *
     * @param    nrd                 a  NodeRevisionDescriptor
     *
     * @throws   ServiceAccessException
     *
     */
    public void updateActions(NodeRevisionDescriptor nrd)
        throws ServiceAccessException {
        NodeProperty grantSet = nrd.getProperty(URMUserDBStore.GRANT_PRIVILEGE_SET, XGlobals.TAMINO_NAMESPACE_URI);
        NodeProperty denySet = nrd.getProperty(URMUserDBStore.DENY_PRIVILEGE_SET, XGlobals.TAMINO_NAMESPACE_URI);
        boolean grant = false;
        boolean deny = false;
        HashMap grants = null;
        HashMap denies = null;
        String actionName = "";
        
        if (grantSet != null )  {
            nrd.removeProperty(grantSet);
            if (grantSet.getValue() != null && (grantSet.getValue() instanceof java.lang.String)) {
                grants = getNewActions(null, grantSet, true);
                grant = true;
            }
        }
        
        if (denySet != null ) {
            nrd.removeProperty(denySet);
            if (denySet.getValue() != null && (denySet.getValue() instanceof java.lang.String)) {
                denies = getNewActions(null, denySet, false);
                deny = true;
            }
        }
        
        try {
            URMRole admin =  null;
            Map currentMap = null;
            if (deny || denySet == null || grant || grantSet == null) {
                admin = getUrmAdministratorWithAdminRole().getRole(role);
                currentMap = urmRole.getPredefinedActions();
            } else return;
            
            Set set = currentMap.keySet();
            Iterator i = set.iterator();
            while ( i.hasNext() ) {
                actionName = (String)i.next();
                boolean actionValue = ((Boolean)currentMap.get(actionName)).booleanValue();
                
                // if grant-privileg-set has been defined, remove all granted
                // permissions from the role
                if (actionValue && (grant || grantSet == null)) {
                    admin.deletePredefinedAction(actionName);
                    
                    // if deny-privileg-set has been defined, remove all denied
                    // permissions from the role
                } else if (!actionValue && (deny || denySet == null)) {
                    admin.deletePredefinedAction(actionName);
                }
                
            }
            
            // insert grant-privilege-set
            if (grant) {
                i = grants.keySet().iterator();
                while ( i.hasNext() ) {
                    actionName = (String)i.next();
                    Boolean b = new Boolean(true);
                    try {
                        admin.setPredefinedAction(actionName, b);
                    } catch (URMInsertException uie){
                        admin.deletePredefinedAction(actionName);
                        admin.setPredefinedAction(actionName, b);
                    }
                }
            }
            
            // insert deny-privilege-set
            if (deny) {
                i = denies.keySet().iterator();
                while ( i.hasNext() ) {
                    actionName = (String)i.next();
                    Boolean b = new Boolean(false);
                    try {
                        admin.setPredefinedAction(actionName, b);
                    } catch (URMInsertException uie){
                        admin.deletePredefinedAction(actionName);
                        admin.setPredefinedAction(actionName, b);
                    }
                }
            }
        }
        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                                             new ForbiddenException(ActionDeclaration.SCOPE+"/"+actionName, e));
        }
        catch (URMDeleteException e) {
            e.printStackTrace();
            throw new ServiceAccessException(parent,
                                             new ConflictException(ActionDeclaration.SCOPE+"/"+actionName, e));
        }
        catch (URMInsertException e) {
            e.printStackTrace();
            throw new ServiceAccessException(parent,
                                             new ConflictException(ActionDeclaration.SCOPE+"/"+actionName, e));
        }
        catch (URMSetRoleException e) {
            throw new ServiceAccessException(parent,
                                             new ForbiddenException(uri.toString(), e));
        }
        catch (URMException e) {
            throw new ServiceAccessException(parent, e);
        }
        
    }
    

    /**
     * updateMembers the members of this role; users, groups and roles
     *
     * @param    nrd                 a  NodeRevisionDescriptor
     *
     * @throws   ServiceAccessException
     *
     */
    public void updateMembers(NodeRevisionDescriptor nrd)
        throws ServiceAccessException {
        try {
            HashSet newMembrs = getNewMembers(nrd, AclConstants.P_GROUP_MEMBER_SET, null);
            
            // process groups
            HashSet groupsSet = (HashSet)getGroups();
            megreMembers(newMembrs, groupsSet, URMUserDBStore.getGroupsPath());
            
            // Process users
            HashSet usersSet = (HashSet)getUsers();
            megreMembers(newMembrs, usersSet, URMUserDBStore.getUsersPath());
            
            // process roles
            HashSet rolesSet = (HashSet)getRoles();
            megreMembers(newMembrs, rolesSet, URMUserDBStore.getRolesPath());
            
            // add new groups and users
            Iterator i = newMembrs.iterator();
            HashSet newGroups = new HashSet();
            HashSet newUsers = new HashSet();
            HashSet newRoles = new HashSet();
            
            // check if they exist
            while ( i.hasNext() ) {
                String subjectUri = (String)i.next();
                if ( subjectUri.startsWith(URMUserDBStore.getRolesPath())) {
                    URMRolesGate rg = new URMRolesGate(parent, subjectUri);
                    if ( rg.objectExists() )
                        newRoles.add(rg.getId());
                    else
                        throw new ConflictException(subjectUri, new XForbiddenException( "A role must exist before it can be added to another role" ));
                    continue;
                }
                
                if ( subjectUri.startsWith(URMUserDBStore.getGroupsPath())) {
                    URMGroupsGate gg = new URMGroupsGate(parent, subjectUri);
                    if ( gg.objectExists() )
                        newGroups.add(gg.getId());
                    else
                        throw new ConflictException(subjectUri, new XForbiddenException( "A group must exist before it can be added to a role" ));
                    continue;
                }
                
                if ( subjectUri.startsWith(URMUserDBStore.getUsersPath())) {
                    URMUsersGate ug = new URMUsersGate(parent, subjectUri);
                    if ( ug.objectExists() )
                        newUsers.add(ug.getId());
                    else
                        throw new ConflictException(subjectUri, new XForbiddenException( "A user must exist before it can be added to a role" ));
                }
                else
                    throw new ConflictException(subjectUri, new XForbiddenException( "The URL is invalid" ));
            }
            
            URMRole admin = getUrmAdministratorWithAdminRole().getRole(role);
            // add roles groups and users
            i = newRoles.iterator();
            while ( i.hasNext() ) {
                admin.addAncestor((String)i.next());
            }
            i = newGroups.iterator();
            while ( i.hasNext() ) {
                admin.addGroupLink((String)i.next(), domain);
            }
            i = newUsers.iterator();
            while ( i.hasNext() ) {
                admin.addUserLink((String)i.next(), domain);
            }
            // remove groups and users
            i = rolesSet.iterator();
            while ( i.hasNext() ) {
                URMSubject subject = (URMSubject)i.next();
                admin.deleteAncestor(subject.getName());
            }
            i = groupsSet.iterator();
            while ( i.hasNext() ) {
                URMSubject subject = (URMSubject)i.next();
                admin.deleteGroupLink(subject.getName(), domain);
            }
            i = usersSet.iterator();
            while ( i.hasNext() ) {
                URMSubject subject = (URMSubject)i.next();
                admin.deleteUserLink(subject.getName(), domain);
            }
            
        }
        catch (URMForbiddenException e) {
            throw new ServiceAccessException(parent,
                                             new ForbiddenException(uri.toString(), e));
        }
        catch (URMDeleteException e) {
            throw new ServiceAccessException(parent,
                                             new ConflictException(uri.toString(), e));
        }
        catch (URMSetRoleException e) {
            throw new ServiceAccessException(parent,
                                             new ForbiddenException(uri.toString(), e));
        }
        catch (Exception e) {
            throw new ServiceAccessException(parent, e);
        }
    }
	/**
     * updateParents the members of this role; users, groups and roles
     *
     * @param    nrd                 a  NodeRevisionDescriptor
     *
     * @throws   ServiceAccessException
     *
     */
    public void updateParents(NodeRevisionDescriptor nrd)
        throws ServiceAccessException {
		updateParents(urmRole, nrd);
	}
}







