/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/urm/org/apache/slide/urm/utils/URMSemanticUtils.java,v 1.4 2005/03/02 10:53:35 eckehard Exp $
 * $Revision: 1.4 $
 * $Date: 2005/03/02 10:53:35 $
 *
 * ====================================================================
 *
 * 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.utils;
import org.apache.slide.urm.common.URMPrincipal;
import org.apache.slide.urm.authenticator.URMSubject;
import org.apache.slide.urm.common.URMConstants;
import org.apache.slide.urm.URMException;
import java.util.Set;
import java.util.Iterator;
import org.apache.slide.urm.authenticator.userdb.URMGroup;
import org.apache.slide.urm.authenticator.rolemanager.URMRole;
import java.util.HashMap;
import org.apache.slide.urm.accesscontroler.URMAclAdministrator;
import org.apache.slide.urm.accesscontroler.URMAction;
import org.apache.slide.urm.common.URMInternalServerException;
import java.util.HashSet;
import java.util.Map;
import java.util.Collection;
import org.apache.slide.urm.authenticator.userdb.URMUser;
import org.apache.slide.urm.authenticator.URMAdministrator;
import org.apache.slide.urm.accesscontroler.URMAclValidator;

/**
 * The class is collection of util methods, used by the URMSemantic implementations
 *
 * @author eckehard.hermann@softwareag.com
 * @author dieter.kessler@softwareag.com
 * @author zsolt.sasvarie@softwareag.com
 */

public class URMSemanticUtils
{
    protected boolean denyOverGrant = true;
    
    /** returns true if the prinicpal is represented by the subject
     *
     * @param    principal           an URMPrincipal
     * @param    subject             an URMSubject
     *
     * @return   true if the prinicpal is represented by the subject, false else
     *
     */
    protected boolean isSubject(URMPrincipal principal, URMSubject subject) throws URMException {
        int subjectType = subject.getType();
        boolean found = false;
        String principalName = principal.getUserName();
        String principalDomain = principal.getDomain();
        if (subjectType == URMConstants.USER) {
            // if Subject is of type User
            boolean domainIsEqual = true;
            boolean nameIsEqual = false;
            String name = subject.getName();
            String domain = subject.getDomain();
                        
            // check if a domain exists, if yes compare it with the principals domain
            if ((domain != null) && (!(domain.equals("")))) {
                // if the subject domain is not null
                if ((principalDomain == null) || !(domain.equals(principalDomain))) {
                    // if the principal domain is null or does not equal the subjects domain
                    domainIsEqual = false;
                }
            }
                        
            // check if pricipal is the subject
            if (name.equals(principalName)) {
                nameIsEqual = true;
            }
                        
            // if principal is the subject set found flag
            if (nameIsEqual && domainIsEqual) found = true;
                           
        } else if (subjectType == URMConstants.GROUP){
     
            URMGroup group = principal.getURMAdministrator().getGroup(subject.getName(),subject.getDomain()) ;
            if (group == null) return false;
            Set validatedGroups = new HashSet();
            found = isUserGroupMember(principalName, principalDomain, group, validatedGroups);
            
        } else if (subjectType == URMConstants.ROLE){
            Set roles = principal.getPossibleRoles();
            Iterator roleIter = roles.iterator();
         
            // check if the user is member of the Subject (group)
            while (roleIter.hasNext() && !found) {
                URMRole role = (URMRole)roleIter.next();
                found = compareSubjects(role, subject);
            }
        } else if (subjectType == URMConstants.ALL){
            found = true;
        }
        return found;
    }
 
