From 09b51a1c878ea040245d1187c3107b87caeed6f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20Mass=C3=A9?= Date: Thu, 14 Mar 2019 10:18:34 +0100 Subject: [PATCH] see #46: Allow setting the OAuth flows per service --- README.md | 12 ++++++++ defaults/main.yml | 1 + tasks/api-calls/update_oidc_configuration.yml | 20 +++++++++++++ tasks/main.yml | 12 ++++++-- tasks/steps/default_application.yml | 14 +++++++-- .../{discover.yml => discover_platform.yml} | 29 ------------------ tasks/steps/discover_service.yml | 30 +++++++++++++++++++ tasks/steps/oidc_configuration.yml | 6 ++++ tasks/steps/read_openapi.yml | 14 +++++++++ .../api-calls/update_oidc_configuration.j2 | 11 +++++++ tests/test-cases/02-echo-api-oidc.yml | 14 ++------- 11 files changed, 116 insertions(+), 47 deletions(-) create mode 100644 tasks/api-calls/update_oidc_configuration.yml rename tasks/steps/{discover.yml => discover_platform.yml} (71%) create mode 100644 tasks/steps/discover_service.yml create mode 100644 tasks/steps/oidc_configuration.yml create mode 100644 templates/api-calls/update_oidc_configuration.j2 diff --git a/README.md b/README.md index 73dc868..0b6ffde 100644 --- a/README.md +++ b/README.md @@ -501,6 +501,18 @@ installed in `{{ threescale_cicd_local_bin_path }}`. - `threescale_cicd_goswagger_command=/usr/local/bin/swagger` - `threescale_cicd_local_bin_path=/tmp` +### `threescale_cicd_oicd_flows` + +Override or update the list of supported OAuth flows for this API. + +- **Syntax:** array of strings (`[ 'application', 'accessCode' ]`) +- **Required:** no +- **Default value:** the `flow` field of the `securityScheme` object in your + OpenAPI Specification file. +- **Examples:** + - `threescale_cicd_oicd_flows="{{ [ 'application', 'accessCode' ] }}"` (override the flow list) + - `threescale_cicd_oicd_flows="{{ [ 'application', threescale_cicd_api_security_scheme.flow ] }}"` (add a flow) + ### Miscellaneous variables Miscellaneous variables defined in [defaults/main.yml](defaults/main.yml) diff --git a/defaults/main.yml b/defaults/main.yml index fed2675..efd7dfb 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -162,3 +162,4 @@ threescale_cicd_smoke_test_url: '{{ lookup(''template'', ''api-calls/smoke-test/ threescale_cicd_promote_proxy_payload: '{{ lookup(''template'', ''api-calls/promote_proxy.j2'') }}' threescale_cicd_update_activedoc_payload: '{{ lookup(''template'', ''api-calls/update_activedoc.j2'') }}' threescale_cicd_create_activedoc_payload: '{{ lookup(''template'', ''api-calls/create_activedoc.j2'') }}' +threescale_cicd_update_oidc_configuration_payload: '{{ lookup(''template'', ''api-calls/update_oidc_configuration.j2'') }}' \ No newline at end of file diff --git a/tasks/api-calls/update_oidc_configuration.yml b/tasks/api-calls/update_oidc_configuration.yml new file mode 100644 index 0000000..f8e1c59 --- /dev/null +++ b/tasks/api-calls/update_oidc_configuration.yml @@ -0,0 +1,20 @@ +--- + +- debug: + var: threescale_cicd_update_oidc_configuration_payload + verbosity: 1 + no_log: '{{ threescale_cicd_nolog }}' + +- name: Update the proxy definition + uri: + url: https://{{ inventory_hostname }}/admin/api/services/{{ threescale_cicd_api_service_id }}/proxy/oidc_configuration.json + validate_certs: no + method: PATCH + body: '{{ threescale_cicd_update_oidc_configuration_payload }}' + register: threescale_cicd_tmpresponse + changed_when: 'threescale_cicd_tmpresponse.status == 200' + no_log: '{{ threescale_cicd_nolog }}' + +- name: Wait for a couple seconds + pause: + seconds: '{{ threescale_cicd_throttling }}' diff --git a/tasks/main.yml b/tasks/main.yml index 037358e..d910c62 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -11,11 +11,14 @@ # Warn the user about those deprecated features - import_tasks: steps/variables_from_inventory.yml +# Discover the current state of the platform +- import_tasks: steps/discover_platform.yml + # Load the API definition from the provided OpenAPI file - import_tasks: steps/read_openapi.yml -# Discover the current state of the platform -- import_tasks: steps/discover.yml +# Discover the current state of the service +- import_tasks: steps/discover_service.yml # Create or update the service definition - import_tasks: steps/service.yml @@ -29,6 +32,9 @@ # Update the proxy - import_tasks: steps/proxy.yml +# Update the OIDC configuration +- import_tasks: steps/oidc_configuration.yml + # Create or update policies - import_tasks: steps/policies.yml @@ -45,7 +51,7 @@ threescale_cicd_smoke_test_env: staging when: >- threescale_cicd_openapi_smoketest_operation|length > 0 and threescale_cicd_application_plans is defined - and threescale_cicd_apicast_sandbox_endpoint != threescale_cicd_apicast_production_endpoint + and threescale_cicd_apicast_discovered_sandbox_endpoint != threescale_cicd_apicast_discovered_production_endpoint # Promote to production - import_tasks: steps/promote.yml diff --git a/tasks/steps/default_application.yml b/tasks/steps/default_application.yml index b0fc537..9d36d8f 100644 --- a/tasks/steps/default_application.yml +++ b/tasks/steps/default_application.yml @@ -15,9 +15,13 @@ ## When using OAuth / OIDC authentication, we need to patch the Keycloak client ## to support the client_credentials grant. ## +## Note: only happens on 3scale AMP < 2.5. Later versions do not need this. +## - include_tasks: api-calls/keycloak/authenticate.yml - when: "threescale_cicd_api_security_scheme.type == 'oauth2'" + when: >- + threescale_cicd_api_security_scheme.type == 'oauth2' + and not threescale_cicd_capabilities.oidc_configuration_api|bool vars: oauth_payload: client_id: '{{ threescale_cicd_sso_issuer_endpoint|urlsplit(''username'') }}' @@ -26,7 +30,11 @@ grant_type: 'client_credentials' - include_tasks: api-calls/keycloak/wait_for_client.yml - when: "threescale_cicd_api_security_scheme.type == 'oauth2'" + when: >- + threescale_cicd_api_security_scheme.type == 'oauth2' + and not threescale_cicd_capabilities.oidc_configuration_api|bool - include_tasks: api-calls/keycloak/patch_client.yml - when: "threescale_cicd_api_security_scheme.type == 'oauth2'" + when: >- + threescale_cicd_api_security_scheme.type == 'oauth2' + and not threescale_cicd_capabilities.oidc_configuration_api|bool diff --git a/tasks/steps/discover.yml b/tasks/steps/discover_platform.yml similarity index 71% rename from tasks/steps/discover.yml rename to tasks/steps/discover_platform.yml index 878e5b3..688bb71 100644 --- a/tasks/steps/discover.yml +++ b/tasks/steps/discover_platform.yml @@ -25,35 +25,6 @@ var: threescale_cicd_existing_services_details verbosity: 1 -- name: Get the list of existing application plans - uri: - url: '{{ service_url }}/application_plans.json?access_token={{ threescale_cicd_access_token|urlencode }}' - validate_certs: no - register: threescale_cicd_tmpresponse - when: threescale_cicd_api_system_name in threescale_cicd_existing_services - no_log: '{{ threescale_cicd_nolog }}' - vars: - service_url: https://{{ inventory_hostname }}/admin/api/services/{{ threescale_cicd_api_service_id }} - -- name: Set the list of existing application plans as a fact - set_fact: - threescale_cicd_existing_application_plans: >- - {{ threescale_cicd_tmpresponse.json|json_query('plans[*].application_plan.system_name') - if threescale_cicd_api_system_name in threescale_cicd_existing_services - else [] }} - threescale_cicd_existing_application_plans_details: >- - {{ threescale_cicd_tmpresponse.json|json_query('plans[].{"system_name": application_plan.system_name, "id": application_plan.id}') - if threescale_cicd_api_system_name in threescale_cicd_existing_services - else [] }} - -- debug: - msg: "Found {{ threescale_cicd_existing_application_plans|length }} application plans" - verbosity: 1 - -- debug: - var: threescale_cicd_existing_application_plans_details - verbosity: 1 - - name: Retrieve existing ActiveDocs from the 3scale Admin Portal uri: url: "https://{{ inventory_hostname }}/admin/api/active_docs.json?access_token={{ threescale_cicd_access_token|urlencode }}" diff --git a/tasks/steps/discover_service.yml b/tasks/steps/discover_service.yml new file mode 100644 index 0000000..737e6f6 --- /dev/null +++ b/tasks/steps/discover_service.yml @@ -0,0 +1,30 @@ +--- + +- name: Get the list of existing application plans + uri: + url: '{{ service_url }}/application_plans.json?access_token={{ threescale_cicd_access_token|urlencode }}' + validate_certs: no + register: threescale_cicd_tmpresponse + when: threescale_cicd_api_system_name in threescale_cicd_existing_services + no_log: '{{ threescale_cicd_nolog }}' + vars: + service_url: https://{{ inventory_hostname }}/admin/api/services/{{ threescale_cicd_api_service_id }} + +- name: Set the list of existing application plans as a fact + set_fact: + threescale_cicd_existing_application_plans: >- + {{ threescale_cicd_tmpresponse.json|json_query('plans[*].application_plan.system_name') + if threescale_cicd_api_system_name in threescale_cicd_existing_services + else [] }} + threescale_cicd_existing_application_plans_details: >- + {{ threescale_cicd_tmpresponse.json|json_query('plans[].{"system_name": application_plan.system_name, "id": application_plan.id}') + if threescale_cicd_api_system_name in threescale_cicd_existing_services + else [] }} + +- debug: + msg: "Found {{ threescale_cicd_existing_application_plans|length }} application plans" + verbosity: 1 + +- debug: + var: threescale_cicd_existing_application_plans_details + verbosity: 1 diff --git a/tasks/steps/oidc_configuration.yml b/tasks/steps/oidc_configuration.yml new file mode 100644 index 0000000..c8e86fb --- /dev/null +++ b/tasks/steps/oidc_configuration.yml @@ -0,0 +1,6 @@ +--- + +- include_tasks: api-calls/update_oidc_configuration.yml + when: >- + threescale_cicd_api_security_scheme.type == 'oauth2' + and threescale_cicd_capabilities.oidc_configuration_api|bool diff --git a/tasks/steps/read_openapi.yml b/tasks/steps/read_openapi.yml index 979725f..6b5eb63 100644 --- a/tasks/steps/read_openapi.yml +++ b/tasks/steps/read_openapi.yml @@ -46,5 +46,19 @@ msg: "The smoketest operation {{ threescale_cicd_openapi_smoketest_operation }} must be a GET and cannot have a placeholder in its path." when: 'threescale_cicd_openapi_smoketest_operation|length > 0' +- name: Make sure the 'application' OAuth flow is enabled if smoke tests are required + assert: + that: + - "'application' in threescale_cicd_oicd_flows|default([ threescale_cicd_api_security_scheme.flow ])" + msg: >- + Since 3scale AMP 2.5, you need to explicitely add the "application" flow to the supported + OAuth flows if you want to run smoke tests. You can either disable smoke tests + (-e threescale_cicd_openapi_smoketest_operation="") or enable the application flow + (-e threescale_cicd_oicd_flows="{{ [ 'application', threescale_cicd_api_security_scheme.flow ] }}"). + when: >- + threescale_cicd_api_security_scheme.type == 'oauth2' + and threescale_cicd_openapi_smoketest_operation|length > 0 + and threescale_cicd_capabilities.oidc_configuration_api|bool + - debug: msg: "Will work on service with system_name = {{ threescale_cicd_api_system_name }}" diff --git a/templates/api-calls/update_oidc_configuration.j2 b/templates/api-calls/update_oidc_configuration.j2 new file mode 100644 index 0000000..5d30916 --- /dev/null +++ b/templates/api-calls/update_oidc_configuration.j2 @@ -0,0 +1,11 @@ +{% set flows = threescale_cicd_oicd_flows|default([ threescale_cicd_api_security_scheme.flow ]) %} +{% + set payload = [ + 'access_token=' ~ threescale_cicd_access_token|urlencode, + 'implicit_flow_enabled=' ~ ('true' if 'implicit' in flows else 'false'), + 'direct_access_grants_enabled=' ~ ('true' if 'password' in flows else 'false'), + 'service_accounts_enabled=' ~ ('true' if 'application' in flows else 'false'), + 'standard_flow_enabled=' ~ ('true' if 'accessCode' in flows else 'false'), + ] +%} +{{ payload|join("&") }} \ No newline at end of file diff --git a/tests/test-cases/02-echo-api-oidc.yml b/tests/test-cases/02-echo-api-oidc.yml index 8da5eac..6fb1f45 100644 --- a/tests/test-cases/02-echo-api-oidc.yml +++ b/tests/test-cases/02-echo-api-oidc.yml @@ -5,27 +5,17 @@ gather_facts: no vars: threescale_cicd_openapi_file: '{{ playbook_dir }}/api-contracts/echo-api-oidc.yaml' + threescale_cicd_oicd_flows: "{{ [ 'application', threescale_cicd_api_security_scheme.flow ] }}" tasks: - # With 3scale SaaS, there is a race condition where Zync would update the SSO client - # after we patched it and thus reverts our changes. - # - # As a temporary fix the smoke tests are disabled with OIDC in the SaaS environment - - name: Generate a random system_name for this test run import_tasks: "common/random-system-name.yml" - + # Test a first deployment - import_role: name: 'nmasse-itix.threescale-cicd' - vars: - # TODO: remove me as soon as proper OIDC support is implemented for SaaS - threescale_cicd_openapi_smoketest_operation: '{{ '''' if inventory_hostname is match(".*[.]3scale[.]net") else ''Echo'' }}' # Verify idempotence - import_role: name: 'nmasse-itix.threescale-cicd' - vars: - # TODO: remove me as soon as proper OIDC support is implemented for SaaS - threescale_cicd_openapi_smoketest_operation: '{{ '''' if inventory_hostname is match(".*[.]3scale[.]net") else ''Echo'' }}' # Delete the service - import_role: name: 'nmasse-itix.threescale-cicd'