Merge "Rewrite Patrole README to be high-level document"
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
new file mode 100644
index 0000000..550d2ce
--- /dev/null
+++ b/doc/source/configuration.rst
@@ -0,0 +1,73 @@
+.. _patrole-configuration:
+
+Patrole Configuration Guide
+===========================
+
+Patrole can be customized by updating Tempest's ``tempest.conf`` configuration
+file. All Patrole-specific configuration options should be included under
+the ``rbac`` group.
+
+RBAC Test Role
+--------------
+
+The RBAC test role governs which role is used when running Patrole tests. For
+example, setting ``rbac_test_role`` to "admin" will execute all RBAC tests
+using admin credentials. Changing the ``rbac_test_role`` value will `override`
+Tempest's primary credentials to use that role.
+
+This implies that, if ``rbac_test_role`` is "admin", regardless of the Tempest
+credentials used by a client, the client will be calling APIs using the admin
+role. That is, ``self.os_primary.servers_client`` will run as though it were
+``self.os_admin.servers_client``.
+
+Similarly, setting ``rbac_test_role`` to a non-admin role results in Tempest's
+primary credentials being overriden by the role specified by
+``rbac_test_role``.
+
+.. note::
+
+    Only the role of the primary Tempest credentials ("os_primary") is
+    modified. The ``user_id`` and ``project_id`` remain unchanged.
+
+Enable RBAC
+-----------
+
+Given the value of ``enable_rbac``, enables or disables Patrole tests. If
+``enable_rbac`` is ``False``, then Patrole tests are skipped.
+
+Strict Policy Check
+-------------------
+
+Currently, many services define their "default" rule to be "anyone allowed".
+If a policy action is not explicitly defined in a policy file, then
+``oslo.policy`` will fall back to the "default" rule. This implies that if
+there's a typo in a policy action specified in a Patrole test, ``oslo.policy``
+can report that the ``rbac_test_role`` will be able to perform the
+non-existent policy action. For a testing framework, this is undesirable
+behavior.
+
+Hence, ``strict_policy_check``, if ``True``, will throw an error in the event
+that a non-existent or bogus policy action is passed to a Patrole test. If
+``False``, however, a ``self.skipException`` will be raised.
+
+Custom Policy Files
+-------------------
+
+Patrole supports testing custom policy file definitions, along with default
+policy definitions. Default policy definitions are used if custom file
+definitions are not specified. If both are specified, the custom policy
+definition takes precedence (that is, replaces the default definition,
+as this is the default behavior in OpenStack).
+
+The ``custom_policy_files`` option allows a user to specify a comma-separated
+list of custom policy file locations that are on the same host as Patrole.
+Each policy file must include the name of the service that is being tested:
+for example, if "compute" tests are executed, then Patrole will use the first
+policy file contained in ``custom_policy_files`` that contains the "nova"
+keyword.
+
+.. note::
+
+    Patrole currently does not support policy files located on a host different
+    than the one on which it is running.
+..
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 35e8439..f58ee7f 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -1,24 +1,30 @@
-.. patrole documentation master file, created by
-   sphinx-quickstart on Tue Jul  9 22:26:36 2013.
-   You can adapt this file completely to your liking, but it should at least
-   contain the root `toctree` directive.
+========================================
+Patrole: Tempest Plugin for RBAC Testing
+========================================
 
-Patrole - an OpenStack Tempest Plugin for RBAC Testing
-========================================================
+User's Guide
+============
 
-Contents:
+Patrole Configuration Guide
+---------------------------
 
 .. toctree::
    :maxdepth: 2
 
-   readme
    installation
+   configuration
    usage
+   sampleconf
+
+Developer's Guide
+=================
+
+.. toctree::
+   :maxdepth: 2
+
    contributing
 
 Indices and tables
 ==================
 
-* :ref:`genindex`
-* :ref:`modindex`
 * :ref:`search`
diff --git a/doc/source/installation.rst b/doc/source/installation.rst
index 31f94f4..c244152 100644
--- a/doc/source/installation.rst
+++ b/doc/source/installation.rst
@@ -1,9 +1,11 @@
-============
-Installation
-============
+.. _patrole-installation:
 