    /** returns true if the prinicpal is represented by the subject
     *
     * @return   true if the prinicpal is represented by the subject, false else
     *
     */
    protected boolean isSubject(URMAdministrator urmAdmin, URMGroup urmGroup, URMSubject subject) throws URMException {
        int subjectType = subject.getType();
        boolean found = false;
        String groupName = urmGroup.getName();
        String groupDomain = urmGroup.getDomain();
        if (subjectType == URMConstants.GROUP){
            boolean domainIsEqual = true;
            boolean nameIsEqual = false;
            String name = subject.getName();
            String domain = subject.getDomain();
                        
            // check if a domain exists, if yes compare it with the group domain
            if ((domain != null) && (!(domain.equals("")))) {
                // if the subject domain is not null
                if ((groupDomain == null) || !(domain.equals(groupDomain))) {
                    // if the user domain is null or does not equal the subjects domain
                    domainIsEqual = false;
                }
            }
                        
            // check if group is the subject
            if (name.equals(groupName)) {
                nameIsEqual = true;
            }
                        
            // if principal is the subject set found flag
            if (nameIsEqual && domainIsEqual) found = true;
            else {
                URMGroup subjectGroup = urmAdmin.getGroup(subject.getName(), subject.getDomain());
                Set validatedGroups = new HashSet();
                found = isGroupGroupMember(groupName, groupDomain, subjectGroup, validatedGroups);
            }
             
        } else if (subjectType == URMConstants.ROLE){
            Set roles = urmGroup.getRoleMapping();
            Iterator roleIter = roles.iterator();
        
            // check if the user is member of the Subject (group)
            while (roleIter.hasNext() && !found) {
                URMRole role = (URMRole)roleIter.next();
                found = isSubject(role, subject);
            }
        
        } else if (subjectType == URMConstants.ALL){
            found = true;
        }
        return found;
    }
    
    /** returns true if the prinicpal is represented by the subject
     *
     * @return   true if the prinicpal is represented by the subject, false else
     *
     */
    protected boolean isSubject(URMAdministrator urmAdmin, URMUser urmUser, URMSubject subject) throws URMException {
        int subjectType = subject.getType();
        boolean found = false;
        String userName = urmUser.getName();
        String userDomain = urmUser.getDomain();
        if (subjectType == URMConstants.USER) {
            // if Subject is of type User

            boolean domainIsEqual = true;
            boolean nameIsEqual = false;
            String name = subject.getName();
            String domain = subject.getDomain();
                        
            // check if a domain exists, if yes compare it with the users domain
            if ((domain != null) && (!(domain.equals("")))) {
                // if the subject domain is not null
                if ((userDomain == null) || !(domain.equals(userDomain))) {
                    // if the user domain is null or does not equal the subjects domain
                    domainIsEqual = false;
                }
            }
                        
            // check if user is the subject
            if (name.equals(userName)) {
                nameIsEqual = true;
            }
                        
            // if principal is the subject set found flag
            if (nameIsEqual && domainIsEqual) found = true;
                           
        } else if (subjectType == URMConstants.GROUP){
            URMGroup subjectGroup = urmAdmin.getGroup(subject.getName(), subject.getDomain());
            Set validatedGroups = new HashSet();
            found = isUserGroupMember(userName, userDomain, subjectGroup, validatedGroups);
             
        } else if (subjectType == URMConstants.ROLE){
            Set roles = urmUser.getRoleMapping();
            Iterator roleIter = roles.iterator();
        
            // check if the user is member of the Subject (group)
            while (roleIter.hasNext() && !found) {
                URMRole role = (URMRole)roleIter.next();
                found = isSubject(role, subject);
            }
            
        } else if (subjectType == URMConstants.ALL){
            found = true;
        }
        return found;
    }
    
    /** returns true if the role is represented by the subject
     *
     * @param    role                an URMRole
     * @param    subject             an URMSubject
     *
     * @return   true if the role is represented by the subject, false else
     *
     */
    protected boolean isSubject(URMRole role, URMSubject subject) throws URMException {
        int subjectType = subject.getType();
        boolean found = false;

        if (subjectType == URMConstants.ROLE){
            found = compareSubjects(role, subject);
            
            if (!found) {
            // if the role does not directly map, compare the ancestor roles
                Set roles = role.getAncestors();
                Iterator roleIter = roles.iterator();
                        
                // check if the role is member of the Subject
                while (roleIter.hasNext() && !found) {
                    URMRole currentRole = (URMRole)roleIter.next();
                    found = isSubject(currentRole, subject);
                }
            }
            
        } else if (subjectType == URMConstants.ALL){
            found = true;
        }
        return found;
    }
    
