//
// Copyright (c) 2016-2017 Eclipse Microprofile Contributors:
// Red Hat
//
// 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.
//

= Overview of the TCK

The MicroProfile JWT(MP-JWT) TCK consists of tests that validate the authentication and authorization of JAX-RS
application endpoints secured using an MP-JWT authentication mechanism that both authenticates and
authorizes access based on an MP-JWT token passed in via the HTTP Authorization header as a bearer
token. There are 4 categories of unit tests in the base required TCK:

1. Tests that validate the basic behavior of the MP-JWT JsonWebToken implementation independent of any
JAX-RS application. Tests in this category are marked with the TestNG groups of "jwt" and "utils".
2. Tests that validate the authentication and authorization of JAX-RS application endpoints
secured using `@RolesAllowed` annotations. This includes testing the requirements for rejecting MP-JWT
tokens. Tests in this category are marked with the TestNG group "jaxrs".
3. Tests that validate the injection of `o.e.m.j.ClaimValue`, `javax.json.JsonValue` and `javax.inject.Provider` wrappers
for the JsonWebToken claim values. Tests in this category are marked with the TestNG groups of "cdi", "cdi-json"
and "cdi-provider".
4. Tests that validate the use of the MicroProfile configuration feature to externalize the information necessariy to verify the MP-JWT signer and optional iss claim.

The TestNG testsuite for these categories of tests can be found in the tck directory root as tck-base-suite.xml.

== Optional Java EE Container Integration Tests
In addition to the categories of tests defined above, there are optional tests that illustrate the expected container API
integration for Java EE container runtimes beyond JAX-RS. These tests are marked with the TestNG groups "ejb-optional",
"jacc-optional", "servlet-optional" and "ee-security-optional". An MP-JWT implementation is not required to pass these tests
in order to be considered a valid implementation. The TestNG testsuite for these categories + the base categories of tests can
be found in the tck directory root as tck-full-suite.xml.

== TCK Resource Files and Information

The TCK includes resources for the the MP-JWT token payload content as well as test issuer public and private RSA keys. These
are used by the unit tests along with the `org.eclipse.microprofile.jwt.tck.util.TokenUtils` class to generate both valid and
invalid tokens. A summary of the tck/resources directory contents is:

* META-INF/microprofile-config.properties
** A base MicroProfile config properties file for the org.eclipse.microprofile.jwt.tck.config.* package tests.
* jwt-content1.json
* testJWTCallerPrincipal.json
* usePreferredName.json
* useSubject.json
** Used by the `TokenUtilsTest` to validate the MP-JWT implementation under test JsonWebToken implementation.
* Token1.json
* Token2.json
** Used by the various JAX-RS and CDI tests
* privateKey.pem
** The test issuer private key used to sign the MP-JWT tokens generated by the org.eclipse.microprofile.jwt.tck.container.jaxrs.* package tests.
* privateKey4k.pem
** The test issuer private key used to sign the MP-JWT tokens generated by the org.eclipse.microprofile.jwt.tck.config.* package tests.
* publicKey.pem
** The test issuer public key that MP-JWT implementations under test use to validate the token signature in the org.eclipse.microprofile.jwt.tck.container.jaxrs.* package tests.
* publicKey4k.pem
** The test issuer public key that MP-JWT implementations under test use to validate the token signature in the org.eclipse.microprofile.jwt.tck.config.* package tests.
* RequiredClaims.json
** Used by the RequiredClaimsTest to generate a MP-JWT with the minimum required claims.
* TokenBadIss.json
** Used by the IssNoValidationBadIssTest to validate that the iss claim is
ignored when validation is disabled.
* signer-key4k.jwk
** A JWK representation of the signer public key used by some of the org.eclipse.microprofile.jwt.tck.config.* package tests.
* signer-keyset4k.jwk
** A JWKS representation of the signer public key used by some of the org.eclipse.microprofile.jwt.tck.config.* package tests.

The test issuer value used in all valid test MP-JWT tokens as the `iss` claim value is "https://server.example.com".

The test issuer public key is the tck/resources/publicKey.pem file. It is included in every test WebArchive artifact 
as an archive classpath resource at the location /WEB-INF/classes/publicKey.pem.