-Installation Information
-========================
+==========================
+Patrole Installation Guide
+==========================
+
+Manual Installation Information
+===============================
 
 At the command line::
 
@@ -30,45 +32,3 @@
     ...
 
     enable_plugin patrole git://git.openstack.org/openstack/patrole
-
-Configuration Information
-=========================
-
-tempest.conf
-++++++++++++
-
-To run the RBAC tempest api test, you have to make the following changes to
-the tempest.conf file.
-
-#. ``auth`` section updates ::
-
-    # Allows test cases to create/destroy projects and users. This option
-    # requires that OpenStack Identity API admin credentials are known. If
-    # false, isolated test cases and parallel execution, can still be
-    # achieved configuring a list of test accounts (boolean value)
-    use_dynamic_credentials = True
-
-#. ``rbac`` section updates ::
-
-    # The role that you want the RBAC tests to use for RBAC testing
-    # This needs to be edited to run the test as a different role.
-    rbac_test_role = _member_
-
-    # Enables RBAC Tempest tests if set to True. Otherwise, they are
-    # skipped.
-    enable_rbac = True
-
-    # If set to true, tests throw a RbacParsingException for policies
-    # not found in the policy.json. Otherwise, they throw a
-    # skipException.
-    strict_policy_check = False
-
-    # The following config options set the location of the service's
-    # policy file. For services that have their policy in code (e.g.,
-    # Nova), this would be the location of a custom policy.json, if
-    # one exists.
-    cinder_policy_file = /etc/cinder/policy.json
-    glance_policy_file = /etc/glance/policy.json
-    keystone_policy_file = /etc/keystone/policy.json
-    neutron_policy_file = /etc/neutron/policy.json
-    nova_policy_file = /etc/nova/policy.json
diff --git a/doc/source/readme.rst b/doc/source/readme.rst
deleted file mode 100644
index a6210d3..0000000
--- a/doc/source/readme.rst
+++ /dev/null
@@ -1 +0,0 @@
-.. include:: ../../README.rst
diff --git a/doc/source/sampleconf.rst b/doc/source/sampleconf.rst
new file mode 100644
index 0000000..c262f2d
--- /dev/null
+++ b/doc/source/sampleconf.rst
@@ -0,0 +1,51 @@
+.. _patrole-sampleconf:
+
+Sample Configuration File
+==========================
+
+The following is a sample Patrole configuration for adaptation and use.
+
+.. code-block:: ini
+
+    [rbac]
+
+    # The role that you want the RBAC tests to use for RBAC testing
+    # This needs to be edited to run the test as a different role.
+    rbac_test_role = Member
+
+    # Enables RBAC Tempest tests if set to True. Otherwise, they are
+    # skipped.
+    enable_rbac = True
+
+    # If set to True, tests throw a RbacParsingException for policies
+    # not found in the policy file. Otherwise, they throw a skipException.
+    strict_policy_check = False
+
+    # List of the paths to search for policy files. Each policy path assumes that
+    # the service name is included in the path once. Also assumes Patrole is on the
+    # same host as the policy files. The paths should be ordered by precedence,
+    # with high-priority paths before low-priority paths. The first path that is
+    # found to contain the service's policy file will be used.
+    custom_policy_files = /etc/nova/policy.json,/etc/neutron/policy.json
+
+    # This option determines whether Patrole should run against a
+    # `custom_requirements_file` which defines RBAC requirements. The
+    # purpose of setting this flag to True is to verify that RBAC policy
+    # is in accordance to requirements. The idea is that the
+    # `custom_requirements_file` perfectly defines what the RBAC requirements
+    # are.
+    test_custom_requirements = False
+
+    File path of the yaml file that defines your RBAC requirements. This
+    # file must be located on the same host that Patrole runs on. The yaml
+    # file should be written as follows:
+    custom_requirements_file = patrole/requirements.txt
+
+    # DEPRECATED: The following config options set the location of the service's
+    # policy file. For services that have their policy in code (e.g., Nova),
+    # this would be the location of a custom policy.json, if one exists.
+    cinder_policy_file = /etc/cinder/policy.json
+    glance_policy_file = /etc/glance/policy.json
+    keystone_policy_file = /etc/keystone/policy.json
+    neutron_policy_file = /etc/neutron/policy.json
+    nova_policy_file = /etc/nova/policy.json
diff --git a/doc/source/usage.rst b/doc/source/usage.rst
index d2570bc..7470e9e 100644
--- a/doc/source/usage.rst
+++ b/doc/source/usage.rst
@@ -1,4 +1,4 @@
-..
+.. _patrole-usage:
 
 ========
 Usage