    /**
     * returns true if the user is direct or indirect memebr of the group by the subject
     *
     * @param    user                a  String
     * @param    domain              a  String
     * @param    group               an URMGroup
     * @param    validatedGroups     a  already validated groups
     *
     * @return   a true if the direct or indirect groups contain the user
     *
     * @throws   URMException
     *
     */
    protected boolean isUserGroupMember(String user, String domain, URMGroup group, Set validatedGroups) throws URMException {
        boolean found = false;
        if (group.isUserMember(user, domain)) {
            return true;
        } else {
            String groupname = group.getName();
            String domainname = group.getDomain();
            String domaingroup = null;
            if (domainname != null) domaingroup = domainname + groupname;
            else domaingroup = groupname;
            
            // check if group has allready been vaildated
            if (validatedGroups.contains(domaingroup)) return false;
            else validatedGroups.add(domaingroup);
            
            // get all parent groups
            Set groups = group.getGroupsMemberOf();
            Iterator groupIter = groups.iterator();
            while (!found && groupIter.hasNext()) {
                URMGroup currentGroup = (URMGroup) groupIter.next();
                found = isUserGroupMember(user, domain, currentGroup, validatedGroups);
            }
        }
        return found;
    }
    
    /**
     * returns true if the user is direct or indirect memebr of the group by the subject
     *
     * @return   a true if the direct or indirect groups contain the user
     *
     * @throws   URMException
     *
     */
    protected boolean isGroupGroupMember(String groupname, String domain, URMGroup group, Set validatedGroups) throws URMException {
        boolean found = false;
        if (group.isGroupMember(groupname, domain)) {
            return true;
        } else {
            String _groupname = group.getName();
            String _domainname = group.getDomain();
            String domaingroup = null;
            if (_domainname != null) domaingroup = _domainname + _groupname;
            else domaingroup = _groupname;
            
            // check if group has allready been vaildated
            if (validatedGroups.contains(domaingroup)) return false;
            else validatedGroups.add(domaingroup);
            
            // get all parent groups
            Set groups = group.getGroupsMemberOf();
            Iterator groupIter = groups.iterator();
            while (!found && groupIter.hasNext()) {
                URMGroup currentGroup = (URMGroup) groupIter.next();
                found = isGroupGroupMember(groupname, domain, currentGroup, validatedGroups);
            }
        }
        return found;
    }
    
    /**
     * Compares the domain and name of two URMSubject objects
     *
     * @param    subject1            an URMSubject
     * @param    subject2            an URMSubject
     *
     * @return   true is equal
     *
     */
    protected boolean compareSubjects(URMSubject subject1, URMSubject subject2) {
        String subj1Domain = subject1.getDomain();
        String subj2Domain = subject2.getDomain();
        String subj1Name = subject1.getName();
        String subj2Name = subject2.getName();
        boolean domainIsEqual = true;
        boolean nameIsEqual = false;
        // check if a domain exists, if yes compare it with the group domain
        if ((subj1Domain != null) && (!(subj1Domain.equals("")))) {
            // if the subject domain is not null
            if ((subj2Domain == null) || (!(subj1Domain.equals(subj2Domain)))) {
                // if the principal domain is null or does not equal the subjects domain
                domainIsEqual = false;
            }
        }
        
        // check if subject names are equal
        if (subj1Name.equals(subj2Name)) {
            nameIsEqual = true;
        }
                        
        // if principal is the subject set found flag
        if (nameIsEqual && domainIsEqual) return true;
        else return false;
    }
    
    /**
     * Resolves a action into its base actions
     *
     * @param    admin               an URMAclAdministrator
     * @param    urmAction           an URMAction
     * @param    actionMap           a  HashMap
     *
     *
     * @throws   URMInternalServerException
     *
     */
    protected void resolveActionMapping(URMAclAdministrator admin, URMAction urmAction, Map actionMap)
        throws URMInternalServerException {
//  ToDo: Removed because it is not clear if requested actions have also to be 'aufgetroeselt' or just the ace actions
//        Set baseActions = urmAction.getBaseActions();
//        if (baseActions.isEmpty()) {
            actionMap.put(urmAction.getName(), new Boolean(false));
//        } else {
//            actionMap.put(urmAction.getName(), new Boolean(false));
//            Iterator baseActionIter = baseActions.iterator();
//            while (baseActionIter.hasNext()) {
//                resolveActionMapping(admin, admin.getAction((String)baseActionIter.next()),actionMap);
//            }
//        }
    }
    