The generated test JWT has its `exp`, `iat` and `auth_time` claims are set to the current time when the token is generated,
as the number of seconds from 1970-01-01T00:00:00Z UTC.

An example of how this information can be used to verify a JWT using the Nimbus library used by the TCK to
generate the test JWTs can be found in the org.eclipse.microprofile.jwt.tck.parsing.DebugTest#testNimbus* methods.

= Running the MicroProfile JWT Auth TCK

The TCK is designed around a set of Arquillian based unit tests that require
the MP-JWT implementation under test to provide a TCK harness
artifact that provides an `org.jboss.arquillian.core.spi.LoadableExtension` that
installs a `org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor`
to augment the base `org.jboss.shrinkwrap.api.spec.WebArchive` with the
implementation specific artifacts, descriptors, libraries, etc. needed for
the implementation to properly deploy the test web archive.

There are base implementations of the `LoadableExtension` and `ApplicationArchiveProcessor`
that can be used for strait-forward augmentation scenarios, but you can always
provide your own implementations. An example of the former is:
https://github.com/MicroProfileJWT/wfswarm-jwt-auth-tck-viabase

while an example of the latter is:
https://github.com/MicroProfileJWT/wfswarm-jwt-auth-tck


== Creating Your Implementation TCK Harness Artifact
As described, you need to create an artifact that bundles a LoadableExtension
using a Java ServiceProvider that installs an ApplicationArchiveProcessor that
augments the base TCK test web application archive with the implementation specific
configuration and dependencies needed to successfully deploy and test the web
application with MP-JWT authentication enabled.

An example skeleton pom.xml is shown here:

[source,maven]
----
<?xml version="1.0" encoding="UTF-8"?>
<!--
    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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>my.groupID</groupId>
    <artifactId>jwt-auth-tck</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>MicroProfile JWT Auth TCK Harness MyCoolMP Implementation</name>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <version.wildfly.swarm>2017.7.0</version.wildfly.swarm>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.jboss.arquillian</groupId>
                <artifactId>arquillian-bom</artifactId>
                <version>1.1.13.Final</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- This is the MP-JWT TCK base extension and utility classes --><1>
        <dependency>
            <groupId>org.eclipse.microprofile.jwt</groupId>
            <artifactId>microprofile-jwt-auth-tck</artifactId>
            <version>1.1-SNAPSHOT</version>
        </dependency>
        <!-- This is the actual MP-JWT TCK test classes --><2>
        <dependency>
            <groupId>org.eclipse.microprofile.jwt</groupId>
            <artifactId>microprofile-jwt-auth-tck</artifactId>
            <version>1.1-SNAPSHOT</version>
            <type>test-jar</type>
            <scope>test</scope>
        </dependency>
        <!-- Arquillian extension SPI --><3>
        <dependency>
            <groupId>org.jboss.arquillian.container</groupId>
            <artifactId>arquillian-container-spi</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jboss.arquillian.container</groupId>
            <artifactId>arquillian-container-test-spi</artifactId>
        </dependency>
        <!-- You need to specify your JAX-RS client implementation as the unit
        tests make use of that API, but do not specify the implementation.
        --><4>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-client</artifactId>
            <version>3.1.1.Final</version>
        </dependency>

        <!-- Specify your container runtime arquillian integration and dependencies -->
        <dependency><5>
            <groupId>MY_GROUP</groupId>
            <artifactId>arquillian-container</artifactId>
            <version>${container-version}</version>
        </dependency>
        ...
    </dependencies>

...

</project>
----
<1> org.eclipse.microprofile.jwt:microprofile-jwt-auth-tck is the MP-JWT artifact
that contains the base `LoadableExtension` and `ApplicationArchiveProcessor` classes and `TokenUtils` class.
<2> org.eclipse.microprofile.jwt:microprofile-jwt-auth-tck type=test-jar is the MP-JWT
TCK tests themselves you need to run for the TCK. You would only need this if you are
running the TCK from within your TCK harness project.
<3> The 2 indicated Arquillian extension SPI dependencies provide the
`LoadableExtension` and `ApplicationArchiveProcessor` interfaces and dependent
classes.
<4> The TCK unit tests make use of the JAX-RS client API, but does not provide an
implementation, so your TCK harness artifact must specify what implementation to use.
Here the Resteasy implementation is being specified.
<5> Lastly, you must specify the property Arquillian container runtime that is
approriate for you MP-JWT implementation, along with whatever container
runtime dependencies are required.