diff --git a/patrole_tempest_plugin/tests/api/identity/rbac_base.py b/patrole_tempest_plugin/tests/api/identity/rbac_base.py
index 1ed081e..e8c402e 100644
--- a/patrole_tempest_plugin/tests/api/identity/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/identity/rbac_base.py
@@ -243,6 +243,7 @@
         cls.trusts_client = cls.os_primary.trusts_client
         cls.users_client = cls.os_primary.users_v3_client
         cls.oauth_token_client = cls.os_primary.oauth_token_client
+        cls.token_client = cls.os_primary.token_v3_client
 
     @classmethod
     def resource_setup(cls):
@@ -254,6 +255,7 @@
         cls.projects = []
         cls.regions = []
         cls.trusts = []
+        cls.tokens = []
 
     @classmethod
     def resource_cleanup(cls):
@@ -289,6 +291,10 @@
             test_utils.call_and_ignore_notfound_exc(
                 cls.trusts_client.delete_trust, trust['id'])
 
+        for token in cls.tokens:
+            test_utils.call_and_ignore_notfound_exc(
+                cls.identity_client.delete_token, token)
+
         super(BaseIdentityV3RbacTest, cls).resource_cleanup()
 
     @classmethod
@@ -375,3 +381,12 @@
         cls.trusts.append(trust)
 
         return trust