    /**
     * Resolves a action into its base actions
     * @throws   URMInternalServerException
     *
     */
    protected void resolveActionMapping(URMAclAdministrator admin, URMAction urmAction, Set actionSet)
        throws URMInternalServerException {

        if (urmAction.getName().equals(URMConstants.URM_ALL_ACTION)) {
            actionSet.add(URMConstants.URM_ALL_ACTION);
        }
        Set baseActions = urmAction.getBaseActions();
        if (baseActions.isEmpty()) {
            actionSet.add(urmAction.getName());
        } else {
//            admin.loadActions(baseActions);
            actionSet.add(urmAction.getName());
            Iterator baseActionIter = baseActions.iterator();
            while (baseActionIter.hasNext()) {
                String currentBaseAction = (String)baseActionIter.next();
                resolveActionMapping(admin, admin.getAction(currentBaseAction),actionSet);
            }
        }
    }
    
    /**
     * does a logic AND concatenation of the actionMap values
     *
     * @param    actionMap           a  Map
     *
     * @return   true if all values are 'true'
     *
     */
    protected boolean verifyGrant(Map actionMap) {
        Collection values = actionMap.values();
        Iterator valueIter = values.iterator();
        boolean result = true;
        while(valueIter.hasNext()) {
            result = result & ((Boolean) valueIter.next()).booleanValue();
        }
        return result;
    }
    
    /**
     * Resolves the predefined actions of a URMPrincipal
     *
     * @param    principal           an URMPrincipal
     * @param    actionMap           a  actionMap
     *
     * @return   a boolean, true if predefined actions deny access
     *
     * @throws   URMException
     *
     */
    protected int resolvePredefinedActions(URMPrincipal principal, URMAclAdministrator admin, Map actionMap) throws URMException {
        
        boolean deny = false;
        Set roles = principal.getPossibleRoles();
        Iterator roleIter = roles.iterator();
        // the denyCollection checks if a deny has been set after the pre-defined actions have been resolved in GrantOverDeny mode
        HashMap denyCollection = new HashMap();
        while ((roleIter.hasNext() && !deny)){
            URMRole role = (URMRole) roleIter.next();
            Map preActionMap = role.getPredefinedActions();
            Set preActionNames = preActionMap.keySet();
            Iterator preActionIter = preActionNames.iterator();

            while (preActionIter.hasNext() && !deny) {
                String currentPreActionName  = (String) preActionIter.next();
                Boolean type = (Boolean)preActionMap.get(currentPreActionName);
                // verify pre-defined action semantic (denyOverGrant or grantOverDeny)
                if (denyOverGrant) {
                    if ((currentPreActionName.equals(URMConstants.URM_ALL_ACTION)) && !type.booleanValue()) return URMAclValidator.URMDeny;
                    deny = resolvePredefinedActionsDenyOverGrant(currentPreActionName, admin, type, actionMap);
                } else {
                    if ((currentPreActionName.equals(URMConstants.URM_ALL_ACTION)) && type.booleanValue()) return URMAclValidator.URMPermit;
                    resolvePredefinedActionsGrantOverDeny(currentPreActionName, admin, type, actionMap, denyCollection);
                }
            }
        }
        
        if (!denyOverGrant) {
            // check if a deny has been set after the pre-defined actions have been resolved in GrantOverDeny mode
            if (!verifyGrant(denyCollection)) deny = true;
        }
        if (deny) return URMAclValidator.URMDeny;
        return URMAclValidator.URMIndeterminate;
    }
    
    /**
     * Resolves the predefined inherited actions of a principal
     *
     * @param    action              an URMAction
     * @param    actionMap           a  Map
     *
     * @return   a boolean, true if predefined actions deny access
     *
     * @throws   URMException
     *
     */
    protected boolean resolvePredefinedActionsDenyOverGrant(String action, URMAclAdministrator admin, Boolean type, Map actionMap) throws URMException {
        boolean deny = false;
        URMAction urmAction = admin.getAction(action);
        Set baseActions = urmAction.getBaseActions();
        if (baseActions.isEmpty()) {
            String actionName = urmAction.getName();
            if (actionMap.containsKey(actionName)) {
                actionMap.put(actionName, new Boolean(type.booleanValue()));
                if (!type.booleanValue()) deny = true;
            }
        } else {
            Iterator baseActionIter = baseActions.iterator();
            while (baseActionIter.hasNext() && !deny) {
                deny = resolvePredefinedActionsDenyOverGrant((String) baseActionIter.next(), admin, type, actionMap);
            }
        }
        return deny;
    }
    
