/*
 * $Header: /home/cvspublic/jakarta-slide/src/share/org/apache/slide/security/SecurityImplAllGrant.java,v 1.9 2004/08/05 15:44:56 unico Exp $
 * $Revision: 1.9 $
 * $Date: 2004/08/05 15:44:56 $
 *
 * ====================================================================
 *
 * Copyright 1999-2002 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.security;

import java.util.Enumeration;

import org.apache.slide.common.Namespace;
import org.apache.slide.common.NamespaceConfig;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.Uri;
import org.apache.slide.structure.ActionNode;
import org.apache.slide.structure.LinkNode;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.structure.SubjectNode;
import org.apache.slide.util.logger.Logger;

/**
 * Security helper.
 *
 * @version $Revision: 1.9 $
 */
public final class SecurityImplAllGrant extends SecurityImpl implements Security {
    
    
    protected static final String LOG_CHANNEL = SecurityImplAllGrant.class.getName();

    // ----------------------------------------------------------- Constructors
    
    /**
     * Constructor.
     */
    public SecurityImplAllGrant() {
        super();
    }
    /**
     * Constructor.
     *
     * @param namespace Namespace
     * @param namespaceConfig Namespace configuration
     */
    public SecurityImplAllGrant(Namespace namespace, NamespaceConfig namespaceConfig) {
        super(namespace, namespaceConfig);
    }
    
    
    /**
     * Check whether or not an actor can perform the specified activity
     * on a collection.
     *
     * @param object Object on which access is tested
     * @param subject Subject who seeks to perform the action
     * @param action Action which is to be performed
     * @return true if the action can be performed
     * @exception ServiceAccessException DataSource access error
     * @exception ObjectNotFoundException Specified object was not found
     * in the DataSource
     */
    public boolean hasPermission(ObjectNode object, SubjectNode subject,
                                 ActionNode action)
        throws ServiceAccessException, ObjectNotFoundException {
        
        // no check for default action (server intitialization)
        if (action.equals(ActionNode.DEFAULT)) {
            return true;
        }
        
        boolean granted = false;
        boolean denied = false;
        boolean rootObjectReached = false;
        
        ObjectNode courObject = object;
        
        Uri subjectUri = namespace.getUri(subject.getUri());
        Uri actionUri = namespace.getUri(action.getUri());
        
        // check if allready granded
        
        while (!granted && !denied && !rootObjectReached) {
            
            Uri courUri = namespace.getUri(courObject.getUri());
            Enumeration permissions = courUri.getStore()
                .enumeratePermissions(courUri);
            
            while (!granted && !denied && permissions.hasMoreElements()) {
                
                boolean oldGranted = granted;
                boolean oldDenied = denied;
                
                NodePermission permission =
                    (NodePermission) permissions.nextElement();
                String permissionSubject = permission.getSubjectUri();

                if (permissionSubject.equals(SubjectNode.SELF_URI)) {
                    boolean check;
                    check = object.getUri().equals(subjectUri.toString());
                    if (permission.isInheritable()) {
                        String subjectUriString = subjectUri.toString();
                        if(!subjectUriString.endsWith("/"))
                            subjectUriString = subjectUriString + "/";
                        
                        check |= object.getUri().startsWith(subjectUriString);
                    }
                    
                    // Self permission
                    granted = (!permission.isNegative())
                        && (check)
                        && (actionUri.toString()
                                .startsWith(permission.getActionUri()));
                    denied = (permission.isNegative())
                        && (check)
                        && (actionUri.toString()
                                .startsWith(permission.getActionUri()));
                    
                } else if (permission.isInheritable()
                           || permission.getObjectUri().equals(object.getUri())) {
                    
                    if (permissionSubject.startsWith("/") || permissionSubject.equals(SubjectNode.ALL_URI)) {
                        
                        // Node permission
                        String permSubj = permission.getSubjectUri();
                        String permActn = permission.getActionUri();
                        boolean match = false;
                        if (permSubj.equals(SubjectNode.ALL_URI)) {
                            match = true;
                        }
                        else {
                            if(!permSubj.endsWith("/"))
                                permSubj = permSubj + "/";
                            match = subjectUri.toString().
                                equals(permission.getSubjectUri()) ||
                                subjectUri.toString().startsWith(permSubj);
                        }
                        if (permActn.equals(ActionNode.ALL_URI)) {
                            match &= true;
                        }
                        else {
                            match &= actionUri.toString().
                                startsWith(permActn);
                        }
                        
                        granted = (!permission.isNegative()) && match;
                        denied = permission.isNegative() && match;
                        
                    } else if (permissionSubject.startsWith("+")) {
                        
                        // Permission group which needs to be expanded
                        Uri permissionSubjectUri =
                            namespace.getUri(permissionSubject.substring(1));
                        ObjectNode group;
                        try {
                            group = permissionSubjectUri.getStore().retrieveObject(permissionSubjectUri);
                        } catch (ObjectNotFoundException onfe) {
                            namespace.getLogger().log(
                                "Gracefully ignoring permission of dangling subject " + permissionSubjectUri,
                                onfe,
                                LOG_CHANNEL,
                                Logger.WARNING);
                            // as a fix of bug #27018, gracefully ignore permissions having dangling subjects 
                            continue;
                        }
                        // if the node is a GroupNode, expand it out to
                        // normal permissions
                        if (group instanceof
                            org.apache.slide.structure.GroupNode ) {
                            if (group.hasChildren()) {
                                Enumeration groupMembers =
                                    group.enumerateChildren();
                                // parse thru the children of the group and
                                // check permissions on each
                                while (groupMembers.hasMoreElements()) {
                                    
                                    oldGranted = granted;
                                    oldDenied = denied;
                                    
                                    Uri childUri =
                                        namespace.getUri
                                        ((String) groupMembers.nextElement());
                                    ObjectNode childNode;
                                    try {
                                        childNode = childUri.getStore().retrieveObject(childUri);
                                    } catch (ObjectNotFoundException onfe) {
                                        namespace.getLogger().log(
                                            "Gracefully ignoring permission of dangling subject "
                                                + childUri,
                                            onfe,
                                            LOG_CHANNEL,
                                            Logger.WARNING);
                                        // as a fix of bug #27018, gracefully ignore permissions having dangling subjects 
                                        continue;
                                    }
                                    String childSubjectUri = childNode
                                        instanceof LinkNode ?
                                        ((LinkNode) childNode)
                                        .getLinkedUri() :
                                        childNode.getUri() ;
                                    
                                    String testUri;
                                    if(!childSubjectUri.endsWith("/"))
                                        testUri = childSubjectUri+"/";
                                    else
                                        testUri = childSubjectUri;
                                    
                                    boolean match = subjectUri.toString().
                                        equals(childSubjectUri) ||
                                        subjectUri.toString().
                                        startsWith(testUri);
                                    match &= actionUri.toString().
                                        startsWith(permission.getActionUri());
                                    
                                    granted = (!permission.isNegative()) &&
                                        match;
                                    denied = permission.isNegative() && match;
                                    
                                    granted = granted | oldGranted;
                                    denied = denied | oldDenied;
                                    
                                }
                            }
                        }
                        
                    } else {
                        
                        // Role permission
                        granted = (!permission.isNegative())
                            && (hasRole(subject, permissionSubject))
                            && (actionUri.toString()
                                    .startsWith(permission.getActionUri()));
                        denied = (permission.isNegative())
                            && (hasRole(subject, permissionSubject))
                            && (actionUri.toString()
                                    .startsWith(permission.getActionUri()));
                        
                    }
                    
                }
                
                granted = granted | oldGranted;
                denied = denied | oldDenied;
                
            }
            
            Uri parentUri = courUri.getParentUri();
            
            if (parentUri != null) {
                courObject = parentUri.getStore()
                    .retrieveObject(parentUri);
            } else {
                rootObjectReached = true;
            }
        }
        
        // Negative permissions have priority (if they're defined on the same
        // node)
        if (denied) {
            return false;
        }
        
        if (!granted) {
            return false;
        }
        
        return true;
        
    }
}
