/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.qpid.server.security.access;

import junit.framework.TestCase;

import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.framing.amqp_0_9.ExchangeDeclareBodyImpl;
import org.apache.qpid.framing.amqp_8_0.QueueBindBodyImpl;
import org.apache.qpid.server.exchange.DirectExchange;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.AMQQueueFactory;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.security.access.ACLPlugin.AuthzResult;
import org.apache.qpid.server.virtualhost.VirtualHost;

public class PrincipalPermissionsTest extends TestCase
{

    private String _user = "user";
    private PrincipalPermissions _perms;

    // Common things that are passed to frame constructors
    private AMQShortString _queueName = new AMQShortString(this.getClass().getName()+"queue");
    private AMQShortString _tempQueueName = new AMQShortString(this.getClass().getName()+"tempqueue");
    private AMQShortString _exchangeName = new AMQShortString("amq.direct");
    private AMQShortString _routingKey = new AMQShortString(this.getClass().getName()+"route");
    private int _ticket = 1;
    private FieldTable _arguments = null;
    private boolean _nowait = false;
    private boolean _passive = false;
    private boolean _durable = false;
    private boolean _autoDelete = false;
    private AMQShortString _exchangeType = new AMQShortString("direct");
    private boolean _internal = false;

    private DirectExchange _exchange;
    private VirtualHost _virtualHost;
    private AMQShortString _owner = new AMQShortString(this.getClass().getName()+"owner");
    private AMQQueue _queue;
    private AMQQueue _temporaryQueue;
    private Boolean _temporary = false;
    private Boolean _ownQueue = false;

    @Override
    public void setUp()
    {
        //Highlight that this test will cause a new AR to be created
        ApplicationRegistry.getInstance();

        _perms = new PrincipalPermissions(_user);
        try
        {
            _virtualHost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test");
            _exchange = DirectExchange.TYPE.newInstance(_virtualHost, _exchangeName, _durable, _ticket, _autoDelete);
            _queue = AMQQueueFactory.createAMQQueueImpl(_queueName, false, _owner , false, _virtualHost, _arguments);
            _temporaryQueue = AMQQueueFactory.createAMQQueueImpl(_tempQueueName, false, _owner , true, _virtualHost, _arguments);
        }
        catch (Exception e)
        {
            fail(e.getMessage());
        }
    }

    @Override
    protected void tearDown() throws Exception
    {
        //Ensure we close the opened Registry
        ApplicationRegistry.remove();
    }


