diff --git a/.github/actions/create-bwc-build/action.yaml b/.github/actions/create-bwc-build/action.yaml index b6ee3d5478..fcfa612a7d 100644 --- a/.github/actions/create-bwc-build/action.yaml +++ b/.github/actions/create-bwc-build/action.yaml @@ -5,7 +5,7 @@ inputs: plugin-branch: description: 'The branch of the plugin that should be built, e.g "2.2", "1.x"' required: true - + outputs: built-version: description: 'The version of OpenSearch that was associated with this branch' diff --git a/.github/actions/start-opensearch-with-one-plugin/action.yml b/.github/actions/start-opensearch-with-one-plugin/action.yml index b562851b0c..fa5681c422 100644 --- a/.github/actions/start-opensearch-with-one-plugin/action.yml +++ b/.github/actions/start-opensearch-with-one-plugin/action.yml @@ -70,7 +70,7 @@ runs: # Run any configuration scripts - name: Run Setup Script for Linux if: ${{ runner.os == 'Linux' && inputs.setup-script-name != '' }} - run: | + run: | echo "running linux setup" chmod +x ./${{ inputs.setup-script-name }}.sh ./${{ inputs.setup-script-name }}.sh diff --git a/.github/workflows/delete_backport_branch.yml b/.github/workflows/delete_backport_branch.yml index 35417b46b3..9964fe1ec8 100644 --- a/.github/workflows/delete_backport_branch.yml +++ b/.github/workflows/delete_backport_branch.yml @@ -1,9 +1,9 @@ name: Delete merged branch of the backport PRs -on: +on: pull_request: types: - closed - + jobs: delete-branch: runs-on: ubuntu-latest diff --git a/.github/workflows/plugin_install.yml b/.github/workflows/plugin_install.yml index 75289d560a..301c193e11 100644 --- a/.github/workflows/plugin_install.yml +++ b/.github/workflows/plugin_install.yml @@ -38,7 +38,7 @@ jobs: if: ${{ runner.os == 'Linux' }} run: | cat > setup.sh <<'EOF' - chmod +x ./opensearch-${{ env.OPENSEARCH_VERSION }}-SNAPSHOT/plugins/${{ env.PLUGIN_NAME }}/tools/install_demo_configuration.sh + chmod +x ./opensearch-${{ env.OPENSEARCH_VERSION }}-SNAPSHOT/plugins/${{ env.PLUGIN_NAME }}/tools/install_demo_configuration.sh /bin/bash -c "yes | ./opensearch-${{ env.OPENSEARCH_VERSION }}-SNAPSHOT/plugins/${{ env.PLUGIN_NAME }}/tools/install_demo_configuration.sh" EOF diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a7e7d89e9a..371de48a2e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,7 +5,7 @@ OpenSearch is a community project that is built and maintained by people just like **you**. [This document](https://github.com/opensearch-project/.github/blob/main/CONTRIBUTING.md) explains how you can contribute to this and related projects. -Visit the following link(s) for more information on specific practices: +Visit the following link(s) for more information on specific practices: - [Triaging](./TRIAGING.md) diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md index 5168d01a46..358b2eac14 100644 --- a/DEVELOPER_GUIDE.md +++ b/DEVELOPER_GUIDE.md @@ -48,9 +48,9 @@ The `curl localhost:9200` call should succeed again. Kill the server with `Ctrl+ >Worth noting:\ > The version of OpenSearch and the security plugin must match as there is an explicit version check at startup. This can be a bit confusing as, for example, at the time of writing this guide, the `main` branch of this security plugin builds version `3.0.0.0-SNAPSHOT` compatible with OpenSearch `3.0.0`. Check the expected compatible version in `build.gradle` file [here](https://github.com/opensearch-project/security/blob/main/build.gradle) and make sure you get the correct branch from OpenSearch when building that project. -> +> > The line to look for: `opensearch_version = System.getProperty("opensearch.version", "x")` -> +> > Alternatively, you can find the compatible version of OpenSearch by running in project root folder > ``` > ./gradlew properties -q | grep -E '^version:' | awk '{print $2}' @@ -164,7 +164,7 @@ Checkstyle enforces several rules within this codebase. Sometimes it will be nec *Execute Checkstyle* ``` -./gradlew checkstyleMain checkstyleTest +./gradlew checkstyleMain checkstyleTest ``` *Example violation* diff --git a/DEVELOPING_WITH_DOCKER.md b/DEVELOPING_WITH_DOCKER.md index a0ba045846..6616e11313 100644 --- a/DEVELOPING_WITH_DOCKER.md +++ b/DEVELOPING_WITH_DOCKER.md @@ -1,40 +1,40 @@ # Developing with Docker -Docker is a powerful tool that can be used to quickly spin up an OpenSearch cluster. When you follow the steps to run [OpenSearch with Docker](https://opensearch.org/docs/latest/install-and-configure/install-opensearch/docker/), you will find the Security Plugin already included in the basic distribution. +Docker is a powerful tool that can be used to quickly spin up an OpenSearch cluster. When you follow the steps to run [OpenSearch with Docker](https://opensearch.org/docs/latest/install-and-configure/install-opensearch/docker/), you will find the Security Plugin already included in the basic distribution. - [Developing with Docker](#developing-with-docker) - [Configuring Security](#configuring-security) - [Mounting Local Volumes](#mounting-local-volumes) - [Example docker-compose](#example-docker-compose) - -## Configuring Security -By default, the Docker installation of OpenSearch does not enable the Security plugin. In order to enable Security development, you will need set `DISABLE_SECURITY_PLUGIN=false`, as well as change `DISABLE_INSTALL_DEMO_CONFIG` and `DISABLE_SECURITY_DASHBOARDS_PLUGIN`. This will install the demo certificates, and allow you to develop with realistic Security configurations. An example of a completely configured docker-compose file is shown below. +## Configuring Security + +By default, the Docker installation of OpenSearch does not enable the Security plugin. In order to enable Security development, you will need set `DISABLE_SECURITY_PLUGIN=false`, as well as change `DISABLE_INSTALL_DEMO_CONFIG` and `DISABLE_SECURITY_DASHBOARDS_PLUGIN`. This will install the demo certificates, and allow you to develop with realistic Security configurations. An example of a completely configured docker-compose file is shown below. > Warning: You should never use the demo certificates for a production environment. Instead, you will need to follow the steps on [configuring security](https://opensearch.org/docs/latest/security/configuration/index/) before using the cluster for production. -### Mounting Local Volumes +### Mounting Local Volumes -In order to test development changes with an OpenSearch Docker-installation, you will need to mount the volumes in your docker-compose file. +In order to test development changes with an OpenSearch Docker-installation, you will need to mount the volumes in your docker-compose file. -To update your cluster to have local changes, follow these steps: +To update your cluster to have local changes, follow these steps: 1. First you will need to make changes in your local `opensearch-project/security` repository. For this example, assume your fork is cloned into a directory called `security`. -2. After you make changes to your cloned repository, you will need to run `./gradlew assemble`. This will create a `.jar` file you can mount into the Docker container. The file will be located at `./security/build/distributions/opensearch-security-.0-SNAPSHOT.jar`, where the `` field is simply the OpenSearch distribution. -3. You will then need to navigate to your `docker-compose.yml` file where you are running you OpenSearch cluster from. For this example, let us assume this is in another directory called `opensearch-docker`. -4. Modify the compose file, so that in the `volumes:` section of each node configuration (the default configuration will have `opensearch-node1` and `opensearch-node2`), you have a new line which reads `~/security/build/distributions/opensearch-security-.0-SNAPSHOT.jar:/usr/share/opensearch/plugins/opensearch-security/opensearch-security-.0.jar`. This line should be added to the volumes section of all nodes in the compose file. You will not need to add it to the `opensearch-dashboards` section. -5. You can now restart the Docker container by running `docker-compose down -v` and `docker-compose up`. Your changes will now be live in the OpenSearch cluster instance. +2. After you make changes to your cloned repository, you will need to run `./gradlew assemble`. This will create a `.jar` file you can mount into the Docker container. The file will be located at `./security/build/distributions/opensearch-security-.0-SNAPSHOT.jar`, where the `` field is simply the OpenSearch distribution. +3. You will then need to navigate to your `docker-compose.yml` file where you are running you OpenSearch cluster from. For this example, let us assume this is in another directory called `opensearch-docker`. +4. Modify the compose file, so that in the `volumes:` section of each node configuration (the default configuration will have `opensearch-node1` and `opensearch-node2`), you have a new line which reads `~/security/build/distributions/opensearch-security-.0-SNAPSHOT.jar:/usr/share/opensearch/plugins/opensearch-security/opensearch-security-.0.jar`. This line should be added to the volumes section of all nodes in the compose file. You will not need to add it to the `opensearch-dashboards` section. +5. You can now restart the Docker container by running `docker-compose down -v` and `docker-compose up`. Your changes will now be live in the OpenSearch cluster instance. -### Example docker-compose +### Example docker-compose -This is an example of a completely configured docker-compose file for a local installation of the 2.5.0 version of OpenSearch. +This is an example of a completely configured docker-compose file for a local installation of the 2.5.0 version of OpenSearch. ``` version: '3' services: opensearch-node1: - image: opensearchstaging/opensearch:2.5.0 # This is a image of the 2.5.0 distribution + image: opensearchstaging/opensearch:2.5.0 # This is a image of the 2.5.0 distribution environment: - cluster.name=opensearch-cluster - node.name=opensearch-node1 @@ -58,7 +58,7 @@ services: # - ./config/opensearch.yml:/usr/share/opensearch/config/opensearch.yml # These paths are relative to the location of the docker-compose file # - ./config/esnode.pem:/usr/share/opensearch/config/esnode.pem # - ./config/esnode-key.pem:/usr/share/opensearch/config/esnode-key.pem - # - ./config/root-ca.pem:/usr/share/opensearch/config/root-ca.pem + # - ./config/root-ca.pem:/usr/share/opensearch/config/root-ca.pem # - ./config/opensearch-security/audit.yml:/usr/share/opensearch/config/opensearch-security/audit.yml # - ./config/opensearch-security/tenants.yml:/usr/share/opensearch/config/opensearch-security/tenants.yml # - /OpenSearch-Snapshots:/mnt/snapshots # This is where your snapshots would be stored @@ -86,8 +86,8 @@ services: # - ./config/root-ca.pem:/usr/share/opensearch/config/root-ca.pem # - ./config/opensearch-security/audit.yml:/usr/share/opensearch/config/opensearch-security/audit.yml # - ./config/opensearch-security/tenants.yml:/usr/share/opensearch/config/opensearch-security/tenants.yml - # - /OpenSearch-Snapshots:/mnt/snapshots - # - /security/build/distributions/opensearch-security-2.5.0.0-SNAPSHOT.jar:/usr/share/opensearch/plugins/opensearch-security/opensearch-security-2.5.0.0.jar + # - /OpenSearch-Snapshots:/mnt/snapshots + # - /security/build/distributions/opensearch-security-2.5.0.0-SNAPSHOT.jar:/usr/share/opensearch/plugins/opensearch-security/opensearch-security-2.5.0.0.jar networks: - opensearch-net opensearch-dashboards: diff --git a/README.md b/README.md index b9ca3b80da..5c89f5d72d 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -[![CI](https://github.com/opensearch-project/security/workflows/CI/badge.svg?branch=main)](https://github.com/opensearch-project/security/actions) [![](https://img.shields.io/github/issues/opensearch-project/security/untriaged?labelColor=red)](https://github.com/opensearch-project/security/issues?q=is%3Aissue+is%3Aopen+label%3A"untriaged") [![](https://img.shields.io/github/issues/opensearch-project/security/security%20vulnerability?labelColor=red)](https://github.com/opensearch-project/security/issues?q=is%3Aissue+is%3Aopen+label%3A"security%20vulnerability") [![](https://img.shields.io/github/issues/opensearch-project/security)](https://github.com/opensearch-project/security/issues) [![](https://img.shields.io/github/issues-pr/opensearch-project/security)](https://github.com/opensearch-project/security/pulls) +[![CI](https://github.com/opensearch-project/security/workflows/CI/badge.svg?branch=main)](https://github.com/opensearch-project/security/actions) [![](https://img.shields.io/github/issues/opensearch-project/security/untriaged?labelColor=red)](https://github.com/opensearch-project/security/issues?q=is%3Aissue+is%3Aopen+label%3A"untriaged") [![](https://img.shields.io/github/issues/opensearch-project/security/security%20vulnerability?labelColor=red)](https://github.com/opensearch-project/security/issues?q=is%3Aissue+is%3Aopen+label%3A"security%20vulnerability") [![](https://img.shields.io/github/issues/opensearch-project/security)](https://github.com/opensearch-project/security/issues) [![](https://img.shields.io/github/issues-pr/opensearch-project/security)](https://github.com/opensearch-project/security/pulls) [![](https://img.shields.io/codecov/c/gh/opensearch-project/security)](https://app.codecov.io/gh/opensearch-project/security) [![](https://img.shields.io/github/issues/opensearch-project/security/v2.4.0)](https://github.com/opensearch-project/security/issues?q=is%3Aissue+is%3Aopen+label%3A"v2.4.0") [![](https://img.shields.io/github/issues/opensearch-project/security/v3.0.0)](https://github.com/opensearch-project/security/issues?q=is%3Aissue+is%3Aopen+label%3A"v3.0.0") [![Slack](https://img.shields.io/badge/Slack-4A154B?&logo=slack&logoColor=white)](https://opensearch.slack.com/archives/C051Y637FKK) -## Announcement: The Slack workspace is live! Please join the [conversation](https://opensearch.slack.com/archives/C051Y637FKK). +## Announcement: The Slack workspace is live! Please join the [conversation](https://opensearch.slack.com/archives/C051Y637FKK). @@ -37,7 +37,7 @@ OpenSearch Security is a plugin for OpenSearch that offers encryption, authentic * Full data in transit encryption * Node-to-node encryption * Certificate revocation lists -* Hot Certificate renewal +* Hot Certificate renewal ### Authentication * Internal user database @@ -60,7 +60,7 @@ OpenSearch Security is a plugin for OpenSearch that offers encryption, authentic * REST management API ### Audit/Compliance logging -* Audit logging +* Audit logging * Compliance logging for GDPR, HIPAA, PCI, SOX and ISO compliance ### OpenSearch Dashboards multi-tenancy @@ -126,7 +126,7 @@ sequenceDiagram participant OpenSearch participant SecurityPlugin participant Cluster as Plugin - + Client->>OpenSearch: Request OpenSearch->>SecurityPlugin: Request SecurityPlugin->>SecurityPlugin: Add Auth information to request context @@ -188,7 +188,7 @@ If you discover a potential security issue in this project we ask that you notif ## License -This code is licensed under the Apache 2.0 License. +This code is licensed under the Apache 2.0 License. ## Copyright diff --git a/TRIAGING.md b/TRIAGING.md index 2c4ea32fdf..bb61779a7c 100644 --- a/TRIAGING.md +++ b/TRIAGING.md @@ -20,7 +20,7 @@ If you have an issue you'd like to bring forth please consider getting a link to ### Is there an agenda for each week? -Meetings are lightly structured as follows: +Meetings are lightly structured as follows: 1. Announcements: If there are any announcements to be made they will happen at the start of the meeting. 2. Review of new issues: The meetings always start with reviewing all untriaged [issues](https://github.com/search?q=label%3Auntriaged+is%3Aopen++repo%3Aopensearch-project%2Fsecurity+repo%3Aopensearch-project%2Fsecurity-dashboards-plugin&type=issues&ref=advsearch&s=created&o=desc) for the security and security-dashboards repositories. @@ -53,7 +53,7 @@ There you can find answers to many common questions as well as speak with implem ### What if my issue is critical to OpenSearch operations, do I have to wait for the weekly meeting for it to be addressed? -All new issues for the [security](https://github.com/opensearch-project/security/issues?q=is%3Aissue+is%3Aopen+label%3Auntriaged) repo and [security-dashboards](https://github.com/opensearch-project/security-dashboards-plugin/issues?q=is%3Aissue+is%3Aopen+-label%3Atriaged) repo are reviewed daily to check for critical issues which require immediate triaging. If an issue relates to a severe concern for OpenSearch operation, it will be triaged by a maintainer mid-week. You can still come to discuss an issue at the following meeting even if it has already been triaged during the week. +All new issues for the [security](https://github.com/opensearch-project/security/issues?q=is%3Aissue+is%3Aopen+label%3Auntriaged) repo and [security-dashboards](https://github.com/opensearch-project/security-dashboards-plugin/issues?q=is%3Aissue+is%3Aopen+-label%3Atriaged) repo are reviewed daily to check for critical issues which require immediate triaging. If an issue relates to a severe concern for OpenSearch operation, it will be triaged by a maintainer mid-week. You can still come to discuss an issue at the following meeting even if it has already been triaged during the week. ### Is this where I should bring up potential security vulnerabilities? diff --git a/build.gradle b/build.gradle index 76a68ddfa0..bd2eba30ba 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,6 @@ */ -import com.diffplug.gradle.spotless.JavaExtension import org.opensearch.gradle.test.RestIntegTestTask buildscript { @@ -70,23 +69,11 @@ apply plugin: 'opensearch.opensearchplugin' apply plugin: 'opensearch.pluginzip' apply plugin: 'opensearch.rest-test' apply plugin: 'opensearch.testclusters' +apply from: 'gradle/formatting.gradle' licenseFile = rootProject.file('LICENSE.txt') noticeFile = rootProject.file('NOTICE.txt') -spotless { - java { - // note: you can use an empty string for all the imports you didn't specify explicitly, and '\\#` prefix for static imports - importOrder('java', 'javax', '', 'com.amazon', 'org.opensearch', '\\#') - targetExclude('src/integrationTest/**') - } - format("integrationTest", JavaExtension) { - target('src/integrationTest/java/**/*.java') - importOrder('java', 'javax', '', 'com.amazon', 'org.opensearch', '\\#') - indentWithTabs(4) - } -} - spotbugs { includeFilter = file('spotbugs-include.xml') } diff --git a/formatter/formatterConfig.xml b/formatter/formatterConfig.xml new file mode 100644 index 0000000000..b0e1ecccb9 --- /dev/null +++ b/formatter/formatterConfig.xml @@ -0,0 +1,362 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gradle/formatting.gradle b/gradle/formatting.gradle new file mode 100644 index 0000000000..fcadb3f45c --- /dev/null +++ b/gradle/formatting.gradle @@ -0,0 +1,36 @@ +allprojects { + project.apply plugin: "com.diffplug.spotless" + spotless { + java { + // Normally this isn't necessary, but we have Java sources in + // non-standard places + target '**/com/amazon/dlic/auth/http/**/*.java' + + removeUnusedImports() + eclipse().configFile rootProject.file('formatter/formatterConfig.xml') + trimTrailingWhitespace() + endWithNewline(); + + // note: you can use an empty string for all the imports you didn't specify explicitly, and '\\#` prefix for static imports + importOrder('java', 'javax', '', 'com.amazon', 'org.opensearch', '\\#') + + custom 'Refuse wildcard imports', { + // Wildcard imports can't be resolved; fail the build + if (it =~ /\s+import .*\*;/) { + throw new AssertionError("Do not use wildcard imports. 'spotlessApply' cannot resolve this issue.") + } + } + + // See DEVELOPER_GUIDE.md for details of when to enable this. + if (System.getProperty('spotless.paddedcell') != null) { + paddedCell() + } + } + format 'misc', { + target '*.md', '*.gradle', '**/*.json', '**/*.yaml', '**/*.yml', '**/*.svg' + + trimTrailingWhitespace() + endWithNewline() + } + } +} diff --git a/legacy/securityconfig_v6/action_groups.yml b/legacy/securityconfig_v6/action_groups.yml index 14c1b3082f..3faa4c5e31 100644 --- a/legacy/securityconfig_v6/action_groups.yml +++ b/legacy/securityconfig_v6/action_groups.yml @@ -128,7 +128,7 @@ CLUSTER_COMPOSITE_OPS: - "indices:admin/aliases*" - "indices:data/write/reindex" - CLUSTER_COMPOSITE_OPS_RO - + MANAGE_SNAPSHOTS: readonly: true permissions: diff --git a/legacy/securityconfig_v6/config.yml b/legacy/securityconfig_v6/config.yml index 15d5ee9973..d867a72200 100644 --- a/legacy/securityconfig_v6/config.yml +++ b/legacy/securityconfig_v6/config.yml @@ -1,14 +1,14 @@ # This is the main OpenSearch Security configuration file where authentication # and authorization is defined. -# +# # You need to configure at least one authentication domain in the authc of this file. -# An authentication domain is responsible for extracting the user credentials from -# the request and for validating them against an authentication backend like Active Directory for example. +# An authentication domain is responsible for extracting the user credentials from +# the request and for validating them against an authentication backend like Active Directory for example. # -# If more than one authentication domain is configured the first one which succeeds wins. +# If more than one authentication domain is configured the first one which succeeds wins. # If all authentication domains fail then the request is unauthenticated. # In this case an exception is thrown and/or the HTTP status is set to 401. -# +# # After authentication authorization (authz) will be applied. There can be zero or more authorizers which collect # the roles from a given backend for the authenticated user. # @@ -21,18 +21,18 @@ # For HTTP it is possible to allow anonymous authentication. If that is the case then the HTTP authenticators try to # find user credentials in the HTTP request. If credentials are found then the user gets regularly authenticated. # If none can be found the user will be authenticated as an "anonymous" user. This user has always the username "opendistro_security_anonymous" -# and one role named "opendistro_security_anonymous_backendrole". +# and one role named "opendistro_security_anonymous_backendrole". # If you enable anonymous authentication all HTTP authenticators will not challenge. -# +# # # Note: If you define more than one HTTP authenticators make sure to put non-challenging authenticators like "proxy" or "clientcert" -# first and the challenging one last. +# first and the challenging one last. # Because it's not possible to challenge a client with two different authentication methods (for example # Kerberos and Basic) only one can have the challenge flag set to true. You can cope with this situation # by using pre-authentication, e.g. sending a HTTP Basic authentication header in the request. # # Default value of the challenge flag is true. -# +# # # HTTP # basic (challenging) @@ -78,7 +78,7 @@ opendistro_security: ###### and here https://tools.ietf.org/html/rfc7239 ###### and https://tomcat.apache.org/tomcat-8.0-doc/config/valve.html#Remote_IP_Valve authc: - kerberos_auth_domain: + kerberos_auth_domain: http_enabled: false transport_enabled: false order: 6 @@ -92,7 +92,7 @@ opendistro_security: strip_realm_from_principal: true authentication_backend: type: noop - basic_internal_auth_domain: + basic_internal_auth_domain: http_enabled: true transport_enabled: true order: 4 @@ -164,11 +164,11 @@ opendistro_security: password: null userbase: 'ou=people,dc=example,dc=com' # Filter to search for users (currently in the whole subtree beneath userbase) - # {0} is substituted with the username + # {0} is substituted with the username usersearch: '(sAMAccountName={0})' # Use this attribute from the user as username (if not set then DN is used) username_attribute: null - authz: + authz: roles_from_myldap: http_enabled: false transport_enabled: false @@ -191,8 +191,8 @@ opendistro_security: rolebase: 'ou=groups,dc=example,dc=com' # Filter to search for roles (currently in the whole subtree beneath rolebase) # {0} is substituted with the DN of the user - # {1} is substituted with the username - # {2} is substituted with an attribute value from user's directory entry, of the authenticated user. Use userroleattribute to specify the name of the attribute + # {1} is substituted with the username + # {2} is substituted with an attribute value from user's directory entry, of the authenticated user. Use userroleattribute to specify the name of the attribute rolesearch: '(member={0})' # Specify the name of the attribute which value should be substituted with {2} above userroleattribute: null @@ -206,12 +206,12 @@ opendistro_security: resolve_nested_roles: true userbase: 'ou=people,dc=example,dc=com' # Filter to search for users (currently in the whole subtree beneath userbase) - # {0} is substituted with the username + # {0} is substituted with the username usersearch: '(uid={0})' # Skip users matching a user name, a wildcard or a regex pattern - #skip_users: + #skip_users: # - 'cn=Michael Jackson,ou*people,o=TEST' - # - '/\S*/' + # - '/\S*/' roles_from_another_ldap: http_enabled: false transport_enabled: false diff --git a/legacy/securityconfig_v6/internal_users.yml b/legacy/securityconfig_v6/internal_users.yml index 19c5eff661..c7d177787d 100644 --- a/legacy/securityconfig_v6/internal_users.yml +++ b/legacy/securityconfig_v6/internal_users.yml @@ -19,13 +19,13 @@ logstash: roles: - logstash -#password is: kibanaserver +#password is: kibanaserver kibanaserver: readonly: true hash: $2a$12$4AcgAt3xwOWadA5s5blL6ev39OXDNhmOesEoo33eZtrq2N0YrU3H. #password is: kibanaro -kibanaro: +kibanaro: hash: $2a$12$JJSXNfTowz7Uu5ttXfeYpeYE0arACvcwlPBStB1F.MI7f0U9Z4DGC roles: - kibanauser diff --git a/legacy/securityconfig_v6/roles.yml b/legacy/securityconfig_v6/roles.yml index 68a5bd6f98..c546b85393 100644 --- a/legacy/securityconfig_v6/roles.yml +++ b/legacy/securityconfig_v6/roles.yml @@ -3,7 +3,7 @@ # - '' # indices: # '': -# '': +# '': # - '' # _dls_: '' # _fls_: @@ -15,9 +15,9 @@ # and a type. If a request is executed against all indices (or all types) then the asterix ('*') is needed. # Every role a user has will be examined if it allows the action against an index (or type). At least one role must match # for the request to be successful. If no role match then the request will be denied. Currently a match must happen within -# one single role - that means that permissions can not span multiple roles. +# one single role - that means that permissions can not span multiple roles. -# For , and simple wildcards and regular expressions are possible. +# For , and simple wildcards and regular expressions are possible. # A asterix (*) will match any character sequence (or an empty sequence) # A question mark (?) will match any single character (but NOT empty character) # Example: '*my*index' will match 'my_first_index' as well as 'myindex' but not 'myindex1' @@ -27,7 +27,7 @@ # '//' # Example: '/\S*/' will match any non whitespace characters -# Important: +# Important: # Index, alias or type names can not contain dots (.) in the or expression. # Reason is that we currently parse the config file into a OpenSearch settings object which cannot cope with dots in keys. # Workaround: Just configure something like '?kibana' instead of '.kibana' or 'my?index' instead of 'my.index' @@ -59,7 +59,7 @@ opendistro_security_readall: '*': - READ -# Read all and monitor, but no write permissions +# Read all and monitor, but no write permissions opendistro_security_readall_and_monitor: cluster: - CLUSTER_MONITOR @@ -99,7 +99,7 @@ opendistro_security_kibana_user: - INDICES_ALL '?management-beats': '*': - - INDICES_ALL + - INDICES_ALL '*': '*': - indices:data/read/field_caps* @@ -135,7 +135,7 @@ opendistro_security_kibana_server: - "indices:admin/aliases*" # For logstash and beats -opendistro_security_logstash: +opendistro_security_logstash: cluster: - CLUSTER_MONITOR - CLUSTER_COMPOSITE_OPS diff --git a/legacy/securityconfig_v6/roles_mapping.yml b/legacy/securityconfig_v6/roles_mapping.yml index b3263eb234..588ba13f6e 100644 --- a/legacy/securityconfig_v6/roles_mapping.yml +++ b/legacy/securityconfig_v6/roles_mapping.yml @@ -9,12 +9,12 @@ opendistro_security_all_access: opendistro_security_logstash: backendroles: - logstash - + opendistro_security_kibana_server: readonly: true users: - kibanaserver - + opendistro_security_kibana_user: backendroles: - kibanauser diff --git a/src/main/java/com/amazon/dlic/auth/http/jwt/AbstractHTTPJwtAuthenticator.java b/src/main/java/com/amazon/dlic/auth/http/jwt/AbstractHTTPJwtAuthenticator.java index 9978186f96..e5416b1130 100644 --- a/src/main/java/com/amazon/dlic/auth/http/jwt/AbstractHTTPJwtAuthenticator.java +++ b/src/main/java/com/amazon/dlic/auth/http/jwt/AbstractHTTPJwtAuthenticator.java @@ -57,7 +57,7 @@ public abstract class AbstractHTTPJwtAuthenticator implements HTTPAuthenticator private final String rolesKey; public static final int DEFAULT_CLOCK_SKEW_TOLERANCE_SECONDS = 30; - private final int clockSkewToleranceSeconds ; + private final int clockSkewToleranceSeconds; public AbstractHTTPJwtAuthenticator(Settings settings, Path configPath) { jwtUrlParameter = settings.get("jwt_url_parameter"); @@ -69,7 +69,7 @@ public AbstractHTTPJwtAuthenticator(Settings settings, Path configPath) { try { this.keyProvider = this.initKeyProvider(settings, configPath); - jwtVerifier = new JwtVerifier(keyProvider, clockSkewToleranceSeconds ); + jwtVerifier = new JwtVerifier(keyProvider, clockSkewToleranceSeconds); } catch (Exception e) { log.error("Error creating JWT authenticator. JWT authentication will not work", e); @@ -79,8 +79,7 @@ public AbstractHTTPJwtAuthenticator(Settings settings, Path configPath) { @Override @SuppressWarnings("removal") - public AuthCredentials extractCredentials(RestRequest request, ThreadContext context) - throws OpenSearchSecurityException { + public AuthCredentials extractCredentials(RestRequest request, ThreadContext context) throws OpenSearchSecurityException { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -182,8 +181,11 @@ public String extractSubject(JwtClaims claims) { // warning if (!(subjectObject instanceof String)) { log.warn( - "Expected type String for roles in the JWT for subject_key {}, but value was '{}' ({}). Will convert this value to String.", - subjectKey, subjectObject, subjectObject.getClass()); + "Expected type String for roles in the JWT for subject_key {}, but value was '{}' ({}). Will convert this value to String.", + subjectKey, + subjectObject, + subjectObject.getClass() + ); subject = String.valueOf(subjectObject); } else { subject = (String) subjectObject; @@ -203,8 +205,9 @@ public String[] extractRoles(JwtClaims claims) { if (rolesObject == null) { log.warn( - "Failed to get roles from JWT claims with roles_key '{}'. Check if this key is correct and available in the JWT payload.", - rolesKey); + "Failed to get roles from JWT claims with roles_key '{}'. Check if this key is correct and available in the JWT payload.", + rolesKey + ); return new String[0]; } @@ -214,8 +217,11 @@ public String[] extractRoles(JwtClaims claims) { // String but issue a warning if (!(rolesObject instanceof String) && !(rolesObject instanceof Collection)) { log.warn( - "Expected type String or Collection for roles in the JWT for roles_key {}, but value was '{}' ({}). Will convert this value to String.", - rolesKey, rolesObject, rolesObject.getClass()); + "Expected type String or Collection for roles in the JWT for roles_key {}, but value was '{}' ({}). Will convert this value to String.", + rolesKey, + rolesObject, + rolesObject.getClass() + ); } else if (rolesObject instanceof Collection) { roles = ((Collection) rolesObject).toArray(new String[0]); } diff --git a/src/main/java/com/amazon/dlic/auth/http/jwt/HTTPJwtAuthenticator.java b/src/main/java/com/amazon/dlic/auth/http/jwt/HTTPJwtAuthenticator.java index 16cc71ffbd..3468bb89af 100644 --- a/src/main/java/com/amazon/dlic/auth/http/jwt/HTTPJwtAuthenticator.java +++ b/src/main/java/com/amazon/dlic/auth/http/jwt/HTTPJwtAuthenticator.java @@ -68,7 +68,7 @@ public HTTPJwtAuthenticator(final Settings settings, final Path configPath) { try { String signingKey = settings.get("signing_key"); - if(signingKey == null || signingKey.length() == 0) { + if (signingKey == null || signingKey.length() == 0) { log.error("signingKey must not be null or empty. JWT authentication will not work"); } else { @@ -90,7 +90,7 @@ public HTTPJwtAuthenticator(final Settings settings, final Path configPath) { log.debug("No public ECDSA key, try other algos ({})", e.toString()); } - if(key != null) { + if (key != null) { _jwtParser = Jwts.parser().setSigningKey(key); } else { _jwtParser = Jwts.parser().setSigningKey(decoded); @@ -121,7 +121,6 @@ public HTTPJwtAuthenticator(final Settings settings, final Path configPath) { jwtParser = _jwtParser; } - @Override @SuppressWarnings("removal") public AuthCredentials extractCredentials(RestRequest request, ThreadContext context) throws OpenSearchSecurityException { @@ -152,25 +151,29 @@ private AuthCredentials extractCredentials0(final RestRequest request) { jwtToken = null; } - if((jwtToken == null || jwtToken.isEmpty()) && jwtUrlParameter != null) { + if ((jwtToken == null || jwtToken.isEmpty()) && jwtUrlParameter != null) { jwtToken = request.param(jwtUrlParameter); } else { - //just consume to avoid "contains unrecognized parameter" + // just consume to avoid "contains unrecognized parameter" request.param(jwtUrlParameter); } if (jwtToken == null || jwtToken.length() == 0) { - if(log.isDebugEnabled()) { - log.debug("No JWT token found in '{}' {} header", jwtUrlParameter==null?jwtHeaderName:jwtUrlParameter, jwtUrlParameter==null?"header":"url parameter"); + if (log.isDebugEnabled()) { + log.debug( + "No JWT token found in '{}' {} header", + jwtUrlParameter == null ? jwtHeaderName : jwtUrlParameter, + jwtUrlParameter == null ? "header" : "url parameter" + ); } return null; } final int index; - if((index = jwtToken.toLowerCase().indexOf(BEARER)) > -1) { //detect Bearer - jwtToken = jwtToken.substring(index+BEARER.length()); + if ((index = jwtToken.toLowerCase().indexOf(BEARER)) > -1) { // detect Bearer + jwtToken = jwtToken.substring(index + BEARER.length()); } else { - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("No Bearer scheme found in header"); } } @@ -181,16 +184,16 @@ private AuthCredentials extractCredentials0(final RestRequest request) { final String subject = extractSubject(claims, request); if (subject == null) { - log.error("No subject found in JWT token"); - return null; + log.error("No subject found in JWT token"); + return null; } final String[] roles = extractRoles(claims, request); final AuthCredentials ac = new AuthCredentials(subject, roles).markComplete(); - for(Entry claim: claims.entrySet()) { - ac.addAttribute("attr.jwt."+claim.getKey(), String.valueOf(claim.getValue())); + for (Entry claim : claims.entrySet()) { + ac.addAttribute("attr.jwt." + claim.getKey(), String.valueOf(claim.getValue())); } return ac; @@ -199,7 +202,7 @@ private AuthCredentials extractCredentials0(final RestRequest request) { log.error("Cannot authenticate user with JWT because of ", e); return null; } catch (Exception e) { - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug("Invalid or expired JWT token.", e); } return null; @@ -208,7 +211,7 @@ private AuthCredentials extractCredentials0(final RestRequest request) { @Override public boolean reRequestAuthentication(final RestChannel channel, AuthCredentials creds) { - final BytesRestResponse wwwAuthenticateResponse = new BytesRestResponse(RestStatus.UNAUTHORIZED,""); + final BytesRestResponse wwwAuthenticateResponse = new BytesRestResponse(RestStatus.UNAUTHORIZED, ""); wwwAuthenticateResponse.addHeader("WWW-Authenticate", "Bearer realm=\"OpenSearch Security\""); channel.sendResponse(wwwAuthenticateResponse); return true; @@ -221,16 +224,21 @@ public String getType() { protected String extractSubject(final Claims claims, final RestRequest request) { String subject = claims.getSubject(); - if(subjectKey != null) { - // try to get roles from claims, first as Object to avoid having to catch the ExpectedTypeException + if (subjectKey != null) { + // try to get roles from claims, first as Object to avoid having to catch the ExpectedTypeException Object subjectObject = claims.get(subjectKey, Object.class); - if(subjectObject == null) { + if (subjectObject == null) { log.warn("Failed to get subject from JWT claims, check if subject_key '{}' is correct.", subjectKey); return null; } - // We expect a String. If we find something else, convert to String but issue a warning - if(!(subjectObject instanceof String)) { - log.warn("Expected type String for roles in the JWT for subject_key {}, but value was '{}' ({}). Will convert this value to String.", subjectKey, subjectObject, subjectObject.getClass()); + // We expect a String. If we find something else, convert to String but issue a warning + if (!(subjectObject instanceof String)) { + log.warn( + "Expected type String for roles in the JWT for subject_key {}, but value was '{}' ({}). Will convert this value to String.", + subjectKey, + subjectObject, + subjectObject.getClass() + ); } subject = String.valueOf(subjectObject); } @@ -239,34 +247,43 @@ protected String extractSubject(final Claims claims, final RestRequest request) @SuppressWarnings("unchecked") protected String[] extractRoles(final Claims claims, final RestRequest request) { - // no roles key specified - if(rolesKey == null) { - return new String[0]; - } - // try to get roles from claims, first as Object to avoid having to catch the ExpectedTypeException - final Object rolesObject = claims.get(rolesKey, Object.class); - if(rolesObject == null) { - log.warn("Failed to get roles from JWT claims with roles_key '{}'. Check if this key is correct and available in the JWT payload.", rolesKey); - return new String[0]; - } - - String[] roles = String.valueOf(rolesObject).split(","); - - // We expect a String or Collection. If we find something else, convert to String but issue a warning - if (!(rolesObject instanceof String) && !(rolesObject instanceof Collection)) { - log.warn("Expected type String or Collection for roles in the JWT for roles_key {}, but value was '{}' ({}). Will convert this value to String.", rolesKey, rolesObject, rolesObject.getClass()); - } else if (rolesObject instanceof Collection) { - roles = ((Collection) rolesObject).toArray(new String[0]); - } - - for (int i = 0; i < roles.length; i++) { - roles[i] = roles[i].trim(); - } - - return roles; + // no roles key specified + if (rolesKey == null) { + return new String[0]; + } + // try to get roles from claims, first as Object to avoid having to catch the ExpectedTypeException + final Object rolesObject = claims.get(rolesKey, Object.class); + if (rolesObject == null) { + log.warn( + "Failed to get roles from JWT claims with roles_key '{}'. Check if this key is correct and available in the JWT payload.", + rolesKey + ); + return new String[0]; + } + + String[] roles = String.valueOf(rolesObject).split(","); + + // We expect a String or Collection. If we find something else, convert to String but issue a warning + if (!(rolesObject instanceof String) && !(rolesObject instanceof Collection)) { + log.warn( + "Expected type String or Collection for roles in the JWT for roles_key {}, but value was '{}' ({}). Will convert this value to String.", + rolesKey, + rolesObject, + rolesObject.getClass() + ); + } else if (rolesObject instanceof Collection) { + roles = ((Collection) rolesObject).toArray(new String[0]); + } + + for (int i = 0; i < roles.length; i++) { + roles[i] = roles[i].trim(); + } + + return roles; } - private static PublicKey getPublicKey(final byte[] keyBytes, final String algo) throws NoSuchAlgorithmException, InvalidKeySpecException { + private static PublicKey getPublicKey(final byte[] keyBytes, final String algo) throws NoSuchAlgorithmException, + InvalidKeySpecException { X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes); KeyFactory kf = KeyFactory.getInstance(algo); return kf.generatePublic(spec); diff --git a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/AuthenticatorUnavailableException.java b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/AuthenticatorUnavailableException.java index d9aa1aebb6..b17663b429 100644 --- a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/AuthenticatorUnavailableException.java +++ b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/AuthenticatorUnavailableException.java @@ -12,27 +12,26 @@ package com.amazon.dlic.auth.http.jwt.keybyoidc; public class AuthenticatorUnavailableException extends RuntimeException { - private static final long serialVersionUID = -7007025852090301416L; + private static final long serialVersionUID = -7007025852090301416L; - public AuthenticatorUnavailableException() { - super(); - } + public AuthenticatorUnavailableException() { + super(); + } - public AuthenticatorUnavailableException(String message, Throwable cause, boolean enableSuppression, - boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } + public AuthenticatorUnavailableException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } - public AuthenticatorUnavailableException(String message, Throwable cause) { - super(message, cause); - } + public AuthenticatorUnavailableException(String message, Throwable cause) { + super(message, cause); + } - public AuthenticatorUnavailableException(String message) { - super(message); - } + public AuthenticatorUnavailableException(String message) { + super(message); + } - public AuthenticatorUnavailableException(Throwable cause) { - super(cause); - } + public AuthenticatorUnavailableException(Throwable cause) { + super(cause); + } } diff --git a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/BadCredentialsException.java b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/BadCredentialsException.java index 12b9195c0e..0d705f98cf 100644 --- a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/BadCredentialsException.java +++ b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/BadCredentialsException.java @@ -13,26 +13,25 @@ public class BadCredentialsException extends Exception { - private static final long serialVersionUID = 9092575587366580869L; + private static final long serialVersionUID = 9092575587366580869L; - public BadCredentialsException() { - super(); - } + public BadCredentialsException() { + super(); + } - public BadCredentialsException(String message, Throwable cause, boolean enableSuppression, - boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } + public BadCredentialsException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } - public BadCredentialsException(String message, Throwable cause) { - super(message, cause); - } + public BadCredentialsException(String message, Throwable cause) { + super(message, cause); + } - public BadCredentialsException(String message) { - super(message); - } + public BadCredentialsException(String message) { + super(message); + } - public BadCredentialsException(Throwable cause) { - super(cause); - } + public BadCredentialsException(Throwable cause) { + super(cause); + } } diff --git a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/HTTPJwtKeyByOpenIdConnectAuthenticator.java b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/HTTPJwtKeyByOpenIdConnectAuthenticator.java index 0cede73911..0c8cdf621c 100644 --- a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/HTTPJwtKeyByOpenIdConnectAuthenticator.java +++ b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/HTTPJwtKeyByOpenIdConnectAuthenticator.java @@ -20,42 +20,44 @@ public class HTTPJwtKeyByOpenIdConnectAuthenticator extends AbstractHTTPJwtAuthenticator { - //private final static Logger log = LogManager.getLogger(HTTPJwtKeyByOpenIdConnectAuthenticator.class); + // private final static Logger log = LogManager.getLogger(HTTPJwtKeyByOpenIdConnectAuthenticator.class); - public HTTPJwtKeyByOpenIdConnectAuthenticator(Settings settings, Path configPath) { - super(settings, configPath); - } + public HTTPJwtKeyByOpenIdConnectAuthenticator(Settings settings, Path configPath) { + super(settings, configPath); + } - protected KeyProvider initKeyProvider(Settings settings, Path configPath) throws Exception { - int idpRequestTimeoutMs = settings.getAsInt("idp_request_timeout_ms", 5000); - int idpQueuedThreadTimeoutMs = settings.getAsInt("idp_queued_thread_timeout_ms", 2500); + protected KeyProvider initKeyProvider(Settings settings, Path configPath) throws Exception { + int idpRequestTimeoutMs = settings.getAsInt("idp_request_timeout_ms", 5000); + int idpQueuedThreadTimeoutMs = settings.getAsInt("idp_queued_thread_timeout_ms", 2500); - int refreshRateLimitTimeWindowMs = settings.getAsInt("refresh_rate_limit_time_window_ms", 10000); - int refreshRateLimitCount = settings.getAsInt("refresh_rate_limit_count", 10); + int refreshRateLimitTimeWindowMs = settings.getAsInt("refresh_rate_limit_time_window_ms", 10000); + int refreshRateLimitCount = settings.getAsInt("refresh_rate_limit_count", 10); - KeySetRetriever keySetRetriever = new KeySetRetriever(settings.get("openid_connect_url"), - getSSLConfig(settings, configPath), settings.getAsBoolean("cache_jwks_endpoint", false)); + KeySetRetriever keySetRetriever = new KeySetRetriever( + settings.get("openid_connect_url"), + getSSLConfig(settings, configPath), + settings.getAsBoolean("cache_jwks_endpoint", false) + ); - keySetRetriever.setRequestTimeoutMs(idpRequestTimeoutMs); + keySetRetriever.setRequestTimeoutMs(idpRequestTimeoutMs); - SelfRefreshingKeySet selfRefreshingKeySet = new SelfRefreshingKeySet(keySetRetriever); + SelfRefreshingKeySet selfRefreshingKeySet = new SelfRefreshingKeySet(keySetRetriever); - selfRefreshingKeySet.setRequestTimeoutMs(idpRequestTimeoutMs); - selfRefreshingKeySet.setQueuedThreadTimeoutMs(idpQueuedThreadTimeoutMs); - selfRefreshingKeySet.setRefreshRateLimitTimeWindowMs(refreshRateLimitTimeWindowMs); - selfRefreshingKeySet.setRefreshRateLimitCount(refreshRateLimitCount); + selfRefreshingKeySet.setRequestTimeoutMs(idpRequestTimeoutMs); + selfRefreshingKeySet.setQueuedThreadTimeoutMs(idpQueuedThreadTimeoutMs); + selfRefreshingKeySet.setRefreshRateLimitTimeWindowMs(refreshRateLimitTimeWindowMs); + selfRefreshingKeySet.setRefreshRateLimitCount(refreshRateLimitCount); - return selfRefreshingKeySet; - } + return selfRefreshingKeySet; + } - private static SettingsBasedSSLConfigurator.SSLConfig getSSLConfig(Settings settings, Path configPath) - throws Exception { - return new SettingsBasedSSLConfigurator(settings, configPath, "openid_connect_idp").buildSSLConfig(); - } + private static SettingsBasedSSLConfigurator.SSLConfig getSSLConfig(Settings settings, Path configPath) throws Exception { + return new SettingsBasedSSLConfigurator(settings, configPath, "openid_connect_idp").buildSSLConfig(); + } - @Override - public String getType() { - return "jwt-key-by-oidc"; - } + @Override + public String getType() { + return "jwt-key-by-oidc"; + } } diff --git a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/JwtVerifier.java b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/JwtVerifier.java index 99074ab233..25b1ce1940 100644 --- a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/JwtVerifier.java +++ b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/JwtVerifier.java @@ -29,89 +29,88 @@ public class JwtVerifier { - private final static Logger log = LogManager.getLogger(JwtVerifier.class); - - private final KeyProvider keyProvider; - private final int clockSkewToleranceSeconds; - - public JwtVerifier(KeyProvider keyProvider, int clockSkewToleranceSeconds ) { - this.keyProvider = keyProvider; - this.clockSkewToleranceSeconds = clockSkewToleranceSeconds; - } - - public JwtToken getVerifiedJwtToken(String encodedJwt) throws BadCredentialsException { - try { - JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(encodedJwt); - JwtToken jwt = jwtConsumer.getJwtToken(); - - String escapedKid = jwt.getJwsHeaders().getKeyId(); - String kid = escapedKid; - if (!Strings.isNullOrEmpty(kid)) { - kid = StringEscapeUtils.unescapeJava(escapedKid); - } - JsonWebKey key = keyProvider.getKey(kid); - - // Algorithm is not mandatory for the key material, so we set it to the same as the JWT - if (key.getAlgorithm() == null && key.getPublicKeyUse() == PublicKeyUse.SIGN && key.getKeyType() == KeyType.RSA) - { - key.setAlgorithm(jwt.getJwsHeaders().getAlgorithm()); - } - - JwsSignatureVerifier signatureVerifier = getInitializedSignatureVerifier(key, jwt); - - - boolean signatureValid = jwtConsumer.verifySignatureWith(signatureVerifier); - - if (!signatureValid && Strings.isNullOrEmpty(kid)) { - key = keyProvider.getKeyAfterRefresh(null); - signatureVerifier = getInitializedSignatureVerifier(key, jwt); - signatureValid = jwtConsumer.verifySignatureWith(signatureVerifier); - } - - if (!signatureValid) { - throw new BadCredentialsException("Invalid JWT signature"); - } - - validateClaims(jwt); - - return jwt; - } catch (JwtException e) { - throw new BadCredentialsException(e.getMessage(), e); - } - } + private final static Logger log = LogManager.getLogger(JwtVerifier.class); + + private final KeyProvider keyProvider; + private final int clockSkewToleranceSeconds; + + public JwtVerifier(KeyProvider keyProvider, int clockSkewToleranceSeconds) { + this.keyProvider = keyProvider; + this.clockSkewToleranceSeconds = clockSkewToleranceSeconds; + } + + public JwtToken getVerifiedJwtToken(String encodedJwt) throws BadCredentialsException { + try { + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(encodedJwt); + JwtToken jwt = jwtConsumer.getJwtToken(); + + String escapedKid = jwt.getJwsHeaders().getKeyId(); + String kid = escapedKid; + if (!Strings.isNullOrEmpty(kid)) { + kid = StringEscapeUtils.unescapeJava(escapedKid); + } + JsonWebKey key = keyProvider.getKey(kid); + + // Algorithm is not mandatory for the key material, so we set it to the same as the JWT + if (key.getAlgorithm() == null && key.getPublicKeyUse() == PublicKeyUse.SIGN && key.getKeyType() == KeyType.RSA) { + key.setAlgorithm(jwt.getJwsHeaders().getAlgorithm()); + } + + JwsSignatureVerifier signatureVerifier = getInitializedSignatureVerifier(key, jwt); + + boolean signatureValid = jwtConsumer.verifySignatureWith(signatureVerifier); + + if (!signatureValid && Strings.isNullOrEmpty(kid)) { + key = keyProvider.getKeyAfterRefresh(null); + signatureVerifier = getInitializedSignatureVerifier(key, jwt); + signatureValid = jwtConsumer.verifySignatureWith(signatureVerifier); + } + + if (!signatureValid) { + throw new BadCredentialsException("Invalid JWT signature"); + } + + validateClaims(jwt); + + return jwt; + } catch (JwtException e) { + throw new BadCredentialsException(e.getMessage(), e); + } + } private void validateSignatureAlgorithm(JsonWebKey key, JwtToken jwt) throws BadCredentialsException { if (Strings.isNullOrEmpty(key.getAlgorithm())) { return; } - SignatureAlgorithm keyAlgorithm =SignatureAlgorithm.getAlgorithm(key.getAlgorithm()); + SignatureAlgorithm keyAlgorithm = SignatureAlgorithm.getAlgorithm(key.getAlgorithm()); SignatureAlgorithm tokenAlgorithm = SignatureAlgorithm.getAlgorithm(jwt.getJwsHeaders().getAlgorithm()); if (!keyAlgorithm.equals(tokenAlgorithm)) { - throw new BadCredentialsException("Algorithm of JWT does not match algorithm of JWK (" + keyAlgorithm + " != " + tokenAlgorithm + ")"); + throw new BadCredentialsException( + "Algorithm of JWT does not match algorithm of JWK (" + keyAlgorithm + " != " + tokenAlgorithm + ")" + ); } } + private JwsSignatureVerifier getInitializedSignatureVerifier(JsonWebKey key, JwtToken jwt) throws BadCredentialsException, + JwtException { - private JwsSignatureVerifier getInitializedSignatureVerifier(JsonWebKey key, JwtToken jwt) - throws BadCredentialsException, JwtException { - - validateSignatureAlgorithm(key, jwt); + validateSignatureAlgorithm(key, jwt); JwsSignatureVerifier result = JwsUtils.getSignatureVerifier(key, jwt.getJwsHeaders().getSignatureAlgorithm()); - if (result == null) { - throw new BadCredentialsException("Cannot verify JWT"); - } else { - return result; - } - } - - private void validateClaims(JwtToken jwt) throws BadCredentialsException, JwtException { - JwtClaims claims = jwt.getClaims(); - - if (claims != null) { - JwtUtils.validateJwtExpiry(claims, clockSkewToleranceSeconds, false); - JwtUtils.validateJwtNotBefore(claims, clockSkewToleranceSeconds, false); - } - } + if (result == null) { + throw new BadCredentialsException("Cannot verify JWT"); + } else { + return result; + } + } + + private void validateClaims(JwtToken jwt) throws BadCredentialsException, JwtException { + JwtClaims claims = jwt.getClaims(); + + if (claims != null) { + JwtUtils.validateJwtExpiry(claims, clockSkewToleranceSeconds, false); + JwtUtils.validateJwtNotBefore(claims, clockSkewToleranceSeconds, false); + } + } } diff --git a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/KeyProvider.java b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/KeyProvider.java index 5eff7cb213..a0e76c918f 100644 --- a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/KeyProvider.java +++ b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/KeyProvider.java @@ -14,6 +14,7 @@ import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; public interface KeyProvider { - public JsonWebKey getKey(String kid) throws AuthenticatorUnavailableException, BadCredentialsException; - public JsonWebKey getKeyAfterRefresh(String kid) throws AuthenticatorUnavailableException, BadCredentialsException; + public JsonWebKey getKey(String kid) throws AuthenticatorUnavailableException, BadCredentialsException; + + public JsonWebKey getKeyAfterRefresh(String kid) throws AuthenticatorUnavailableException, BadCredentialsException; } diff --git a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/KeySetProvider.java b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/KeySetProvider.java index edbe39f020..53ea0237db 100644 --- a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/KeySetProvider.java +++ b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/KeySetProvider.java @@ -15,5 +15,5 @@ @FunctionalInterface public interface KeySetProvider { - JsonWebKeys get() throws AuthenticatorUnavailableException; + JsonWebKeys get() throws AuthenticatorUnavailableException; } diff --git a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/KeySetRetriever.java b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/KeySetRetriever.java index 50be122aec..3fed940d54 100644 --- a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/KeySetRetriever.java +++ b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/KeySetRetriever.java @@ -38,185 +38,196 @@ import org.opensearch.security.DefaultObjectMapper; - public class KeySetRetriever implements KeySetProvider { - private final static Logger log = LogManager.getLogger(KeySetRetriever.class); - private static final long CACHE_STATUS_LOG_INTERVAL_MS = 60L * 60L * 1000L; - - private String openIdConnectEndpoint; - private SSLConfig sslConfig; - private int requestTimeoutMs = 10000; - private CacheConfig cacheConfig; - private HttpCacheStorage oidcHttpCacheStorage; - private int oidcCacheHits = 0; - private int oidcCacheMisses = 0; - private int oidcCacheHitsValidated = 0; - private int oidcCacheModuleResponses = 0; - private long oidcRequests = 0; - private long lastCacheStatusLog = 0; - - KeySetRetriever(String openIdConnectEndpoint, SSLConfig sslConfig, boolean useCacheForOidConnectEndpoint) { - this.openIdConnectEndpoint = openIdConnectEndpoint; - this.sslConfig = sslConfig; - - if (useCacheForOidConnectEndpoint) { - cacheConfig = CacheConfig.custom().setMaxCacheEntries(10).setMaxObjectSize(1024L * 1024L).build(); - oidcHttpCacheStorage = new BasicHttpCacheStorage(cacheConfig); - } - } - - public JsonWebKeys get() throws AuthenticatorUnavailableException { - String uri = getJwksUri(); - - try (CloseableHttpClient httpClient = createHttpClient(null)) { - - HttpGet httpGet = new HttpGet(uri); - - RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(getRequestTimeoutMs(), TimeUnit.MILLISECONDS) - .setConnectTimeout(getRequestTimeoutMs(), TimeUnit.MILLISECONDS).build(); - - httpGet.setConfig(requestConfig); - - try (CloseableHttpResponse response = httpClient.execute(httpGet)) { - if (response.getCode() < 200 || response.getCode() >= 300) { - throw new AuthenticatorUnavailableException("Error while getting " + uri + ": " + response.getReasonPhrase()); - } - - HttpEntity httpEntity = response.getEntity(); - - if (httpEntity == null) { - throw new AuthenticatorUnavailableException( - "Error while getting " + uri + ": Empty response entity"); - } - - JsonWebKeys keySet = JwkUtils.readJwkSet(httpEntity.getContent()); - - return keySet; - } - } catch (IOException e) { - throw new AuthenticatorUnavailableException("Error while getting " + uri + ": " + e, e); - } + private final static Logger log = LogManager.getLogger(KeySetRetriever.class); + private static final long CACHE_STATUS_LOG_INTERVAL_MS = 60L * 60L * 1000L; - } + private String openIdConnectEndpoint; + private SSLConfig sslConfig; + private int requestTimeoutMs = 10000; + private CacheConfig cacheConfig; + private HttpCacheStorage oidcHttpCacheStorage; + private int oidcCacheHits = 0; + private int oidcCacheMisses = 0; + private int oidcCacheHitsValidated = 0; + private int oidcCacheModuleResponses = 0; + private long oidcRequests = 0; + private long lastCacheStatusLog = 0; - String getJwksUri() throws AuthenticatorUnavailableException { + KeySetRetriever(String openIdConnectEndpoint, SSLConfig sslConfig, boolean useCacheForOidConnectEndpoint) { + this.openIdConnectEndpoint = openIdConnectEndpoint; + this.sslConfig = sslConfig; - try (CloseableHttpClient httpClient = createHttpClient(oidcHttpCacheStorage)) { + if (useCacheForOidConnectEndpoint) { + cacheConfig = CacheConfig.custom().setMaxCacheEntries(10).setMaxObjectSize(1024L * 1024L).build(); + oidcHttpCacheStorage = new BasicHttpCacheStorage(cacheConfig); + } + } - HttpGet httpGet = new HttpGet(openIdConnectEndpoint); + public JsonWebKeys get() throws AuthenticatorUnavailableException { + String uri = getJwksUri(); - RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(getRequestTimeoutMs(), TimeUnit.MILLISECONDS) - .setConnectTimeout(getRequestTimeoutMs(), TimeUnit.MILLISECONDS).build(); + try (CloseableHttpClient httpClient = createHttpClient(null)) { - httpGet.setConfig(requestConfig); + HttpGet httpGet = new HttpGet(uri); - HttpCacheContext httpContext = null; + RequestConfig requestConfig = RequestConfig.custom() + .setConnectionRequestTimeout(getRequestTimeoutMs(), TimeUnit.MILLISECONDS) + .setConnectTimeout(getRequestTimeoutMs(), TimeUnit.MILLISECONDS) + .build(); - if (oidcHttpCacheStorage != null) { - httpContext = new HttpCacheContext(); - } + httpGet.setConfig(requestConfig); - try (CloseableHttpResponse response = httpClient.execute(httpGet, httpContext)) { - if (httpContext != null) { - logCacheResponseStatus(httpContext); - } + try (CloseableHttpResponse response = httpClient.execute(httpGet)) { + if (response.getCode() < 200 || response.getCode() >= 300) { + throw new AuthenticatorUnavailableException("Error while getting " + uri + ": " + response.getReasonPhrase()); + } - if (response.getCode() < 200 || response.getCode() >= 300) { - throw new AuthenticatorUnavailableException( - "Error while getting " + openIdConnectEndpoint + ": " + response.getReasonPhrase()); - } + HttpEntity httpEntity = response.getEntity(); - HttpEntity httpEntity = response.getEntity(); + if (httpEntity == null) { + throw new AuthenticatorUnavailableException("Error while getting " + uri + ": Empty response entity"); + } + + JsonWebKeys keySet = JwkUtils.readJwkSet(httpEntity.getContent()); + + return keySet; + } + } catch (IOException e) { + throw new AuthenticatorUnavailableException("Error while getting " + uri + ": " + e, e); + } - if (httpEntity == null) { - throw new AuthenticatorUnavailableException( - "Error while getting " + openIdConnectEndpoint + ": Empty response entity"); - } + } + + String getJwksUri() throws AuthenticatorUnavailableException { + + try (CloseableHttpClient httpClient = createHttpClient(oidcHttpCacheStorage)) { + + HttpGet httpGet = new HttpGet(openIdConnectEndpoint); - OpenIdProviderConfiguration parsedEntity = DefaultObjectMapper.objectMapper.readValue(httpEntity.getContent(), - OpenIdProviderConfiguration.class); + RequestConfig requestConfig = RequestConfig.custom() + .setConnectionRequestTimeout(getRequestTimeoutMs(), TimeUnit.MILLISECONDS) + .setConnectTimeout(getRequestTimeoutMs(), TimeUnit.MILLISECONDS) + .build(); - return parsedEntity.getJwksUri(); + httpGet.setConfig(requestConfig); - } + HttpCacheContext httpContext = null; - } catch (IOException e) { - throw new AuthenticatorUnavailableException("Error while getting " + openIdConnectEndpoint + ": " + e, e); - } + if (oidcHttpCacheStorage != null) { + httpContext = new HttpCacheContext(); + } - } + try (CloseableHttpResponse response = httpClient.execute(httpGet, httpContext)) { + if (httpContext != null) { + logCacheResponseStatus(httpContext); + } - public int getRequestTimeoutMs() { - return requestTimeoutMs; - } + if (response.getCode() < 200 || response.getCode() >= 300) { + throw new AuthenticatorUnavailableException( + "Error while getting " + openIdConnectEndpoint + ": " + response.getReasonPhrase() + ); + } - public void setRequestTimeoutMs(int httpTimeoutMs) { - this.requestTimeoutMs = httpTimeoutMs; - } + HttpEntity httpEntity = response.getEntity(); - private void logCacheResponseStatus(HttpCacheContext httpContext) { - this.oidcRequests++; + if (httpEntity == null) { + throw new AuthenticatorUnavailableException("Error while getting " + openIdConnectEndpoint + ": Empty response entity"); + } - switch (httpContext.getCacheResponseStatus()) { - case CACHE_HIT: - this.oidcCacheHits++; - break; - case CACHE_MODULE_RESPONSE: - this.oidcCacheModuleResponses++; - break; - case CACHE_MISS: - this.oidcCacheMisses++; - break; - case VALIDATED: - this.oidcCacheHitsValidated++; - break; - } + OpenIdProviderConfiguration parsedEntity = DefaultObjectMapper.objectMapper.readValue( + httpEntity.getContent(), + OpenIdProviderConfiguration.class + ); - long now = System.currentTimeMillis(); + return parsedEntity.getJwksUri(); - if (this.oidcRequests >= 2 && now - lastCacheStatusLog > CACHE_STATUS_LOG_INTERVAL_MS) { - log.info("Cache status for KeySetRetriever:\noidcCacheHits: {}\noidcCacheHitsValidated: {}" - + "\noidcCacheModuleResponses: {}" + "\noidcCacheMisses: {}", oidcCacheHits, oidcCacheHitsValidated, oidcCacheModuleResponses, oidcCacheMisses); - lastCacheStatusLog = now; - } + } - } + } catch (IOException e) { + throw new AuthenticatorUnavailableException("Error while getting " + openIdConnectEndpoint + ": " + e, e); + } - private CloseableHttpClient createHttpClient(HttpCacheStorage httpCacheStorage) { - HttpClientBuilder builder; + } - if (httpCacheStorage != null) { - builder = CachingHttpClients.custom().setCacheConfig(cacheConfig).setHttpCacheStorage(httpCacheStorage); - } else { - builder = HttpClients.custom(); - } + public int getRequestTimeoutMs() { + return requestTimeoutMs; + } - builder.useSystemProperties(); + public void setRequestTimeoutMs(int httpTimeoutMs) { + this.requestTimeoutMs = httpTimeoutMs; + } - if (sslConfig != null) { - final HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create() - .setSSLSocketFactory(sslConfig.toSSLConnectionSocketFactory()) - .build(); + private void logCacheResponseStatus(HttpCacheContext httpContext) { + this.oidcRequests++; - builder.setConnectionManager(cm); - } + switch (httpContext.getCacheResponseStatus()) { + case CACHE_HIT: + this.oidcCacheHits++; + break; + case CACHE_MODULE_RESPONSE: + this.oidcCacheModuleResponses++; + break; + case CACHE_MISS: + this.oidcCacheMisses++; + break; + case VALIDATED: + this.oidcCacheHitsValidated++; + break; + } - return builder.build(); - } + long now = System.currentTimeMillis(); - public int getOidcCacheHits() { - return oidcCacheHits; - } + if (this.oidcRequests >= 2 && now - lastCacheStatusLog > CACHE_STATUS_LOG_INTERVAL_MS) { + log.info( + "Cache status for KeySetRetriever:\noidcCacheHits: {}\noidcCacheHitsValidated: {}" + + "\noidcCacheModuleResponses: {}" + + "\noidcCacheMisses: {}", + oidcCacheHits, + oidcCacheHitsValidated, + oidcCacheModuleResponses, + oidcCacheMisses + ); + lastCacheStatusLog = now; + } - public int getOidcCacheMisses() { - return oidcCacheMisses; - } + } - public int getOidcCacheHitsValidated() { - return oidcCacheHitsValidated; - } + private CloseableHttpClient createHttpClient(HttpCacheStorage httpCacheStorage) { + HttpClientBuilder builder; - public int getOidcCacheModuleResponses() { - return oidcCacheModuleResponses; - } + if (httpCacheStorage != null) { + builder = CachingHttpClients.custom().setCacheConfig(cacheConfig).setHttpCacheStorage(httpCacheStorage); + } else { + builder = HttpClients.custom(); + } + + builder.useSystemProperties(); + + if (sslConfig != null) { + final HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create() + .setSSLSocketFactory(sslConfig.toSSLConnectionSocketFactory()) + .build(); + + builder.setConnectionManager(cm); + } + + return builder.build(); + } + + public int getOidcCacheHits() { + return oidcCacheHits; + } + + public int getOidcCacheMisses() { + return oidcCacheMisses; + } + + public int getOidcCacheHitsValidated() { + return oidcCacheHitsValidated; + } + + public int getOidcCacheModuleResponses() { + return oidcCacheModuleResponses; + } } diff --git a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/SelfRefreshingKeySet.java b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/SelfRefreshingKeySet.java index 7e3cec8246..fe410b171c 100644 --- a/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/SelfRefreshingKeySet.java +++ b/src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/SelfRefreshingKeySet.java @@ -25,295 +25,300 @@ import org.apache.logging.log4j.Logger; public class SelfRefreshingKeySet implements KeyProvider { - private static final Logger log = LogManager.getLogger(SelfRefreshingKeySet.class); - - private final KeySetProvider keySetProvider; - private final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 10, 1000, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue()); - private volatile JsonWebKeys jsonWebKeys = new JsonWebKeys(); - private boolean refreshInProgress = false; - private long refreshCount = 0; - private long queuedGetCount = 0; - private long recentRefreshCount = 0; - private long refreshTime = 0; - private Throwable lastRefreshFailure = null; - private int requestTimeoutMs = 5000; - private int queuedThreadTimeoutMs = 2500; - private int refreshRateLimitTimeWindowMs = 10000; - private int refreshRateLimitCount = 10; - - public SelfRefreshingKeySet(KeySetProvider refreshFunction) { - this.keySetProvider = refreshFunction; - } - - public JsonWebKey getKey(String kid) throws AuthenticatorUnavailableException, BadCredentialsException { - if (Strings.isNullOrEmpty(kid)) { - return getKeyWithoutKeyId(); - } else { - return getKeyWithKeyId(kid); - } - } - - public synchronized JsonWebKey getKeyAfterRefresh(String kid) - throws AuthenticatorUnavailableException, BadCredentialsException { - JsonWebKey result = getKeyAfterRefreshInternal(kid); - - if (result != null) { - return result; - } else if (jsonWebKeys.getKeys().size() == 0) { - throw new AuthenticatorUnavailableException("No JWK are available from IdP"); - } else { - throw new BadCredentialsException("JWT did not contain KID which is required if IdP provides multiple JWK"); - } - } - - private synchronized JsonWebKey getKeyAfterRefreshInternal(String kid) throws AuthenticatorUnavailableException { - if (refreshInProgress) { - return waitForRefreshToFinish(kid); - } else { - return performRefresh(kid); - } - } - - private JsonWebKey getKeyWithoutKeyId() throws AuthenticatorUnavailableException, BadCredentialsException { - List keys = jsonWebKeys.getKeys(); - - if (keys == null || keys.size() == 0) { - JsonWebKey result = getKeyWithRefresh(null); - - if (result != null) { - return result; - } else { - throw new AuthenticatorUnavailableException("No JWK are available from IdP"); - } - } else if (keys.size() == 1) { - return keys.get(0); - } else { - JsonWebKey result = getKeyWithRefresh(null); - - if (result != null) { - return result; - } else { - throw new BadCredentialsException( - "JWT did not contain KID which is required if IdP provides multiple JWK"); - } - } - } - - private JsonWebKey getKeyWithKeyId(String kid) throws AuthenticatorUnavailableException, BadCredentialsException { - JsonWebKey result = jsonWebKeys.getKey(kid); - - if (result != null) { - return result; - } - - result = getKeyWithRefresh(kid); - - if (result == null) { - throw new BadCredentialsException("Unknown kid " + kid); - } - - return result; - } - - private synchronized JsonWebKey getKeyWithRefresh(String kid) throws AuthenticatorUnavailableException { - - // Always re-check within synchronized to handle any races - - JsonWebKey result = getKeySimple(kid); - - if (result != null) { - return result; - } - - return getKeyAfterRefreshInternal(kid); - } - - private JsonWebKey getKeySimple(String kid) { - if (Strings.isNullOrEmpty(kid)) { - List keys = jsonWebKeys.getKeys(); - - if (keys != null && keys.size() == 1) { - return keys.get(0); - } else { - return null; - } - - } else { - return jsonWebKeys.getKey(kid); - } - } - - private synchronized JsonWebKey waitForRefreshToFinish(String kid) { - queuedGetCount++; - long currentRefreshCount = refreshCount; - - try { - wait(queuedThreadTimeoutMs); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - log.debug(e.toString()); - } - - // Just be optimistic and re-check the key - - JsonWebKey result = getKeySimple(kid); - - if (result != null) { - return result; - } - - if (refreshInProgress && currentRefreshCount == refreshCount) { - // The wait() call returned due to the timeout. - throw new AuthenticatorUnavailableException("Authentication backend timed out"); - } else if (lastRefreshFailure != null) { - throw new AuthenticatorUnavailableException("Authentication backend failed", lastRefreshFailure); - } else { - // Refresh was successful, but we did not get a matching key - return null; - } - } - - private synchronized JsonWebKey performRefresh(String kid) { - if (log.isDebugEnabled()) { - log.debug("performRefresh({})", kid); - } - - final boolean recentRefresh; - - if (System.currentTimeMillis() - refreshTime < refreshRateLimitTimeWindowMs) { - recentRefreshCount++; - recentRefresh = true; - - if (recentRefreshCount > refreshRateLimitCount) { - throw new AuthenticatorUnavailableException("Too many unknown kids recently: " + recentRefreshCount); - } - } else { - recentRefresh = false; - } - - refreshInProgress = true; - refreshCount++; - - log.info("Performing refresh {}", refreshCount); - - long currentRefreshCount = refreshCount; - - try { - - Future future = threadPoolExecutor.submit(new Runnable() { - - @Override - public void run() { - try { - JsonWebKeys newKeys = keySetProvider.get(); - - if (newKeys == null) { - throw new RuntimeException("Refresh function " + keySetProvider + " yielded null"); - } - - log.info("KeySetProvider finished"); - - synchronized (SelfRefreshingKeySet.this) { - jsonWebKeys = newKeys; - refreshInProgress = false; - lastRefreshFailure = null; - SelfRefreshingKeySet.this.notifyAll(); - } - } catch (Throwable e) { - synchronized (SelfRefreshingKeySet.this) { - lastRefreshFailure = e; - refreshInProgress = false; - SelfRefreshingKeySet.this.notifyAll(); - } - log.warn("KeySetProvider threw error", e); - } finally { - if (!recentRefresh) { - recentRefreshCount = 0; - refreshTime = System.currentTimeMillis(); - } - } - - } - }); - - try { - wait(requestTimeoutMs); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - log.debug(e.toString()); - } - - JsonWebKey result = getKeySimple(kid); - - if (result != null) { - return result; - } - - if (refreshInProgress && currentRefreshCount == refreshCount) { - if (!future.isDone()) { - future.cancel(true); - } - - lastRefreshFailure = new AuthenticatorUnavailableException("Authentication backend timed out"); - - throw new AuthenticatorUnavailableException("Authentication backend timed out"); - } - - if (lastRefreshFailure != null) { - throw new AuthenticatorUnavailableException("Authentication backend failed", lastRefreshFailure); - } - - return null; - - } catch (RejectedExecutionException e) { - throw new AuthenticatorUnavailableException("Did not try to call authentication backend because of " - + threadPoolExecutor.getActiveCount() + " pending threads", e); - } finally { - if (refreshInProgress && currentRefreshCount == refreshCount) { - refreshInProgress = false; - notifyAll(); - } - } - } - - public int getRequestTimeoutMs() { - return requestTimeoutMs; - } - - public void setRequestTimeoutMs(int requestTimeoutMs) { - this.requestTimeoutMs = requestTimeoutMs; - } - - public int getQueuedThreadTimeoutMs() { - return queuedThreadTimeoutMs; - } - - public void setQueuedThreadTimeoutMs(int queuedThreadTimeoutMs) { - this.queuedThreadTimeoutMs = queuedThreadTimeoutMs; - } - - public long getRefreshCount() { - return refreshCount; - } - - public long getQueuedGetCount() { - return queuedGetCount; - } - - public int getRefreshRateLimitTimeWindowMs() { - return refreshRateLimitTimeWindowMs; - } - - public void setRefreshRateLimitTimeWindowMs(int refreshRateLimitTimeWindowMs) { - this.refreshRateLimitTimeWindowMs = refreshRateLimitTimeWindowMs; - } - - public int getRefreshRateLimitCount() { - return refreshRateLimitCount; - } - - public void setRefreshRateLimitCount(int refreshRateLimitCount) { - this.refreshRateLimitCount = refreshRateLimitCount; - } + private static final Logger log = LogManager.getLogger(SelfRefreshingKeySet.class); + + private final KeySetProvider keySetProvider; + private final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( + 1, + 10, + 1000, + TimeUnit.MILLISECONDS, + new LinkedBlockingQueue() + ); + private volatile JsonWebKeys jsonWebKeys = new JsonWebKeys(); + private boolean refreshInProgress = false; + private long refreshCount = 0; + private long queuedGetCount = 0; + private long recentRefreshCount = 0; + private long refreshTime = 0; + private Throwable lastRefreshFailure = null; + private int requestTimeoutMs = 5000; + private int queuedThreadTimeoutMs = 2500; + private int refreshRateLimitTimeWindowMs = 10000; + private int refreshRateLimitCount = 10; + + public SelfRefreshingKeySet(KeySetProvider refreshFunction) { + this.keySetProvider = refreshFunction; + } + + public JsonWebKey getKey(String kid) throws AuthenticatorUnavailableException, BadCredentialsException { + if (Strings.isNullOrEmpty(kid)) { + return getKeyWithoutKeyId(); + } else { + return getKeyWithKeyId(kid); + } + } + + public synchronized JsonWebKey getKeyAfterRefresh(String kid) throws AuthenticatorUnavailableException, BadCredentialsException { + JsonWebKey result = getKeyAfterRefreshInternal(kid); + + if (result != null) { + return result; + } else if (jsonWebKeys.getKeys().size() == 0) { + throw new AuthenticatorUnavailableException("No JWK are available from IdP"); + } else { + throw new BadCredentialsException("JWT did not contain KID which is required if IdP provides multiple JWK"); + } + } + + private synchronized JsonWebKey getKeyAfterRefreshInternal(String kid) throws AuthenticatorUnavailableException { + if (refreshInProgress) { + return waitForRefreshToFinish(kid); + } else { + return performRefresh(kid); + } + } + + private JsonWebKey getKeyWithoutKeyId() throws AuthenticatorUnavailableException, BadCredentialsException { + List keys = jsonWebKeys.getKeys(); + + if (keys == null || keys.size() == 0) { + JsonWebKey result = getKeyWithRefresh(null); + + if (result != null) { + return result; + } else { + throw new AuthenticatorUnavailableException("No JWK are available from IdP"); + } + } else if (keys.size() == 1) { + return keys.get(0); + } else { + JsonWebKey result = getKeyWithRefresh(null); + + if (result != null) { + return result; + } else { + throw new BadCredentialsException("JWT did not contain KID which is required if IdP provides multiple JWK"); + } + } + } + + private JsonWebKey getKeyWithKeyId(String kid) throws AuthenticatorUnavailableException, BadCredentialsException { + JsonWebKey result = jsonWebKeys.getKey(kid); + + if (result != null) { + return result; + } + + result = getKeyWithRefresh(kid); + + if (result == null) { + throw new BadCredentialsException("Unknown kid " + kid); + } + + return result; + } + + private synchronized JsonWebKey getKeyWithRefresh(String kid) throws AuthenticatorUnavailableException { + + // Always re-check within synchronized to handle any races + + JsonWebKey result = getKeySimple(kid); + + if (result != null) { + return result; + } + + return getKeyAfterRefreshInternal(kid); + } + + private JsonWebKey getKeySimple(String kid) { + if (Strings.isNullOrEmpty(kid)) { + List keys = jsonWebKeys.getKeys(); + + if (keys != null && keys.size() == 1) { + return keys.get(0); + } else { + return null; + } + + } else { + return jsonWebKeys.getKey(kid); + } + } + + private synchronized JsonWebKey waitForRefreshToFinish(String kid) { + queuedGetCount++; + long currentRefreshCount = refreshCount; + + try { + wait(queuedThreadTimeoutMs); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.debug(e.toString()); + } + + // Just be optimistic and re-check the key + + JsonWebKey result = getKeySimple(kid); + + if (result != null) { + return result; + } + + if (refreshInProgress && currentRefreshCount == refreshCount) { + // The wait() call returned due to the timeout. + throw new AuthenticatorUnavailableException("Authentication backend timed out"); + } else if (lastRefreshFailure != null) { + throw new AuthenticatorUnavailableException("Authentication backend failed", lastRefreshFailure); + } else { + // Refresh was successful, but we did not get a matching key + return null; + } + } + + private synchronized JsonWebKey performRefresh(String kid) { + if (log.isDebugEnabled()) { + log.debug("performRefresh({})", kid); + } + + final boolean recentRefresh; + + if (System.currentTimeMillis() - refreshTime < refreshRateLimitTimeWindowMs) { + recentRefreshCount++; + recentRefresh = true; + + if (recentRefreshCount > refreshRateLimitCount) { + throw new AuthenticatorUnavailableException("Too many unknown kids recently: " + recentRefreshCount); + } + } else { + recentRefresh = false; + } + + refreshInProgress = true; + refreshCount++; + + log.info("Performing refresh {}", refreshCount); + + long currentRefreshCount = refreshCount; + + try { + + Future future = threadPoolExecutor.submit(new Runnable() { + + @Override + public void run() { + try { + JsonWebKeys newKeys = keySetProvider.get(); + + if (newKeys == null) { + throw new RuntimeException("Refresh function " + keySetProvider + " yielded null"); + } + + log.info("KeySetProvider finished"); + + synchronized (SelfRefreshingKeySet.this) { + jsonWebKeys = newKeys; + refreshInProgress = false; + lastRefreshFailure = null; + SelfRefreshingKeySet.this.notifyAll(); + } + } catch (Throwable e) { + synchronized (SelfRefreshingKeySet.this) { + lastRefreshFailure = e; + refreshInProgress = false; + SelfRefreshingKeySet.this.notifyAll(); + } + log.warn("KeySetProvider threw error", e); + } finally { + if (!recentRefresh) { + recentRefreshCount = 0; + refreshTime = System.currentTimeMillis(); + } + } + + } + }); + + try { + wait(requestTimeoutMs); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.debug(e.toString()); + } + + JsonWebKey result = getKeySimple(kid); + + if (result != null) { + return result; + } + + if (refreshInProgress && currentRefreshCount == refreshCount) { + if (!future.isDone()) { + future.cancel(true); + } + + lastRefreshFailure = new AuthenticatorUnavailableException("Authentication backend timed out"); + + throw new AuthenticatorUnavailableException("Authentication backend timed out"); + } + + if (lastRefreshFailure != null) { + throw new AuthenticatorUnavailableException("Authentication backend failed", lastRefreshFailure); + } + + return null; + + } catch (RejectedExecutionException e) { + throw new AuthenticatorUnavailableException( + "Did not try to call authentication backend because of " + threadPoolExecutor.getActiveCount() + " pending threads", + e + ); + } finally { + if (refreshInProgress && currentRefreshCount == refreshCount) { + refreshInProgress = false; + notifyAll(); + } + } + } + + public int getRequestTimeoutMs() { + return requestTimeoutMs; + } + + public void setRequestTimeoutMs(int requestTimeoutMs) { + this.requestTimeoutMs = requestTimeoutMs; + } + + public int getQueuedThreadTimeoutMs() { + return queuedThreadTimeoutMs; + } + + public void setQueuedThreadTimeoutMs(int queuedThreadTimeoutMs) { + this.queuedThreadTimeoutMs = queuedThreadTimeoutMs; + } + + public long getRefreshCount() { + return refreshCount; + } + + public long getQueuedGetCount() { + return queuedGetCount; + } + + public int getRefreshRateLimitTimeWindowMs() { + return refreshRateLimitTimeWindowMs; + } + + public void setRefreshRateLimitTimeWindowMs(int refreshRateLimitTimeWindowMs) { + this.refreshRateLimitTimeWindowMs = refreshRateLimitTimeWindowMs; + } + + public int getRefreshRateLimitCount() { + return refreshRateLimitCount; + } + + public void setRefreshRateLimitCount(int refreshRateLimitCount) { + this.refreshRateLimitCount = refreshRateLimitCount; + } } diff --git a/src/main/java/com/amazon/dlic/auth/http/jwt/oidc/json/OpenIdProviderConfiguration.java b/src/main/java/com/amazon/dlic/auth/http/jwt/oidc/json/OpenIdProviderConfiguration.java index 58a4310a2d..3bcfb796b0 100644 --- a/src/main/java/com/amazon/dlic/auth/http/jwt/oidc/json/OpenIdProviderConfiguration.java +++ b/src/main/java/com/amazon/dlic/auth/http/jwt/oidc/json/OpenIdProviderConfiguration.java @@ -17,15 +17,15 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class OpenIdProviderConfiguration { - @JsonProperty("jwks_uri") - private String jwksUri; + @JsonProperty("jwks_uri") + private String jwksUri; - public String getJwksUri() { - return jwksUri; - } + public String getJwksUri() { + return jwksUri; + } - public void setJwksUri(String jwksUri) { - this.jwksUri = jwksUri; - } + public void setJwksUri(String jwksUri) { + this.jwksUri = jwksUri; + } } diff --git a/src/main/java/com/amazon/dlic/auth/http/kerberos/HTTPSpnegoAuthenticator.java b/src/main/java/com/amazon/dlic/auth/http/kerberos/HTTPSpnegoAuthenticator.java index 15aff90f1a..d8e11960d6 100644 --- a/src/main/java/com/amazon/dlic/auth/http/kerberos/HTTPSpnegoAuthenticator.java +++ b/src/main/java/com/amazon/dlic/auth/http/kerberos/HTTPSpnegoAuthenticator.java @@ -58,7 +58,7 @@ public class HTTPSpnegoAuthenticator implements HTTPAuthenticator { private static final String EMPTY_STRING = ""; - private static final Oid[] KRB_OIDS = new Oid[] {KrbConstants.SPNEGO, KrbConstants.KRB5MECH}; + private static final Oid[] KRB_OIDS = new Oid[] { KrbConstants.SPNEGO, KrbConstants.KRB5MECH }; protected final Logger log = LogManager.getLogger(this.getClass()); @@ -98,17 +98,17 @@ public Void run() { } } catch (Throwable e) { log.error("Unable to enable krb_debug due to ", e); - System.err.println("Unable to enable krb_debug due to "+ExceptionsHelper.stackTrace(e)); - System.out.println("Unable to enable krb_debug due to "+ExceptionsHelper.stackTrace(e)); + System.err.println("Unable to enable krb_debug due to " + ExceptionsHelper.stackTrace(e)); + System.out.println("Unable to enable krb_debug due to " + ExceptionsHelper.stackTrace(e)); } System.setProperty(KrbConstants.USE_SUBJECT_CREDS_ONLY_PROP, "false"); String krb5Path = krb5PathSetting; - if(!Strings.isNullOrEmpty(krb5Path)) { + if (!Strings.isNullOrEmpty(krb5Path)) { - if(Paths.get(krb5Path).isAbsolute()) { + if (Paths.get(krb5Path).isAbsolute()) { log.debug("krb5_filepath: {}", krb5Path); System.setProperty(KrbConstants.KRB5_CONF_PROP, krb5Path); } else { @@ -118,29 +118,36 @@ public Void run() { System.setProperty(KrbConstants.KRB5_CONF_PROP, krb5Path); } else { - if(Strings.isNullOrEmpty(System.getProperty(KrbConstants.KRB5_CONF_PROP))) { + if (Strings.isNullOrEmpty(System.getProperty(KrbConstants.KRB5_CONF_PROP))) { System.setProperty(KrbConstants.KRB5_CONF_PROP, "/etc/krb5.conf"); log.debug("krb5_filepath (was not set or configured, set to default): /etc/krb5.conf"); } } stripRealmFromPrincipalName = settings.getAsBoolean("strip_realm_from_principal", true); - acceptorPrincipal = new HashSet<>(settings.getAsList("plugins.security.kerberos.acceptor_principal", Collections.emptyList())); + acceptorPrincipal = new HashSet<>( + settings.getAsList("plugins.security.kerberos.acceptor_principal", Collections.emptyList()) + ); final String _acceptorKeyTabPath = settings.get("plugins.security.kerberos.acceptor_keytab_filepath"); - if(acceptorPrincipal == null || acceptorPrincipal.size() == 0) { + if (acceptorPrincipal == null || acceptorPrincipal.size() == 0) { log.error("acceptor_principal must not be null or empty. Kerberos authentication will not work"); acceptorPrincipal = null; } - if(_acceptorKeyTabPath == null || _acceptorKeyTabPath.length() == 0) { - log.error("plugins.security.kerberos.acceptor_keytab_filepath must not be null or empty. Kerberos authentication will not work"); + if (_acceptorKeyTabPath == null || _acceptorKeyTabPath.length() == 0) { + log.error( + "plugins.security.kerberos.acceptor_keytab_filepath must not be null or empty. Kerberos authentication will not work" + ); acceptorKeyTabPath = null; } else { acceptorKeyTabPath = configDir.resolve(settings.get("plugins.security.kerberos.acceptor_keytab_filepath")); - if(!Files.exists(acceptorKeyTabPath)) { - log.error("Unable to read keytab from {} - Maybe the file does not exist or is not readable. Kerberos authentication will not work", acceptorKeyTabPath); + if (!Files.exists(acceptorKeyTabPath)) { + log.error( + "Unable to read keytab from {} - Maybe the file does not exist or is not readable. Kerberos authentication will not work", + acceptorKeyTabPath + ); acceptorKeyTabPath = null; } } @@ -155,7 +162,9 @@ public Void run() { } catch (Throwable e) { log.error("Cannot construct HTTPSpnegoAuthenticator due to {}", e.getMessage(), e); - log.error("Please make sure you configured 'plugins.security.kerberos.acceptor_keytab_filepath' realtive to the ES config/ dir!"); + log.error( + "Please make sure you configured 'plugins.security.kerberos.acceptor_keytab_filepath' realtive to the ES config/ dir!" + ); throw e; } @@ -252,11 +261,13 @@ public GSSCredential run() throws GSSException { return new AuthCredentials("_incomplete_", (Object) outToken); } - final String username = ((SimpleUserPrincipal) principal).getName(); - if(username == null || username.length() == 0) { - log.error("Got empty or null user from kerberos. Normally this means that you acceptor principal {} does not match the server hostname", acceptorPrincipal); + if (username == null || username.length() == 0) { + log.error( + "Got empty or null user from kerberos. Normally this means that you acceptor principal {} does not match the server hostname", + acceptorPrincipal + ); } return new AuthCredentials(username, (Object) outToken).markComplete(); @@ -272,19 +283,22 @@ public GSSCredential run() throws GSSException { @Override public boolean reRequestAuthentication(final RestChannel channel, AuthCredentials creds) { - final BytesRestResponse wwwAuthenticateResponse; - XContentBuilder response = getNegotiateResponseBody(); + final BytesRestResponse wwwAuthenticateResponse; + XContentBuilder response = getNegotiateResponseBody(); - if (response != null) { - wwwAuthenticateResponse = new BytesRestResponse(RestStatus.UNAUTHORIZED, response); + if (response != null) { + wwwAuthenticateResponse = new BytesRestResponse(RestStatus.UNAUTHORIZED, response); } else { - wwwAuthenticateResponse = new BytesRestResponse(RestStatus.UNAUTHORIZED, EMPTY_STRING); + wwwAuthenticateResponse = new BytesRestResponse(RestStatus.UNAUTHORIZED, EMPTY_STRING); } - if(creds == null || creds.getNativeCredentials() == null) { + if (creds == null || creds.getNativeCredentials() == null) { wwwAuthenticateResponse.addHeader("WWW-Authenticate", "Negotiate"); } else { - wwwAuthenticateResponse.addHeader("WWW-Authenticate", "Negotiate "+Base64.getEncoder().encodeToString((byte[]) creds.getNativeCredentials())); + wwwAuthenticateResponse.addHeader( + "WWW-Authenticate", + "Negotiate " + Base64.getEncoder().encodeToString((byte[]) creds.getNativeCredentials()) + ); } channel.sendResponse(wwwAuthenticateResponse); return true; @@ -298,7 +312,7 @@ public String getType() { /** * This class gets a gss credential via a privileged action. */ - //borrowed from Apache Tomcat 8 http://svn.apache.org/repos/asf/tomcat/tc8.0.x/trunk/ + // borrowed from Apache Tomcat 8 http://svn.apache.org/repos/asf/tomcat/tc8.0.x/trunk/ private static class AcceptAction implements PrivilegedExceptionAction { GSSContext gssContext; @@ -316,7 +330,7 @@ public byte[] run() throws GSSException { } } - //borrowed from Apache Tomcat 8 http://svn.apache.org/repos/asf/tomcat/tc8.0.x/trunk/ + // borrowed from Apache Tomcat 8 http://svn.apache.org/repos/asf/tomcat/tc8.0.x/trunk/ private static class AuthenticateAction implements PrivilegedAction { private final Logger logger; @@ -336,7 +350,7 @@ public Principal run() { } } - //borrowed from Apache Tomcat 8 http://svn.apache.org/repos/asf/tomcat/tc8.0.x/trunk/ + // borrowed from Apache Tomcat 8 http://svn.apache.org/repos/asf/tomcat/tc8.0.x/trunk/ private static String getUsernameFromGSSContext(final GSSContext gssContext, final boolean strip, final Logger logger) { if (gssContext.isEstablished()) { GSSName gssName = null; @@ -359,26 +373,26 @@ private static String getUsernameFromGSSContext(final GSSContext gssContext, fin return null; } - private XContentBuilder getNegotiateResponseBody() { - try { - XContentBuilder negotiateResponseBody = XContentFactory.jsonBuilder(); - negotiateResponseBody.startObject(); - negotiateResponseBody.field("error"); - negotiateResponseBody.startObject(); - negotiateResponseBody.field("header"); - negotiateResponseBody.startObject(); - negotiateResponseBody.field("WWW-Authenticate", "Negotiate"); - negotiateResponseBody.endObject(); - negotiateResponseBody.endObject(); - negotiateResponseBody.endObject(); - return negotiateResponseBody; - } catch (Exception ex) { - log.error("Can't construct response body", ex); - return null; - } - } - - private static String stripRealmName(String name, boolean strip){ + private XContentBuilder getNegotiateResponseBody() { + try { + XContentBuilder negotiateResponseBody = XContentFactory.jsonBuilder(); + negotiateResponseBody.startObject(); + negotiateResponseBody.field("error"); + negotiateResponseBody.startObject(); + negotiateResponseBody.field("header"); + negotiateResponseBody.startObject(); + negotiateResponseBody.field("WWW-Authenticate", "Negotiate"); + negotiateResponseBody.endObject(); + negotiateResponseBody.endObject(); + negotiateResponseBody.endObject(); + return negotiateResponseBody; + } catch (Exception ex) { + log.error("Can't construct response body", ex); + return null; + } + } + + private static String stripRealmName(String name, boolean strip) { if (strip && name != null) { final int i = name.indexOf('@'); if (i > 0) { diff --git a/src/main/java/com/amazon/dlic/auth/http/kerberos/util/JaasKrbUtil.java b/src/main/java/com/amazon/dlic/auth/http/kerberos/util/JaasKrbUtil.java index 6574728da4..619c780027 100644 --- a/src/main/java/com/amazon/dlic/auth/http/kerberos/util/JaasKrbUtil.java +++ b/src/main/java/com/amazon/dlic/auth/http/kerberos/util/JaasKrbUtil.java @@ -38,177 +38,177 @@ */ public final class JaasKrbUtil { - private static boolean debug = false; - - private JaasKrbUtil() { - } - - public static void setDebug(final boolean debug) { - JaasKrbUtil.debug = debug; - } - - public static Subject loginUsingPassword(final String principal, final String password) throws LoginException { - final Set principals = new HashSet(); - principals.add(new KerberosPrincipal(principal)); - - final Subject subject = new Subject(false, principals, new HashSet(), new HashSet()); - - final Configuration conf = usePassword(principal); - final String confName = "PasswordConf"; - final CallbackHandler callback = new KrbCallbackHandler(principal, password); - final LoginContext loginContext = new LoginContext(confName, subject, callback, conf); - loginContext.login(); - return loginContext.getSubject(); - } - - public static Subject loginUsingTicketCache(final String principal, final Path cachePath) throws LoginException { - final Set principals = new HashSet(); - principals.add(new KerberosPrincipal(principal)); - - final Subject subject = new Subject(false, principals, new HashSet(), new HashSet()); - - final Configuration conf = useTicketCache(principal, cachePath); - final String confName = "TicketCacheConf"; - final LoginContext loginContext = new LoginContext(confName, subject, null, conf); - loginContext.login(); - return loginContext.getSubject(); - } - - public static Subject loginUsingKeytab(final Set principalAsStrings, final Path keytabPath, final boolean initiator) throws LoginException { - final Set principals = new HashSet(); - - for(String p: principalAsStrings) { - principals.add(new KerberosPrincipal(p)); - } - - - final Subject subject = new Subject(false, principals, new HashSet(), new HashSet()); - - final Configuration conf = useKeytab("*", keytabPath, initiator); - final String confName = "KeytabConf"; - final LoginContext loginContext = new LoginContext(confName, subject, null, conf); - loginContext.login(); - return loginContext.getSubject(); - } - - public static Configuration usePassword(final String principal) { - return new PasswordJaasConf(principal); - } - - public static Configuration useTicketCache(final String principal, final Path credentialPath) { - return new TicketCacheJaasConf(principal, credentialPath); - } - - public static Configuration useKeytab(final String principal, final Path keytabPath, final boolean initiator) { - return new KeytabJaasConf(principal, keytabPath, initiator); - } - - private static String getKrb5LoginModuleName() { - return System.getProperty("java.vendor").contains("IBM") ? "com.ibm.security.auth.module.Krb5LoginModule" - : "com.sun.security.auth.module.Krb5LoginModule"; - } - - static class KeytabJaasConf extends Configuration { - private final String principal; - private final Path keytabPath; - private final boolean initiator; - - public KeytabJaasConf(final String principal, final Path keytab, final boolean initiator) { - this.principal = principal; - this.keytabPath = keytab; - this.initiator = initiator; - } - - @Override - public AppConfigurationEntry[] getAppConfigurationEntry(final String name) { - final Map options = new HashMap(); - options.put("keyTab", keytabPath.toAbsolutePath().toString()); - options.put("principal", principal); - options.put("useKeyTab", "true"); - options.put("storeKey", "true"); - options.put("doNotPrompt", "true"); - options.put("renewTGT", "false"); - options.put("refreshKrb5Config", "true"); - options.put("isInitiator", String.valueOf(initiator)); - options.put("debug", String.valueOf(debug)); - - return new AppConfigurationEntry[] { new AppConfigurationEntry(getKrb5LoginModuleName(), - AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options) }; - } - } - - static class TicketCacheJaasConf extends Configuration { - private final String principal; - private final Path clientCredentialPath; - - public TicketCacheJaasConf(final String principal, final Path clientCredentialPath) { - this.principal = principal; - this.clientCredentialPath = clientCredentialPath; - } - - @Override - public AppConfigurationEntry[] getAppConfigurationEntry(final String name) { - final Map options = new HashMap(); - options.put("principal", principal); - options.put("storeKey", "false"); - options.put("doNotPrompt", "false"); - options.put("useTicketCache", "true"); - options.put("renewTGT", "true"); - options.put("refreshKrb5Config", "true"); - options.put("isInitiator", "true"); - options.put("ticketCache", clientCredentialPath.toAbsolutePath().toString()); - options.put("debug", String.valueOf(debug)); - - return new AppConfigurationEntry[] { new AppConfigurationEntry(getKrb5LoginModuleName(), - AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options) }; - } - } - - static class PasswordJaasConf extends Configuration { - private final String principal; - - public PasswordJaasConf(final String principal) { - this.principal = principal; - } - - @Override - public AppConfigurationEntry[] getAppConfigurationEntry(final String name) { - final Map options = new HashMap<>(); - options.put("principal", principal); - options.put("storeKey", "true"); - options.put("useTicketCache", "true"); - options.put("useKeyTab", "false"); - options.put("renewTGT", "true"); - options.put("refreshKrb5Config", "true"); - options.put("isInitiator", "true"); - options.put("debug", String.valueOf(debug)); - - return new AppConfigurationEntry[] { new AppConfigurationEntry(getKrb5LoginModuleName(), - AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options) }; - } - } - - public static class KrbCallbackHandler implements CallbackHandler { - private final String principal; - private final String password; - - public KrbCallbackHandler(final String principal, final String password) { - this.principal = principal; - this.password = password; - } - - @Override - public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException { - for (int i = 0; i < callbacks.length; i++) { - if (callbacks[i] instanceof PasswordCallback) { - final PasswordCallback pc = (PasswordCallback) callbacks[i]; - if (pc.getPrompt().contains(principal)) { - pc.setPassword(password.toCharArray()); - break; - } - } - } - } - } + private static boolean debug = false; + + private JaasKrbUtil() {} + + public static void setDebug(final boolean debug) { + JaasKrbUtil.debug = debug; + } + + public static Subject loginUsingPassword(final String principal, final String password) throws LoginException { + final Set principals = new HashSet(); + principals.add(new KerberosPrincipal(principal)); + + final Subject subject = new Subject(false, principals, new HashSet(), new HashSet()); + + final Configuration conf = usePassword(principal); + final String confName = "PasswordConf"; + final CallbackHandler callback = new KrbCallbackHandler(principal, password); + final LoginContext loginContext = new LoginContext(confName, subject, callback, conf); + loginContext.login(); + return loginContext.getSubject(); + } + + public static Subject loginUsingTicketCache(final String principal, final Path cachePath) throws LoginException { + final Set principals = new HashSet(); + principals.add(new KerberosPrincipal(principal)); + + final Subject subject = new Subject(false, principals, new HashSet(), new HashSet()); + + final Configuration conf = useTicketCache(principal, cachePath); + final String confName = "TicketCacheConf"; + final LoginContext loginContext = new LoginContext(confName, subject, null, conf); + loginContext.login(); + return loginContext.getSubject(); + } + + public static Subject loginUsingKeytab(final Set principalAsStrings, final Path keytabPath, final boolean initiator) + throws LoginException { + final Set principals = new HashSet(); + + for (String p : principalAsStrings) { + principals.add(new KerberosPrincipal(p)); + } + + final Subject subject = new Subject(false, principals, new HashSet(), new HashSet()); + + final Configuration conf = useKeytab("*", keytabPath, initiator); + final String confName = "KeytabConf"; + final LoginContext loginContext = new LoginContext(confName, subject, null, conf); + loginContext.login(); + return loginContext.getSubject(); + } + + public static Configuration usePassword(final String principal) { + return new PasswordJaasConf(principal); + } + + public static Configuration useTicketCache(final String principal, final Path credentialPath) { + return new TicketCacheJaasConf(principal, credentialPath); + } + + public static Configuration useKeytab(final String principal, final Path keytabPath, final boolean initiator) { + return new KeytabJaasConf(principal, keytabPath, initiator); + } + + private static String getKrb5LoginModuleName() { + return System.getProperty("java.vendor").contains("IBM") + ? "com.ibm.security.auth.module.Krb5LoginModule" + : "com.sun.security.auth.module.Krb5LoginModule"; + } + + static class KeytabJaasConf extends Configuration { + private final String principal; + private final Path keytabPath; + private final boolean initiator; + + public KeytabJaasConf(final String principal, final Path keytab, final boolean initiator) { + this.principal = principal; + this.keytabPath = keytab; + this.initiator = initiator; + } + + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(final String name) { + final Map options = new HashMap(); + options.put("keyTab", keytabPath.toAbsolutePath().toString()); + options.put("principal", principal); + options.put("useKeyTab", "true"); + options.put("storeKey", "true"); + options.put("doNotPrompt", "true"); + options.put("renewTGT", "false"); + options.put("refreshKrb5Config", "true"); + options.put("isInitiator", String.valueOf(initiator)); + options.put("debug", String.valueOf(debug)); + + return new AppConfigurationEntry[] { + new AppConfigurationEntry(getKrb5LoginModuleName(), AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options) }; + } + } + + static class TicketCacheJaasConf extends Configuration { + private final String principal; + private final Path clientCredentialPath; + + public TicketCacheJaasConf(final String principal, final Path clientCredentialPath) { + this.principal = principal; + this.clientCredentialPath = clientCredentialPath; + } + + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(final String name) { + final Map options = new HashMap(); + options.put("principal", principal); + options.put("storeKey", "false"); + options.put("doNotPrompt", "false"); + options.put("useTicketCache", "true"); + options.put("renewTGT", "true"); + options.put("refreshKrb5Config", "true"); + options.put("isInitiator", "true"); + options.put("ticketCache", clientCredentialPath.toAbsolutePath().toString()); + options.put("debug", String.valueOf(debug)); + + return new AppConfigurationEntry[] { + new AppConfigurationEntry(getKrb5LoginModuleName(), AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options) }; + } + } + + static class PasswordJaasConf extends Configuration { + private final String principal; + + public PasswordJaasConf(final String principal) { + this.principal = principal; + } + + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(final String name) { + final Map options = new HashMap<>(); + options.put("principal", principal); + options.put("storeKey", "true"); + options.put("useTicketCache", "true"); + options.put("useKeyTab", "false"); + options.put("renewTGT", "true"); + options.put("refreshKrb5Config", "true"); + options.put("isInitiator", "true"); + options.put("debug", String.valueOf(debug)); + + return new AppConfigurationEntry[] { + new AppConfigurationEntry(getKrb5LoginModuleName(), AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options) }; + } + } + + public static class KrbCallbackHandler implements CallbackHandler { + private final String principal; + private final String password; + + public KrbCallbackHandler(final String principal, final String password) { + this.principal = principal; + this.password = password; + } + + @Override + public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof PasswordCallback) { + final PasswordCallback pc = (PasswordCallback) callbacks[i]; + if (pc.getPrompt().contains(principal)) { + pc.setPassword(password.toCharArray()); + break; + } + } + } + } + } } diff --git a/src/main/java/com/amazon/dlic/auth/http/kerberos/util/KrbConstants.java b/src/main/java/com/amazon/dlic/auth/http/kerberos/util/KrbConstants.java index 801ca78e27..a8a57633dc 100644 --- a/src/main/java/com/amazon/dlic/auth/http/kerberos/util/KrbConstants.java +++ b/src/main/java/com/amazon/dlic/auth/http/kerberos/util/KrbConstants.java @@ -16,28 +16,27 @@ public final class KrbConstants { - static { - Oid spnegoTmp = null; - Oid krbTmp = null; - try { - spnegoTmp = new Oid("1.3.6.1.5.5.2"); - krbTmp = new Oid("1.2.840.113554.1.2.2"); - } catch (final GSSException e) { - - } - SPNEGO = spnegoTmp; - KRB5MECH = krbTmp; - } - - public static final Oid SPNEGO; - public static final Oid KRB5MECH; - public static final String KRB5_CONF_PROP = "java.security.krb5.conf"; - public static final String JAAS_LOGIN_CONF_PROP = "java.security.auth.login.config"; - public static final String USE_SUBJECT_CREDS_ONLY_PROP = "javax.security.auth.useSubjectCredsOnly"; - public static final String NEGOTIATE = "Negotiate"; - public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; - - private KrbConstants() { - } + static { + Oid spnegoTmp = null; + Oid krbTmp = null; + try { + spnegoTmp = new Oid("1.3.6.1.5.5.2"); + krbTmp = new Oid("1.2.840.113554.1.2.2"); + } catch (final GSSException e) { + + } + SPNEGO = spnegoTmp; + KRB5MECH = krbTmp; + } + + public static final Oid SPNEGO; + public static final Oid KRB5MECH; + public static final String KRB5_CONF_PROP = "java.security.krb5.conf"; + public static final String JAAS_LOGIN_CONF_PROP = "java.security.auth.login.config"; + public static final String USE_SUBJECT_CREDS_ONLY_PROP = "javax.security.auth.useSubjectCredsOnly"; + public static final String NEGOTIATE = "Negotiate"; + public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; + + private KrbConstants() {} } diff --git a/src/main/java/com/amazon/dlic/auth/http/saml/AuthTokenProcessorHandler.java b/src/main/java/com/amazon/dlic/auth/http/saml/AuthTokenProcessorHandler.java index 4ab5673adb..1c49d10b2e 100644 --- a/src/main/java/com/amazon/dlic/auth/http/saml/AuthTokenProcessorHandler.java +++ b/src/main/java/com/amazon/dlic/auth/http/saml/AuthTokenProcessorHandler.java @@ -83,8 +83,7 @@ class AuthTokenProcessorHandler { private JsonMapObjectReaderWriter jsonMapReaderWriter = new JsonMapObjectReaderWriter(); private Pattern samlRolesSeparatorPattern; - AuthTokenProcessorHandler(Settings settings, Settings jwtSettings, Saml2SettingsProvider saml2SettingsProvider) - throws Exception { + AuthTokenProcessorHandler(Settings settings, Settings jwtSettings, Saml2SettingsProvider saml2SettingsProvider) throws Exception { this.saml2SettingsProvider = saml2SettingsProvider; this.jwtRolesKey = jwtSettings.get("roles_key", "roles"); @@ -133,8 +132,8 @@ boolean handle(RestRequest restRequest, RestChannel restChannel) throws Exceptio return AccessController.doPrivileged(new PrivilegedExceptionAction() { @Override - public Boolean run() throws XPathExpressionException, SamlConfigException, IOException, - ParserConfigurationException, SAXException, SettingsException { + public Boolean run() throws XPathExpressionException, SamlConfigException, IOException, ParserConfigurationException, + SAXException, SettingsException { return handleLowLevel(restRequest, restChannel); } }); @@ -147,17 +146,23 @@ public Boolean run() throws XPathExpressionException, SamlConfigException, IOExc } } - private AuthTokenProcessorAction.Response handleImpl(RestRequest restRequest, RestChannel restChannel, - String samlResponseBase64, String samlRequestId, String acsEndpoint, Saml2Settings saml2Settings) - throws XPathExpressionException, ParserConfigurationException, SAXException, IOException, - SettingsException { + private AuthTokenProcessorAction.Response handleImpl( + RestRequest restRequest, + RestChannel restChannel, + String samlResponseBase64, + String samlRequestId, + String acsEndpoint, + Saml2Settings saml2Settings + ) throws XPathExpressionException, ParserConfigurationException, SAXException, IOException, SettingsException { if (token_log.isDebugEnabled()) { try { - token_log.debug("SAMLResponse for {}\n{}", samlRequestId, new String(Util.base64decoder(samlResponseBase64), StandardCharsets.UTF_8)); + token_log.debug( + "SAMLResponse for {}\n{}", + samlRequestId, + new String(Util.base64decoder(samlResponseBase64), StandardCharsets.UTF_8) + ); } catch (Exception e) { - token_log.warn( - "SAMLResponse for {} cannot be decoded from base64\n{}", - samlRequestId, samlResponseBase64, e); + token_log.warn("SAMLResponse for {} cannot be decoded from base64\n{}", samlRequestId, samlResponseBase64, e); } } @@ -185,20 +190,23 @@ private AuthTokenProcessorAction.Response handleImpl(RestRequest restRequest, Re } } - private boolean handleLowLevel(RestRequest restRequest, RestChannel restChannel) throws SamlConfigException, - IOException, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException { + private boolean handleLowLevel(RestRequest restRequest, RestChannel restChannel) throws SamlConfigException, IOException, + XPathExpressionException, ParserConfigurationException, SAXException, SettingsException { try { if (restRequest.getXContentType() != XContentType.JSON) { throw new OpenSearchSecurityException( - "/_opendistro/_security/api/authtoken expects content with type application/json", - RestStatus.UNSUPPORTED_MEDIA_TYPE); + "/_opendistro/_security/api/authtoken expects content with type application/json", + RestStatus.UNSUPPORTED_MEDIA_TYPE + ); } if (restRequest.method() != Method.POST) { - throw new OpenSearchSecurityException("/_opendistro/_security/api/authtoken expects POST requests", - RestStatus.METHOD_NOT_ALLOWED); + throw new OpenSearchSecurityException( + "/_opendistro/_security/api/authtoken expects POST requests", + RestStatus.METHOD_NOT_ALLOWED + ); } Saml2Settings saml2Settings = this.saml2SettingsProvider.getCached(); @@ -214,24 +222,28 @@ private boolean handleLowLevel(RestRequest restRequest, RestChannel restChannel) if (((ObjectNode) jsonRoot).get("SAMLResponse") == null) { log.warn("SAMLResponse is missing from request "); - throw new OpenSearchSecurityException("SAMLResponse is missing from request", - RestStatus.BAD_REQUEST); + throw new OpenSearchSecurityException("SAMLResponse is missing from request", RestStatus.BAD_REQUEST); } String samlResponseBase64 = ((ObjectNode) jsonRoot).get("SAMLResponse").asText(); String samlRequestId = ((ObjectNode) jsonRoot).get("RequestId") != null - ? ((ObjectNode) jsonRoot).get("RequestId").textValue() - : null; + ? ((ObjectNode) jsonRoot).get("RequestId").textValue() + : null; String acsEndpoint = saml2Settings.getSpAssertionConsumerServiceUrl().toString(); - if (((ObjectNode) jsonRoot).get("acsEndpoint") != null - && ((ObjectNode) jsonRoot).get("acsEndpoint").textValue() != null) { + if (((ObjectNode) jsonRoot).get("acsEndpoint") != null && ((ObjectNode) jsonRoot).get("acsEndpoint").textValue() != null) { acsEndpoint = getAbsoluteAcsEndpoint(((ObjectNode) jsonRoot).get("acsEndpoint").textValue()); } - AuthTokenProcessorAction.Response responseBody = this.handleImpl(restRequest, restChannel, - samlResponseBase64, samlRequestId, acsEndpoint, saml2Settings); + AuthTokenProcessorAction.Response responseBody = this.handleImpl( + restRequest, + restChannel, + samlResponseBase64, + samlRequestId, + acsEndpoint, + saml2Settings + ); if (responseBody == null) { return false; @@ -239,16 +251,14 @@ private boolean handleLowLevel(RestRequest restRequest, RestChannel restChannel) String responseBodyString = DefaultObjectMapper.objectMapper.writeValueAsString(responseBody); - BytesRestResponse authenticateResponse = new BytesRestResponse(RestStatus.OK, "application/json", - responseBodyString); + BytesRestResponse authenticateResponse = new BytesRestResponse(RestStatus.OK, "application/json", responseBodyString); restChannel.sendResponse(authenticateResponse); return true; } catch (JsonProcessingException e) { log.warn("Error while parsing JSON for /_opendistro/_security/api/authtoken", e); - BytesRestResponse authenticateResponse = new BytesRestResponse(RestStatus.BAD_REQUEST, - "JSON could not be parsed"); + BytesRestResponse authenticateResponse = new BytesRestResponse(RestStatus.BAD_REQUEST, "JSON could not be parsed"); restChannel.sendResponse(authenticateResponse); return true; } @@ -274,7 +284,8 @@ JsonWebKey createJwkFromSettings(Settings settings, Settings jwtSettings) throws if (jwkSettings.isEmpty()) { throw new Exception( - "Settings for key exchange missing. Please specify at least the option exchange_key with a shared secret."); + "Settings for key exchange missing. Please specify at least the option exchange_key with a shared secret." + ); } JsonWebKey jwk = new JsonWebKey(); @@ -319,8 +330,14 @@ private String createJwt(SamlResponse samlResponse) throws Exception { String encodedJwt = this.jwtProducer.processJwt(jwt); if (token_log.isDebugEnabled()) { - token_log.debug("Created JWT: " + encodedJwt + "\n" + jsonMapReaderWriter.toJson(jwt.getJwsHeaders()) + "\n" - + JwtUtils.claimsToJson(jwt.getClaims())); + token_log.debug( + "Created JWT: " + + encodedJwt + + "\n" + + jsonMapReaderWriter.toJson(jwt.getJwsHeaders()) + + "\n" + + JwtUtils.claimsToJson(jwt.getClaims()) + ); } return encodedJwt; @@ -335,8 +352,7 @@ private long getJwtExpiration(SamlResponse samlResponse) throws Exception { if (sessionNotOnOrAfter != null) { return sessionNotOnOrAfter.getMillis() / 1000 + this.expiryOffset; } else { - throw new Exception( - "Error while determining JWT expiration time: SamlResponse did not contain sessionNotOnOrAfter value"); + throw new Exception("Error while determining JWT expiration time: SamlResponse did not contain sessionNotOnOrAfter value"); } } else { // AUTO @@ -419,9 +435,9 @@ private String[] extractRoles(SamlResponse samlResponse) throws XPathExpressionE private List splitRoles(List values) { return values.stream() - .flatMap(v -> samlRolesSeparatorPattern.splitAsStream(v)) - .filter(r -> !Strings.isNullOrEmpty(r)) - .collect(Collectors.toList()); + .flatMap(v -> samlRolesSeparatorPattern.splitAsStream(v)) + .filter(r -> !Strings.isNullOrEmpty(r)) + .collect(Collectors.toList()); } private String getAbsoluteAcsEndpoint(String acsEndpoint) { @@ -440,7 +456,9 @@ private String getAbsoluteAcsEndpoint(String acsEndpoint) { } private enum ExpiryBaseValue { - AUTO, NOW, SESSION + AUTO, + NOW, + SESSION } public JsonWebKey getSigningKey() { diff --git a/src/main/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticator.java b/src/main/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticator.java index 7015017fc7..d3068b852a 100644 --- a/src/main/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticator.java +++ b/src/main/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticator.java @@ -68,7 +68,6 @@ import static org.opensearch.security.OpenSearchSecurityPlugin.LEGACY_OPENDISTRO_PREFIX; import static org.opensearch.security.OpenSearchSecurityPlugin.PLUGINS_PREFIX; - public class HTTPSamlAuthenticator implements HTTPAuthenticator, Destroyable { protected final static Logger log = LogManager.getLogger(HTTPSamlAuthenticator.class); @@ -78,9 +77,8 @@ public class HTTPSamlAuthenticator implements HTTPAuthenticator, Destroyable { private static final String API_AUTHTOKEN_SUFFIX = "api/authtoken"; private static final String AUTHINFO_SUFFIX = "authinfo"; - private static final String REGEX_PATH_PREFIX = "/(" + LEGACY_OPENDISTRO_PREFIX + "|" + PLUGINS_PREFIX + ")/" +"(.*)"; - private static final Pattern PATTERN_PATH_PREFIX = Pattern.compile(REGEX_PATH_PREFIX); - + private static final String REGEX_PATH_PREFIX = "/(" + LEGACY_OPENDISTRO_PREFIX + "|" + PLUGINS_PREFIX + ")/" + "(.*)"; + private static final Pattern PATTERN_PATH_PREFIX = Pattern.compile(REGEX_PATH_PREFIX); private static boolean openSamlInitialized = false; @@ -132,13 +130,15 @@ public HTTPSamlAuthenticator(final Settings settings, final Path configPath) { try { this.saml2SettingsProvider.getCached(); } catch (Exception e) { - log.debug("Exception while initializing Saml2SettingsProvider. Possibly, the IdP is unreachable right now. This is recoverable by a meta data refresh.", e); + log.debug( + "Exception while initializing Saml2SettingsProvider. Possibly, the IdP is unreachable right now. This is recoverable by a meta data refresh.", + e + ); } this.jwtSettings = this.createJwtAuthenticatorSettings(settings); - this.authTokenProcessorHandler = new AuthTokenProcessorHandler(settings, jwtSettings, - this.saml2SettingsProvider); + this.authTokenProcessorHandler = new AuthTokenProcessorHandler(settings, jwtSettings, this.saml2SettingsProvider); this.httpJwtAuthenticator = new HTTPJwtAuthenticator(this.jwtSettings, configPath); @@ -149,8 +149,7 @@ public HTTPSamlAuthenticator(final Settings settings, final Path configPath) { } @Override - public AuthCredentials extractCredentials(RestRequest restRequest, ThreadContext threadContext) - throws OpenSearchSecurityException { + public AuthCredentials extractCredentials(RestRequest restRequest, ThreadContext threadContext) throws OpenSearchSecurityException { Matcher matcher = PATTERN_PATH_PREFIX.matcher(restRequest.path()); final String suffix = matcher.matches() ? matcher.group(2) : null; if (API_AUTHTOKEN_SUFFIX.equals(suffix)) { @@ -177,8 +176,7 @@ public boolean reRequestAuthentication(RestChannel restChannel, AuthCredentials RestRequest restRequest = restChannel.request(); Matcher matcher = PATTERN_PATH_PREFIX.matcher(restRequest.path()); final String suffix = matcher.matches() ? matcher.group(2) : null; - if (API_AUTHTOKEN_SUFFIX.equals(suffix) - && this.authTokenProcessorHandler.handle(restRequest, restChannel)){ + if (API_AUTHTOKEN_SUFFIX.equals(suffix) && this.authTokenProcessorHandler.handle(restRequest, restChannel)) { return true; } @@ -200,9 +198,12 @@ private String getWwwAuthenticateHeader(Saml2Settings saml2Settings) throws Exce AuthnRequest authnRequest = this.buildAuthnRequest(saml2Settings); return "X-Security-IdP realm=\"OpenSearch Security\" location=\"" - + StringEscapeUtils.escapeJava(getSamlRequestRedirectBindingLocation(IdpEndpointType.SSO, saml2Settings, - authnRequest.getEncodedAuthnRequest(true))) - + "\" requestId=\"" + StringEscapeUtils.escapeJava(authnRequest.getId()) + "\""; + + StringEscapeUtils.escapeJava( + getSamlRequestRedirectBindingLocation(IdpEndpointType.SSO, saml2Settings, authnRequest.getEncodedAuthnRequest(true)) + ) + + "\" requestId=\"" + + StringEscapeUtils.escapeJava(authnRequest.getId()) + + "\""; } private AuthnRequest buildAuthnRequest(Saml2Settings saml2Settings) { @@ -221,12 +222,16 @@ private AuthnRequest buildAuthnRequest(Saml2Settings saml2Settings) { private PrivateKey getSpSignaturePrivateKey(Settings settings, Path configPath) throws Exception { try { - PrivateKey result = PemKeyReader.loadKeyFromStream(settings.get("sp.signature_private_key_password"), - PemKeyReader.resolveStream("sp.signature_private_key", settings)); + PrivateKey result = PemKeyReader.loadKeyFromStream( + settings.get("sp.signature_private_key_password"), + PemKeyReader.resolveStream("sp.signature_private_key", settings) + ); if (result == null) { - result = PemKeyReader.loadKeyFromFile(settings.get("sp.signature_private_key_password"), - PemKeyReader.resolve("sp.signature_private_key_filepath", settings, configPath, false)); + result = PemKeyReader.loadKeyFromFile( + settings.get("sp.signature_private_key_password"), + PemKeyReader.resolve("sp.signature_private_key_filepath", settings, configPath, false) + ); } return result; @@ -297,8 +302,7 @@ public Void run() throws InitializationException { } @SuppressWarnings("removal") - private MetadataResolver createMetadataResolver(final Settings settings, final Path configPath) - throws Exception { + private MetadataResolver createMetadataResolver(final Settings settings, final Path configPath) throws Exception { final AbstractMetadataResolver metadataResolver; final String idpMetadataUrl = settings.get(IDP_METADATA_URL); @@ -311,7 +315,9 @@ private MetadataResolver createMetadataResolver(final Settings settings, final P } else if (idpMetadataBody != null) { metadataResolver = new DOMMetadataResolver(getMetadataDOM(idpMetadataBody)); } else { - throw new Exception(String.format("One of %s, %s or %s must be configured", IDP_METADATA_URL, IDP_METADATA_FILE, IDP_METADATA_CONTENT)); + throw new Exception( + String.format("One of %s, %s or %s must be configured", IDP_METADATA_URL, IDP_METADATA_FILE, IDP_METADATA_CONTENT) + ); } metadataResolver.setId(HTTPSamlAuthenticator.class.getName() + "_" + (++resolverIdCounter)); @@ -378,14 +384,12 @@ String buildLogoutUrl(AuthCredentials authCredentials) { String nameIdClaim = this.subjectKey == null ? "sub" : "saml_ni"; String nameId = authCredentials.getAttributes().get("attr.jwt." + nameIdClaim); - String nameIdFormat = SamlNameIdFormat - .getByShortName(authCredentials.getAttributes().get("attr.jwt.saml_nif")).getUri(); + String nameIdFormat = SamlNameIdFormat.getByShortName(authCredentials.getAttributes().get("attr.jwt.saml_nif")).getUri(); String sessionIndex = authCredentials.getAttributes().get("attr.jwt.saml_si"); LogoutRequest logoutRequest = new LogoutRequest(saml2Settings, null, nameId, sessionIndex, nameIdFormat); - return getSamlRequestRedirectBindingLocation(IdpEndpointType.SLO, saml2Settings, - logoutRequest.getEncodedLogoutRequest(true)); + return getSamlRequestRedirectBindingLocation(IdpEndpointType.SLO, saml2Settings, logoutRequest.getEncodedLogoutRequest(true)); } catch (Exception e) { log.error("Error while creating logout URL. Logout will be not available", e); @@ -398,8 +402,8 @@ private void initLogoutUrl(RestRequest restRequest, ThreadContext threadContext, threadContext.putTransient(ConfigConstants.SSO_LOGOUT_URL, buildLogoutUrl(authCredentials)); } - private String getSamlRequestRedirectBindingLocation(IdpEndpointType idpEndpointType, Saml2Settings saml2Settings, - String samlRequest) throws Exception { + private String getSamlRequestRedirectBindingLocation(IdpEndpointType idpEndpointType, Saml2Settings saml2Settings, String samlRequest) + throws Exception { URL idpUrl = getIdpUrl(idpEndpointType, saml2Settings); @@ -417,8 +421,7 @@ private String getSamlRequestQueryString(String samlRequest) throws Exception { return "SAMLRequest=" + Util.urlEncoder(samlRequest); } - String queryString = "SAMLRequest=" + Util.urlEncoder(samlRequest) + "&SigAlg=" - + Util.urlEncoder(this.spSignatureAlgorithm); + String queryString = "SAMLRequest=" + Util.urlEncoder(samlRequest) + "&SigAlg=" + Util.urlEncoder(this.spSignatureAlgorithm); String signature = getSamlRequestQueryStringSignature(queryString); @@ -429,8 +432,7 @@ private String getSamlRequestQueryString(String samlRequest) throws Exception { private String getSamlRequestQueryStringSignature(String samlRequestQueryString) throws Exception { try { - return Util.base64encoder( - Util.sign(samlRequestQueryString, this.spSignaturePrivateKey, this.spSignatureAlgorithm)); + return Util.base64encoder(Util.sign(samlRequestQueryString, this.spSignaturePrivateKey, this.spSignatureAlgorithm)); } catch (Exception e) { throw new Exception("Error while signing SAML request", e); } @@ -462,8 +464,7 @@ protected KeyProvider initKeyProvider(Settings settings, Path configPath) throws return new KeyProvider() { @Override - public JsonWebKey getKeyAfterRefresh(String kid) - throws AuthenticatorUnavailableException, BadCredentialsException { + public JsonWebKey getKeyAfterRefresh(String kid) throws AuthenticatorUnavailableException, BadCredentialsException { return authTokenProcessorHandler.getSigningKey(); } @@ -477,6 +478,7 @@ public JsonWebKey getKey(String kid) throws AuthenticatorUnavailableException, B } private enum IdpEndpointType { - SSO, SLO + SSO, + SLO } } diff --git a/src/main/java/com/amazon/dlic/auth/http/saml/Saml2SettingsProvider.java b/src/main/java/com/amazon/dlic/auth/http/saml/Saml2SettingsProvider.java index 5274569502..881c9c3553 100644 --- a/src/main/java/com/amazon/dlic/auth/http/saml/Saml2SettingsProvider.java +++ b/src/main/java/com/amazon/dlic/auth/http/saml/Saml2SettingsProvider.java @@ -68,19 +68,23 @@ Saml2Settings get() throws SamlConfigException { try { HashMap configProperties = new HashMap<>(); - EntityDescriptor entityDescriptor = this.metadataResolver - .resolveSingle(new CriteriaSet(new EntityIdCriterion(this.idpEntityId))); + EntityDescriptor entityDescriptor = this.metadataResolver.resolveSingle( + new CriteriaSet(new EntityIdCriterion(this.idpEntityId)) + ); if (entityDescriptor == null) { throw new SamlConfigException("Could not find entity descriptor for " + this.idpEntityId); } - IDPSSODescriptor idpSsoDescriptor = entityDescriptor - .getIDPSSODescriptor("urn:oasis:names:tc:SAML:2.0:protocol"); + IDPSSODescriptor idpSsoDescriptor = entityDescriptor.getIDPSSODescriptor("urn:oasis:names:tc:SAML:2.0:protocol"); if (idpSsoDescriptor == null) { - throw new SamlConfigException("Could not find IDPSSODescriptor supporting SAML 2.0 in " - + this.idpEntityId + "; role descriptors: " + entityDescriptor.getRoleDescriptors()); + throw new SamlConfigException( + "Could not find IDPSSODescriptor supporting SAML 2.0 in " + + this.idpEntityId + + "; role descriptors: " + + entityDescriptor.getRoleDescriptors() + ); } initIdpEndpoints(idpSsoDescriptor, configProperties); @@ -121,8 +125,7 @@ Saml2Settings getCached() throws SamlConfigException { private boolean isUpdateRequired() { RefreshableMetadataResolver refreshableMetadataResolver = (RefreshableMetadataResolver) this.metadataResolver; - if (this.cachedSaml2Settings == null || this.metadataUpdateTime == null - || refreshableMetadataResolver.getLastUpdate() == null) { + if (this.cachedSaml2Settings == null || this.metadataUpdateTime == null || refreshableMetadataResolver.getLastUpdate() == null) { return true; } @@ -140,35 +143,39 @@ private void initMisc(HashMap configProperties) { } private void initSpEndpoints(HashMap configProperties) { - configProperties.put(SettingsBuilder.SP_ASSERTION_CONSUMER_SERVICE_URL_PROPERTY_KEY, - this.buildAssertionConsumerEndpoint(this.opensearchSettings.get("kibana_url"))); - configProperties.put(SettingsBuilder.SP_ASSERTION_CONSUMER_SERVICE_BINDING_PROPERTY_KEY, - "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"); + configProperties.put( + SettingsBuilder.SP_ASSERTION_CONSUMER_SERVICE_URL_PROPERTY_KEY, + this.buildAssertionConsumerEndpoint(this.opensearchSettings.get("kibana_url")) + ); + configProperties.put( + SettingsBuilder.SP_ASSERTION_CONSUMER_SERVICE_BINDING_PROPERTY_KEY, + "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + ); configProperties.put(SettingsBuilder.SP_ENTITYID_PROPERTY_KEY, this.opensearchSettings.get("sp.entity_id")); } - private void initIdpEndpoints(IDPSSODescriptor idpSsoDescriptor, HashMap configProperties) - throws SamlConfigException { - SingleSignOnService singleSignOnService = this.findSingleSignOnService(idpSsoDescriptor, - "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + private void initIdpEndpoints(IDPSSODescriptor idpSsoDescriptor, HashMap configProperties) throws SamlConfigException { + SingleSignOnService singleSignOnService = this.findSingleSignOnService( + idpSsoDescriptor, + "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + ); - configProperties.put(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY, - singleSignOnService.getLocation()); - configProperties.put(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY, - singleSignOnService.getBinding()); + configProperties.put(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_URL_PROPERTY_KEY, singleSignOnService.getLocation()); + configProperties.put(SettingsBuilder.IDP_SINGLE_SIGN_ON_SERVICE_BINDING_PROPERTY_KEY, singleSignOnService.getBinding()); configProperties.put(SettingsBuilder.IDP_ENTITYID_PROPERTY_KEY, this.opensearchSettings.get("idp.entity_id")); - SingleLogoutService singleLogoutService = this.findSingleLogoutService(idpSsoDescriptor, - "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); + SingleLogoutService singleLogoutService = this.findSingleLogoutService( + idpSsoDescriptor, + "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + ); if (singleLogoutService != null) { - configProperties.put(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY, - singleLogoutService.getLocation()); - configProperties.put(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY, - singleLogoutService.getBinding()); + configProperties.put(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_URL_PROPERTY_KEY, singleLogoutService.getLocation()); + configProperties.put(SettingsBuilder.IDP_SINGLE_LOGOUT_SERVICE_BINDING_PROPERTY_KEY, singleLogoutService.getBinding()); } else { log.warn( - "The IdP does not provide a Single Logout Service. In order to ensure that users have to re-enter their password after logging out, OpenSearch Security will issue all SAML authentication requests with a mandatory password input (ForceAuthn=true)"); + "The IdP does not provide a Single Logout Service. In order to ensure that users have to re-enter their password after logging out, OpenSearch Security will issue all SAML authentication requests with a mandatory password input (ForceAuthn=true)" + ); } } @@ -176,32 +183,32 @@ private void initIdpCerts(IDPSSODescriptor idpSsoDescriptor, HashMap headers = new HashMap(); - headers.put("Authorization", "Bearer "+jwsToken); + headers.put("Authorization", "Bearer " + jwsToken); AuthCredentials credentials = jwtAuth.extractCredentials(new FakeRestRequest(headers, new HashMap()), null); Assert.assertNull(credentials); @@ -110,11 +110,15 @@ public void testBearer() throws Exception { Settings settings = Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).build(); - String jwsToken = Jwts.builder().setSubject("Leonard McCoy").setAudience("myaud").signWith(secretKey, SignatureAlgorithm.HS512).compact(); + String jwsToken = Jwts.builder() + .setSubject("Leonard McCoy") + .setAudience("myaud") + .signWith(secretKey, SignatureAlgorithm.HS512) + .compact(); HTTPJwtAuthenticator jwtAuth = new HTTPJwtAuthenticator(settings, null); Map headers = new HashMap(); - headers.put("Authorization", "Bearer "+jwsToken); + headers.put("Authorization", "Bearer " + jwsToken); AuthCredentials credentials = jwtAuth.extractCredentials(new FakeRestRequest(headers, new HashMap()), null); @@ -140,7 +144,6 @@ public void testBearerWrongPosition() throws Exception { Assert.assertNull(credentials); } - @Test public void testBasicAuthHeader() throws Exception { Settings settings = Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).build(); @@ -157,8 +160,9 @@ public void testBasicAuthHeader() throws Exception { public void testRoles() throws Exception { final AuthCredentials credentials = extractCredentialsFromJwtHeader( - Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("roles_key", "roles"), - Jwts.builder().setSubject("Leonard McCoy").claim("roles", "role1,role2")); + Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("roles_key", "roles"), + Jwts.builder().setSubject("Leonard McCoy").claim("roles", "role1,role2") + ); Assert.assertNotNull(credentials); Assert.assertEquals("Leonard McCoy", credentials.getUsername()); @@ -169,8 +173,9 @@ public void testRoles() throws Exception { public void testNullClaim() throws Exception { final AuthCredentials credentials = extractCredentialsFromJwtHeader( - Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("roles_key", "roles"), - Jwts.builder().setSubject("Leonard McCoy").claim("roles", null)); + Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("roles_key", "roles"), + Jwts.builder().setSubject("Leonard McCoy").claim("roles", null) + ); Assert.assertNotNull(credentials); Assert.assertEquals("Leonard McCoy", credentials.getUsername()); @@ -181,21 +186,23 @@ public void testNullClaim() throws Exception { public void testNonStringClaim() throws Exception { final AuthCredentials credentials = extractCredentialsFromJwtHeader( - Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("roles_key", "roles"), - Jwts.builder().setSubject("Leonard McCoy").claim("roles", 123L)); + Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("roles_key", "roles"), + Jwts.builder().setSubject("Leonard McCoy").claim("roles", 123L) + ); Assert.assertNotNull(credentials); Assert.assertEquals("Leonard McCoy", credentials.getUsername()); Assert.assertEquals(1, credentials.getBackendRoles().size()); - Assert.assertTrue( credentials.getBackendRoles().contains("123")); + Assert.assertTrue(credentials.getBackendRoles().contains("123")); } @Test public void testRolesMissing() throws Exception { final AuthCredentials credentials = extractCredentialsFromJwtHeader( - Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("roles_key", "roles"), - Jwts.builder().setSubject("Leonard McCoy")); + Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("roles_key", "roles"), + Jwts.builder().setSubject("Leonard McCoy") + ); Assert.assertNotNull(credentials); Assert.assertEquals("Leonard McCoy", credentials.getUsername()); @@ -206,8 +213,9 @@ public void testRolesMissing() throws Exception { public void testWrongSubjectKey() throws Exception { final AuthCredentials credentials = extractCredentialsFromJwtHeader( - Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("subject_key", "missing"), - Jwts.builder().claim("roles", "role1,role2").claim("asub", "Dr. Who")); + Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("subject_key", "missing"), + Jwts.builder().claim("roles", "role1,role2").claim("asub", "Dr. Who") + ); Assert.assertNull(credentials); } @@ -216,8 +224,9 @@ public void testWrongSubjectKey() throws Exception { public void testAlternativeSubject() throws Exception { final AuthCredentials credentials = extractCredentialsFromJwtHeader( - Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("subject_key", "asub"), - Jwts.builder().setSubject("Leonard McCoy").claim("roles", "role1,role2").claim("asub", "Dr. Who")); + Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("subject_key", "asub"), + Jwts.builder().setSubject("Leonard McCoy").claim("roles", "role1,role2").claim("asub", "Dr. Who") + ); Assert.assertNotNull(credentials); Assert.assertEquals("Dr. Who", credentials.getUsername()); @@ -228,8 +237,9 @@ public void testAlternativeSubject() throws Exception { public void testNonStringAlternativeSubject() throws Exception { final AuthCredentials credentials = extractCredentialsFromJwtHeader( - Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("subject_key", "asub"), - Jwts.builder().setSubject("Leonard McCoy").claim("roles", "role1,role2").claim("asub", false)); + Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("subject_key", "asub"), + Jwts.builder().setSubject("Leonard McCoy").claim("roles", "role1,role2").claim("asub", false) + ); Assert.assertNotNull(credentials); Assert.assertEquals("false", credentials.getUsername()); @@ -239,7 +249,10 @@ public void testNonStringAlternativeSubject() throws Exception { @Test public void testUrlParam() throws Exception { - Settings settings = Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("jwt_url_parameter", "abc").build(); + Settings settings = Settings.builder() + .put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)) + .put("jwt_url_parameter", "abc") + .build(); String jwsToken = Jwts.builder().setSubject("Leonard McCoy").signWith(secretKey, SignatureAlgorithm.HS512).compact(); @@ -259,8 +272,9 @@ public void testUrlParam() throws Exception { public void testExp() throws Exception { final AuthCredentials credentials = extractCredentialsFromJwtHeader( - Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)), - Jwts.builder().setSubject("Expired").setExpiration(new Date(100))); + Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)), + Jwts.builder().setSubject("Expired").setExpiration(new Date(100)) + ); Assert.assertNull(credentials); } @@ -269,9 +283,10 @@ public void testExp() throws Exception { public void testNbf() throws Exception { final AuthCredentials credentials = extractCredentialsFromJwtHeader( - Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)), - Jwts.builder().setSubject("Expired").setNotBefore(new Date(System.currentTimeMillis()+(1000*36000)))); - + Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)), + Jwts.builder().setSubject("Expired").setNotBefore(new Date(System.currentTimeMillis() + (1000 * 36000))) + ); + Assert.assertNull(credentials); } @@ -285,11 +300,16 @@ public void testRS256() throws Exception { PublicKey pub = pair.getPublic(); String jwsToken = Jwts.builder().setSubject("Leonard McCoy").signWith(priv, SignatureAlgorithm.RS256).compact(); - Settings settings = Settings.builder().put("signing_key", "-----BEGIN PUBLIC KEY-----\n"+BaseEncoding.base64().encode(pub.getEncoded())+"-----END PUBLIC KEY-----").build(); + Settings settings = Settings.builder() + .put( + "signing_key", + "-----BEGIN PUBLIC KEY-----\n" + BaseEncoding.base64().encode(pub.getEncoded()) + "-----END PUBLIC KEY-----" + ) + .build(); HTTPJwtAuthenticator jwtAuth = new HTTPJwtAuthenticator(settings, null); Map headers = new HashMap(); - headers.put("Authorization", "Bearer "+jwsToken); + headers.put("Authorization", "Bearer " + jwsToken); AuthCredentials creds = jwtAuth.extractCredentials(new FakeRestRequest(headers, new HashMap()), null); @@ -306,10 +326,10 @@ public void testES512() throws Exception { KeyPair pair = keyGen.generateKeyPair(); PrivateKey priv = pair.getPrivate(); PublicKey pub = pair.getPublic(); - + Settings settings = Settings.builder().put("signing_key", BaseEncoding.base64().encode(pub.getEncoded())).build(); String jwsToken = Jwts.builder().setSubject("Leonard McCoy").signWith(priv, SignatureAlgorithm.ES512).compact(); - + HTTPJwtAuthenticator jwtAuth = new HTTPJwtAuthenticator(settings, null); Map headers = new HashMap(); headers.put("Authorization", jwsToken); @@ -324,16 +344,13 @@ public void testES512() throws Exception { @Test public void testRolesArray() throws Exception { - JwtBuilder builder = Jwts.builder() - .setPayload("{"+ - "\"sub\": \"John Doe\","+ - "\"roles\": [\"a\",\"b\",\"3rd\"]"+ - "}"); + JwtBuilder builder = Jwts.builder().setPayload("{" + "\"sub\": \"John Doe\"," + "\"roles\": [\"a\",\"b\",\"3rd\"]" + "}"); final AuthCredentials credentials = extractCredentialsFromJwtHeader( - Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("roles_key", "roles"), - builder); - + Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("roles_key", "roles"), + builder + ); + Assert.assertNotNull(credentials); Assert.assertEquals("John Doe", credentials.getUsername()); Assert.assertEquals(3, credentials.getBackendRoles().size()); @@ -346,9 +363,10 @@ public void testRolesArray() throws Exception { public void testRequiredAudienceWithCorrectAudience() { final AuthCredentials credentials = extractCredentialsFromJwtHeader( - Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("required_audience", "test_audience"), - Jwts.builder().setSubject("Leonard McCoy").setAudience("test_audience")); - + Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("required_audience", "test_audience"), + Jwts.builder().setSubject("Leonard McCoy").setAudience("test_audience") + ); + Assert.assertNotNull(credentials); Assert.assertEquals("Leonard McCoy", credentials.getUsername()); } @@ -356,19 +374,21 @@ public void testRequiredAudienceWithCorrectAudience() { @Test public void testRequiredAudienceWithIncorrectAudience() { - final AuthCredentials credentials = extractCredentialsFromJwtHeader( - Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("required_audience", "test_audience"), - Jwts.builder().setSubject("Leonard McCoy").setAudience("wrong_audience")); - + final AuthCredentials credentials = extractCredentialsFromJwtHeader( + Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("required_audience", "test_audience"), + Jwts.builder().setSubject("Leonard McCoy").setAudience("wrong_audience") + ); + Assert.assertNull(credentials); } @Test public void testRequiredIssuerWithCorrectAudience() { - + final AuthCredentials credentials = extractCredentialsFromJwtHeader( - Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("required_issuer", "test_issuer"), - Jwts.builder().setSubject("Leonard McCoy").setIssuer("test_issuer")); + Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("required_issuer", "test_issuer"), + Jwts.builder().setSubject("Leonard McCoy").setIssuer("test_issuer") + ); Assert.assertNotNull(credentials); Assert.assertEquals("Leonard McCoy", credentials.getUsername()); @@ -376,18 +396,17 @@ public void testRequiredIssuerWithCorrectAudience() { @Test public void testRequiredIssuerWithIncorrectAudience() { - - final AuthCredentials credentials = extractCredentialsFromJwtHeader( - Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("required_issuer", "test_issuer"), - Jwts.builder().setSubject("Leonard McCoy").setIssuer("wrong_issuer")); - + + final AuthCredentials credentials = extractCredentialsFromJwtHeader( + Settings.builder().put("signing_key", BaseEncoding.base64().encode(secretKeyBytes)).put("required_issuer", "test_issuer"), + Jwts.builder().setSubject("Leonard McCoy").setIssuer("wrong_issuer") + ); + Assert.assertNull(credentials); } /** extracts a default user credential from a request header */ - private AuthCredentials extractCredentialsFromJwtHeader( - final Settings.Builder settingsBuilder, - final JwtBuilder jwtBuilder) { + private AuthCredentials extractCredentialsFromJwtHeader(final Settings.Builder settingsBuilder, final JwtBuilder jwtBuilder) { final Settings settings = settingsBuilder.build(); final String jwsToken = jwtBuilder.signWith(secretKey, SignatureAlgorithm.HS512).compact(); final HTTPJwtAuthenticator jwtAuth = new HTTPJwtAuthenticator(settings, null); diff --git a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/CxfTestTools.java b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/CxfTestTools.java index e9920995ac..b2958193c2 100644 --- a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/CxfTestTools.java +++ b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/CxfTestTools.java @@ -16,7 +16,7 @@ class CxfTestTools { - static String toJson(JsonMapObject jsonMapObject) { - return new JsonMapObjectReaderWriter().toJson(jsonMapObject); - } + static String toJson(JsonMapObject jsonMapObject) { + return new JsonMapObjectReaderWriter().toJson(jsonMapObject); + } } diff --git a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/HTTPJwtKeyByOpenIdConnectAuthenticatorTest.java b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/HTTPJwtKeyByOpenIdConnectAuthenticatorTest.java index 6419e84891..a354189b0e 100644 --- a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/HTTPJwtKeyByOpenIdConnectAuthenticatorTest.java +++ b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/HTTPJwtKeyByOpenIdConnectAuthenticatorTest.java @@ -24,216 +24,240 @@ public class HTTPJwtKeyByOpenIdConnectAuthenticatorTest { - protected static MockIpdServer mockIdpServer; - - @BeforeClass - public static void setUp() throws Exception { - mockIdpServer = new MockIpdServer(TestJwk.Jwks.ALL); - } - - @AfterClass - public static void tearDown() { - if (mockIdpServer != null) { - try { - mockIdpServer.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - @Test - public void basicTest() { - Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); - - HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); - - AuthCredentials creds = jwtAuth.extractCredentials(new FakeRestRequest( - ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_1), new HashMap()), null); - - Assert.assertNotNull(creds); - Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); - Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); - Assert.assertEquals(0, creds.getBackendRoles().size()); - Assert.assertEquals(3, creds.getAttributes().size()); - } - - @Test - public void testEscapeKid() { - Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); - - HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); - - AuthCredentials creds = jwtAuth.extractCredentials(new FakeRestRequest( - ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_SIGNED_OCT_1_INVALID_KID), new HashMap()), null); - - Assert.assertNotNull(creds); - Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); - Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); - Assert.assertEquals(0, creds.getBackendRoles().size()); - Assert.assertEquals(3, creds.getAttributes().size()); - } - - @Test - public void bearerTest() { - Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); - - HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); - - AuthCredentials creds = jwtAuth.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_SIGNED_OCT_1), - new HashMap()), - null); - - Assert.assertNotNull(creds); - Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); - Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); - Assert.assertEquals(0, creds.getBackendRoles().size()); - Assert.assertEquals(3, creds.getAttributes().size()); - } - - @Test - public void testRoles() throws Exception { - Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("roles_key", TestJwts.ROLES_CLAIM).build(); - - HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); - - AuthCredentials creds = jwtAuth.extractCredentials(new FakeRestRequest( - ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_1), new HashMap()), null); - - Assert.assertNotNull(creds); - Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); - Assert.assertEquals(TestJwts.TEST_ROLES, creds.getBackendRoles()); - } - - @Test - public void testExp() throws Exception { - Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); - - HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); - - AuthCredentials creds = jwtAuth.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_EXPIRED_SIGNED_OCT_1), - new HashMap()), - null); - - Assert.assertNull(creds); - } - - @Test - public void testExpInSkew() throws Exception { - Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("jwt_clock_skew_tolerance_seconds", "10") - .build(); - - HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); - - long expiringDate = System.currentTimeMillis()/1000-5; - long notBeforeDate = System.currentTimeMillis()/1000-25; - - AuthCredentials creds = jwtAuth.extractCredentials( - new FakeRestRequest( - ImmutableMap.of( - "Authorization", - "Bearer "+TestJwts.createMcCoySignedOct1(notBeforeDate, expiringDate)), - new HashMap()), - null); - - Assert.assertNotNull(creds); - } - - @Test - public void testNbf() throws Exception { - Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("jwt_clock_skew_tolerance_seconds", "0") - .build(); - - HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); - - long expiringDate = 20+System.currentTimeMillis()/1000; - long notBeforeDate = 5+System.currentTimeMillis()/1000; - - AuthCredentials creds = jwtAuth.extractCredentials( - new FakeRestRequest( - ImmutableMap.of( - "Authorization", - "Bearer "+TestJwts.createMcCoySignedOct1(notBeforeDate, expiringDate)), - new HashMap()), - null); - - Assert.assertNull(creds); - } - - @Test - public void testNbfInSkew() throws Exception { - Settings settings = Settings.builder() - .put("openid_connect_url", mockIdpServer.getDiscoverUri()) - .put("jwt_clock_skew_tolerance_seconds", "10") - .build(); - - HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); - - long expiringDate = 20+System.currentTimeMillis()/1000; - long notBeforeDate = 5+System.currentTimeMillis()/1000;; - - AuthCredentials creds = jwtAuth.extractCredentials( - new FakeRestRequest( - ImmutableMap.of("Authorization", "Bearer "+TestJwts.createMcCoySignedOct1(notBeforeDate, expiringDate)), - new HashMap()), - null); - - Assert.assertNotNull(creds); - } - - - @Test - public void testRS256() throws Exception { - - Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); - - HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); - - AuthCredentials creds = jwtAuth.extractCredentials(new FakeRestRequest( - ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_RSA_1), new HashMap()), null); - - Assert.assertNotNull(creds); - Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); - Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); - Assert.assertEquals(0, creds.getBackendRoles().size()); - Assert.assertEquals(3, creds.getAttributes().size()); - } - - @Test - public void testBadSignature() throws Exception { - - Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); - - HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); - - AuthCredentials creds = jwtAuth.extractCredentials(new FakeRestRequest( - ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_RSA_X), new HashMap()), null); - - Assert.assertNull(creds); - } - - @Test - public void testPeculiarJsonEscaping() { - Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); - - HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); - - AuthCredentials creds = jwtAuth.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.PeculiarEscaping.MC_COY_SIGNED_RSA_1), new HashMap()), - null); - - Assert.assertNotNull(creds); - Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); - Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); - Assert.assertEquals(0, creds.getBackendRoles().size()); - Assert.assertEquals(3, creds.getAttributes().size()); - } + protected static MockIpdServer mockIdpServer; + + @BeforeClass + public static void setUp() throws Exception { + mockIdpServer = new MockIpdServer(TestJwk.Jwks.ALL); + } + + @AfterClass + public static void tearDown() { + if (mockIdpServer != null) { + try { + mockIdpServer.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Test + public void basicTest() { + Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); + + HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); + + AuthCredentials creds = jwtAuth.extractCredentials( + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_1), new HashMap()), + null + ); + + Assert.assertNotNull(creds); + Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); + Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); + Assert.assertEquals(0, creds.getBackendRoles().size()); + Assert.assertEquals(3, creds.getAttributes().size()); + } + + @Test + public void testEscapeKid() { + Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); + + HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); + + AuthCredentials creds = jwtAuth.extractCredentials( + new FakeRestRequest( + ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_SIGNED_OCT_1_INVALID_KID), + new HashMap() + ), + null + ); + + Assert.assertNotNull(creds); + Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); + Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); + Assert.assertEquals(0, creds.getBackendRoles().size()); + Assert.assertEquals(3, creds.getAttributes().size()); + } + + @Test + public void bearerTest() { + Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); + + HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); + + AuthCredentials creds = jwtAuth.extractCredentials( + new FakeRestRequest(ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_SIGNED_OCT_1), new HashMap()), + null + ); + + Assert.assertNotNull(creds); + Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); + Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); + Assert.assertEquals(0, creds.getBackendRoles().size()); + Assert.assertEquals(3, creds.getAttributes().size()); + } + + @Test + public void testRoles() throws Exception { + Settings settings = Settings.builder() + .put("openid_connect_url", mockIdpServer.getDiscoverUri()) + .put("roles_key", TestJwts.ROLES_CLAIM) + .build(); + + HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); + + AuthCredentials creds = jwtAuth.extractCredentials( + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_1), new HashMap()), + null + ); + + Assert.assertNotNull(creds); + Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); + Assert.assertEquals(TestJwts.TEST_ROLES, creds.getBackendRoles()); + } + + @Test + public void testExp() throws Exception { + Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); + + HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); + + AuthCredentials creds = jwtAuth.extractCredentials( + new FakeRestRequest( + ImmutableMap.of("Authorization", "Bearer " + TestJwts.MC_COY_EXPIRED_SIGNED_OCT_1), + new HashMap() + ), + null + ); + + Assert.assertNull(creds); + } + + @Test + public void testExpInSkew() throws Exception { + Settings settings = Settings.builder() + .put("openid_connect_url", mockIdpServer.getDiscoverUri()) + .put("jwt_clock_skew_tolerance_seconds", "10") + .build(); + + HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); + + long expiringDate = System.currentTimeMillis() / 1000 - 5; + long notBeforeDate = System.currentTimeMillis() / 1000 - 25; + + AuthCredentials creds = jwtAuth.extractCredentials( + new FakeRestRequest( + ImmutableMap.of("Authorization", "Bearer " + TestJwts.createMcCoySignedOct1(notBeforeDate, expiringDate)), + new HashMap() + ), + null + ); + + Assert.assertNotNull(creds); + } + + @Test + public void testNbf() throws Exception { + Settings settings = Settings.builder() + .put("openid_connect_url", mockIdpServer.getDiscoverUri()) + .put("jwt_clock_skew_tolerance_seconds", "0") + .build(); + + HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); + + long expiringDate = 20 + System.currentTimeMillis() / 1000; + long notBeforeDate = 5 + System.currentTimeMillis() / 1000; + + AuthCredentials creds = jwtAuth.extractCredentials( + new FakeRestRequest( + ImmutableMap.of("Authorization", "Bearer " + TestJwts.createMcCoySignedOct1(notBeforeDate, expiringDate)), + new HashMap() + ), + null + ); + + Assert.assertNull(creds); + } + + @Test + public void testNbfInSkew() throws Exception { + Settings settings = Settings.builder() + .put("openid_connect_url", mockIdpServer.getDiscoverUri()) + .put("jwt_clock_skew_tolerance_seconds", "10") + .build(); + + HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); + + long expiringDate = 20 + System.currentTimeMillis() / 1000; + long notBeforeDate = 5 + System.currentTimeMillis() / 1000; + ; + + AuthCredentials creds = jwtAuth.extractCredentials( + new FakeRestRequest( + ImmutableMap.of("Authorization", "Bearer " + TestJwts.createMcCoySignedOct1(notBeforeDate, expiringDate)), + new HashMap() + ), + null + ); + + Assert.assertNotNull(creds); + } + + @Test + public void testRS256() throws Exception { + + Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); + + HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); + + AuthCredentials creds = jwtAuth.extractCredentials( + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_RSA_1), new HashMap()), + null + ); + + Assert.assertNotNull(creds); + Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); + Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); + Assert.assertEquals(0, creds.getBackendRoles().size()); + Assert.assertEquals(3, creds.getAttributes().size()); + } + + @Test + public void testBadSignature() throws Exception { + + Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); + + HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); + + AuthCredentials creds = jwtAuth.extractCredentials( + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_RSA_X), new HashMap()), + null + ); + + Assert.assertNull(creds); + } + + @Test + public void testPeculiarJsonEscaping() { + Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); + + HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); + + AuthCredentials creds = jwtAuth.extractCredentials( + new FakeRestRequest( + ImmutableMap.of("Authorization", TestJwts.PeculiarEscaping.MC_COY_SIGNED_RSA_1), + new HashMap() + ), + null + ); + + Assert.assertNotNull(creds); + Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); + Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); + Assert.assertEquals(0, creds.getBackendRoles().size()); + Assert.assertEquals(3, creds.getAttributes().size()); + } } diff --git a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/KeySetRetrieverTest.java b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/KeySetRetrieverTest.java index b30a6326b6..07c0250504 100644 --- a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/KeySetRetrieverTest.java +++ b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/KeySetRetrieverTest.java @@ -78,12 +78,10 @@ public void cacheTest() { @Test public void clientCertTest() throws Exception { - try (MockIpdServer sslMockIdpServer = new MockIpdServer(TestJwk.Jwks.ALL, SocketUtils.findAvailableTcpPort(), - true) { + try (MockIpdServer sslMockIdpServer = new MockIpdServer(TestJwk.Jwks.ALL, SocketUtils.findAvailableTcpPort(), true) { @Override - protected void handleDiscoverRequest(HttpRequest request, ClassicHttpResponse response, HttpContext context) - throws IOException, HttpException { - + protected void handleDiscoverRequest(HttpRequest request, ClassicHttpResponse response, HttpContext context) throws IOException, + HttpException { SSLSession sslSession = ((HttpCoreContext) context).getSSLSession(); @@ -92,8 +90,7 @@ protected void handleDiscoverRequest(HttpRequest request, ClassicHttpResponse re try { String sha256Fingerprint = Hashing.sha256().hashBytes(peerCert.getEncoded()).toString(); - Assert.assertEquals("04b2b8baea7a0a893f0223d95b72081e9a1e154a0f9b1b4e75998085972b1b68", - sha256Fingerprint); + Assert.assertEquals("04b2b8baea7a0a893f0223d95b72081e9a1e154a0f9b1b4e75998085972b1b68", sha256Fingerprint); } catch (CertificateEncodingException e) { throw new RuntimeException(e); @@ -105,13 +102,11 @@ protected void handleDiscoverRequest(HttpRequest request, ClassicHttpResponse re SSLContextBuilder sslContextBuilder = SSLContexts.custom(); KeyStore trustStore = KeyStore.getInstance("JKS"); - InputStream trustStream = new FileInputStream( - FileHelper.getAbsoluteFilePathFromClassPath("jwt/truststore.jks").toFile()); + InputStream trustStream = new FileInputStream(FileHelper.getAbsoluteFilePathFromClassPath("jwt/truststore.jks").toFile()); trustStore.load(trustStream, "changeit".toCharArray()); KeyStore keyStore = KeyStore.getInstance("JKS"); - InputStream keyStream = new FileInputStream( - FileHelper.getAbsoluteFilePathFromClassPath("jwt/spock-keystore.jks").toFile()); + InputStream keyStream = new FileInputStream(FileHelper.getAbsoluteFilePathFromClassPath("jwt/spock-keystore.jks").toFile()); keyStore.load(keyStream, "changeit".toCharArray()); @@ -126,8 +121,19 @@ public String chooseAlias(Map aliases, SSLParameters }); SettingsBasedSSLConfigurator.SSLConfig sslConfig = new SettingsBasedSSLConfigurator.SSLConfig( - sslContextBuilder.build(), new String[] { "TLSv1.2", "TLSv1.1" }, null, null, false, false, false, - trustStore, null, keyStore, null, null); + sslContextBuilder.build(), + new String[] { "TLSv1.2", "TLSv1.1" }, + null, + null, + false, + false, + false, + trustStore, + null, + keyStore, + null, + null + ); KeySetRetriever keySetRetriever = new KeySetRetriever(sslMockIdpServer.getDiscoverUri(), sslConfig, false); diff --git a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/MockIpdServer.java b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/MockIpdServer.java index 21a9d239c3..7b5d9ab519 100644 --- a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/MockIpdServer.java +++ b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/MockIpdServer.java @@ -45,123 +45,127 @@ import static com.amazon.dlic.auth.http.jwt.keybyoidc.CxfTestTools.toJson; class MockIpdServer implements Closeable { - final static String CTX_DISCOVER = "/discover"; - final static String CTX_KEYS = "/api/oauth/keys"; - - private final HttpServer httpServer; - private final int port; - private final String uri; - private final boolean ssl; - private final JsonWebKeys jwks; - - MockIpdServer(JsonWebKeys jwks) throws IOException { - this(jwks, SocketUtils.findAvailableTcpPort(), false); - } - - MockIpdServer(JsonWebKeys jwks, int port, boolean ssl) throws IOException { - this.port = port; - this.uri = (ssl ? "https" : "http") + "://localhost:" + port; - this.ssl = ssl; - this.jwks = jwks; - - ServerBootstrap serverBootstrap = ServerBootstrap.bootstrap().setListenerPort(port) - .register(CTX_DISCOVER, new HttpRequestHandler() { - - @Override - public void handle(ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context) throws HttpException, IOException { - handleDiscoverRequest(request, response, context); - } - }).register(CTX_KEYS, new HttpRequestHandler() { - - @Override - public void handle(ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context) throws HttpException, IOException { - handleKeysRequest(request, response, context); - } - }); - - if (ssl) { - serverBootstrap = serverBootstrap.setSslContext(createSSLContext()) - .setSslSetupHandler(new Callback() { - @Override - public void execute(SSLParameters object) { - object.setNeedClientAuth(true); - } - }).setConnectionFactory(new HttpConnectionFactory() { - @Override - public DefaultBHttpServerConnection createConnection(final Socket socket) throws IOException { - final DefaultBHttpServerConnection conn = new DefaultBHttpServerConnection(ssl ? "https" : "http", Http1Config.DEFAULT); - conn.bind(socket); - return conn; - } - }); - } - - this.httpServer = serverBootstrap.create(); - - httpServer.start(); - } - - @Override - public void close() throws IOException { - httpServer.stop(); - } - - public HttpServer getHttpServer() { - return httpServer; - } - - public String getUri() { - return uri; - } - - public String getDiscoverUri() { - return uri + CTX_DISCOVER; - } - - public int getPort() { - return port; - } - - protected void handleDiscoverRequest(HttpRequest request, ClassicHttpResponse response, HttpContext context) - throws HttpException, IOException { - response.setCode(200); - response.setHeader("Cache-Control", "public, max-age=31536000"); - response.setEntity(new StringEntity("{\"jwks_uri\": \"" + uri + CTX_KEYS + "\",\n" + "\"issuer\": \"" + uri - + "\", \"unknownPropertyToBeIgnored\": 42}")); - } - - protected void handleKeysRequest(HttpRequest request, ClassicHttpResponse response, HttpContext context) - throws HttpException, IOException { - response.setCode(200); - response.setEntity(new StringEntity(toJson(jwks))); - } - - private SSLContext createSSLContext() { - if (!this.ssl) { - return null; - } - - try { - final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - final KeyStore trustStore = KeyStore.getInstance("JKS"); - InputStream trustStream = new FileInputStream( - FileHelper.getAbsoluteFilePathFromClassPath("jwt/truststore.jks").toFile()); - trustStore.load(trustStream, "changeit".toCharArray()); - tmf.init(trustStore); - - final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - final KeyStore keyStore = KeyStore.getInstance("JKS"); - InputStream keyStream = new FileInputStream( - FileHelper.getAbsoluteFilePathFromClassPath("jwt/node-0-keystore.jks").toFile()); - - keyStore.load(keyStream, "changeit".toCharArray()); - kmf.init(keyStore, "changeit".toCharArray()); - - SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); - sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); - return sslContext; - } catch (GeneralSecurityException | IOException e) { - throw new RuntimeException(e); - } - } + final static String CTX_DISCOVER = "/discover"; + final static String CTX_KEYS = "/api/oauth/keys"; + + private final HttpServer httpServer; + private final int port; + private final String uri; + private final boolean ssl; + private final JsonWebKeys jwks; + + MockIpdServer(JsonWebKeys jwks) throws IOException { + this(jwks, SocketUtils.findAvailableTcpPort(), false); + } + + MockIpdServer(JsonWebKeys jwks, int port, boolean ssl) throws IOException { + this.port = port; + this.uri = (ssl ? "https" : "http") + "://localhost:" + port; + this.ssl = ssl; + this.jwks = jwks; + + ServerBootstrap serverBootstrap = ServerBootstrap.bootstrap() + .setListenerPort(port) + .register(CTX_DISCOVER, new HttpRequestHandler() { + + @Override + public void handle(ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context) throws HttpException, + IOException { + handleDiscoverRequest(request, response, context); + } + }) + .register(CTX_KEYS, new HttpRequestHandler() { + + @Override + public void handle(ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context) throws HttpException, + IOException { + handleKeysRequest(request, response, context); + } + }); + + if (ssl) { + serverBootstrap = serverBootstrap.setSslContext(createSSLContext()).setSslSetupHandler(new Callback() { + @Override + public void execute(SSLParameters object) { + object.setNeedClientAuth(true); + } + }).setConnectionFactory(new HttpConnectionFactory() { + @Override + public DefaultBHttpServerConnection createConnection(final Socket socket) throws IOException { + final DefaultBHttpServerConnection conn = new DefaultBHttpServerConnection(ssl ? "https" : "http", Http1Config.DEFAULT); + conn.bind(socket); + return conn; + } + }); + } + + this.httpServer = serverBootstrap.create(); + + httpServer.start(); + } + + @Override + public void close() throws IOException { + httpServer.stop(); + } + + public HttpServer getHttpServer() { + return httpServer; + } + + public String getUri() { + return uri; + } + + public String getDiscoverUri() { + return uri + CTX_DISCOVER; + } + + public int getPort() { + return port; + } + + protected void handleDiscoverRequest(HttpRequest request, ClassicHttpResponse response, HttpContext context) throws HttpException, + IOException { + response.setCode(200); + response.setHeader("Cache-Control", "public, max-age=31536000"); + response.setEntity( + new StringEntity( + "{\"jwks_uri\": \"" + uri + CTX_KEYS + "\",\n" + "\"issuer\": \"" + uri + "\", \"unknownPropertyToBeIgnored\": 42}" + ) + ); + } + + protected void handleKeysRequest(HttpRequest request, ClassicHttpResponse response, HttpContext context) throws HttpException, + IOException { + response.setCode(200); + response.setEntity(new StringEntity(toJson(jwks))); + } + + private SSLContext createSSLContext() { + if (!this.ssl) { + return null; + } + + try { + final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + final KeyStore trustStore = KeyStore.getInstance("JKS"); + InputStream trustStream = new FileInputStream(FileHelper.getAbsoluteFilePathFromClassPath("jwt/truststore.jks").toFile()); + trustStore.load(trustStream, "changeit".toCharArray()); + tmf.init(trustStore); + + final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + final KeyStore keyStore = KeyStore.getInstance("JKS"); + InputStream keyStream = new FileInputStream(FileHelper.getAbsoluteFilePathFromClassPath("jwt/node-0-keystore.jks").toFile()); + + keyStore.load(keyStream, "changeit".toCharArray()); + kmf.init(keyStore, "changeit".toCharArray()); + + SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); + sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + return sslContext; + } catch (GeneralSecurityException | IOException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/SelfRefreshingKeySetTest.java b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/SelfRefreshingKeySetTest.java index 50bd2fcc0e..6bbce7d85d 100644 --- a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/SelfRefreshingKeySetTest.java +++ b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/SelfRefreshingKeySetTest.java @@ -22,100 +22,97 @@ public class SelfRefreshingKeySetTest { - @Test - public void basicTest() throws AuthenticatorUnavailableException, BadCredentialsException { - SelfRefreshingKeySet selfRefreshingKeySet = new SelfRefreshingKeySet(new MockKeySetProvider()); + @Test + public void basicTest() throws AuthenticatorUnavailableException, BadCredentialsException { + SelfRefreshingKeySet selfRefreshingKeySet = new SelfRefreshingKeySet(new MockKeySetProvider()); - JsonWebKey key1 = selfRefreshingKeySet.getKey("kid/a"); - Assert.assertEquals(TestJwk.OCT_1_K, key1.getProperty("k")); - Assert.assertEquals(1, selfRefreshingKeySet.getRefreshCount()); + JsonWebKey key1 = selfRefreshingKeySet.getKey("kid/a"); + Assert.assertEquals(TestJwk.OCT_1_K, key1.getProperty("k")); + Assert.assertEquals(1, selfRefreshingKeySet.getRefreshCount()); - JsonWebKey key2 = selfRefreshingKeySet.getKey("kid/b"); - Assert.assertEquals(TestJwk.OCT_2_K, key2.getProperty("k")); - Assert.assertEquals(1, selfRefreshingKeySet.getRefreshCount()); + JsonWebKey key2 = selfRefreshingKeySet.getKey("kid/b"); + Assert.assertEquals(TestJwk.OCT_2_K, key2.getProperty("k")); + Assert.assertEquals(1, selfRefreshingKeySet.getRefreshCount()); - try { - selfRefreshingKeySet.getKey("kid/X"); - Assert.fail("Expected a BadCredentialsException"); - } catch (BadCredentialsException e) { - Assert.assertEquals(2, selfRefreshingKeySet.getRefreshCount()); - } + try { + selfRefreshingKeySet.getKey("kid/X"); + Assert.fail("Expected a BadCredentialsException"); + } catch (BadCredentialsException e) { + Assert.assertEquals(2, selfRefreshingKeySet.getRefreshCount()); + } - } + } + @Test(timeout = 10000) + public void twoThreadedTest() throws Exception { + BlockingMockKeySetProvider provider = new BlockingMockKeySetProvider(); + final SelfRefreshingKeySet selfRefreshingKeySet = new SelfRefreshingKeySet(provider); - @Test(timeout = 10000) - public void twoThreadedTest() throws Exception { - BlockingMockKeySetProvider provider = new BlockingMockKeySetProvider(); + ExecutorService executorService = Executors.newCachedThreadPool(); - final SelfRefreshingKeySet selfRefreshingKeySet = new SelfRefreshingKeySet(provider); + Future f1 = executorService.submit(() -> selfRefreshingKeySet.getKey("kid/a")); - ExecutorService executorService = Executors.newCachedThreadPool(); + provider.waitForCalled(); - Future f1 = executorService.submit(() -> selfRefreshingKeySet.getKey("kid/a")); + Future f2 = executorService.submit(() -> selfRefreshingKeySet.getKey("kid/b")); - provider.waitForCalled(); + while (selfRefreshingKeySet.getQueuedGetCount() == 0) { + Thread.sleep(10); + } - Future f2 = executorService.submit(() -> selfRefreshingKeySet.getKey("kid/b")); + provider.unblock(); - while (selfRefreshingKeySet.getQueuedGetCount() == 0) { - Thread.sleep(10); - } + Assert.assertEquals(TestJwk.OCT_1_K, f1.get().getProperty("k")); + Assert.assertEquals(TestJwk.OCT_2_K, f2.get().getProperty("k")); - provider.unblock(); + Assert.assertEquals(1, selfRefreshingKeySet.getRefreshCount()); + Assert.assertEquals(1, selfRefreshingKeySet.getQueuedGetCount()); - Assert.assertEquals(TestJwk.OCT_1_K, f1.get().getProperty("k")); - Assert.assertEquals(TestJwk.OCT_2_K, f2.get().getProperty("k")); + } - Assert.assertEquals(1, selfRefreshingKeySet.getRefreshCount()); - Assert.assertEquals(1, selfRefreshingKeySet.getQueuedGetCount()); + static class MockKeySetProvider implements KeySetProvider { - } + @Override + public JsonWebKeys get() throws AuthenticatorUnavailableException { + return TestJwk.OCT_1_2_3; + } - static class MockKeySetProvider implements KeySetProvider { + } - @Override - public JsonWebKeys get() throws AuthenticatorUnavailableException { - return TestJwk.OCT_1_2_3; - } + static class BlockingMockKeySetProvider extends MockKeySetProvider { + private boolean blocked = true; + private boolean called = false; - } + @Override + public synchronized JsonWebKeys get() throws AuthenticatorUnavailableException { - static class BlockingMockKeySetProvider extends MockKeySetProvider { - private boolean blocked = true; - private boolean called = false; + called = true; + notifyAll(); - @Override - public synchronized JsonWebKeys get() throws AuthenticatorUnavailableException { + waitForUnblock(); - called = true; - notifyAll(); + return super.get(); + } - waitForUnblock(); + public synchronized void unblock() { + blocked = false; + notifyAll(); + } - return super.get(); - } + public synchronized void waitForCalled() throws InterruptedException { + while (!called) { + wait(); + } + } - public synchronized void unblock() { - blocked = false; - notifyAll(); - } + private synchronized void waitForUnblock() { + while (blocked) { + try { + wait(); + } catch (InterruptedException e) {} - public synchronized void waitForCalled() throws InterruptedException { - while (!called) { - wait(); - } - } - - private synchronized void waitForUnblock() { - while (blocked) { - try { - wait(); - } catch (InterruptedException e) { - } - - } - } - } + } + } + } } diff --git a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/SingleKeyHTTPJwtKeyByOpenIdConnectAuthenticatorTest.java b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/SingleKeyHTTPJwtKeyByOpenIdConnectAuthenticatorTest.java index 4400dd1cdc..7038eb4548 100644 --- a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/SingleKeyHTTPJwtKeyByOpenIdConnectAuthenticatorTest.java +++ b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/SingleKeyHTTPJwtKeyByOpenIdConnectAuthenticatorTest.java @@ -23,186 +23,185 @@ public class SingleKeyHTTPJwtKeyByOpenIdConnectAuthenticatorTest { - @Test - public void basicTest() throws Exception { - MockIpdServer mockIdpServer = new MockIpdServer(TestJwk.Jwks.RSA_1); - try { - Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); - - HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); - - AuthCredentials creds = jwtAuth.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_RSA_1), - new HashMap()), - null); - - Assert.assertNotNull(creds); - Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); - Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); - Assert.assertEquals(0, creds.getBackendRoles().size()); - Assert.assertEquals(3, creds.getAttributes().size()); - - } finally { - try { - mockIdpServer.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - @Test - public void wrongSigTest() throws Exception { - MockIpdServer mockIdpServer = new MockIpdServer(TestJwk.Jwks.RSA_1); - try { - Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); - - HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); - - AuthCredentials creds = jwtAuth.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.NoKid.MC_COY_SIGNED_RSA_X), - new HashMap()), - null); - - Assert.assertNull(creds); - - } finally { - try { - mockIdpServer.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - @Test - public void noAlgTest() throws Exception { - MockIpdServer mockIdpServer = new MockIpdServer(TestJwk.Jwks.RSA_1_NO_ALG); - try { - Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); - - HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); - - AuthCredentials creds = jwtAuth.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_RSA_1), - new HashMap()), - null); - - Assert.assertNotNull(creds); - Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); - Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); - Assert.assertEquals(0, creds.getBackendRoles().size()); - Assert.assertEquals(3, creds.getAttributes().size()); - } finally { - try { - mockIdpServer.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - @Test - public void mismatchedAlgTest() throws Exception { - MockIpdServer mockIdpServer = new MockIpdServer(TestJwk.Jwks.RSA_1_WRONG_ALG); - try { - Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); - - HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); - - AuthCredentials creds = jwtAuth.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.NoKid.MC_COY_SIGNED_RSA_1), - new HashMap()), - null); - - Assert.assertNull(creds); - - } finally { - try { - mockIdpServer.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - - @Test - public void keyExchangeTest() throws Exception { - MockIpdServer mockIdpServer = new MockIpdServer(TestJwk.Jwks.RSA_1); - - Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); - - HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); - - try { - AuthCredentials creds = jwtAuth.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.NoKid.MC_COY_SIGNED_RSA_1), - new HashMap()), - null); - - Assert.assertNotNull(creds); - Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); - Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); - Assert.assertEquals(0, creds.getBackendRoles().size()); - Assert.assertEquals(3, creds.getAttributes().size()); - - creds = jwtAuth.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.NoKid.MC_COY_SIGNED_RSA_2), - new HashMap()), - null); - - Assert.assertNull(creds); - - creds = jwtAuth.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.NoKid.MC_COY_SIGNED_RSA_X), - new HashMap()), - null); - - Assert.assertNull(creds); - - creds = jwtAuth.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.NoKid.MC_COY_SIGNED_RSA_1), - new HashMap()), - null); - - Assert.assertNotNull(creds); - Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); - Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); - Assert.assertEquals(0, creds.getBackendRoles().size()); - Assert.assertEquals(3, creds.getAttributes().size()); - - } finally { - try { - mockIdpServer.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - mockIdpServer = new MockIpdServer(TestJwk.Jwks.RSA_2); - settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); //port changed - jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); - - try { - AuthCredentials creds = jwtAuth.extractCredentials( - new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.NoKid.MC_COY_SIGNED_RSA_2), - new HashMap()), - null); - - Assert.assertNotNull(creds); - Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); - Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); - Assert.assertEquals(0, creds.getBackendRoles().size()); - Assert.assertEquals(3, creds.getAttributes().size()); - - } finally { - try { - mockIdpServer.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } + @Test + public void basicTest() throws Exception { + MockIpdServer mockIdpServer = new MockIpdServer(TestJwk.Jwks.RSA_1); + try { + Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); + + HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); + + AuthCredentials creds = jwtAuth.extractCredentials( + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_RSA_1), new HashMap()), + null + ); + + Assert.assertNotNull(creds); + Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); + Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); + Assert.assertEquals(0, creds.getBackendRoles().size()); + Assert.assertEquals(3, creds.getAttributes().size()); + + } finally { + try { + mockIdpServer.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Test + public void wrongSigTest() throws Exception { + MockIpdServer mockIdpServer = new MockIpdServer(TestJwk.Jwks.RSA_1); + try { + Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); + + HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); + + AuthCredentials creds = jwtAuth.extractCredentials( + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.NoKid.MC_COY_SIGNED_RSA_X), new HashMap()), + null + ); + + Assert.assertNull(creds); + + } finally { + try { + mockIdpServer.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Test + public void noAlgTest() throws Exception { + MockIpdServer mockIdpServer = new MockIpdServer(TestJwk.Jwks.RSA_1_NO_ALG); + try { + Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); + + HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); + + AuthCredentials creds = jwtAuth.extractCredentials( + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_RSA_1), new HashMap()), + null + ); + + Assert.assertNotNull(creds); + Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); + Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); + Assert.assertEquals(0, creds.getBackendRoles().size()); + Assert.assertEquals(3, creds.getAttributes().size()); + } finally { + try { + mockIdpServer.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Test + public void mismatchedAlgTest() throws Exception { + MockIpdServer mockIdpServer = new MockIpdServer(TestJwk.Jwks.RSA_1_WRONG_ALG); + try { + Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); + + HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); + + AuthCredentials creds = jwtAuth.extractCredentials( + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.NoKid.MC_COY_SIGNED_RSA_1), new HashMap()), + null + ); + + Assert.assertNull(creds); + + } finally { + try { + mockIdpServer.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Test + public void keyExchangeTest() throws Exception { + MockIpdServer mockIdpServer = new MockIpdServer(TestJwk.Jwks.RSA_1); + + Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); + + HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); + + try { + AuthCredentials creds = jwtAuth.extractCredentials( + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.NoKid.MC_COY_SIGNED_RSA_1), new HashMap()), + null + ); + + Assert.assertNotNull(creds); + Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); + Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); + Assert.assertEquals(0, creds.getBackendRoles().size()); + Assert.assertEquals(3, creds.getAttributes().size()); + + creds = jwtAuth.extractCredentials( + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.NoKid.MC_COY_SIGNED_RSA_2), new HashMap()), + null + ); + + Assert.assertNull(creds); + + creds = jwtAuth.extractCredentials( + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.NoKid.MC_COY_SIGNED_RSA_X), new HashMap()), + null + ); + + Assert.assertNull(creds); + + creds = jwtAuth.extractCredentials( + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.NoKid.MC_COY_SIGNED_RSA_1), new HashMap()), + null + ); + + Assert.assertNotNull(creds); + Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); + Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); + Assert.assertEquals(0, creds.getBackendRoles().size()); + Assert.assertEquals(3, creds.getAttributes().size()); + + } finally { + try { + mockIdpServer.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + mockIdpServer = new MockIpdServer(TestJwk.Jwks.RSA_2); + settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build(); // port changed + jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null); + + try { + AuthCredentials creds = jwtAuth.extractCredentials( + new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.NoKid.MC_COY_SIGNED_RSA_2), new HashMap()), + null + ); + + Assert.assertNotNull(creds); + Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername()); + Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud")); + Assert.assertEquals(0, creds.getBackendRoles().size()); + Assert.assertEquals(3, creds.getAttributes().size()); + + } finally { + try { + mockIdpServer.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } } diff --git a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/TestJwk.java b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/TestJwk.java index b0f996a03f..5b0d5738a3 100644 --- a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/TestJwk.java +++ b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/TestJwk.java @@ -20,94 +20,102 @@ class TestJwk { - // Keys generated with https://mkjwk.org/ - - static final String OCT_1_K = "eTDZjSqRD9Abhod9iqeGX_7o93a-eElTeXWAF6FmzQshmRIrPD-C9ET3pFjJ_IBrzmWIZDk8ig-X_PIyGmKsxNMsrU-0BNWF5gJq5xOp4rYTl8z66Tw9wr8tHLxLxgJqkLSuUCRBZvlZlQ7jNdhBBxgM-hdSSzsN1T33qdIwhrUeJ-KXI5yKUXHjoWFYb9tETbYQ4NvONowkCsXK_flp-E3F_OcKe_z5iVUszAV8QfCod1zhbya540kDejXCL6N_XMmhWJqum7UJ3hgf6DEtroPSnVpHt4iR5w9ArKK-IBgluPght03gNcoNqwz7p77TFbdOmUKF_PWy1bcdbaUoSg"; - static final String OCT_2_K = "YP6Q3IF2qJEagV948dsicXKpG43Ci2W7ZxUpiVTBLZr1vFN9ZGUKxeXGgVWuMFYTmoHvv5AOC8BvoNOpcE3rcJNuNOqTMdujxD92CxjOykiLEKQ0Te_7xQ4LnSQjlqdIJ4U3S7qCnJLd1LxhKOGZcUhE_pjhwf7q2RUUpvC3UOyZZLog9yeflnp9nqqDy5yVqRYWZRcPI06kJTh3Z8IFi2JRJV14iUFQtOHQKuyJRMcsldKnfWl7YW3JdQ9IRN-c1lEYSEBmsavEejcqHZkbli2svqLfmCBJVWffXDRxhq0_VafiL83HC0bP9qeNKivhemw6foVmg8UMs7yJ6ao02A"; - static final String OCT_3_K = "r3aeW3OK7-B4Hs3hq9BmlT1D3jRiolH9PL82XUz9xAS7dniAdmvMnN5GkOc1vqibOe2T-CC_103UglDm9D0iU9S9zn6wTuQt1L5wfZIoHd9f5IjJ_YFEzZMvsoUY_-ji_0K_ugVvBPwi9JnBQHHS4zrgmP06dGjmcnZDcIf4W_iFas3lDYSXilL1V2QhNaynpSqTarpfBGSphKv4Zg2JhsX8xB0VSaTlEq4lF8pzvpWSxXCW9CtomhB80daSuTizrmSTEPpdN3XzQ2-Tovo1ieMOfDU4csvjEk7Bwc2ThjpnA8ucKQUYpUv9joBxKuCdUltssthWnetrogjYOn_xGA"; - - static final JsonWebKey OCT_1 = createOct("kid/a", "HS256", OCT_1_K); - static final JsonWebKey OCT_2 = createOct("kid/b", "HS256", OCT_2_K); - static final JsonWebKey OCT_3 = createOct("kid/c", "HS256", OCT_3_K); - static final JsonWebKey ESCAPED_SLASH_KID_OCT_1 = createOct("kid\\/_a", "HS256", OCT_1_K); - static final JsonWebKey FORWARD_SLASH_KID_OCT_1 = createOct("kid/_a", "HS256", OCT_1_K); - - static final JsonWebKeys OCT_1_2_3 = createJwks(OCT_1, OCT_2, OCT_3, FORWARD_SLASH_KID_OCT_1,ESCAPED_SLASH_KID_OCT_1); - - static final String RSA_1_D = "On8XGMmdM5Fm5hvuhQk-qAkIP2CoK5QMx0OH5m_WDzKXZv8lZ2eg89I4ehBiOKGdw1h_mjmWwTah-evpXV-BF5QpejPQqxkXS-8s5r2AvietQq32jl-gwIwZWTvfzjpT9On0YJZ4q01tMDj3r-YOLUW2xrz3za9tl6pPU_5kP63C-hoj1ybTwcC7ujbCPwhY6yAopMA1v10uVmCxsjsNikEjB6YePgHixez51wO3Z8mXNwefWukFWYJ5T7t4kHMSf5P_8FJZ14u5yvYZnngE_tJCyHFdIDb6UWsrgxomtlQU-SdZYK_NY6gw6mCkjjlqOoYqlsrRJ16kJ81Ds269oQ"; - static final String RSA_1_N = "hMSoV74FRtoaU7xpp0llsXbHE4oUseKoSNga-C_YIXuoGc3pajHh1WtJppZQNYM1Xy07nHchLJAdgqL2_q_Lk8cFHmmL1KTjwPflK9zZ9C0-8QTOrrqU9vkp3gT00jWWJ0HJbUvXIGxPGPnxoJoI--ToE0EWsYEWqWyx1TqYol--oUUPlY5r7vXRKIn5UZNz6VGkW8nI4fXaqDUpXH9uVM9A-nJX2B0Xjwu3VOn2zrgkCZeGTHjNgfLISOTFe9m8lHWLKcuxOWPuCZyCN0C6ZdWB1YP2NhxYFQwQfGV8yfnTImgL-DuV4WPSRVj7W_GJr213-oXBrBR0CnQEPbi_3w"; - static final String RSA_1_E = "AQAB"; - - static final String RSA_2_D = "QQ18k_buZHOSVYzkXL1FaqdodZVNZ_hrBtDcmCVUYjm3dfDVQYt70h8LUdLUCSUA2-_VEwqVdQ-L2FTg7NZVvZJXIyQXp3yrdY1vGKebs3oaIB_VQT8jt-64s12r_8V2ksK2myRrvfm2Fgqi32H5QkspuaQYb9s4NJwKSk7mVAz5dRWQdCx9JNVWknWDJxgHzh3Uku1tNwUOyvSYcRnSZ9X7oWNHaHkSGLEYE_mxD7YXs6HEdCDwc3WuvR5AiVKg2OGec0lL1hY_AWX5UxnR00mhAa0qPytFfaPe-Sc5tQ5regQRqRNDyDESVGIvqXsY8ePjZPOFyoxrcJ2wN3bt4Q"; - static final String RSA_2_N = "lt4EID7tbrE9E8l7VfVGhiwSx4O8nLO5AZo5pJNE1fUy4bM56wH_DeU3YspXh0UvH-vcn4uKjhwJdOCjzalBc2wXD0aRd3JXzWwbjveo6oBFz6kU7VnY8nFMYLMlb6FDcl066OZOtW4PIFtAStXj5rX_J94He3sfTClodpNljTi4qeQwoNsrnZ5Eq82pCp20zCgvbdes8HQBq_QgApvzhL3c-PXd2I_4pBnaPoZwAnufthk7-v8V0Zf5CrDuqEczKKr38pvwggnxZqsfUy2X0bXPBvDXh5B2ljWxWl8tHJbKXzOhfV5Nx5rllJnNabFoVxh3hnlxdOZ88zcaslWBLQ"; - static final String RSA_2_E = "AQAB"; - - static final String RSA_X_D = "iXym57VmwbWvcHtf--xSDPTagEJdnceuErjH6lbuabFXeBx42ZpuAICvo6_YpMcqLybD37ArIu2SD5J_ZBALp4v4KecMPFI5lZr7GKlGgqnForvcC7EWA_ZtZ9uY746cKun8NtemcOlAenn2dvc9NP2S4JtE3FHxmqs2MMmz-ki-ar8-zu0j0HLPLl_Wj2SZ9yCeFmmH3eocX5IRRiWwPnudQJM2t0kt9V-M88YzobqzoMEoFjTfi-owa-w6xGAgJxAUKk02vTiTivH3Qmkk-uAXyj-VtcyzYXD74ICN8EplcAEUKegDR59T4-u18GdpDbPU20XzxDaO4lZiQ7TIEQ"; - static final String RSA_X_N = "jDDVUMXOXDVcaRVAT5TtuiAsLxk7XAAwyyECfmySZul7D5XVLMtGe6rP2900q3nM4BaCEiuwXjmTCZDAGlFGs2a3eQ1vbBSv9_0KGHL-gZGFPNiv0v8aR7QzZ-abhGnRy5F52PlTWsypGgG_kQpF2t2TBotvYhvVPagAt4ljllDKvY1siOvS3nh4TqcUtWcbgQZEWPmaXuhx0eLmhQJca7UEw99YlGNew48AEzt7ZnfU0Qkz3JwSz7IcPx-NfIh6BN6LwAg_ASdoM3MR8rDOtLYavmJVhutrfOpE-4-fw1mf3eLYu7xrxIplSiOIsHunTUssnTiBkXAaGqGJs604Pw"; - static final String RSA_X_E = "AQAB"; - - static final JsonWebKey RSA_1 = createRsa("kid/1", "RS256", RSA_1_E, RSA_1_N, RSA_1_D); - static final JsonWebKey RSA_1_PUBLIC = createRsaPublic("kid/1", "RS256", RSA_1_E, RSA_1_N); - static final JsonWebKey RSA_1_PUBLIC_NO_ALG = createRsaPublic("kid/1", null, RSA_1_E, RSA_1_N); + // Keys generated with https://mkjwk.org/ + + static final String OCT_1_K = + "eTDZjSqRD9Abhod9iqeGX_7o93a-eElTeXWAF6FmzQshmRIrPD-C9ET3pFjJ_IBrzmWIZDk8ig-X_PIyGmKsxNMsrU-0BNWF5gJq5xOp4rYTl8z66Tw9wr8tHLxLxgJqkLSuUCRBZvlZlQ7jNdhBBxgM-hdSSzsN1T33qdIwhrUeJ-KXI5yKUXHjoWFYb9tETbYQ4NvONowkCsXK_flp-E3F_OcKe_z5iVUszAV8QfCod1zhbya540kDejXCL6N_XMmhWJqum7UJ3hgf6DEtroPSnVpHt4iR5w9ArKK-IBgluPght03gNcoNqwz7p77TFbdOmUKF_PWy1bcdbaUoSg"; + static final String OCT_2_K = + "YP6Q3IF2qJEagV948dsicXKpG43Ci2W7ZxUpiVTBLZr1vFN9ZGUKxeXGgVWuMFYTmoHvv5AOC8BvoNOpcE3rcJNuNOqTMdujxD92CxjOykiLEKQ0Te_7xQ4LnSQjlqdIJ4U3S7qCnJLd1LxhKOGZcUhE_pjhwf7q2RUUpvC3UOyZZLog9yeflnp9nqqDy5yVqRYWZRcPI06kJTh3Z8IFi2JRJV14iUFQtOHQKuyJRMcsldKnfWl7YW3JdQ9IRN-c1lEYSEBmsavEejcqHZkbli2svqLfmCBJVWffXDRxhq0_VafiL83HC0bP9qeNKivhemw6foVmg8UMs7yJ6ao02A"; + static final String OCT_3_K = + "r3aeW3OK7-B4Hs3hq9BmlT1D3jRiolH9PL82XUz9xAS7dniAdmvMnN5GkOc1vqibOe2T-CC_103UglDm9D0iU9S9zn6wTuQt1L5wfZIoHd9f5IjJ_YFEzZMvsoUY_-ji_0K_ugVvBPwi9JnBQHHS4zrgmP06dGjmcnZDcIf4W_iFas3lDYSXilL1V2QhNaynpSqTarpfBGSphKv4Zg2JhsX8xB0VSaTlEq4lF8pzvpWSxXCW9CtomhB80daSuTizrmSTEPpdN3XzQ2-Tovo1ieMOfDU4csvjEk7Bwc2ThjpnA8ucKQUYpUv9joBxKuCdUltssthWnetrogjYOn_xGA"; + + static final JsonWebKey OCT_1 = createOct("kid/a", "HS256", OCT_1_K); + static final JsonWebKey OCT_2 = createOct("kid/b", "HS256", OCT_2_K); + static final JsonWebKey OCT_3 = createOct("kid/c", "HS256", OCT_3_K); + static final JsonWebKey ESCAPED_SLASH_KID_OCT_1 = createOct("kid\\/_a", "HS256", OCT_1_K); + static final JsonWebKey FORWARD_SLASH_KID_OCT_1 = createOct("kid/_a", "HS256", OCT_1_K); + + static final JsonWebKeys OCT_1_2_3 = createJwks(OCT_1, OCT_2, OCT_3, FORWARD_SLASH_KID_OCT_1, ESCAPED_SLASH_KID_OCT_1); + + static final String RSA_1_D = + "On8XGMmdM5Fm5hvuhQk-qAkIP2CoK5QMx0OH5m_WDzKXZv8lZ2eg89I4ehBiOKGdw1h_mjmWwTah-evpXV-BF5QpejPQqxkXS-8s5r2AvietQq32jl-gwIwZWTvfzjpT9On0YJZ4q01tMDj3r-YOLUW2xrz3za9tl6pPU_5kP63C-hoj1ybTwcC7ujbCPwhY6yAopMA1v10uVmCxsjsNikEjB6YePgHixez51wO3Z8mXNwefWukFWYJ5T7t4kHMSf5P_8FJZ14u5yvYZnngE_tJCyHFdIDb6UWsrgxomtlQU-SdZYK_NY6gw6mCkjjlqOoYqlsrRJ16kJ81Ds269oQ"; + static final String RSA_1_N = + "hMSoV74FRtoaU7xpp0llsXbHE4oUseKoSNga-C_YIXuoGc3pajHh1WtJppZQNYM1Xy07nHchLJAdgqL2_q_Lk8cFHmmL1KTjwPflK9zZ9C0-8QTOrrqU9vkp3gT00jWWJ0HJbUvXIGxPGPnxoJoI--ToE0EWsYEWqWyx1TqYol--oUUPlY5r7vXRKIn5UZNz6VGkW8nI4fXaqDUpXH9uVM9A-nJX2B0Xjwu3VOn2zrgkCZeGTHjNgfLISOTFe9m8lHWLKcuxOWPuCZyCN0C6ZdWB1YP2NhxYFQwQfGV8yfnTImgL-DuV4WPSRVj7W_GJr213-oXBrBR0CnQEPbi_3w"; + static final String RSA_1_E = "AQAB"; + + static final String RSA_2_D = + "QQ18k_buZHOSVYzkXL1FaqdodZVNZ_hrBtDcmCVUYjm3dfDVQYt70h8LUdLUCSUA2-_VEwqVdQ-L2FTg7NZVvZJXIyQXp3yrdY1vGKebs3oaIB_VQT8jt-64s12r_8V2ksK2myRrvfm2Fgqi32H5QkspuaQYb9s4NJwKSk7mVAz5dRWQdCx9JNVWknWDJxgHzh3Uku1tNwUOyvSYcRnSZ9X7oWNHaHkSGLEYE_mxD7YXs6HEdCDwc3WuvR5AiVKg2OGec0lL1hY_AWX5UxnR00mhAa0qPytFfaPe-Sc5tQ5regQRqRNDyDESVGIvqXsY8ePjZPOFyoxrcJ2wN3bt4Q"; + static final String RSA_2_N = + "lt4EID7tbrE9E8l7VfVGhiwSx4O8nLO5AZo5pJNE1fUy4bM56wH_DeU3YspXh0UvH-vcn4uKjhwJdOCjzalBc2wXD0aRd3JXzWwbjveo6oBFz6kU7VnY8nFMYLMlb6FDcl066OZOtW4PIFtAStXj5rX_J94He3sfTClodpNljTi4qeQwoNsrnZ5Eq82pCp20zCgvbdes8HQBq_QgApvzhL3c-PXd2I_4pBnaPoZwAnufthk7-v8V0Zf5CrDuqEczKKr38pvwggnxZqsfUy2X0bXPBvDXh5B2ljWxWl8tHJbKXzOhfV5Nx5rllJnNabFoVxh3hnlxdOZ88zcaslWBLQ"; + static final String RSA_2_E = "AQAB"; + + static final String RSA_X_D = + "iXym57VmwbWvcHtf--xSDPTagEJdnceuErjH6lbuabFXeBx42ZpuAICvo6_YpMcqLybD37ArIu2SD5J_ZBALp4v4KecMPFI5lZr7GKlGgqnForvcC7EWA_ZtZ9uY746cKun8NtemcOlAenn2dvc9NP2S4JtE3FHxmqs2MMmz-ki-ar8-zu0j0HLPLl_Wj2SZ9yCeFmmH3eocX5IRRiWwPnudQJM2t0kt9V-M88YzobqzoMEoFjTfi-owa-w6xGAgJxAUKk02vTiTivH3Qmkk-uAXyj-VtcyzYXD74ICN8EplcAEUKegDR59T4-u18GdpDbPU20XzxDaO4lZiQ7TIEQ"; + static final String RSA_X_N = + "jDDVUMXOXDVcaRVAT5TtuiAsLxk7XAAwyyECfmySZul7D5XVLMtGe6rP2900q3nM4BaCEiuwXjmTCZDAGlFGs2a3eQ1vbBSv9_0KGHL-gZGFPNiv0v8aR7QzZ-abhGnRy5F52PlTWsypGgG_kQpF2t2TBotvYhvVPagAt4ljllDKvY1siOvS3nh4TqcUtWcbgQZEWPmaXuhx0eLmhQJca7UEw99YlGNew48AEzt7ZnfU0Qkz3JwSz7IcPx-NfIh6BN6LwAg_ASdoM3MR8rDOtLYavmJVhutrfOpE-4-fw1mf3eLYu7xrxIplSiOIsHunTUssnTiBkXAaGqGJs604Pw"; + static final String RSA_X_E = "AQAB"; + + static final JsonWebKey RSA_1 = createRsa("kid/1", "RS256", RSA_1_E, RSA_1_N, RSA_1_D); + static final JsonWebKey RSA_1_PUBLIC = createRsaPublic("kid/1", "RS256", RSA_1_E, RSA_1_N); + static final JsonWebKey RSA_1_PUBLIC_NO_ALG = createRsaPublic("kid/1", null, RSA_1_E, RSA_1_N); static final JsonWebKey RSA_1_PUBLIC_WRONG_ALG = createRsaPublic("kid/1", "HS256", RSA_1_E, RSA_1_N); - static final JsonWebKey RSA_2 = createRsa("kid/2", "RS256", RSA_2_E, RSA_2_N, RSA_2_D); - static final JsonWebKey RSA_2_PUBLIC = createRsaPublic("kid/2", "RS256", RSA_2_E, RSA_2_N); - - static final JsonWebKey RSA_X = createRsa("kid/2", "RS256", RSA_X_E, RSA_X_N, RSA_X_D); - static final JsonWebKey RSA_X_PUBLIC = createRsaPublic("kid/2", "RS256", RSA_X_E, RSA_X_N); + static final JsonWebKey RSA_2 = createRsa("kid/2", "RS256", RSA_2_E, RSA_2_N, RSA_2_D); + static final JsonWebKey RSA_2_PUBLIC = createRsaPublic("kid/2", "RS256", RSA_2_E, RSA_2_N); - static final JsonWebKeys RSA_1_2_PUBLIC = createJwks(RSA_1_PUBLIC, RSA_2_PUBLIC); + static final JsonWebKey RSA_X = createRsa("kid/2", "RS256", RSA_X_E, RSA_X_N, RSA_X_D); + static final JsonWebKey RSA_X_PUBLIC = createRsaPublic("kid/2", "RS256", RSA_X_E, RSA_X_N); - static class Jwks { - static final JsonWebKeys ALL = createJwks(OCT_1, OCT_2, OCT_3, FORWARD_SLASH_KID_OCT_1, RSA_1_PUBLIC, RSA_2_PUBLIC); - static final JsonWebKeys RSA_1 = createJwks(RSA_1_PUBLIC); - static final JsonWebKeys RSA_2 = createJwks(RSA_2_PUBLIC); - static final JsonWebKeys RSA_1_NO_ALG = createJwks(RSA_1_PUBLIC_NO_ALG); - static final JsonWebKeys RSA_1_WRONG_ALG = createJwks(RSA_1_PUBLIC_WRONG_ALG); - } + static final JsonWebKeys RSA_1_2_PUBLIC = createJwks(RSA_1_PUBLIC, RSA_2_PUBLIC); + static class Jwks { + static final JsonWebKeys ALL = createJwks(OCT_1, OCT_2, OCT_3, FORWARD_SLASH_KID_OCT_1, RSA_1_PUBLIC, RSA_2_PUBLIC); + static final JsonWebKeys RSA_1 = createJwks(RSA_1_PUBLIC); + static final JsonWebKeys RSA_2 = createJwks(RSA_2_PUBLIC); + static final JsonWebKeys RSA_1_NO_ALG = createJwks(RSA_1_PUBLIC_NO_ALG); + static final JsonWebKeys RSA_1_WRONG_ALG = createJwks(RSA_1_PUBLIC_WRONG_ALG); + } - private static JsonWebKey createOct(String keyId, String algorithm, String k) { - JsonWebKey result = new JsonWebKey(); + private static JsonWebKey createOct(String keyId, String algorithm, String k) { + JsonWebKey result = new JsonWebKey(); - result.setKeyId(keyId); - result.setKeyType(KeyType.OCTET); - result.setAlgorithm(algorithm); - result.setPublicKeyUse(PublicKeyUse.SIGN); - result.setProperty("k", k); + result.setKeyId(keyId); + result.setKeyType(KeyType.OCTET); + result.setAlgorithm(algorithm); + result.setPublicKeyUse(PublicKeyUse.SIGN); + result.setProperty("k", k); - return result; - } + return result; + } - private static JsonWebKey createRsa(String keyId, String algorithm, String e, String n, String d) { - JsonWebKey result = new JsonWebKey(); + private static JsonWebKey createRsa(String keyId, String algorithm, String e, String n, String d) { + JsonWebKey result = new JsonWebKey(); - result.setKeyId(keyId); - result.setKeyType(KeyType.RSA); - result.setAlgorithm(algorithm); - result.setPublicKeyUse(PublicKeyUse.SIGN); + result.setKeyId(keyId); + result.setKeyType(KeyType.RSA); + result.setAlgorithm(algorithm); + result.setPublicKeyUse(PublicKeyUse.SIGN); - if (d != null) { - result.setProperty("d", d); - } + if (d != null) { + result.setProperty("d", d); + } - result.setProperty("e", e); - result.setProperty("n", n); + result.setProperty("e", e); + result.setProperty("n", n); - return result; - } + return result; + } - private static JsonWebKey createRsaPublic(String keyId, String algorithm, String e, String n) { - return createRsa(keyId, algorithm, e, n, null); - } + private static JsonWebKey createRsaPublic(String keyId, String algorithm, String e, String n) { + return createRsa(keyId, algorithm, e, n, null); + } - private static JsonWebKeys createJwks(JsonWebKey... array) { - JsonWebKeys result = new JsonWebKeys(); + private static JsonWebKeys createJwks(JsonWebKey... array) { + JsonWebKeys result = new JsonWebKeys(); - result.setKeys(Arrays.asList(array)); + result.setKeys(Arrays.asList(array)); - return result; - } + return result; + } } diff --git a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/TestJwts.java b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/TestJwts.java index 217e64926c..d35c4d58ac 100644 --- a/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/TestJwts.java +++ b/src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/TestJwts.java @@ -25,95 +25,106 @@ import org.apache.logging.log4j.util.Strings; class TestJwts { - static final String ROLES_CLAIM = "roles"; - static final Set TEST_ROLES = ImmutableSet.of("role1", "role2"); - static final String TEST_ROLES_STRING = Strings.join(TEST_ROLES, ','); + static final String ROLES_CLAIM = "roles"; + static final Set TEST_ROLES = ImmutableSet.of("role1", "role2"); + static final String TEST_ROLES_STRING = Strings.join(TEST_ROLES, ','); - static final String TEST_AUDIENCE = "TestAudience"; + static final String TEST_AUDIENCE = "TestAudience"; - static final String MCCOY_SUBJECT = "Leonard McCoy"; + static final String MCCOY_SUBJECT = "Leonard McCoy"; - static final JwtToken MC_COY = create(MCCOY_SUBJECT, TEST_AUDIENCE, ROLES_CLAIM, TEST_ROLES_STRING); + static final JwtToken MC_COY = create(MCCOY_SUBJECT, TEST_AUDIENCE, ROLES_CLAIM, TEST_ROLES_STRING); - static final JwtToken MC_COY_EXPIRED = create(MCCOY_SUBJECT, TEST_AUDIENCE, ROLES_CLAIM, TEST_ROLES_STRING, JwtConstants.CLAIM_EXPIRY, 10); + static final JwtToken MC_COY_EXPIRED = create( + MCCOY_SUBJECT, + TEST_AUDIENCE, + ROLES_CLAIM, + TEST_ROLES_STRING, + JwtConstants.CLAIM_EXPIRY, + 10 + ); - static final String MC_COY_SIGNED_OCT_1 = createSigned(MC_COY, TestJwk.OCT_1); + static final String MC_COY_SIGNED_OCT_1 = createSigned(MC_COY, TestJwk.OCT_1); - static final String MC_COY_SIGNED_OCT_1_INVALID_KID = createSigned(MC_COY, TestJwk.FORWARD_SLASH_KID_OCT_1); + static final String MC_COY_SIGNED_OCT_1_INVALID_KID = createSigned(MC_COY, TestJwk.FORWARD_SLASH_KID_OCT_1); - static final String MC_COY_SIGNED_RSA_1 = createSigned(MC_COY, TestJwk.RSA_1); + static final String MC_COY_SIGNED_RSA_1 = createSigned(MC_COY, TestJwk.RSA_1); - static final String MC_COY_SIGNED_RSA_X = createSigned(MC_COY, TestJwk.RSA_X); + static final String MC_COY_SIGNED_RSA_X = createSigned(MC_COY, TestJwk.RSA_X); - static final String MC_COY_EXPIRED_SIGNED_OCT_1 = createSigned(MC_COY_EXPIRED, TestJwk.OCT_1); + static final String MC_COY_EXPIRED_SIGNED_OCT_1 = createSigned(MC_COY_EXPIRED, TestJwk.OCT_1); - static class NoKid { - static final String MC_COY_SIGNED_RSA_1 = createSignedWithoutKeyId(MC_COY, TestJwk.RSA_1); - static final String MC_COY_SIGNED_RSA_2 = createSignedWithoutKeyId(MC_COY, TestJwk.RSA_2); - static final String MC_COY_SIGNED_RSA_X = createSignedWithoutKeyId(MC_COY, TestJwk.RSA_X); - } + static class NoKid { + static final String MC_COY_SIGNED_RSA_1 = createSignedWithoutKeyId(MC_COY, TestJwk.RSA_1); + static final String MC_COY_SIGNED_RSA_2 = createSignedWithoutKeyId(MC_COY, TestJwk.RSA_2); + static final String MC_COY_SIGNED_RSA_X = createSignedWithoutKeyId(MC_COY, TestJwk.RSA_X); + } - static class PeculiarEscaping { - static final String MC_COY_SIGNED_RSA_1 = createSignedWithPeculiarEscaping(MC_COY, TestJwk.RSA_1); - } + static class PeculiarEscaping { + static final String MC_COY_SIGNED_RSA_1 = createSignedWithPeculiarEscaping(MC_COY, TestJwk.RSA_1); + } - static JwtToken create(String subject, String audience, Object... moreClaims) { - JwtClaims claims = new JwtClaims(); + static JwtToken create(String subject, String audience, Object... moreClaims) { + JwtClaims claims = new JwtClaims(); - claims.setSubject(subject); - claims.setAudience(audience); + claims.setSubject(subject); + claims.setAudience(audience); - if (moreClaims != null) { - for (int i = 0; i < moreClaims.length; i += 2) { - claims.setClaim(String.valueOf(moreClaims[i]), moreClaims[i + 1]); - } - } + if (moreClaims != null) { + for (int i = 0; i < moreClaims.length; i += 2) { + claims.setClaim(String.valueOf(moreClaims[i]), moreClaims[i + 1]); + } + } - JwtToken result = new JwtToken(claims); + JwtToken result = new JwtToken(claims); - return result; - } + return result; + } - static String createSigned(JwtToken baseJwt, JsonWebKey jwk) { + static String createSigned(JwtToken baseJwt, JsonWebKey jwk) { return createSigned(baseJwt, jwk, JwsUtils.getSignatureProvider(jwk)); } static String createSigned(JwtToken baseJwt, JsonWebKey jwk, JwsSignatureProvider signatureProvider) { - JwsHeaders jwsHeaders = new JwsHeaders(); - JwtToken signedToken = new JwtToken(jwsHeaders, baseJwt.getClaims()); + JwsHeaders jwsHeaders = new JwsHeaders(); + JwtToken signedToken = new JwtToken(jwsHeaders, baseJwt.getClaims()); - jwsHeaders.setKeyId(jwk.getKeyId()); + jwsHeaders.setKeyId(jwk.getKeyId()); return new JoseJwtProducer().processJwt(signedToken, null, signatureProvider); - } - - static String createSignedWithoutKeyId(JwtToken baseJwt, JsonWebKey jwk) { - JwsHeaders jwsHeaders = new JwsHeaders(); - JwtToken signedToken = new JwtToken(jwsHeaders, baseJwt.getClaims()); + } - return new JoseJwtProducer().processJwt(signedToken, null, JwsUtils.getSignatureProvider(jwk)); - } + static String createSignedWithoutKeyId(JwtToken baseJwt, JsonWebKey jwk) { + JwsHeaders jwsHeaders = new JwsHeaders(); + JwtToken signedToken = new JwtToken(jwsHeaders, baseJwt.getClaims()); - static String createSignedWithPeculiarEscaping(JwtToken baseJwt, JsonWebKey jwk) { - JwsSignatureProvider signatureProvider = JwsUtils.getSignatureProvider(jwk); - JwsHeaders jwsHeaders = new JwsHeaders(); - JwtToken signedToken = new JwtToken(jwsHeaders, baseJwt.getClaims()); + return new JoseJwtProducer().processJwt(signedToken, null, JwsUtils.getSignatureProvider(jwk)); + } - // Depends on CXF not escaping the input string. This may fail for other frameworks or versions. - jwsHeaders.setKeyId(jwk.getKeyId().replace("/", "\\/")); + static String createSignedWithPeculiarEscaping(JwtToken baseJwt, JsonWebKey jwk) { + JwsSignatureProvider signatureProvider = JwsUtils.getSignatureProvider(jwk); + JwsHeaders jwsHeaders = new JwsHeaders(); + JwtToken signedToken = new JwtToken(jwsHeaders, baseJwt.getClaims()); - return new JoseJwtProducer().processJwt(signedToken, null, signatureProvider); - } + // Depends on CXF not escaping the input string. This may fail for other frameworks or versions. + jwsHeaders.setKeyId(jwk.getKeyId().replace("/", "\\/")); - static String createMcCoySignedOct1(long nbf, long exp) - { - JwtToken jwt_token = create( - MCCOY_SUBJECT, TEST_AUDIENCE, - ROLES_CLAIM, TEST_ROLES_STRING, - JwtConstants.CLAIM_NOT_BEFORE, nbf, - JwtConstants.CLAIM_EXPIRY, exp); + return new JoseJwtProducer().processJwt(signedToken, null, signatureProvider); + } - return createSigned(jwt_token, TestJwk.OCT_1); - } + static String createMcCoySignedOct1(long nbf, long exp) { + JwtToken jwt_token = create( + MCCOY_SUBJECT, + TEST_AUDIENCE, + ROLES_CLAIM, + TEST_ROLES_STRING, + JwtConstants.CLAIM_NOT_BEFORE, + nbf, + JwtConstants.CLAIM_EXPIRY, + exp + ); + + return createSigned(jwt_token, TestJwk.OCT_1); + } } diff --git a/src/test/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticatorTest.java b/src/test/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticatorTest.java index bfaf33049d..5a70a963c6 100644 --- a/src/test/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticatorTest.java +++ b/src/test/java/com/amazon/dlic/auth/http/saml/HTTPSamlAuthenticatorTest.java @@ -62,37 +62,39 @@ public class HTTPSamlAuthenticatorTest { protected MockSamlIdpServer mockSamlIdpServer; - private static final Pattern WWW_AUTHENTICATE_PATTERN = Pattern - .compile("([^\\s]+)\\s*([^\\s=]+)=\"([^\"]+)\"\\s*([^\\s=]+)=\"([^\"]+)\"\\s*([^\\s=]+)=\"([^\"]+)\"\\s*"); + private static final Pattern WWW_AUTHENTICATE_PATTERN = Pattern.compile( + "([^\\s]+)\\s*([^\\s=]+)=\"([^\"]+)\"\\s*([^\\s=]+)=\"([^\"]+)\"\\s*([^\\s=]+)=\"([^\"]+)\"\\s*" + ); private static final String SPOCK_KEY = "-----BEGIN ENCRYPTED PRIVATE KEY-----\n" - + "MIIE6TAbBgkqhkiG9w0BBQMwDgQI0JMa7PyPedwCAggABIIEyLdPL2RXj8jjKqFT\n" - + "p+7vywwyxyUQOQvvIIU6H+lKZPd/y6pxzYtGd1suT2aermrrlh4b/ZXXfj/EcKcw\n" - + "GgcXB60Kr7UHIv7Xr498S4EKa9R7UG0NtWtsA3FVR5ndwXI+CiRSShhkskmpseVH\n" - + "dNWAoUsKQFbZRLnoINMKIw1/lpQBUwAUcYVB7LxLeKSTVHn/h9kvq0tad1kbE5OY\n" - + "GnOLEVW311++XQ3Ep/13tGEZCrxef+QsnmXuYxXBq4RvbyGZOvyM2FC7va8KzJxl\n" - + "P38SPEL1TzqokQB/eLDBMBOCqkhTbP/8lNuoEVm44T6//ijBp6VdBB+YRIFh3NrS\n" - + "1fPuDVgHr1jrRGICe8lzWy/bSa+4FlxYjn5qpEzZQtbC6C+iRzlwtlCiDdKl8zJ1\n" - + "YF80OW9Gr3Kvph2LJukBiODcyWUAsAf5vJH3vfPV4T9kWTNMu2NCy3Ch8u9d906k\n" - + "zojB/tRRdZ/XCftkU05gYU/5ruU1YA49U60s0KWXvSLmecFo2SjkcEoPDI+Y80Uw\n" - + "OB/5kdh1M1uu/qjoJTPWBbZ28L6e0fiMsr7eWSG7PQFwnN6VzY6Oesm8AS8LMe3V\n" - + "Dr4Syec8vVfGg/EDsjNC1yeZTzlO66NQYGkpnHwK1kgX/XXe7fjDfztPyM9crBXj\n" - + "YcYpNULAkMj9QUVDQqQ7L8TjoAFQiSdvNa+kkDhaxnAXoxfqeacTtkpKcHADsAQL\n" - + "azfoyflnpuZ1dIn0noRFsVuguKDp4k990bhXu9RkQ1H5IzIoYqJwypacVdt3m74o\n" - + "jpZvBY6z0EtBNkze6WA0Vj0BSWpy/IzndDwroG4Xf+54hn0R/Tp5K5UNttOaJN8c\n" - + "9U/NTiGJTJg1O4x6xbPD7C5bBdoJ/MH5yJuk/dUc7pVkisLpuH9sAPETjYCdFIjX\n" - + "MSRJCtq2ouT0ZRW1yBIrKIadgHLExhjZjTSQCBXJMbO7r2DjPHMZU23GTiPtC8ua\n" - + "L2BmC+AW7RQ2Fyo3hJDT2TM4XlMMlTtGuFxkWwmjV+FiwfjbiR3cp0+99/X6OFu5\n" - + "ysgZLuTMQsmWNJ8ZARZqBnkGnN92Aw4D5GLCFv3QXO+fqJnOP1PbkPwpjq59Yytf\n" - + "U4XqyTwRYSXRzwPFFb7RcgL9HbmjpRBEnvqEjKYeXxkBnhs+WOWN/PuJzGgP5uAk\n" - + "jAjQbtgLEPd4WpGcwEhkX6S1DBi8NrGapuehCjXsN1axify8Kx4eRuTiPdINlgsq\n" - + "d2MsPIuDgU2+0QXrXjRLwABcMGuKcmmfZjC+zZomj+yr4+Togs3vhSj9yGK3HHMh\n" - + "NgOlPBTibruXXa4AI07c28j3sEry+CMZrUGyYg6o1HLBpBfOmp7V5HJcvkMFWCVy\n" - + "DPFm5LZu0jZMDj9a+oGkv4hfp1xSXSUjhjiGz47xFJb6PH9pOUIkhTEdFCgEXbaR\n" - + "fXcR+kakLOotL4X1cT9cpxdimN3CCTBpr03gCv2NCVYMYhHKHK+CQVngJrY+PzMH\n" - + "q6fw81bUNcixZyeXFfLFN6GK75k51UV7YS/X2H8YkqGeIVNaFjrcqUoVAN8jQOeb\n" - + "XXIa8gT/MdNT0+W3NHKcbE31pDhOI92COZWlhOyp1cLhyo1ytayjxPTl/2RM/Vtj\n" + "T9IKkp7810LOKhrCDQ==\n" - + "-----END ENCRYPTED PRIVATE KEY-----"; + + "MIIE6TAbBgkqhkiG9w0BBQMwDgQI0JMa7PyPedwCAggABIIEyLdPL2RXj8jjKqFT\n" + + "p+7vywwyxyUQOQvvIIU6H+lKZPd/y6pxzYtGd1suT2aermrrlh4b/ZXXfj/EcKcw\n" + + "GgcXB60Kr7UHIv7Xr498S4EKa9R7UG0NtWtsA3FVR5ndwXI+CiRSShhkskmpseVH\n" + + "dNWAoUsKQFbZRLnoINMKIw1/lpQBUwAUcYVB7LxLeKSTVHn/h9kvq0tad1kbE5OY\n" + + "GnOLEVW311++XQ3Ep/13tGEZCrxef+QsnmXuYxXBq4RvbyGZOvyM2FC7va8KzJxl\n" + + "P38SPEL1TzqokQB/eLDBMBOCqkhTbP/8lNuoEVm44T6//ijBp6VdBB+YRIFh3NrS\n" + + "1fPuDVgHr1jrRGICe8lzWy/bSa+4FlxYjn5qpEzZQtbC6C+iRzlwtlCiDdKl8zJ1\n" + + "YF80OW9Gr3Kvph2LJukBiODcyWUAsAf5vJH3vfPV4T9kWTNMu2NCy3Ch8u9d906k\n" + + "zojB/tRRdZ/XCftkU05gYU/5ruU1YA49U60s0KWXvSLmecFo2SjkcEoPDI+Y80Uw\n" + + "OB/5kdh1M1uu/qjoJTPWBbZ28L6e0fiMsr7eWSG7PQFwnN6VzY6Oesm8AS8LMe3V\n" + + "Dr4Syec8vVfGg/EDsjNC1yeZTzlO66NQYGkpnHwK1kgX/XXe7fjDfztPyM9crBXj\n" + + "YcYpNULAkMj9QUVDQqQ7L8TjoAFQiSdvNa+kkDhaxnAXoxfqeacTtkpKcHADsAQL\n" + + "azfoyflnpuZ1dIn0noRFsVuguKDp4k990bhXu9RkQ1H5IzIoYqJwypacVdt3m74o\n" + + "jpZvBY6z0EtBNkze6WA0Vj0BSWpy/IzndDwroG4Xf+54hn0R/Tp5K5UNttOaJN8c\n" + + "9U/NTiGJTJg1O4x6xbPD7C5bBdoJ/MH5yJuk/dUc7pVkisLpuH9sAPETjYCdFIjX\n" + + "MSRJCtq2ouT0ZRW1yBIrKIadgHLExhjZjTSQCBXJMbO7r2DjPHMZU23GTiPtC8ua\n" + + "L2BmC+AW7RQ2Fyo3hJDT2TM4XlMMlTtGuFxkWwmjV+FiwfjbiR3cp0+99/X6OFu5\n" + + "ysgZLuTMQsmWNJ8ZARZqBnkGnN92Aw4D5GLCFv3QXO+fqJnOP1PbkPwpjq59Yytf\n" + + "U4XqyTwRYSXRzwPFFb7RcgL9HbmjpRBEnvqEjKYeXxkBnhs+WOWN/PuJzGgP5uAk\n" + + "jAjQbtgLEPd4WpGcwEhkX6S1DBi8NrGapuehCjXsN1axify8Kx4eRuTiPdINlgsq\n" + + "d2MsPIuDgU2+0QXrXjRLwABcMGuKcmmfZjC+zZomj+yr4+Togs3vhSj9yGK3HHMh\n" + + "NgOlPBTibruXXa4AI07c28j3sEry+CMZrUGyYg6o1HLBpBfOmp7V5HJcvkMFWCVy\n" + + "DPFm5LZu0jZMDj9a+oGkv4hfp1xSXSUjhjiGz47xFJb6PH9pOUIkhTEdFCgEXbaR\n" + + "fXcR+kakLOotL4X1cT9cpxdimN3CCTBpr03gCv2NCVYMYhHKHK+CQVngJrY+PzMH\n" + + "q6fw81bUNcixZyeXFfLFN6GK75k51UV7YS/X2H8YkqGeIVNaFjrcqUoVAN8jQOeb\n" + + "XXIa8gT/MdNT0+W3NHKcbE31pDhOI92COZWlhOyp1cLhyo1ytayjxPTl/2RM/Vtj\n" + + "T9IKkp7810LOKhrCDQ==\n" + + "-----END ENCRYPTED PRIVATE KEY-----"; private static X509Certificate spSigningCertificate; private static PrivateKey spSigningPrivateKey; @@ -121,9 +123,14 @@ public void basicTest() throws Exception { mockSamlIdpServer.setAuthenticateUser("horst"); mockSamlIdpServer.setEndpointQueryString(null); - Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) - .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); + Settings settings = Settings.builder() + .put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) + .put("kibana_url", "http://wherever") + .put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) + .put("exchange_key", "abc") + .put("roles_key", "roles") + .put("path.home", ".") + .build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -137,9 +144,11 @@ public void basicTest() throws Exception { samlAuthenticator.reRequestAuthentication(tokenRestChannel, null); String responseJson = new String(BytesReference.toBytes(tokenRestChannel.response.content())); - HashMap response = DefaultObjectMapper.objectMapper.readValue(responseJson, - new TypeReference>() { - }); + HashMap response = DefaultObjectMapper.objectMapper.readValue( + responseJson, + new TypeReference>() { + } + ); String authorization = (String) response.get("authorization"); Assert.assertNotNull("Expected authorization attribute in JSON: " + responseJson, authorization); @@ -157,11 +166,18 @@ public void decryptAssertionsTest() throws Exception { mockSamlIdpServer.setSpSignatureCertificate(spSigningCertificate); mockSamlIdpServer.setEncryptAssertion(true); - Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) - .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("sp.signature_private_key", "-BEGIN PRIVATE KEY-\n" - + Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()) + "-END PRIVATE KEY-") - .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); + Settings settings = Settings.builder() + .put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) + .put("kibana_url", "http://wherever") + .put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) + .put( + "sp.signature_private_key", + "-BEGIN PRIVATE KEY-\n" + Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()) + "-END PRIVATE KEY-" + ) + .put("exchange_key", "abc") + .put("roles_key", "roles") + .put("path.home", ".") + .build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -175,9 +191,11 @@ public void decryptAssertionsTest() throws Exception { samlAuthenticator.reRequestAuthentication(tokenRestChannel, null); String responseJson = new String(BytesReference.toBytes(tokenRestChannel.response.content())); - HashMap response = DefaultObjectMapper.objectMapper.readValue(responseJson, - new TypeReference>() { - }); + HashMap response = DefaultObjectMapper.objectMapper.readValue( + responseJson, + new TypeReference>() { + } + ); String authorization = (String) response.get("authorization"); Assert.assertNotNull("Expected authorization attribute in JSON: " + responseJson, authorization); @@ -196,11 +214,18 @@ public void shouldUnescapeSamlEntitiesTest() throws Exception { mockSamlIdpServer.setEncryptAssertion(true); mockSamlIdpServer.setAuthenticateUserRoles(Arrays.asList("ABC\\Admin")); - Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) - .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("sp.signature_private_key", "-BEGIN PRIVATE KEY-\n" - + Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()) + "-END PRIVATE KEY-") - .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); + Settings settings = Settings.builder() + .put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) + .put("kibana_url", "http://wherever") + .put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) + .put( + "sp.signature_private_key", + "-BEGIN PRIVATE KEY-\n" + Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()) + "-END PRIVATE KEY-" + ) + .put("exchange_key", "abc") + .put("roles_key", "roles") + .put("path.home", ".") + .build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -214,9 +239,11 @@ public void shouldUnescapeSamlEntitiesTest() throws Exception { samlAuthenticator.reRequestAuthentication(tokenRestChannel, null); String responseJson = new String(BytesReference.toBytes(tokenRestChannel.response.content())); - HashMap response = DefaultObjectMapper.objectMapper.readValue(responseJson, - new TypeReference>() { - }); + HashMap response = DefaultObjectMapper.objectMapper.readValue( + responseJson, + new TypeReference>() { + } + ); String authorization = (String) response.get("authorization"); Assert.assertNotNull("Expected authorization attribute in JSON: " + responseJson, authorization); @@ -225,7 +252,7 @@ public void shouldUnescapeSamlEntitiesTest() throws Exception { JwtToken jwt = jwtConsumer.getJwtToken(); Assert.assertEquals("ABC\\User1", jwt.getClaim("sub")); - Assert.assertEquals("ABC\\User1", samlAuthenticator.httpJwtAuthenticator.extractSubject(jwt.getClaims())); + Assert.assertEquals("ABC\\User1", samlAuthenticator.httpJwtAuthenticator.extractSubject(jwt.getClaims())); Assert.assertEquals("[ABC\\Admin]", String.valueOf(jwt.getClaim("roles"))); Assert.assertEquals("ABC\\Admin", samlAuthenticator.httpJwtAuthenticator.extractRoles(jwt.getClaims())[0]); } @@ -238,11 +265,18 @@ public void shouldUnescapeSamlEntitiesTest2() throws Exception { mockSamlIdpServer.setEncryptAssertion(true); mockSamlIdpServer.setAuthenticateUserRoles(Arrays.asList("ABC\"Admin")); - Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) - .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("sp.signature_private_key", "-BEGIN PRIVATE KEY-\n" - + Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()) + "-END PRIVATE KEY-") - .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); + Settings settings = Settings.builder() + .put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) + .put("kibana_url", "http://wherever") + .put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) + .put( + "sp.signature_private_key", + "-BEGIN PRIVATE KEY-\n" + Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()) + "-END PRIVATE KEY-" + ) + .put("exchange_key", "abc") + .put("roles_key", "roles") + .put("path.home", ".") + .build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -256,9 +290,11 @@ public void shouldUnescapeSamlEntitiesTest2() throws Exception { samlAuthenticator.reRequestAuthentication(tokenRestChannel, null); String responseJson = new String(BytesReference.toBytes(tokenRestChannel.response.content())); - HashMap response = DefaultObjectMapper.objectMapper.readValue(responseJson, - new TypeReference>() { - }); + HashMap response = DefaultObjectMapper.objectMapper.readValue( + responseJson, + new TypeReference>() { + } + ); String authorization = (String) response.get("authorization"); Assert.assertNotNull("Expected authorization attribute in JSON: " + responseJson, authorization); @@ -267,7 +303,7 @@ public void shouldUnescapeSamlEntitiesTest2() throws Exception { JwtToken jwt = jwtConsumer.getJwtToken(); Assert.assertEquals("ABC\"User1", jwt.getClaim("sub")); - Assert.assertEquals("ABC\"User1", samlAuthenticator.httpJwtAuthenticator.extractSubject(jwt.getClaims())); + Assert.assertEquals("ABC\"User1", samlAuthenticator.httpJwtAuthenticator.extractSubject(jwt.getClaims())); Assert.assertEquals("[ABC\"Admin]", String.valueOf(jwt.getClaim("roles"))); Assert.assertEquals("ABC\"Admin", samlAuthenticator.httpJwtAuthenticator.extractRoles(jwt.getClaims())[0]); } @@ -280,11 +316,18 @@ public void shouldNotEscapeSamlEntities() throws Exception { mockSamlIdpServer.setEncryptAssertion(true); mockSamlIdpServer.setAuthenticateUserRoles(Arrays.asList("ABC/Admin")); - Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) - .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("sp.signature_private_key", "-BEGIN PRIVATE KEY-\n" - + Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()) + "-END PRIVATE KEY-") - .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); + Settings settings = Settings.builder() + .put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) + .put("kibana_url", "http://wherever") + .put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) + .put( + "sp.signature_private_key", + "-BEGIN PRIVATE KEY-\n" + Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()) + "-END PRIVATE KEY-" + ) + .put("exchange_key", "abc") + .put("roles_key", "roles") + .put("path.home", ".") + .build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -298,9 +341,11 @@ public void shouldNotEscapeSamlEntities() throws Exception { samlAuthenticator.reRequestAuthentication(tokenRestChannel, null); String responseJson = new String(BytesReference.toBytes(tokenRestChannel.response.content())); - HashMap response = DefaultObjectMapper.objectMapper.readValue(responseJson, - new TypeReference>() { - }); + HashMap response = DefaultObjectMapper.objectMapper.readValue( + responseJson, + new TypeReference>() { + } + ); String authorization = (String) response.get("authorization"); Assert.assertNotNull("Expected authorization attribute in JSON: " + responseJson, authorization); @@ -309,7 +354,7 @@ public void shouldNotEscapeSamlEntities() throws Exception { JwtToken jwt = jwtConsumer.getJwtToken(); Assert.assertEquals("ABC/User1", jwt.getClaim("sub")); - Assert.assertEquals("ABC/User1", samlAuthenticator.httpJwtAuthenticator.extractSubject(jwt.getClaims())); + Assert.assertEquals("ABC/User1", samlAuthenticator.httpJwtAuthenticator.extractSubject(jwt.getClaims())); Assert.assertEquals("[ABC/Admin]", String.valueOf(jwt.getClaim("roles"))); Assert.assertEquals("ABC/Admin", samlAuthenticator.httpJwtAuthenticator.extractRoles(jwt.getClaims())[0]); } @@ -322,11 +367,18 @@ public void shouldNotTrimWhitespaceInJwtRoles() throws Exception { mockSamlIdpServer.setEncryptAssertion(true); mockSamlIdpServer.setAuthenticateUserRoles(Arrays.asList(" ABC/Admin ")); - Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) - .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("sp.signature_private_key", "-BEGIN PRIVATE KEY-\n" - + Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()) + "-END PRIVATE KEY-") - .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); + Settings settings = Settings.builder() + .put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) + .put("kibana_url", "http://wherever") + .put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) + .put( + "sp.signature_private_key", + "-BEGIN PRIVATE KEY-\n" + Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()) + "-END PRIVATE KEY-" + ) + .put("exchange_key", "abc") + .put("roles_key", "roles") + .put("path.home", ".") + .build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -340,9 +392,11 @@ public void shouldNotTrimWhitespaceInJwtRoles() throws Exception { samlAuthenticator.reRequestAuthentication(tokenRestChannel, null); String responseJson = new String(BytesReference.toBytes(tokenRestChannel.response.content())); - HashMap response = DefaultObjectMapper.objectMapper.readValue(responseJson, - new TypeReference>() { - }); + HashMap response = DefaultObjectMapper.objectMapper.readValue( + responseJson, + new TypeReference>() { + } + ); String authorization = (String) response.get("authorization"); Assert.assertNotNull("Expected authorization attribute in JSON: " + responseJson, authorization); @@ -362,12 +416,16 @@ public void testMetadataBody() throws Exception { // Note: We need to replace endpoint with mockSamlIdpServer endpoint final String metadataBody = FileHelper.loadFile("saml/metadata.xml") - .replaceAll("http://localhost:33667/", mockSamlIdpServer.getMetadataUri()); + .replaceAll("http://localhost:33667/", mockSamlIdpServer.getMetadataUri()); - Settings settings = Settings.builder().put(IDP_METADATA_CONTENT, metadataBody) + Settings settings = Settings.builder() + .put(IDP_METADATA_CONTENT, metadataBody) .put("kibana_url", "http://wherever") .put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); + .put("exchange_key", "abc") + .put("roles_key", "roles") + .put("path.home", ".") + .build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -381,9 +439,11 @@ public void testMetadataBody() throws Exception { samlAuthenticator.reRequestAuthentication(tokenRestChannel, null); String responseJson = new String(BytesReference.toBytes(tokenRestChannel.response.content())); - HashMap response = DefaultObjectMapper.objectMapper.readValue(responseJson, + HashMap response = DefaultObjectMapper.objectMapper.readValue( + responseJson, new TypeReference>() { - }); + } + ); String authorization = (String) response.get("authorization"); Assert.assertNotNull("Expected authorization attribute in JSON: " + responseJson, authorization); @@ -394,18 +454,21 @@ public void testMetadataBody() throws Exception { Assert.assertEquals("horst", jwt.getClaim("sub")); } - - @Test(expected= RuntimeException.class) + @Test(expected = RuntimeException.class) public void testEmptyMetadataBody() throws Exception { mockSamlIdpServer.setSignResponses(true); mockSamlIdpServer.loadSigningKeys("saml/kirk-keystore.jks", "kirk"); mockSamlIdpServer.setAuthenticateUser("horst"); mockSamlIdpServer.setEndpointQueryString(null); - Settings settings = Settings.builder().put(IDP_METADATA_CONTENT, "") + Settings settings = Settings.builder() + .put(IDP_METADATA_CONTENT, "") .put("kibana_url", "http://wherever") .put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); + .put("exchange_key", "abc") + .put("roles_key", "roles") + .put("path.home", ".") + .build(); new HTTPSamlAuthenticator(settings, null); } @@ -418,24 +481,34 @@ public void unsolicitedSsoTest() throws Exception { mockSamlIdpServer.setEndpointQueryString(null); mockSamlIdpServer.setDefaultAssertionConsumerService("http://wherever/opendistrosecurity/saml/acs/idpinitiated"); - Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) - .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); + Settings settings = Settings.builder() + .put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) + .put("kibana_url", "http://wherever") + .put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) + .put("exchange_key", "abc") + .put("roles_key", "roles") + .put("path.home", ".") + .build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); String encodedSamlResponse = mockSamlIdpServer.createUnsolicitedSamlResponse(); - RestRequest tokenRestRequest = buildTokenExchangeRestRequest(encodedSamlResponse, null, - "/opendistrosecurity/saml/acs/idpinitiated"); + RestRequest tokenRestRequest = buildTokenExchangeRestRequest( + encodedSamlResponse, + null, + "/opendistrosecurity/saml/acs/idpinitiated" + ); TestRestChannel tokenRestChannel = new TestRestChannel(tokenRestRequest); samlAuthenticator.reRequestAuthentication(tokenRestChannel, null); String responseJson = new String(BytesReference.toBytes(tokenRestChannel.response.content())); - HashMap response = DefaultObjectMapper.objectMapper.readValue(responseJson, - new TypeReference>() { - }); + HashMap response = DefaultObjectMapper.objectMapper.readValue( + responseJson, + new TypeReference>() { + } + ); String authorization = (String) response.get("authorization"); Assert.assertNotNull("Expected authorization attribute in JSON: " + responseJson, authorization); @@ -454,19 +527,29 @@ public void badUnsolicitedSsoTest() throws Exception { mockSamlIdpServer.setEndpointQueryString(null); mockSamlIdpServer.setDefaultAssertionConsumerService("http://wherever/opendistrosecurity/saml/acs/idpinitiated"); - Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) - .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); + Settings settings = Settings.builder() + .put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) + .put("kibana_url", "http://wherever") + .put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) + .put("exchange_key", "abc") + .put("roles_key", "roles") + .put("path.home", ".") + .build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); String encodedSamlResponse = mockSamlIdpServer.createUnsolicitedSamlResponse(); - AuthenticateHeaders authenticateHeaders = new AuthenticateHeaders("http://wherever/opendistrosecurity/saml/acs/", - "wrong_request_id"); + AuthenticateHeaders authenticateHeaders = new AuthenticateHeaders( + "http://wherever/opendistrosecurity/saml/acs/", + "wrong_request_id" + ); - RestRequest tokenRestRequest = buildTokenExchangeRestRequest(encodedSamlResponse, authenticateHeaders, - "/opendistrosecurity/saml/acs/idpinitiated"); + RestRequest tokenRestRequest = buildTokenExchangeRestRequest( + encodedSamlResponse, + authenticateHeaders, + "/opendistrosecurity/saml/acs/idpinitiated" + ); TestRestChannel tokenRestChannel = new TestRestChannel(tokenRestRequest); samlAuthenticator.reRequestAuthentication(tokenRestChannel, null); @@ -481,9 +564,14 @@ public void wrongCertTest() throws Exception { mockSamlIdpServer.setAuthenticateUser("horst"); mockSamlIdpServer.setEndpointQueryString(null); - Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) - .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); + Settings settings = Settings.builder() + .put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) + .put("kibana_url", "http://wherever") + .put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) + .put("exchange_key", "abc") + .put("roles_key", "roles") + .put("path.home", ".") + .build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -507,9 +595,14 @@ public void noSignatureTest() throws Exception { mockSamlIdpServer.setAuthenticateUser("horst"); mockSamlIdpServer.setEndpointQueryString(null); - Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) - .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); + Settings settings = Settings.builder() + .put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) + .put("kibana_url", "http://wherever") + .put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) + .put("exchange_key", "abc") + .put("roles_key", "roles") + .put("path.home", ".") + .build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -534,9 +627,15 @@ public void rolesTest() throws Exception { mockSamlIdpServer.setAuthenticateUserRoles(Arrays.asList("a ,c", "b ,d, e", "f", "g,,h, ,i")); mockSamlIdpServer.setEndpointQueryString(null); - Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) - .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").put("roles_seperator", ",").build(); + Settings settings = Settings.builder() + .put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) + .put("kibana_url", "http://wherever") + .put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) + .put("exchange_key", "abc") + .put("roles_key", "roles") + .put("path.home", ".") + .put("roles_seperator", ",") + .build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -550,9 +649,11 @@ public void rolesTest() throws Exception { samlAuthenticator.reRequestAuthentication(tokenRestChannel, null); String responseJson = new String(BytesReference.toBytes(tokenRestChannel.response.content())); - HashMap response = DefaultObjectMapper.objectMapper.readValue(responseJson, - new TypeReference>() { - }); + HashMap response = DefaultObjectMapper.objectMapper.readValue( + responseJson, + new TypeReference>() { + } + ); String authorization = (String) response.get("authorization"); Assert.assertNotNull("Expected authorization attribute in JSON: " + responseJson, authorization); @@ -561,8 +662,10 @@ public void rolesTest() throws Exception { JwtToken jwt = jwtConsumer.getJwtToken(); Assert.assertEquals("horst", jwt.getClaim("sub")); - Assert.assertArrayEquals(new String[] { "a ", "c", "b ", "d", " e", "f", "g", "h", " ", "i" }, - ((List) jwt.getClaim("roles")).toArray(new String[0])); + Assert.assertArrayEquals( + new String[] { "a ", "c", "b ", "d", " e", "f", "g", "h", " ", "i" }, + ((List) jwt.getClaim("roles")).toArray(new String[0]) + ); } @Test @@ -572,9 +675,14 @@ public void idpEndpointWithQueryStringTest() throws Exception { mockSamlIdpServer.setAuthenticateUser("horst"); mockSamlIdpServer.setEndpointQueryString("extra=query"); - Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) - .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); + Settings settings = Settings.builder() + .put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) + .put("kibana_url", "http://wherever") + .put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) + .put("exchange_key", "abc") + .put("roles_key", "roles") + .put("path.home", ".") + .build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -588,9 +696,11 @@ public void idpEndpointWithQueryStringTest() throws Exception { samlAuthenticator.reRequestAuthentication(tokenRestChannel, null); String responseJson = new String(BytesReference.toBytes(tokenRestChannel.response.content())); - HashMap response = DefaultObjectMapper.objectMapper.readValue(responseJson, - new TypeReference>() { - }); + HashMap response = DefaultObjectMapper.objectMapper.readValue( + responseJson, + new TypeReference>() { + } + ); String authorization = (String) response.get("authorization"); Assert.assertNotNull("Expected authorization attribute in JSON: " + responseJson, authorization); @@ -622,9 +732,12 @@ private void commaSeparatedRoles(final String rolesAsString, final Settings.Buil mockSamlIdpServer.setEndpointQueryString(null); Settings settings = settingsBuilder.put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) - .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".") - .build(); + .put("kibana_url", "http://wherever") + .put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) + .put("exchange_key", "abc") + .put("roles_key", "roles") + .put("path.home", ".") + .build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); AuthenticateHeaders authenticateHeaders = getAutenticateHeaders(samlAuthenticator); @@ -637,9 +750,11 @@ private void commaSeparatedRoles(final String rolesAsString, final Settings.Buil samlAuthenticator.reRequestAuthentication(tokenRestChannel, null); String responseJson = new String(BytesReference.toBytes(tokenRestChannel.response.content())); - HashMap response = DefaultObjectMapper.objectMapper.readValue(responseJson, - new TypeReference>() { - }); + HashMap response = DefaultObjectMapper.objectMapper.readValue( + responseJson, + new TypeReference>() { + } + ); String authorization = (String) response.get("authorization"); Assert.assertNotNull("Expected authorization attribute in JSON: " + responseJson, authorization); @@ -648,8 +763,7 @@ private void commaSeparatedRoles(final String rolesAsString, final Settings.Buil JwtToken jwt = jwtConsumer.getJwtToken(); Assert.assertEquals("horst", jwt.getClaim("sub")); - Assert.assertArrayEquals(new String[] { "a", "b" }, - ((List) jwt.getClaim("roles")).toArray(new String[0])); + Assert.assertArrayEquals(new String[] { "a", "b" }, ((List) jwt.getClaim("roles")).toArray(new String[0])); } @Test @@ -660,12 +774,18 @@ public void basicLogoutTest() throws Exception { mockSamlIdpServer.setSpSignatureCertificate(spSigningCertificate); mockSamlIdpServer.setEndpointQueryString(null); - Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) - .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("exchange_key", "abc").put("roles_key", "roles") - .put("sp.signature_private_key", "-BEGIN PRIVATE KEY-\n" - + Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()) + "-END PRIVATE KEY-") - .put("path.home", ".").build(); + Settings settings = Settings.builder() + .put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) + .put("kibana_url", "http://wherever") + .put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) + .put("exchange_key", "abc") + .put("roles_key", "roles") + .put( + "sp.signature_private_key", + "-BEGIN PRIVATE KEY-\n" + Base64.getEncoder().encodeToString(spSigningPrivateKey.getEncoded()) + "-END PRIVATE KEY-" + ) + .put("path.home", ".") + .build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -688,10 +808,16 @@ public void basicLogoutTestEncryptedKey() throws Exception { mockSamlIdpServer.setSpSignatureCertificate(spSigningCertificate); mockSamlIdpServer.setEndpointQueryString(null); - Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) - .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("exchange_key", "abc").put("roles_key", "roles").put("sp.signature_private_key", SPOCK_KEY) - .put("sp.signature_private_key_password", "changeit").put("path.home", ".").build(); + Settings settings = Settings.builder() + .put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) + .put("kibana_url", "http://wherever") + .put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) + .put("exchange_key", "abc") + .put("roles_key", "roles") + .put("sp.signature_private_key", SPOCK_KEY) + .put("sp.signature_private_key_password", "changeit") + .put("path.home", ".") + .build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -710,10 +836,15 @@ public void basicLogoutTestEncryptedKey() throws Exception { public void initialConnectionFailureTest() throws Exception { try (MockSamlIdpServer mockSamlIdpServer = new MockSamlIdpServer()) { - Settings settings = Settings.builder().put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) - .put("idp.min_refresh_delay", 100) - .put("kibana_url", "http://wherever").put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) - .put("exchange_key", "abc").put("roles_key", "roles").put("path.home", ".").build(); + Settings settings = Settings.builder() + .put(IDP_METADATA_URL, mockSamlIdpServer.getMetadataUri()) + .put("idp.min_refresh_delay", 100) + .put("kibana_url", "http://wherever") + .put("idp.entity_id", mockSamlIdpServer.getIdpEntityId()) + .put("exchange_key", "abc") + .put("roles_key", "roles") + .put("path.home", ".") + .build(); HTTPSamlAuthenticator samlAuthenticator = new HTTPSamlAuthenticator(settings, null); @@ -742,9 +873,11 @@ public void initialConnectionFailureTest() throws Exception { samlAuthenticator.reRequestAuthentication(tokenRestChannel, null); String responseJson = new String(BytesReference.toBytes(tokenRestChannel.response.content())); - HashMap response = DefaultObjectMapper.objectMapper.readValue(responseJson, - new TypeReference>() { - }); + HashMap response = DefaultObjectMapper.objectMapper.readValue( + responseJson, + new TypeReference>() { + } + ); String authorization = (String) response.get("authorization"); Assert.assertNotNull("Expected authorization attribute in JSON: " + responseJson, authorization); @@ -765,8 +898,7 @@ private AuthenticateHeaders getAutenticateHeaders(HTTPSamlAuthenticator samlAuth List wwwAuthenticateHeaders = restChannel.response.getHeaders().get("WWW-Authenticate"); Assert.assertNotNull(wwwAuthenticateHeaders); - Assert.assertEquals("More than one WWW-Authenticate header: " + wwwAuthenticateHeaders, 1, - wwwAuthenticateHeaders.size()); + Assert.assertEquals("More than one WWW-Authenticate header: " + wwwAuthenticateHeaders, 1, wwwAuthenticateHeaders.size()); String wwwAuthenticateHeader = wwwAuthenticateHeaders.get(0); @@ -786,26 +918,36 @@ private AuthenticateHeaders getAutenticateHeaders(HTTPSamlAuthenticator samlAuth return new AuthenticateHeaders(location, requestId); } - private RestRequest buildTokenExchangeRestRequest(String encodedSamlResponse, - AuthenticateHeaders authenticateHeaders) { + private RestRequest buildTokenExchangeRestRequest(String encodedSamlResponse, AuthenticateHeaders authenticateHeaders) { return buildTokenExchangeRestRequest(encodedSamlResponse, authenticateHeaders, "/opendistrosecurity/saml/acs"); } - private RestRequest buildTokenExchangeRestRequest(String encodedSamlResponse, - AuthenticateHeaders authenticateHeaders, String acsEndpoint) { + private RestRequest buildTokenExchangeRestRequest( + String encodedSamlResponse, + AuthenticateHeaders authenticateHeaders, + String acsEndpoint + ) { String authtokenPostJson; if (authenticateHeaders != null) { - authtokenPostJson = "{\"SAMLResponse\": \"" + encodedSamlResponse + "\", \"RequestId\": \"" - + authenticateHeaders.requestId + "\"}"; + authtokenPostJson = "{\"SAMLResponse\": \"" + + encodedSamlResponse + + "\", \"RequestId\": \"" + + authenticateHeaders.requestId + + "\"}"; } else { - authtokenPostJson = "{\"SAMLResponse\": \"" + encodedSamlResponse - + "\", \"RequestId\": null, \"acsEndpoint\": \"" + acsEndpoint + "\" }"; + authtokenPostJson = "{\"SAMLResponse\": \"" + + encodedSamlResponse + + "\", \"RequestId\": null, \"acsEndpoint\": \"" + + acsEndpoint + + "\" }"; } - return new FakeRestRequest.Builder().withPath("/_opendistro/_security/api/authtoken").withMethod(Method.POST) - .withContent(new BytesArray(authtokenPostJson)) - .withHeaders(ImmutableMap.of("Content-Type", "application/json")).build(); + return new FakeRestRequest.Builder().withPath("/_opendistro/_security/api/authtoken") + .withMethod(Method.POST) + .withContent(new BytesArray(authtokenPostJson)) + .withHeaders(ImmutableMap.of("Content-Type", "application/json")) + .build(); } @BeforeClass @@ -814,8 +956,7 @@ public static void initSpSigningKeys() { KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); KeyStore keyStore = KeyStore.getInstance("JKS"); - InputStream keyStream = new FileInputStream( - FileHelper.getAbsoluteFilePathFromClassPath("saml/spock-keystore.jks").toFile()); + InputStream keyStream = new FileInputStream(FileHelper.getAbsoluteFilePathFromClassPath("saml/spock-keystore.jks").toFile()); keyStore.load(keyStream, "changeit".toCharArray()); kmf.init(keyStore, "changeit".toCharArray()); @@ -824,8 +965,7 @@ public static void initSpSigningKeys() { spSigningPrivateKey = (PrivateKey) keyStore.getKey("spock", "changeit".toCharArray()); - } catch (NoSuchAlgorithmException | KeyStoreException | CertificateException | IOException - | UnrecoverableKeyException e) { + } catch (NoSuchAlgorithmException | KeyStoreException | CertificateException | IOException | UnrecoverableKeyException e) { throw new RuntimeException(e); } } @@ -876,7 +1016,8 @@ public void sendResponse(RestResponse response) { } @Override - public XContentBuilder newBuilder(XContentType xContentType, XContentType responseContentType, boolean useFiltering) throws IOException { + public XContentBuilder newBuilder(XContentType xContentType, XContentType responseContentType, boolean useFiltering) + throws IOException { return null; } diff --git a/src/test/java/com/amazon/dlic/auth/http/saml/MockSamlIdpServer.java b/src/test/java/com/amazon/dlic/auth/http/saml/MockSamlIdpServer.java index 4f4a8c9640..ef54e3e833 100644 --- a/src/test/java/com/amazon/dlic/auth/http/saml/MockSamlIdpServer.java +++ b/src/test/java/com/amazon/dlic/auth/http/saml/MockSamlIdpServer.java @@ -193,46 +193,50 @@ class MockSamlIdpServer implements Closeable { this.loadSigningKeys("saml/kirk-keystore.jks", "kirk"); - ServerBootstrap serverBootstrap = ServerBootstrap.bootstrap().setListenerPort(port) - .register(CTX_METADATA, new HttpRequestHandler() { + ServerBootstrap serverBootstrap = ServerBootstrap.bootstrap() + .setListenerPort(port) + .register(CTX_METADATA, new HttpRequestHandler() { - @Override - public void handle(ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context) throws HttpException, IOException { + @Override + public void handle(ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context) throws HttpException, + IOException { - handleMetadataRequest(request, response, context); + handleMetadataRequest(request, response, context); - } - }).register(CTX_SAML_SSO, new HttpRequestHandler() { + } + }) + .register(CTX_SAML_SSO, new HttpRequestHandler() { - @Override - public void handle(ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context) throws HttpException, IOException { - handleSsoRequest(request, response, context); - } - }).register(CTX_SAML_SLO, new HttpRequestHandler() { + @Override + public void handle(ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context) throws HttpException, + IOException { + handleSsoRequest(request, response, context); + } + }) + .register(CTX_SAML_SLO, new HttpRequestHandler() { - @Override - public void handle(ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context) throws HttpException, IOException { - handleSloRequest(request, response, context); - } - }); + @Override + public void handle(ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context) throws HttpException, + IOException { + handleSloRequest(request, response, context); + } + }); if (ssl) { - serverBootstrap = serverBootstrap.setSslContext(createSSLContext()) - .setSslSetupHandler(new Callback() { - @Override - public void execute(SSLParameters object) { - object.setNeedClientAuth(true); - } - }) - .setConnectionFactory(new HttpConnectionFactory() { - @Override - public DefaultBHttpServerConnection createConnection(final Socket socket) throws IOException { - final DefaultBHttpServerConnection conn = new DefaultBHttpServerConnection(ssl ? "https" : "http", Http1Config.DEFAULT); - conn.bind(socket); - return conn; - } - }); + serverBootstrap = serverBootstrap.setSslContext(createSSLContext()).setSslSetupHandler(new Callback() { + @Override + public void execute(SSLParameters object) { + object.setNeedClientAuth(true); + } + }).setConnectionFactory(new HttpConnectionFactory() { + @Override + public DefaultBHttpServerConnection createConnection(final Socket socket) throws IOException { + final DefaultBHttpServerConnection conn = new DefaultBHttpServerConnection(ssl ? "https" : "http", Http1Config.DEFAULT); + conn.bind(socket); + return conn; + } + }); } this.httpServer = serverBootstrap.create(); @@ -289,16 +293,15 @@ public int getPort() { return port; } - protected void handleMetadataRequest(HttpRequest request, ClassicHttpResponse response, HttpContext context) - throws HttpException, IOException { + protected void handleMetadataRequest(HttpRequest request, ClassicHttpResponse response, HttpContext context) throws HttpException, + IOException { response.setCode(200); response.setHeader("Cache-Control", "public, max-age=31536000"); response.setHeader("Content-Type", "application/xml"); response.setEntity(new StringEntity(createMetadata())); } - protected void handleSsoRequest(HttpRequest request, HttpResponse response, HttpContext context) - throws HttpException, IOException { + protected void handleSsoRequest(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException { if ("GET".equalsIgnoreCase(request.getMethod())) { handleSsoGetRequestBase(request); @@ -308,8 +311,7 @@ protected void handleSsoRequest(HttpRequest request, HttpResponse response, Http } - protected void handleSloRequest(HttpRequest request, HttpResponse response, HttpContext context) - throws HttpException, IOException { + protected void handleSloRequest(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException { if ("GET".equalsIgnoreCase(request.getMethod())) { handleSloGetRequestBase(request); @@ -375,10 +377,10 @@ public void handleSloGetRequestBase(HttpRequest request) { LogoutRequest logoutRequest = (LogoutRequest) messageContext.getMessage(); - SAML2HTTPRedirectDeflateSignatureSecurityHandler signatureSecurityHandler = new SAML2HTTPRedirectDeflateSignatureSecurityHandler(); + SAML2HTTPRedirectDeflateSignatureSecurityHandler signatureSecurityHandler = + new SAML2HTTPRedirectDeflateSignatureSecurityHandler(); SignatureValidationParameters validationParams = new SignatureValidationParameters(); - SecurityParametersContext securityParametersContext = messageContext - .getSubcontext(SecurityParametersContext.class, true); + SecurityParametersContext securityParametersContext = messageContext.getSubcontext(SecurityParametersContext.class, true); SAMLPeerEntityContext peerEntityContext = messageContext.getSubcontext(SAMLPeerEntityContext.class, true); peerEntityContext.setEntityId(idpEntityId); @@ -397,8 +399,7 @@ public void handleSloGetRequestBase(HttpRequest request) { throw new RuntimeException("Unexpected NameID in LogoutRequest: " + logoutRequest); } - } catch (URISyntaxException | ComponentInitializationException | MessageDecodingException - | MessageHandlerException e) { + } catch (URISyntaxException | ComponentInitializationException | MessageDecodingException | MessageHandlerException e) { throw new RuntimeException(e); } } @@ -436,12 +437,24 @@ private String createSamlAuthResponse(AuthnRequest authnRequest) { if (authnRequest != null) { subject.getSubjectConfirmations() - .add(createSubjectConfirmation("urn:oasis:names:tc:SAML:2.0:cm:bearer", - new DateTime().plusMinutes(1), authnRequest.getID(), - authnRequest.getAssertionConsumerServiceURL())); + .add( + createSubjectConfirmation( + "urn:oasis:names:tc:SAML:2.0:cm:bearer", + new DateTime().plusMinutes(1), + authnRequest.getID(), + authnRequest.getAssertionConsumerServiceURL() + ) + ); } else { - subject.getSubjectConfirmations().add(createSubjectConfirmation("urn:oasis:names:tc:SAML:2.0:cm:bearer", - new DateTime().plusMinutes(1), null, defaultAssertionConsumerService)); + subject.getSubjectConfirmations() + .add( + createSubjectConfirmation( + "urn:oasis:names:tc:SAML:2.0:cm:bearer", + new DateTime().plusMinutes(1), + null, + defaultAssertionConsumerService + ) + ); } Conditions conditions = createSamlElement(Conditions.class); @@ -464,7 +477,7 @@ private String createSamlAuthResponse(AuthnRequest authnRequest) { attribute.getAttributeValues().add(createXSAny(AttributeValue.DEFAULT_ELEMENT_NAME, role)); } } - + if (signResponses) { Signature signature = createSamlElement(Signature.class); assertion.setSignature(signature); @@ -478,7 +491,7 @@ private String createSamlAuthResponse(AuthnRequest authnRequest) { Signer.signObject(signature); } - if (this.encryptAssertion){ + if (this.encryptAssertion) { Encrypter encrypter = getEncrypter(); EncryptedAssertion encryptedAssertion = encrypter.encrypt(assertion); response.getEncryptedAssertions().add(encryptedAssertion); @@ -486,7 +499,6 @@ private String createSamlAuthResponse(AuthnRequest authnRequest) { response.getAssertions().add(assertion); } - String marshalledXml = marshallSamlXml(response); return Base64Support.encode(marshalledXml.getBytes("UTF-8"), Base64Support.UNCHUNKED); @@ -498,10 +510,11 @@ private String createSamlAuthResponse(AuthnRequest authnRequest) { private Encrypter getEncrypter() { KeyEncryptionParameters kek = new KeyEncryptionParameters(); - // Algorithm from https://santuario.apache.org/Java/api/constant-values.html#org.apache.xml.security.utils.EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSA15 + // Algorithm from + // https://santuario.apache.org/Java/api/constant-values.html#org.apache.xml.security.utils.EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSA15 kek.setAlgorithm("http://www.w3.org/2001/04/xmlenc#rsa-1_5"); kek.setEncryptionCredential(new BasicX509Credential(spSignatureCertificate)); - Encrypter encrypter = new Encrypter( new DataEncryptionParameters(),kek); + Encrypter encrypter = new Encrypter(new DataEncryptionParameters(), kek); encrypter.setKeyPlacement(Encrypter.KeyPlacement.INLINE); return encrypter; } @@ -554,8 +567,7 @@ private NameID createNameID(String format, String value) { return nameID; } - private SubjectConfirmation createSubjectConfirmation(String method, DateTime notOnOrAfter, String inResponseTo, - String recipient) { + private SubjectConfirmation createSubjectConfirmation(String method, DateTime notOnOrAfter, String inResponseTo, String recipient) { SubjectConfirmation result = createSamlElement(SubjectConfirmation.class); result.setMethod(method); @@ -601,8 +613,7 @@ private String createMetadata() { redirectSingleLogoutService.setBinding("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"); redirectSingleLogoutService.setLocation(getSamlSloUri()); - idpSsoDescriptor.getNameIDFormats() - .add(createNameIDFormat("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified")); + idpSsoDescriptor.getNameIDFormats().add(createNameIDFormat("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified")); SingleSignOnService redirectSingleSignOnService = createSamlElement(SingleSignOnService.class); idpSsoDescriptor.getSingleSignOnServices().add(redirectSingleSignOnService); @@ -619,8 +630,7 @@ private String createMetadata() { signingKeyDescriptor.setUse(UsageType.SIGNING); - signingKeyDescriptor - .setKeyInfo(keyInfoGenerator.generate(new BasicX509Credential(this.signingCertificate))); + signingKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(new BasicX509Credential(this.signingCertificate))); return marshallSamlXml(idpEntityDescriptor); } catch (org.opensaml.security.SecurityException e) { @@ -640,16 +650,14 @@ private String marshallSamlXml(XMLObject xmlObject) { transformer.transform(source, new StreamResult(stringWriter)); return stringWriter.toString(); - } catch (ParserConfigurationException | MarshallingException | TransformerFactoryConfigurationError - | TransformerException e) { + } catch (ParserConfigurationException | MarshallingException | TransformerFactoryConfigurationError | TransformerException e) { throw new RuntimeException(e); } } private SignatureTrustEngine buildSignatureTrustEngine(X509Certificate certificate) { CredentialResolver credentialResolver = new StaticCredentialResolver(new BasicX509Credential(certificate)); - KeyInfoCredentialResolver keyInfoCredentialResolver = new StaticKeyInfoCredentialResolver( - new BasicX509Credential(certificate)); + KeyInfoCredentialResolver keyInfoCredentialResolver = new StaticKeyInfoCredentialResolver(new BasicX509Credential(certificate)); return new ExplicitKeySignatureTrustEngine(credentialResolver, keyInfoCredentialResolver); } @@ -666,11 +674,12 @@ void loadSigningKeys(String path, String alias) { this.signingCertificate = (X509Certificate) keyStore.getCertificate(alias); - this.signingCredential = new BasicX509Credential(this.signingCertificate, - (PrivateKey) keyStore.getKey(alias, "changeit".toCharArray())); + this.signingCredential = new BasicX509Credential( + this.signingCertificate, + (PrivateKey) keyStore.getKey(alias, "changeit".toCharArray()) + ); - } catch (NoSuchAlgorithmException | KeyStoreException | CertificateException | IOException - | UnrecoverableKeyException e) { + } catch (NoSuchAlgorithmException | KeyStoreException | CertificateException | IOException | UnrecoverableKeyException e) { throw new RuntimeException(e); } } @@ -683,15 +692,13 @@ private SSLContext createSSLContext() { try { final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); final KeyStore trustStore = KeyStore.getInstance("JKS"); - InputStream trustStream = new FileInputStream( - FileHelper.getAbsoluteFilePathFromClassPath("jwt/truststore.jks").toFile()); + InputStream trustStream = new FileInputStream(FileHelper.getAbsoluteFilePathFromClassPath("jwt/truststore.jks").toFile()); trustStore.load(trustStream, "changeit".toCharArray()); tmf.init(trustStore); final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); final KeyStore keyStore = KeyStore.getInstance("JKS"); - InputStream keyStream = new FileInputStream( - FileHelper.getAbsoluteFilePathFromClassPath("jwt/node-0-keystore.jks").toFile()); + InputStream keyStream = new FileInputStream(FileHelper.getAbsoluteFilePathFromClassPath("jwt/node-0-keystore.jks").toFile()); keyStore.load(keyStream, "changeit".toCharArray()); kmf.init(keyStore, "changeit".toCharArray()); @@ -709,14 +716,26 @@ private String nextId() { } static class SSLTestHttpServerConnection extends DefaultBHttpServerConnection { - public SSLTestHttpServerConnection(final String scheme, Http1Config http1Config, - final CharsetDecoder charDecoder, final CharsetEncoder charEncoder, - final ContentLengthStrategy incomingContentStrategy, - final ContentLengthStrategy outgoingContentStrategy, - final HttpMessageParserFactory requestParserFactory, - final HttpMessageWriterFactory responseWriterFactory) { - super(scheme, http1Config, charDecoder, charEncoder, incomingContentStrategy, - outgoingContentStrategy, requestParserFactory, responseWriterFactory); + public SSLTestHttpServerConnection( + final String scheme, + Http1Config http1Config, + final CharsetDecoder charDecoder, + final CharsetEncoder charEncoder, + final ContentLengthStrategy incomingContentStrategy, + final ContentLengthStrategy outgoingContentStrategy, + final HttpMessageParserFactory requestParserFactory, + final HttpMessageWriterFactory responseWriterFactory + ) { + super( + scheme, + http1Config, + charDecoder, + charEncoder, + incomingContentStrategy, + outgoingContentStrategy, + requestParserFactory, + responseWriterFactory + ); } } @@ -729,8 +748,9 @@ static class FakeHttpServletRequest implements HttpServletRequest { this.delegate = delegate; String uri = delegate.getRequestUri(); this.uriBuilder = new URIBuilder(uri); - this.queryParams = uriBuilder.getQueryParams().stream() - .collect(Collectors.toMap(NameValuePair::getName, NameValuePair::getValue)); + this.queryParams = uriBuilder.getQueryParams() + .stream() + .collect(Collectors.toMap(NameValuePair::getName, NameValuePair::getValue)); } @Override @@ -959,8 +979,7 @@ public String getHeader(String name) { @SuppressWarnings("rawtypes") @Override public Enumeration getHeaderNames() { - return Collections.enumeration( - Arrays.asList(delegate.getHeaders()).stream().map(Header::getName).collect(Collectors.toSet())); + return Collections.enumeration(Arrays.asList(delegate.getHeaders()).stream().map(Header::getName).collect(Collectors.toSet())); } @SuppressWarnings("rawtypes") @@ -969,8 +988,7 @@ public Enumeration getHeaders(String name) { Header[] headers = delegate.getHeaders(name); if (headers != null) { - return Collections - .enumeration(Arrays.asList(headers).stream().map(Header::getName).collect(Collectors.toSet())); + return Collections.enumeration(Arrays.asList(headers).stream().map(Header::getName).collect(Collectors.toSet())); } else { return null; } diff --git a/src/test/resources/auditlog/data2.json b/src/test/resources/auditlog/data2.json index 1d37b6abc2..cc4748ed82 100644 --- a/src/test/resources/auditlog/data2.json +++ b/src/test/resources/auditlog/data2.json @@ -1,6 +1,6 @@ { "text": "text question value", "joinfield": { - "name": "question" + "name": "question" } } diff --git a/src/test/resources/auditlog/data3.json b/src/test/resources/auditlog/data3.json index 05c678b114..6846fe0f68 100644 --- a/src/test/resources/auditlog/data3.json +++ b/src/test/resources/auditlog/data3.json @@ -1,7 +1,7 @@ { "text": "text answer value", "joinfield": { - "name": "answer", - "parent": "1" + "name": "answer", + "parent": "1" } } diff --git a/src/test/resources/auditlog/endpoints/configuration_wrong_endpoint_names.yml b/src/test/resources/auditlog/endpoints/configuration_wrong_endpoint_names.yml index 24fb5dcae5..dee8c95641 100644 --- a/src/test/resources/auditlog/endpoints/configuration_wrong_endpoint_names.yml +++ b/src/test/resources/auditlog/endpoints/configuration_wrong_endpoint_names.yml @@ -7,7 +7,7 @@ plugins.security: type: external_opensearch config: http_endpoints: ['localhost:9200','localhost:9201','localhost:9202'] - index: auditlog + index: auditlog username: auditloguser password: auditlogpassword enable_ssl: false diff --git a/src/test/resources/auditlog/endpoints/routing/configuration_valid.yml b/src/test/resources/auditlog/endpoints/routing/configuration_valid.yml index 75df18aac7..046e4d6ee5 100644 --- a/src/test/resources/auditlog/endpoints/routing/configuration_valid.yml +++ b/src/test/resources/auditlog/endpoints/routing/configuration_valid.yml @@ -3,7 +3,7 @@ plugins.security: type: external_opensearch config: http_endpoints: ['localhost:9200','localhost:9201','localhost:9202'] - index: auditlog + index: auditlog username: auditloguser password: auditlogpassword enable_ssl: false @@ -16,7 +16,7 @@ plugins.security: type: external_opensearch config: http_endpoints: ['localhost:9200','localhost:9201','localhost:9202'] - index: auditlog + index: auditlog username: auditloguser password: auditlogpassword enable_ssl: false diff --git a/src/test/resources/auditlog/endpoints/routing/configuration_wrong_endpoint_names.yml b/src/test/resources/auditlog/endpoints/routing/configuration_wrong_endpoint_names.yml index 2361b6049b..2b96265492 100644 --- a/src/test/resources/auditlog/endpoints/routing/configuration_wrong_endpoint_names.yml +++ b/src/test/resources/auditlog/endpoints/routing/configuration_wrong_endpoint_names.yml @@ -8,7 +8,7 @@ plugins.security: type: external_opensearch config: http_endpoints: ['localhost:9200','localhost:9201','localhost:9202'] - index: auditlog + index: auditlog username: auditloguser password: auditlogpassword enable_ssl: false diff --git a/src/test/resources/auditlog/endpoints/routing/configuration_wrong_endpoint_types.yml b/src/test/resources/auditlog/endpoints/routing/configuration_wrong_endpoint_types.yml index 6b6929b2c7..c59adc4ee1 100644 --- a/src/test/resources/auditlog/endpoints/routing/configuration_wrong_endpoint_types.yml +++ b/src/test/resources/auditlog/endpoints/routing/configuration_wrong_endpoint_types.yml @@ -8,7 +8,7 @@ plugins.security: type: external_opensearch config: http_endpoints: ['localhost:9200','localhost:9201','localhost:9202'] - index: auditlog + index: auditlog username: auditloguser password: auditlogpassword enable_ssl: false diff --git a/src/test/resources/auditlog/endpoints/routing/routing.yml b/src/test/resources/auditlog/endpoints/routing/routing.yml index ac6596424e..4135b800e2 100644 --- a/src/test/resources/auditlog/endpoints/routing/routing.yml +++ b/src/test/resources/auditlog/endpoints/routing/routing.yml @@ -19,4 +19,4 @@ plugins.security: - endpoint3 COMPLIANCE_DOC_WRITE: endpoints: - - default + - default diff --git a/src/test/resources/auditlog/endpoints/sink/configuration_all_variants.yml b/src/test/resources/auditlog/endpoints/sink/configuration_all_variants.yml index e441322c17..f1c8620e88 100644 --- a/src/test/resources/auditlog/endpoints/sink/configuration_all_variants.yml +++ b/src/test/resources/auditlog/endpoints/sink/configuration_all_variants.yml @@ -17,10 +17,10 @@ plugins.security: type: external_opensearch something: key: value - endpoint6: + endpoint6: something: key: value - endpoint7: + endpoint7: config: key: value endpoint8: diff --git a/src/test/resources/auditlog/endpoints/sink/configuration_no_default.yml b/src/test/resources/auditlog/endpoints/sink/configuration_no_default.yml index f81b37dde2..013410ea77 100644 --- a/src/test/resources/auditlog/endpoints/sink/configuration_no_default.yml +++ b/src/test/resources/auditlog/endpoints/sink/configuration_no_default.yml @@ -16,10 +16,10 @@ plugins.security: type: external_opensearch something: key: value - endpoint6: + endpoint6: something: key: value - endpoint7: + endpoint7: config: key: value endpoint8: diff --git a/src/test/resources/config_auth_ratelimiting.yml b/src/test/resources/config_auth_ratelimiting.yml index 3675afc33e..a55ad0b5d3 100644 --- a/src/test/resources/config_auth_ratelimiting.yml +++ b/src/test/resources/config_auth_ratelimiting.yml @@ -23,5 +23,5 @@ config: allowed_tries: 10 internal_authentication_backend_limiting: type: username - authentication_backend: intern + authentication_backend: intern allowed_tries: 3 diff --git a/src/test/resources/config_ldap.yml b/src/test/resources/config_ldap.yml index c2aebf869b..5dc0895d8d 100644 --- a/src/test/resources/config_ldap.yml +++ b/src/test/resources/config_ldap.yml @@ -7,7 +7,7 @@ plugins.security: remoteIpHeader: "x-forwarded-for" proxiesHeader: "x-forwarded-by" trustedProxies: "proxy1|proxy2" - authenticator: + authenticator: type: org.opensearch.security.http.HTTPBasicAuthenticator authcz: authentication_domain_basic_internal: diff --git a/src/test/resources/data2.json b/src/test/resources/data2.json index 1d37b6abc2..cc4748ed82 100644 --- a/src/test/resources/data2.json +++ b/src/test/resources/data2.json @@ -1,6 +1,6 @@ { "text": "text question value", "joinfield": { - "name": "question" + "name": "question" } } diff --git a/src/test/resources/data3.json b/src/test/resources/data3.json index 05c678b114..6846fe0f68 100644 --- a/src/test/resources/data3.json +++ b/src/test/resources/data3.json @@ -1,7 +1,7 @@ { "text": "text answer value", "joinfield": { - "name": "answer", - "parent": "1" + "name": "answer", + "parent": "1" } } diff --git a/src/test/resources/dlsfls/doc1.json b/src/test/resources/dlsfls/doc1.json index 6067eedc9a..7dccde9b93 100644 --- a/src/test/resources/dlsfls/doc1.json +++ b/src/test/resources/dlsfls/doc1.json @@ -1,5 +1,5 @@ { - + "customer": { "name": "", "type": "normal", @@ -9,8 +9,8 @@ "street": "street1", "zip": "12345", "city": "mycity" - - } + + } }, "secret": "a secret value", @@ -34,7 +34,7 @@ "boolfield5": true, "boolfield6": false, "nullfield": null, - + "@timestamp": "", "timestamp": "" diff --git a/src/test/resources/dlsfls/flsquery.json b/src/test/resources/dlsfls/flsquery.json index 9418e8721b..19d3897971 100644 --- a/src/test/resources/dlsfls/flsquery.json +++ b/src/test/resources/dlsfls/flsquery.json @@ -34,9 +34,9 @@ } }, "query":{ - - - + + + "bool":{ "must":[ { @@ -53,8 +53,8 @@ ] } - - + + }, "stored_fields":[ "*", diff --git a/src/test/resources/dlsfls/flsquery2.json b/src/test/resources/dlsfls/flsquery2.json index a5ad1da20e..26fce316db 100644 --- a/src/test/resources/dlsfls/flsquery2.json +++ b/src/test/resources/dlsfls/flsquery2.json @@ -38,8 +38,8 @@ "match_all":{ - - + + } }, "stored_fields":[ diff --git a/src/test/resources/dlsfls/internal_users_tlq.yml b/src/test/resources/dlsfls/internal_users_tlq.yml index 5bbec586f0..dff0a67633 100644 --- a/src/test/resources/dlsfls/internal_users_tlq.yml +++ b/src/test/resources/dlsfls/internal_users_tlq.yml @@ -2,7 +2,7 @@ _meta: type: "internalusers" config_version: 2 - + tlq_1337: hash: "$2y$12$SP9z.rBgEHTlueKkiqSK/OxqB2PLJN/eRoNJ8WOPoHWIpirvbFAAy" # "password" backend_roles: ["os_dls_tlq_lookup"] diff --git a/src/test/resources/dlsfls/masked_field_mapping.json b/src/test/resources/dlsfls/masked_field_mapping.json index f3629282fd..56863f546c 100644 --- a/src/test/resources/dlsfls/masked_field_mapping.json +++ b/src/test/resources/dlsfls/masked_field_mapping.json @@ -1,5 +1,5 @@ { - + "_doc": { "properties": { @@ -142,6 +142,6 @@ } } } - - + + } diff --git a/src/test/resources/dlsfls/roles_tlq.yml b/src/test/resources/dlsfls/roles_tlq.yml index c2d08ca948..1420a7a965 100644 --- a/src/test/resources/dlsfls/roles_tlq.yml +++ b/src/test/resources/dlsfls/roles_tlq.yml @@ -2,7 +2,7 @@ _meta: type: "roles" config_version: 2 - + os_dls_tlq_lookup: cluster_permissions: - "*" diff --git a/src/test/resources/ldap/test1.yml b/src/test/resources/ldap/test1.yml index e1e396ba01..e0ad96ceea 100644 --- a/src/test/resources/ldap/test1.yml +++ b/src/test/resources/ldap/test1.yml @@ -1,8 +1,8 @@ ---- +--- enable_ssl: true enable_ssl_client_auth: false enable_start_tls: false -#hosts: +#hosts: # - "localhost:${ldapport}" path.home: "." pemcert_content: | diff --git a/src/test/resources/legacy/securityconfig_v6/action_groups.yml b/src/test/resources/legacy/securityconfig_v6/action_groups.yml index 5acbe1aea8..ac564e7421 100644 --- a/src/test/resources/legacy/securityconfig_v6/action_groups.yml +++ b/src/test/resources/legacy/securityconfig_v6/action_groups.yml @@ -128,7 +128,7 @@ OPENDISTRO_SECURITY_CLUSTER_COMPOSITE_OPS: - "indices:admin/aliases*" - "indices:data/write/reindex" - CLUSTER_COMPOSITE_OPS_RO - + OPENDISTRO_SECURITY_MANAGE_SNAPSHOTS: readonly: true permissions: diff --git a/src/test/resources/legacy/securityconfig_v6/config.yml b/src/test/resources/legacy/securityconfig_v6/config.yml index 031be9bb15..19b1fd76cd 100644 --- a/src/test/resources/legacy/securityconfig_v6/config.yml +++ b/src/test/resources/legacy/securityconfig_v6/config.yml @@ -1,14 +1,14 @@ # This is the main OpenSearch Security configuration file where authentication # and authorization is defined. -# +# # You need to configure at least one authentication domain in the authc of this file. -# An authentication domain is responsible for extracting the user credentials from -# the request and for validating them against an authentication backend like Active Directory for example. +# An authentication domain is responsible for extracting the user credentials from +# the request and for validating them against an authentication backend like Active Directory for example. # -# If more than one authentication domain is configured the first one which succeeds wins. +# If more than one authentication domain is configured the first one which succeeds wins. # If all authentication domains fail then the request is unauthenticated. # In this case an exception is thrown and/or the HTTP status is set to 401. -# +# # After authentication authorization (authz) will be applied. There can be zero or more authorizers which collect # the roles from a given backend for the authenticated user. # @@ -21,18 +21,18 @@ # For HTTP it is possible to allow anonymous authentication. If that is the case then the HTTP authenticators try to # find user credentials in the HTTP request. If credentials are found then the user gets regularly authenticated. # If none can be found the user will be authenticated as an "anonymous" user. This user has always the username "opendistro_security_anonymous" -# and one role named "opendistro_security_anonymous_backendrole". +# and one role named "opendistro_security_anonymous_backendrole". # If you enable anonymous authentication all HTTP authenticators will not challenge. -# +# # # Note: If you define more than one HTTP authenticators make sure to put non-challenging authenticators like "proxy" or "clientcert" -# first and the challenging one last. +# first and the challenging one last. # Because it's not possible to challenge a client with two different authentication methods (for example # Kerberos and Basic) only one can have the challenge flag set to true. You can cope with this situation # by using pre-authentication, e.g. sending a HTTP Basic authentication header in the request. # # Default value of the challenge flag is true. -# +# # # HTTP # basic (challenging) @@ -77,7 +77,7 @@ opendistro_security: ###### and here https://tools.ietf.org/html/rfc7239 ###### and https://tomcat.apache.org/tomcat-8.0-doc/config/valve.html#Remote_IP_Valve authc: - kerberos_auth_domain: + kerberos_auth_domain: http_enabled: false transport_enabled: false order: 6 @@ -91,7 +91,7 @@ opendistro_security: strip_realm_from_principal: true authentication_backend: type: noop - basic_internal_auth_domain: + basic_internal_auth_domain: http_enabled: true transport_enabled: true order: 4 @@ -163,11 +163,11 @@ opendistro_security: password: null userbase: 'ou=people,dc=example,dc=com' # Filter to search for users (currently in the whole subtree beneath userbase) - # {0} is substituted with the username + # {0} is substituted with the username usersearch: '(sAMAccountName={0})' # Use this attribute from the user as username (if not set then DN is used) username_attribute: null - authz: + authz: roles_from_myldap: http_enabled: false transport_enabled: false @@ -190,8 +190,8 @@ opendistro_security: rolebase: 'ou=groups,dc=example,dc=com' # Filter to search for roles (currently in the whole subtree beneath rolebase) # {0} is substituted with the DN of the user - # {1} is substituted with the username - # {2} is substituted with an attribute value from user's directory entry, of the authenticated user. Use userroleattribute to specify the name of the attribute + # {1} is substituted with the username + # {2} is substituted with an attribute value from user's directory entry, of the authenticated user. Use userroleattribute to specify the name of the attribute rolesearch: '(member={0})' # Specify the name of the attribute which value should be substituted with {2} above userroleattribute: null @@ -205,12 +205,12 @@ opendistro_security: resolve_nested_roles: true userbase: 'ou=people,dc=example,dc=com' # Filter to search for users (currently in the whole subtree beneath userbase) - # {0} is substituted with the username + # {0} is substituted with the username usersearch: '(uid={0})' # Skip users matching a user name, a wildcard or a regex pattern - #skip_users: + #skip_users: # - 'cn=Michael Jackson,ou*people,o=TEST' - # - '/\S*/' + # - '/\S*/' roles_from_another_ldap: enabled: false authorization_backend: diff --git a/src/test/resources/legacy/securityconfig_v6/internal_users.yml b/src/test/resources/legacy/securityconfig_v6/internal_users.yml index d8d5b0d3a4..64b6295bbe 100644 --- a/src/test/resources/legacy/securityconfig_v6/internal_users.yml +++ b/src/test/resources/legacy/securityconfig_v6/internal_users.yml @@ -19,13 +19,13 @@ opendistro_security_logstash: roles: - logstash -#password is: kibanaserver +#password is: kibanaserver kibanaserver: readonly: true hash: $2a$12$4AcgAt3xwOWadA5s5blL6ev39OXDNhmOesEoo33eZtrq2N0YrU3H. #password is: kibanaro -kibanaro: +kibanaro: hash: $2a$12$JJSXNfTowz7Uu5ttXfeYpeYE0arACvcwlPBStB1F.MI7f0U9Z4DGC roles: - kibanauser diff --git a/src/test/resources/legacy/securityconfig_v6/migration/roles.yml b/src/test/resources/legacy/securityconfig_v6/migration/roles.yml index 0163e4a7bb..52f9dd60a4 100644 --- a/src/test/resources/legacy/securityconfig_v6/migration/roles.yml +++ b/src/test/resources/legacy/securityconfig_v6/migration/roles.yml @@ -3,7 +3,7 @@ # - '' # indices: # '': -# '': +# '': # - '' # _dls_: '' # _fls_: @@ -15,9 +15,9 @@ # and a type. If a request is executed against all indices (or all types) then the asterix ('*') is needed. # Every role a user has will be examined if it allows the action against an index (or type). At least one role must match # for the request to be successful. If no role match then the request will be denied. Currently a match must happen within -# one single role - that means that permissions can not span multiple roles. +# one single role - that means that permissions can not span multiple roles. -# For , and simple wildcards and regular expressions are possible. +# For , and simple wildcards and regular expressions are possible. # A asterix (*) will match any character sequence (or an empty sequence) # A question mark (?) will match any single character (but NOT empty character) # Example: '*my*index' will match 'my_first_index' as well as 'myindex' but not 'myindex1' @@ -27,7 +27,7 @@ # '//' # Example: '/\S*/' will match any non whitespace characters -# Important: +# Important: # Index, alias or type names can not contain dots (.) in the or expression. # Reason is that we currently parse the config file into a opensearch settings object which cannot cope with dots in keys. # Workaround: Just configure something like '?kibana' instead of '.kibana' or 'my?index' instead of 'my.index' @@ -54,7 +54,7 @@ opendistro_security_readall: '*': - READ -# Read all and monitor, but no write permissions +# Read all and monitor, but no write permissions opendistro_security_readall_and_monitor: cluster: - CLUSTER_MONITOR diff --git a/src/test/resources/legacy/securityconfig_v6/roles.yml b/src/test/resources/legacy/securityconfig_v6/roles.yml index d755d67dbe..65f02a7106 100644 --- a/src/test/resources/legacy/securityconfig_v6/roles.yml +++ b/src/test/resources/legacy/securityconfig_v6/roles.yml @@ -3,7 +3,7 @@ # - '' # indices: # '': -# '': +# '': # - '' # _dls_: '' # _fls_: @@ -15,9 +15,9 @@ # and a type. If a request is executed against all indices (or all types) then the asterix ('*') is needed. # Every role a user has will be examined if it allows the action against an index (or type). At least one role must match # for the request to be successful. If no role match then the request will be denied. Currently a match must happen within -# one single role - that means that permissions can not span multiple roles. +# one single role - that means that permissions can not span multiple roles. -# For , and simple wildcards and regular expressions are possible. +# For , and simple wildcards and regular expressions are possible. # A asterix (*) will match any character sequence (or an empty sequence) # A question mark (?) will match any single character (but NOT empty character) # Example: '*my*index' will match 'my_first_index' as well as 'myindex' but not 'myindex1' @@ -27,7 +27,7 @@ # '//' # Example: '/\S*/' will match any non whitespace characters -# Important: +# Important: # Index, alias or type names can not contain dots (.) in the or expression. # Reason is that we currently parse the config file into a opensearch settings object which cannot cope with dots in keys. # Workaround: Just configure something like '?kibana' instead of '.kibana' or 'my?index' instead of 'my.index' @@ -57,7 +57,7 @@ opendistro_security_readall: '*': - READ -# Read all and monitor, but no write permissions +# Read all and monitor, but no write permissions opendistro_security_readall_and_monitor: cluster: - CLUSTER_MONITOR @@ -97,7 +97,7 @@ opendistro_security_kibana_user: - INDICES_ALL '?management-beats': '*': - - INDICES_ALL + - INDICES_ALL '*': '*': - indices:data/read/field_caps* @@ -133,7 +133,7 @@ opendistro_security_kibana_server: - "indices:admin/aliases*" # For logstash and beats -opendistro_security_logstash: +opendistro_security_logstash: cluster: - CLUSTER_MONITOR - CLUSTER_COMPOSITE_OPS diff --git a/src/test/resources/legacy/securityconfig_v6/roles_mapping.yml b/src/test/resources/legacy/securityconfig_v6/roles_mapping.yml index b3263eb234..588ba13f6e 100644 --- a/src/test/resources/legacy/securityconfig_v6/roles_mapping.yml +++ b/src/test/resources/legacy/securityconfig_v6/roles_mapping.yml @@ -9,12 +9,12 @@ opendistro_security_all_access: opendistro_security_logstash: backendroles: - logstash - + opendistro_security_kibana_server: readonly: true users: - kibanaserver - + opendistro_security_kibana_user: backendroles: - kibanauser diff --git a/src/test/resources/multitenancy/config_basic_auth.yml b/src/test/resources/multitenancy/config_basic_auth.yml index f60caab0b7..72f73f8305 100644 --- a/src/test/resources/multitenancy/config_basic_auth.yml +++ b/src/test/resources/multitenancy/config_basic_auth.yml @@ -22,7 +22,7 @@ config: internalProxies: ".*" remoteIpHeader: "x-forwarded-for" authc: - basic_internal_auth_domain: + basic_internal_auth_domain: http_enabled: true transport_enabled: true order: 0 diff --git a/src/test/resources/restapi/audit.yml b/src/test/resources/restapi/audit.yml index 06eb47a56d..796c92f828 100644 --- a/src/test/resources/restapi/audit.yml +++ b/src/test/resources/restapi/audit.yml @@ -33,7 +33,7 @@ config: # configs internal_config: true external_config: false - + # compliance read read_metadata_only: false read_watched_fields: {} diff --git a/src/test/resources/restapi/security_config.json b/src/test/resources/restapi/security_config.json index e8acc0a22a..e5c09050cc 100644 --- a/src/test/resources/restapi/security_config.json +++ b/src/test/resources/restapi/security_config.json @@ -1,5 +1,5 @@ { - + "dynamic":{ "filtered_alias_mode":"warn", "disable_rest_auth": false, @@ -29,13 +29,13 @@ "challenge":true, "type":"kerberos", "config":{ - + } }, "authentication_backend":{ "type":"noop", "config":{ - + } }, "description":"Migrated from v6" @@ -48,13 +48,13 @@ "challenge":true, "type":"clientcert", "config":{ - + } }, "authentication_backend":{ "type":"noop", "config":{ - + } }, "description":"Migrated from v6" @@ -74,7 +74,7 @@ "authentication_backend":{ "type":"noop", "config":{ - + } }, "description":"Migrated from v6" @@ -87,13 +87,13 @@ "challenge":true, "type":"basic", "config":{ - + } }, "authentication_backend":{ "type":"intern", "config":{ - + } }, "description":"Migrated from v6" @@ -106,7 +106,7 @@ "authorization_backend":{ "type":"xxx", "config":{ - + } }, "description":"Migrated from v6" @@ -127,12 +127,12 @@ } }, "auth_failure_listeners":{ - + }, "do_not_fail_on_forbidden":false, "multi_rolespan_enabled":false, "hosts_resolver_mode":"ip-only", "do_not_fail_on_forbidden_empty":false } - + } diff --git a/src/test/resources/restapi/users_key_not_quoted.json b/src/test/resources/restapi/users_key_not_quoted.json index e69de29bb2..8b13789179 100644 --- a/src/test/resources/restapi/users_key_not_quoted.json +++ b/src/test/resources/restapi/users_key_not_quoted.json @@ -0,0 +1 @@ + diff --git a/src/test/resources/roles_invalidxcontent.yml b/src/test/resources/roles_invalidxcontent.yml index 8d805273cf..f67c09a823 100644 --- a/src/test/resources/roles_invalidxcontent.yml +++ b/src/test/resources/roles_invalidxcontent.yml @@ -5,6 +5,6 @@ opendistro_security_public: indices: indices: '.notexistingindexcvnjl9809991' - '*': + '*': - ALL invalid yml