+
+    @classmethod
+    def setup_test_token(cls, user_id, password):
+        """Set up a test token."""
+        token = cls.token_client.auth(user_id=user_id,
+                                      password=password).response
+        token_id = token['x-subject-token']
+        cls.tokens.append(token_id)
+        return token_id
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_tokens_negative_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_tokens_negative_rbac.py
new file mode 100644
index 0000000..1ab296a
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_tokens_negative_rbac.py
@@ -0,0 +1,100 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+#    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.
+
+from tempest import config
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+from tempest import test
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.identity import rbac_base
+
+CONF = config.CONF
+
+
+class IdentityTokenV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
+
+    credentials = ['primary', 'admin', 'alt']
+
+    @classmethod
+    def skip_checks(cls):
+        super(IdentityTokenV3RbacTest, cls).skip_checks()
+        # In case of admin, the positive testcase would be used, hence
+        # skipping negative testcase
+        if CONF.rbac.rbac_test_role == CONF.identity.admin_role:
+            raise cls.skipException(
+                "Skipped as admin role doesn't require negative testing")
+
+    def _setup_alt_token(self):
+        return self.setup_test_token(
+            self.os_alt.auth_provider.credentials.user_id,
+            self.os_alt.auth_provider.credentials.password)
+
+    @decorators.idempotent_id('c83c8f1a-79cb-4dc4-b55f-c7d2bfd98b1e')
+    @test.attr(type=['negative'])
+    @rbac_rule_validation.action(
+        service="keystone",
+        rule="identity:validate_token",
+        extra_target_data={
+            "target.token.user_id":
+            "os_alt.auth_provider.credentials.user_id"
+        })
+    def test_show_token_negative(self):
+        # Explicit negative test for identity:validate_token policy action.
+        # Assert expected exception is Forbidden and then reraise it.
+        alt_token_id = self._setup_alt_token()
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        e = self.assertRaises(lib_exc.Forbidden,
+                              self.identity_client.show_token,
+                              alt_token_id)
+        raise e
+
+    @decorators.idempotent_id('2786a55d-a818-433a-af7a-41ebf72ab4da')
+    @test.attr(type=['negative'])
+    @rbac_rule_validation.action(
+        service="keystone",
+        rule="identity:revoke_token",
+        extra_target_data={
+            "target.token.user_id":
+            "os_alt.auth_provider.credentials.user_id"
+        })
+    def test_delete_token_negative(self):
+        # Explicit negative test for identity:revoke_token policy action.
+        # Assert expected exception is Forbidden and then reraise it.
+        alt_token_id = self._setup_alt_token()
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        e = self.assertRaises(lib_exc.Forbidden,
+                              self.identity_client.delete_token,
+                              alt_token_id)
+        raise e
+
+    @decorators.idempotent_id('1ea02ac0-9a96-44bd-bdc3-4dae3c10cc2e')
+    @test.attr(type=['negative'])
+    @rbac_rule_validation.action(
+        service="keystone",
+        rule="identity:check_token",
+        extra_target_data={
+            "target.token.user_id":
+            "os_alt.auth_provider.credentials.user_id"
+        })
+    def test_check_token_existence_negative(self):
+        # Explicit negative test for identity:check_token policy action.
+        # Assert expected exception is Forbidden and then reraise it.
+        alt_token_id = self._setup_alt_token()
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        e = self.assertRaises(lib_exc.Forbidden,
+                              self.identity_client.check_token_existence,
+                              alt_token_id)
+        raise e
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_tokens_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_tokens_rbac.py
new file mode 100644
index 0000000..e6d0dd1
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_tokens_rbac.py
@@ -0,0 +1,67 @@
+# Copyright 2017 AT&T Corporation.
+# All Rights Reserved.
+#
+#    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.
+
+from tempest.lib import decorators
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.identity import rbac_base
+
+
+class IdentityTokenV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
+
+    @classmethod
+    def resource_setup(cls):
+        super(IdentityTokenV3RbacTest, cls).resource_setup()
+        cls.user_id = cls.os_primary.auth_provider.credentials.user_id
+        cls.password = cls.os_primary.auth_provider.credentials.password
+
+    @decorators.idempotent_id('201e2fe5-2023-4bce-9189-78b51520a91e')
+    @rbac_rule_validation.action(
+        service="keystone",
+        rule="identity:validate_token",
+        extra_target_data={
+            "target.token.user_id":
+            "os_primary.auth_provider.credentials.user_id"
+        })
+    def test_show_token(self):
+        token_id = self.setup_test_token(self.user_id, self.password)
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.identity_client.show_token(token_id)
+
+    @decorators.idempotent_id('42a299db-fe0a-4ea0-9824-0bfd13155886')
+    @rbac_rule_validation.action(
+        service="keystone",
+        rule="identity:revoke_token",
+        extra_target_data={
+            "target.token.user_id":
+            "os_primary.auth_provider.credentials.user_id"
+        })
+    def test_delete_token(self):
+        token_id = self.setup_test_token(self.user_id, self.password)
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.identity_client.delete_token(token_id)
+
+    @decorators.idempotent_id('3554d218-8cd6-4730-a1b2-0e22f9b78f45')
+    @rbac_rule_validation.action(
+        service="keystone",
+        rule="identity:check_token",
+        extra_target_data={
+            "target.token.user_id":
+            "os_primary.auth_provider.credentials.user_id"
+        })
+    def test_check_token_exsitence(self):
+        token_id = self.setup_test_token(self.user_id, self.password)
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.identity_client.check_token_existence(token_id)
diff --git a/releasenotes/notes/test_tokens_rbac-63a93e507d079a03.yaml b/releasenotes/notes/test_tokens_rbac-63a93e507d079a03.yaml
new file mode 100644
index 0000000..da285eb
--- /dev/null
+++ b/releasenotes/notes/test_tokens_rbac-63a93e507d079a03.yaml
@@ -0,0 +1,3 @@
+---
+features:
+  - Added RBAC test scenarios for the token-related v3 identity API