/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/security/URMSecurityImpl.java,v 1.4 2004/09/15 14:58:25 pnever Exp $
 * $Revision: 1.4 $
 * $Date: 2004/09/15 14:58:25 $
 *
 * ====================================================================
 *
 * 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;

import com.softwareag.common.instrumentation.logging.Level;
import com.softwareag.common.instrumentation.logging.Logger;
import com.softwareag.common.instrumentation.logging.LoggerFactory;
import com.softwareag.common.instrumentation.logging.LoggerUtil;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import org.apache.slide.common.Namespace;
import org.apache.slide.common.NamespaceConfig;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.SlideToken;
import org.apache.slide.common.SlideTokenWrapper;
import org.apache.slide.common.Uri;
import org.apache.slide.security.SecurityImpl;
import org.apache.slide.structure.ActionNode;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.structure.SubjectNode;
import org.apache.slide.urm.URMException;
import org.apache.slide.urm.accesscontroler.URMAclValidator;
import org.apache.slide.urm.common.URMPrincipal;
import org.apache.slide.util.ClassName;

/**
 * URM-based security helper implementation.
 *
 * @author <a href="mailto:peter.nevermann@softwareag.com">Peter Nevermann</a>
 * @version $Revision: 1.4 $
 */
public final class URMSecurityImpl extends SecurityImpl {

    private static final String LOGNAME = LoggerUtil.getThisClassName();
    private static final String CLASSNAME = new ClassName(LOGNAME).getPlainName();
    private static Logger logger = LoggerFactory.getLogger(LOGNAME);

    private int aclInheritanceType;

    /**
     * Method init
     *
     * @param    namespace           a  Namespace
     * @param    namespaceConfig     a  NamespaceConfig
     */
    public void init(Namespace namespace, NamespaceConfig namespaceConfig) {
        super.init(namespace, namespaceConfig);
        aclInheritanceType = namespaceConfig.getAclInheritanceType();
    }

    /**
     * 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 {

        throw new UnsupportedOperationException(
            "Please use alternate signature: "
                +"hasPermission(SlideToken token, ObjectNode object, ActionNode action)");
    }

    /**
     * Check whether or not an actor (principal) can perform the specified activity
     * on the specified resource.
     *
     * @param    token               a  SlideToken
     * @param    object              Object on which access is tested
     * @param    action              Action which is to be performed
     *
     * @return   true if the action can be performed
     *
     * @throws   ServiceAccessException
     * @throws   ObjectNotFoundException
     */
    public boolean hasPermission(SlideToken token, ObjectNode object, ActionNode action)
        throws ServiceAccessException, ObjectNotFoundException {

        // no check for default action (server intitialization)
        if (action == namespaceConfig.getDefaultAction()) {
            return true;
        }

        boolean result = false;

        try {
            URMPrincipal principal = (URMPrincipal)token.getCredentialsToken().getPrincipal();
            URMAclValidator validator = URMAclValidator.newInstance();
            String actionName = action.getPath().lastSegment();
            String principalName = principal != null ? principal.getName() : SubjectNode.UNAUTHENTICATED_URI;

            List checkList = inheritedAclSet(token, object);
            long timeStart = System.currentTimeMillis();
            int validatorResult = validator.accessCheck( principal, checkList, actionName);

//System.out.println("@@@ ( "+(System.currentTimeMillis()-timeStart)+" ms) URM-Check: "+principalName+", "+actionName+", "+checkList+", result= "+urmresponseToString(validatorResult));

            if( logger.isLoggable(Level.FINE) )
                logger.fine("@@@ ( "+(System.currentTimeMillis()-timeStart)+" ms) URM-Check: "+principalName+", "+actionName+", "+checkList+", result= "+urmresponseToString(validatorResult));


            result = URMAclValidator.URMPermit == validatorResult;
        }
        catch (URMException e) {
            // TODO: which Service to pass?
            throw new ServiceAccessException(null, e);
        }

        return result;
    }


    private String urmresponseToString(int response) {
        switch (response) {
            case URMAclValidator.URMDeny:
                return "URMDeny";
            case URMAclValidator.URMPermit:
                return "URMPermit";
            case URMAclValidator.URMIndeterminate:
                return "URMIndeterminate";
            case URMAclValidator.URMNotApplicable:
                return "URMNotApplicable";
            default:
                return "undefined = ";
        }
    }


    /**
     * Method inheritedAclSet
     *
     * @param    token               a  SlideToken
     * @param    object              an ObjectNode
     *
     * @return   a List of UURIs (String)
     *
     * @throws   ServiceAccessException
     * @throws   ObjectNotFoundException
     */
    public List inheritedAclSet(SlideToken token, ObjectNode object)
        throws ServiceAccessException, ObjectNotFoundException {

        List result = new ArrayList();
        if (token.isForceStoreEnlistment()) {
            // get read-only token
            token = new SlideTokenWrapper(token);
            token.setForceStoreEnlistment(false);
        }

        Uri uri;
        switch (aclInheritanceType) {
            case NamespaceConfig.ACL_INHERIT_TYPE_NONE:
                uri = namespace.getUri(token, object.getUri());
                result.add( uri.getStore().retrieveObject(uri).getUuri() );
                break;
            case NamespaceConfig.ACL_INHERIT_TYPE_ROOT:
                uri = namespace.getUri(token, object.getUri());
                Uri rootUri = namespace.getUri(token, uri.getScope().toString());
                result.add( uri.getStore().retrieveObject(uri).getUuri() );
                result.add( rootUri.getStore().retrieveObject(rootUri).getUuri() );
                break;
            case NamespaceConfig.ACL_INHERIT_TYPE_PATH:
                uri = namespace.getUri(token, object.getUri());
                Enumeration en = uri.getScopes();
                while (en.hasMoreElements()) {
                    Uri element = namespace.getUri(token, (String)en.nextElement(), false);
                    ObjectNode objectNode = element.getStore().retrieveObject(element);
                    result.add(objectNode.getUuri() );  // thanks to Eckehard, the list is expected as inverted ==> performance
                }
                break;
            case NamespaceConfig.ACL_INHERIT_TYPE_FULL:
                //TODO
                break;
            default:
                break;
        }

        return result;
    }
}