   /**
     * Resolves the predefined inherited actions of a principal
     *
     * @param    action              an URMAction
     * @param    actionMap           a  Map
     *
     * @return   a boolean, true if predefined actions deny access
     *
     * @throws   URMException
     *
     */
    protected boolean resolvePredefinedActionsGrantOverDeny(String action, URMAclAdministrator admin, Boolean type, Map actionMap, Map denyCollection) throws URMException {
        
        boolean deny = false;
        URMAction urmAction = admin.getAction(action);
        if (urmAction == null) return deny;
        Set baseActions = urmAction.getBaseActions();
        if (baseActions.isEmpty()) {
            String actionName = urmAction.getName();
            if (actionMap.containsKey(actionName)) {
                if (type.booleanValue()) {
                    actionMap.put(actionName, new Boolean(type.booleanValue()));
                }
                // collect the values of the pre-defined actions
                if (denyCollection.containsKey(actionName)) {
                    Boolean value = (Boolean) denyCollection.get(actionName);
                    if (!value.booleanValue()) denyCollection.put(actionName, new Boolean(type.booleanValue()));
                } else {
                    denyCollection.put(actionName,new Boolean(type.booleanValue()));
                }
            }
        } else {
            Iterator baseActionIter = baseActions.iterator();
            while (baseActionIter.hasNext()) {
                resolvePredefinedActionsGrantOverDeny((String) baseActionIter.next(), admin, type, actionMap, denyCollection);
            }
        }
        return deny;
    }
    
    /**
     * Resolves the predefined actions of a URMRole
     * @return   a boolean, true if predefined actions deny access
     *
     * @throws   URMException
     *
     */
    protected int resolvePredefinedActions(URMRole activerole, URMAclAdministrator admin, Map actionMap) throws URMException {
        
        boolean deny = false;
        
        Set roles = activerole.getAllAncestors();
        roles.add(activerole);
        Iterator roleIter = roles.iterator();
        // the denyCollection checks if a deny has been set after the pre-defined actions have been resolved in GrantOverDeny mode
        HashMap denyCollection = new HashMap();
        while ((roleIter.hasNext() && !deny)){
            URMRole role = (URMRole) roleIter.next();
            Map preActionMap = role.getPredefinedActions();
            Set preActionNames = preActionMap.keySet();
            Iterator preActionIter = preActionNames.iterator();

            while (preActionIter.hasNext() && !deny) {
                String currentPreActionName  = (String) preActionIter.next();
                Boolean type = (Boolean)preActionMap.get(currentPreActionName);
                // verify pre-defined action semantic (denyOverGrant or grantOverDeny)
                if (denyOverGrant) {
                    if ((currentPreActionName.equals(URMConstants.URM_ALL_ACTION)) && !type.booleanValue()) return URMAclValidator.URMDeny;
                    deny = resolvePredefinedActionsDenyOverGrant(currentPreActionName, admin, type, actionMap);
                } else {
                    if ((currentPreActionName.equals(URMConstants.URM_ALL_ACTION)) && type.booleanValue()) return URMAclValidator.URMPermit;
                    resolvePredefinedActionsGrantOverDeny(currentPreActionName, admin, type, actionMap, denyCollection);
                }
            }
        }
        
        if (!denyOverGrant) {
            // check if a deny has been set after the pre-defined actions have been resolved in GrantOverDeny mode
            if (!verifyGrant(denyCollection)) deny = true;
        }
        if (deny) return URMAclValidator.URMDeny;
        return URMAclValidator.URMIndeterminate;
    }
    
    /** returns true if the role is Root
     *
     * @param    role                an URMRole
     *
     * @return   true if the role is URMRoot
     *
     */
    protected boolean isRoot(URMRole role) throws URMException {
        
        boolean isRoot = false;
            // check if role is the URMRoot role
            if ((role.getName()).equals(URMConstants.URM_ROOT_ROLE)) {
                return true;
            } else {
                // if the role does not map URMRoot, go on....
                Set roles = role.getAncestors();
                Iterator roleIter = roles.iterator();
                            
                // check if the role is member of the Subject
                while (roleIter.hasNext() && !isRoot) {
                    URMRole currentRole = (URMRole)roleIter.next();
                    isRoot = isRoot(currentRole);
                }
        }
        return isRoot;
    }
    

}