== What Your TCK Harness Needs to Configure
The primary elements that need to be configured in the MP-JWT runtime are the security domain settings.
This will entail the installation of the MP-JWT authentication mechanism, the token validation settings,
and a group to role mapping.

=== Role Mapping
Some tests in the "ee-security-optional" group require a mapping from a group name in the "groups" claim of the
MP-JWT token to a custom role name used in an endpoint @RolesAllowed statement. The following table lists the
group names and the role mappings that are expected in the TCK unit tests. This includes the required one-to-one
mapping of the group name:

Echoer:: Echoer
Tester:: Tester
Token2Role:: Token2Role
group1:: group1, Group1MappedRole

An example of how a TCK harness implementation that is based on Glassfish might perform the group1 required mappings via  
is shown in the following glassfish-web.xml descriptor that the TCK harness would add to the test WebArchive in it's
ApplicationArchiveProcessor:
[source,xml]
-----
<glassfish-web-app>
    ...
    <security-role-mapping>
        <role-name>group1</role-name>
        <group-name>group1</group-name>
    </security-role-mapping>

    <security-role-mapping>
        <role-name>group1</role-name>
        <group-name>Group1MappedRole</group-name>
    </security-role-mapping>
    ...
</glassfish-web-app>
-----

=== Equivalent Security Constraints
This section describes the equivalent web.xml style of security constraints that are 
expected for the various TCK deployments.