    public void testPrincipalPermissions()
    {
        assertNotNull(_perms);
        assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.ACCESS, (Object[]) null));
    }

    // FIXME: test has been disabled since the permissions assume that the user has tried to create
    // the queue first. QPID-1597
    public void disableTestBind() throws Exception
    {
        QueueBindBodyImpl bind = new QueueBindBodyImpl(_ticket, _queueName, _exchangeName, _routingKey, _nowait, _arguments);
        Object[] args = new Object[]{bind, _exchange, _queue, _routingKey};

        assertEquals(AuthzResult.DENIED, _perms.authorise(Permission.BIND, args));
        _perms.grant(Permission.BIND, (Object[]) null);
        assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.BIND, args));
    }

    public void testQueueCreate()
    {
        Object[] grantArgs = new Object[]{_temporary , _queueName, _exchangeName, _routingKey};
        Object[] authArgs = new Object[]{_autoDelete, _queueName};

        assertEquals(AuthzResult.DENIED, _perms.authorise(Permission.CREATEQUEUE, authArgs));
        _perms.grant(Permission.CREATEQUEUE, grantArgs);
        assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.CREATEQUEUE, authArgs));
    }

     public void testQueueCreateWithNullRoutingKey()
    {
        Object[] grantArgs = new Object[]{_temporary , _queueName, _exchangeName, null};
        Object[] authArgs = new Object[]{_autoDelete, _queueName};

        assertEquals(AuthzResult.DENIED, _perms.authorise(Permission.CREATEQUEUE, authArgs));
        _perms.grant(Permission.CREATEQUEUE, grantArgs);
        assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.CREATEQUEUE, authArgs));
    }

    // FIXME disabled, this fails due to grant putting the grant into the wrong map QPID-1598
    public void disableTestExchangeCreate()
    {
        ExchangeDeclareBodyImpl exchangeDeclare =
            new ExchangeDeclareBodyImpl(_ticket, _exchangeName, _exchangeType, _passive, _durable,
                                        _autoDelete, _internal, _nowait, _arguments);
        Object[] authArgs = new Object[]{exchangeDeclare};
        Object[] grantArgs = new Object[]{_exchangeName, _exchangeType};

        assertEquals(AuthzResult.DENIED, _perms.authorise(Permission.CREATEEXCHANGE, authArgs));
        _perms.grant(Permission.CREATEEXCHANGE, grantArgs);
        assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.CREATEEXCHANGE, authArgs));
    }

    public void testConsume()
    {
        Object[] authArgs = new Object[]{_queue};
        Object[] grantArgs = new Object[]{_queueName, _ownQueue};

        /* FIXME: This throws a null pointer exception QPID-1599
         * assertFalse(_perms.authorise(Permission.CONSUME, authArgs));
         */
        _perms.grant(Permission.CONSUME, grantArgs);
        assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.CONSUME, authArgs));
    }

    public void testPublish()
    {
        Object[] authArgs = new Object[]{_exchange, _routingKey};
        Object[] grantArgs = new Object[]{_exchange.getNameShortString(), _routingKey};

        assertEquals(AuthzResult.DENIED, _perms.authorise(Permission.PUBLISH, authArgs));
        _perms.grant(Permission.PUBLISH, grantArgs);
        assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.PUBLISH, authArgs));
    }

    public void testVhostAccess()
    {
        //Tests that granting a user Virtualhost level access allows all authorisation requests
        //where previously they would be denied

        //QPID-2133 createExchange rights currently allow all exchange creation unless rights for creating some
        //specific exchanges are granted. Grant a specific exchange creation to cause all others to be denied.
        Object[] createArgsCreateExchange = new Object[]{new AMQShortString("madeup"), _exchangeType};
        Object[] authArgsCreateExchange = new Object[]{_exchangeName,_exchangeType};
        assertEquals("Exchange creation was not allowed", AuthzResult.ALLOWED, _perms.authorise(Permission.CREATEEXCHANGE, authArgsCreateExchange));
        _perms.grant(Permission.CREATEEXCHANGE, createArgsCreateExchange);

        Object[] authArgsPublish = new Object[]{_exchange, _routingKey};
        Object[] authArgsConsume = new Object[]{_queue};
        Object[] authArgsCreateQueue = new Object[]{_autoDelete, _queueName};
        QueueBindBodyImpl bind = new QueueBindBodyImpl(_ticket, _queueName, _exchangeName, _routingKey, _nowait, _arguments);
        Object[] authArgsBind = new Object[]{bind, _exchange, _queue, _routingKey};

        assertEquals("Exchange creation was not denied", AuthzResult.DENIED, _perms.authorise(Permission.CREATEEXCHANGE, authArgsCreateExchange));
        assertEquals("Publish was not denied", AuthzResult.DENIED, _perms.authorise(Permission.PUBLISH, authArgsPublish));
        assertEquals("Consume creation was not denied", AuthzResult.DENIED, _perms.authorise(Permission.CONSUME, authArgsConsume));
        assertEquals("Queue creation was not denied", AuthzResult.DENIED, _perms.authorise(Permission.CREATEQUEUE, authArgsCreateQueue));
        //BIND pre-grant authorise check disabled due to QPID-1597
        //assertEquals("Binding creation was not denied", AuthzResult.DENIED, _perms.authorise(Permission.BIND, authArgsBind));

        _perms.grant(Permission.ACCESS);

        assertEquals("Exchange creation was not allowed", AuthzResult.ALLOWED, _perms.authorise(Permission.CREATEEXCHANGE, authArgsCreateExchange));
        assertEquals("Publish was not allowed", AuthzResult.ALLOWED, _perms.authorise(Permission.PUBLISH, authArgsPublish));
        assertEquals("Consume creation was not allowed", AuthzResult.ALLOWED, _perms.authorise(Permission.CONSUME, authArgsConsume));
        assertEquals("Queue creation was not allowed", AuthzResult.ALLOWED, _perms.authorise(Permission.CREATEQUEUE, authArgsCreateQueue));
        assertEquals("Binding creation was not allowed", AuthzResult.ALLOWED, _perms.authorise(Permission.BIND, authArgsBind));
    }

    /**
    * If the consume permission for temporary queues is for an unnamed queue then is should
    * be global for any temporary queue but not for any non-temporary queue
    */
    public void testTemporaryUnnamedQueueConsume()
    {
       Object[] authNonTempQArgs = new Object[]{_queue};
       Object[] authTempQArgs = new Object[]{_temporaryQueue};
       Object[] grantArgs = new Object[]{true};

       _perms.grant(Permission.CONSUME, grantArgs);

       //Next line shows up bug - non temp queue should be denied
       assertEquals(AuthzResult.DENIED, _perms.authorise(Permission.CONSUME, authNonTempQArgs));
       assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.CONSUME, authTempQArgs));
    }

    /**
    * Test that temporary queue permissions before queue perms in the ACL config work correctly
    */
    public void testTemporaryQueueFirstConsume()
    {
       Object[] authNonTempQArgs = new Object[]{_queue};
       Object[] authTempQArgs = new Object[]{_temporaryQueue};
       Object[] grantArgs = new Object[]{true};
       Object[] grantNonTempQArgs = new Object[]{_queueName, _ownQueue};

       //should not matter if the temporary permission is processed first or last
       _perms.grant(Permission.CONSUME, grantNonTempQArgs);
       _perms.grant(Permission.CONSUME, grantArgs);

       assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.CONSUME, authNonTempQArgs));
       assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.CONSUME, authTempQArgs));
    }

    /**
    * Test that temporary queue permissions after queue perms in the ACL config work correctly
    */
    public void testTemporaryQueueLastConsume()
    {
       Object[] authNonTempQArgs = new Object[]{_queue};
       Object[] authTempQArgs = new Object[]{_temporaryQueue};
       Object[] grantArgs = new Object[]{true};
       Object[] grantNonTempQArgs = new Object[]{_queueName, _ownQueue};

       //should not matter if the temporary permission is processed first or last
       _perms.grant(Permission.CONSUME, grantArgs);
       _perms.grant(Permission.CONSUME, grantNonTempQArgs);

       assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.CONSUME, authNonTempQArgs));
       assertEquals(AuthzResult.ALLOWED, _perms.authorise(Permission.CONSUME, authTempQArgs));
    }

}