ClaimValueInjectionTest::
    * url-pattern: /endp/*
    * role-name: Echoer
    * role-name: Tester
InvalidTokenTest::
    * url-pattern: /endp/*
    * role-name: Echoer
JsonValueInjectionTest::
    * url-pattern: /endp/*
    * role-name: Echoer
    * role-name: Tester
ProviderValueInjectionTest::
    * url-pattern: /endp/*
    * role-name: Echoer
    * role-name: Tester
RequiredClaimsEndpoint::
    * url-pattern: /endp/*
    * role-name: Tester
RolesAllowedTest::
    * url-pattern: /endp/echo
    * role-name: Echoer
    * url-pattern: /endp/echo2
    * role-name: NoSuchUser - This role is not granted to any test token
    * url-pattern/endp/getPrincipalClass
    * url-pattern/endp/checkIsUserInRole
    * url-pattern/endp/getInjectedPrincipal
    * role-name: Echoer
    * url-pattern: /endp/needsGroup1Mapping
    * role-name: Group1MappedRole - This role needs to be mapped to the token group1 group
    * url-pattern/endp/echoNeedsToken2Role
    * role-name: Token2Role
UnsecuredPingTest::
    No authentication required

=== Information Available to the Harness ApplicationArchiveProcessor
The TCK harness `ApplicationArchiveProcessor` implementation has access to information added to the archive during the deployment creation. Some of the key items are shown by this implementation fragment:

[source,java]
----
public class WFSwarmWarArchiveProcessor implements ApplicationArchiveProcessor {
    private static Logger log = Logger.getLogger(WFSwarmWarArchiveProcessor.class.getName());

    @Override
    public void process(Archive<?> appArchive, TestClass testClass) {
        if (!(appArchive instanceof WebArchive)) {
            return;
        }
        WebArchive war = WebArchive.class.cast(appArchive);
        Node configProps = war.get("/META-INF/microprofile-config.properties");<1>
        Node publicKeyNode = war.get("/WEB-INF/classes/publicKey.pem");<2>
        Node publicKey4kNode = war.get("/WEB-INF/classes/publicKey4k.pem");<3>
        Node mpJWT = war.get("MP-JWT");<4>
----
<1> The optional microprofile-config.properties. Only the config related tests currently have this asset.
<2> The optional public key content of the token signer. 
<3> The optional 4096 bit public key content of the token signer.
<4> The optional base64 encoded string of the MP-JWT that will be passed by the test. Currently only the `Iss*Validation*` tests pass this in.

You can use this information to set vendor specific settings that are need to support proper operation of your MP-JWT implementation.

== Running Your Implementation With the TCK
Once you have built and installed your TCK harness artifact, you can run the
TCK tests against it by using either the `tokens-se` or `container` profiles.

=== container Profile
The container profile is a test of JAX-RS client tests that validate a JAX-RS endpoint bundled in a WebArchive deployment
via your implementation. These tests require Arquillian container runtime integration to properly deploy and start
your container. You typically provide this via a dependency on an arquillian container artificat, for example,
Tomcat based containers might include a dependency like:

```maven
<dependency>
  <groupId>org.jboss.arquillian.container</groupId>
  <artifactId>arquillian-tomcat-embedded-7</artifactId>
  <version>1.0.0</version>
  <scope>test</scope>
</dependency>
```

This test of tests also require the  `org.jboss.arquillian.core.spi.LoadableExtension` and `org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor`
implementations as discussed above.

To run this set of tests, issue the following command from within the microprofile-jwt-auth/tck directory:

`mvn -Pcontainer -Dtck.container.groupId={MY_GROUP} -Dtck.container.artifactId={MY_ARTIFACT} -Dtck.container.version={MY_VERSION} test`

where you would replace the `{MY_GROUP}`, `{MY_ARTIFACT}` and `{MY_VERSION}` with
the `<groupId>...<groupId>`, `<artifactId>...</artifactId>`, and `<version>...</version>`
respectively from your TCK harness artifact.

A concrete example is for running with the TCK harness artifiact from the
https://github.com/MicroProfileJWT/wfswarm-jwt-auth-tck project is:

`mvn -Pcontainer -Dtck.container.groupId=org.wildfly.swarm -Dtck.container.artifactId=jwt-auth-tck -Dtck.container.version=1.0-SNAPSHOT`

== Running the TCK Tests in Your Build
You can run the TCK tests from within your TCK harness build by including the
following in your pom.xml:

```maven
    </dependencies>
    ...
        <!-- Include the MP-JWT TCK dependencies, utility and base classes + actual test classes -->
        <dependency>
            <groupId>org.eclipse.microprofile.jwt</groupId>
            <artifactId>microprofile-jwt-auth-tck</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.eclipse.microprofile.jwt</groupId>
            <artifactId>microprofile-jwt-auth-tck</artifactId>
            <version>1.0-SNAPSHOT</version>
            <type>test-jar</type>
            <scope>test</scope>
        </dependency>
        <!-- You need to add a dependency for a JAX-RS client implementation -->
        <dependency>
            <groupId>FIXME</groupId>
            <artifactId>some-jaxrs-client-impl</artifactId>
            <version>x.y</version>
            <scope>test</scope>
        </dependency>
        <!-- Your additional container dependences... -->
    </dependencies>

    <build>
        <plugins>
        ...
            <!-- Run the TCK tests aginst the tck-base-suite.xml -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.20</version>
                <configuration>
                    <redirectTestOutputToFile>true</redirectTestOutputToFile>
                    <suiteXmlFiles>
                        <suiteXmlFile>tck-base-suite.xml</suiteXmlFile>
                    </suiteXmlFiles>
                    <forkCount>1</forkCount>
                </configuration>
            </plugin>
        </plugins>
    </build>
```

and then either copy the tck-base-suite.xml file from the TCK source tree into your
build root, or copy the following and paste if into a tck-base-suite.xml file in
your build root:

```maven
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="microprofile-jwt-auth-BaseTCK" verbose="1" preserve-order="true" configfailurepolicy="continue" >

    <!-- The required base JAX-RS and CDI based tests that all MP-JWT implementations
    must pass.
    -->
    <test name="base-tests" verbose="10">
        <groups>
            <define name="base-groups">
                <include name="arquillian" description="Arquillian internal"/>
                <include name="utils" description="Utility tests"/>
                <include name="jwt" description="Base JsonWebToken tests"/>
                <include name="jaxrs" description="JAX-RS invocation tests"/>
                <include name="cdi" description="Base CDI injection of ClaimValues"/>
                <include name="cdi-json" description="CDI injection of JSON-P values"/>
                <include name="cdi-provider" description="CDI injection of javax.inject.Provider values"/>
                <include name="config" description="Validate configuration using MP-config"/>
            </define>
            <define name="excludes">
                <include name="debug" description="Internal debugging tests" />
            </define>
            <run>
                <include name="base-groups" />
                <exclude name="excludes" />
            </run>
        </groups>
        <classes>
            <class name="org.eclipse.microprofile.jwt.tck.util.TokenUtilsTest" />
            <class name="org.eclipse.microprofile.jwt.tck.container.jaxrs.UnsecuredPingTest" />
            <class name="org.eclipse.microprofile.jwt.tck.container.jaxrs.RequiredClaimsTest" />
            <class name="org.eclipse.microprofile.jwt.tck.container.jaxrs.ClaimValueInjectionTest" />
            <class name="org.eclipse.microprofile.jwt.tck.container.jaxrs.JsonValueInjectionTest" />
            <class name="org.eclipse.microprofile.jwt.tck.container.jaxrs.ProviderInjectionTest" />
            <class name="org.eclipse.microprofile.jwt.tck.container.jaxrs.RolesAllowedTest" />
            <class name="org.eclipse.microprofile.jwt.tck.container.jaxrs.InvalidTokenTest" />
            <class name="org.eclipse.microprofile.jwt.tck.container.jaxrs.PrimitiveInjectionTest" />
            <class name="org.eclipse.microprofile.jwt.tck.container.jaxrs.PrincipalInjectionTest" />
            <class name="org.eclipse.microprofile.jwt.tck.config.PublicKeyAsPEMTest" />
            <class name="org.eclipse.microprofile.jwt.tck.config.PublicKeyAsPEMLocationTest" />
            <class name="org.eclipse.microprofile.jwt.tck.config.PublicKeyAsPEMLocationURLTest" />
            <class name="org.eclipse.microprofile.jwt.tck.config.PublicKeyAsJWKTest" />
            <class name="org.eclipse.microprofile.jwt.tck.config.PublicKeyAsJWKLocationTest" />
            <class name="org.eclipse.microprofile.jwt.tck.config.PublicKeyAsJWKLocationURLTest" />
            <class name="org.eclipse.microprofile.jwt.tck.config.PublicKeyAsJWKSTest" />
            <class name="org.eclipse.microprofile.jwt.tck.config.PublicKeyAsJWKSLocationTest" />
            <class name="org.eclipse.microprofile.jwt.tck.config.PublicKeyAsBase64JWKTest" />
            <class name="org.eclipse.microprofile.jwt.tck.config.PublicKeyAsFileLocationURLTest" />
            <class name="org.eclipse.microprofile.jwt.tck.config.IssNoValidationNoIssTest" />
            <class name="org.eclipse.microprofile.jwt.tck.config.IssNoValidationBadIssTest" />
            <class name="org.eclipse.microprofile.jwt.tck.config.IssValidationTest" />
            <class name="org.eclipse.microprofile.jwt.tck.config.IssValidationFailTest" />

        </classes>
    </test>

</suite>
```

You then simply run `mvn test` to run the TCK tests. An example of using this approach
can be found in the https://github.com/MicroProfileJWT/wfswarm-jwt-auth-tck repo.
Running

```bash
[wfswarm-jwt-auth-tck 664]$ mvn -Dswarm.resolver.offline=true test
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building MicroProfile JWT Auth TCK Harness WFSwarm Implementation 1.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ jwt-auth-tck ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 6 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ jwt-auth-tck ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ jwt-auth-tck ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /Users/starksm/Dev/JBoss/Microprofile/wfswarm-jwt-auth-tck/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ jwt-auth-tck ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-surefire-plugin:2.20:test (default-test) @ jwt-auth-tck ---
[INFO] No tests to run.
[INFO]
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running TestSuite
[INFO] Tests run: 116, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 469.176 s - in TestSuite
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 116, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 07:51 min
[INFO] Finished at: 2018-05-25T23:10:16-07:00
[INFO] Final Memory: 73M/909M
[INFO] ------------------------------------------------------------------------
```
