From 7f2be27ede77e26f61d8ab4d18995e859d4d08db Mon Sep 17 00:00:00 2001 From: John ODonnell Date: Fri, 28 Jul 2023 13:25:11 -0400 Subject: [PATCH 1/5] Cukes: use CONJUR_APPLIANCE_URL before http://conjur --- config/environments/test.rb | 2 +- cucumber/_authenticators_common/features/support/env.rb | 2 +- cucumber/_authenticators_common/features/support/hooks.rb | 2 +- cucumber/api/features/support/env.rb | 2 +- cucumber/api/features/support/hooks.rb | 2 +- cucumber/authenticators/features/support/hooks.rb | 2 +- cucumber/policy/features/support/env.rb | 2 +- cucumber/policy/features/support/hooks.rb | 2 +- cucumber/rotators/features/support/env.rb | 2 +- engines/conjur_audit/config/routes.rb | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/config/environments/test.rb b/config/environments/test.rb index b8c1ff3fd6..f889b55552 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -4,7 +4,7 @@ require 'test/audit_sink' parallel_cuke_vars = {} -parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" +parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = ENV.fetch('CONJUR_APPLIANCE_URL', "http://conjur#{ENV['TEST_ENV_NUMBER']}") parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] parallel_cuke_vars['AUTHN_LOCAL_SOCKET'] = ENV["AUTHN_LOCAL_SOCKET#{ENV['TEST_ENV_NUMBER']}"] diff --git a/cucumber/_authenticators_common/features/support/env.rb b/cucumber/_authenticators_common/features/support/env.rb index 35f864e9f1..7b45442a89 100644 --- a/cucumber/_authenticators_common/features/support/env.rb +++ b/cucumber/_authenticators_common/features/support/env.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true parallel_cuke_vars = {} -parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" +parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = ENV.fetch('CONJUR_APPLIANCE_URL', "http://conjur#{ENV['TEST_ENV_NUMBER']}") parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] diff --git a/cucumber/_authenticators_common/features/support/hooks.rb b/cucumber/_authenticators_common/features/support/hooks.rb index 08d641f6fc..81da8e83a3 100644 --- a/cucumber/_authenticators_common/features/support/hooks.rb +++ b/cucumber/_authenticators_common/features/support/hooks.rb @@ -10,7 +10,7 @@ # run independently. Before do parallel_cuke_vars = {} - parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" + parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = ENV.fetch('CONJUR_APPLIANCE_URL', "http://conjur#{ENV['TEST_ENV_NUMBER']}") parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] diff --git a/cucumber/api/features/support/env.rb b/cucumber/api/features/support/env.rb index 63ff28a4ee..8c1d6bb56a 100644 --- a/cucumber/api/features/support/env.rb +++ b/cucumber/api/features/support/env.rb @@ -5,7 +5,7 @@ ENV['CONJUR_LOG_LEVEL'] ||= 'debug' parallel_cuke_vars = {} -parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" +parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = ENV.fetch('CONJUR_APPLIANCE_URL', "http://conjur#{ENV['TEST_ENV_NUMBER']}") parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] parallel_cuke_vars['AUTHN_LOCAL_SOCKET'] = ENV["AUTHN_LOCAL_SOCKET#{ENV['TEST_ENV_NUMBER']}"] diff --git a/cucumber/api/features/support/hooks.rb b/cucumber/api/features/support/hooks.rb index 578804ebb1..735dc4d095 100644 --- a/cucumber/api/features/support/hooks.rb +++ b/cucumber/api/features/support/hooks.rb @@ -11,7 +11,7 @@ # run independently. Before do parallel_cuke_vars = {} - parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" + parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = ENV.fetch('CONJUR_APPLIANCE_URL', "http://conjur#{ENV['TEST_ENV_NUMBER']}") parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] parallel_cuke_vars['AUTHN_LOCAL_SOCKET'] = ENV["AUTHN_LOCAL_SOCKET#{ENV['TEST_ENV_NUMBER']}"] diff --git a/cucumber/authenticators/features/support/hooks.rb b/cucumber/authenticators/features/support/hooks.rb index 46c551b9ee..735d695167 100644 --- a/cucumber/authenticators/features/support/hooks.rb +++ b/cucumber/authenticators/features/support/hooks.rb @@ -6,7 +6,7 @@ # run independently. Before do parallel_cuke_vars = {} - parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" + parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = ENV.fetch('CONJUR_APPLIANCE_URL', "http://conjur#{ENV['TEST_ENV_NUMBER']}") parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] diff --git a/cucumber/policy/features/support/env.rb b/cucumber/policy/features/support/env.rb index 179752d970..ad3eef79ca 100644 --- a/cucumber/policy/features/support/env.rb +++ b/cucumber/policy/features/support/env.rb @@ -6,7 +6,7 @@ require 'rest-client' parallel_cuke_vars = {} -parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" +parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = ENV.fetch('CONJUR_APPLIANCE_URL', "http://conjur#{ENV['TEST_ENV_NUMBER']}") parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] diff --git a/cucumber/policy/features/support/hooks.rb b/cucumber/policy/features/support/hooks.rb index 2f61a26020..ac44b858e6 100644 --- a/cucumber/policy/features/support/hooks.rb +++ b/cucumber/policy/features/support/hooks.rb @@ -19,7 +19,7 @@ # run independently. Before do parallel_cuke_vars = {} - parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" + parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = ENV.fetch('CONJUR_APPLIANCE_URL', "http://conjur#{ENV['TEST_ENV_NUMBER']}") parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] diff --git a/cucumber/rotators/features/support/env.rb b/cucumber/rotators/features/support/env.rb index 74ec8c5016..245a630c85 100644 --- a/cucumber/rotators/features/support/env.rb +++ b/cucumber/rotators/features/support/env.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true parallel_cuke_vars = {} -parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" +parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = ENV.fetch('CONJUR_APPLIANCE_URL', "http://conjur#{ENV['TEST_ENV_NUMBER']}") parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] diff --git a/engines/conjur_audit/config/routes.rb b/engines/conjur_audit/config/routes.rb index 70e6a08710..cc43cf2c63 100644 --- a/engines/conjur_audit/config/routes.rb +++ b/engines/conjur_audit/config/routes.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true parallel_cuke_vars = {} -parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = "http://conjur#{ENV['TEST_ENV_NUMBER']}" +parallel_cuke_vars['CONJUR_APPLIANCE_URL'] = ENV.fetch('CONJUR_APPLIANCE_URL', "http://conjur#{ENV['TEST_ENV_NUMBER']}") parallel_cuke_vars['DATABASE_URL'] = "postgres://postgres@pg#{ENV['TEST_ENV_NUMBER']}/postgres" parallel_cuke_vars['CONJUR_AUTHN_API_KEY'] = ENV["CONJUR_AUTHN_API_KEY#{ENV['TEST_ENV_NUMBER']}"] From c50cdeac804a8b7ee9ab6c8df55059363f46ff70 Mon Sep 17 00:00:00 2001 From: John ODonnell Date: Mon, 31 Jul 2023 10:09:39 -0400 Subject: [PATCH 2/5] Capture current behavior of policy load to create/update annot --- .../features/policy_load_annotations.feature | 427 ++++++++++++++++++ 1 file changed, 427 insertions(+) create mode 100644 cucumber/api/features/policy_load_annotations.feature diff --git a/cucumber/api/features/policy_load_annotations.feature b/cucumber/api/features/policy_load_annotations.feature new file mode 100644 index 0000000000..2020ee2977 --- /dev/null +++ b/cucumber/api/features/policy_load_annotations.feature @@ -0,0 +1,427 @@ +@api +Feature: Updating Policies with Annotations + + The following describes the use-case for the different policy load types: + - PUT requests replace an existing policy, or loads a nonexistent one. + Requires update privilege on the target policy. + - POST requests add data to an existing policy. + Requires create privilege on the target policy. + - PATCH requests modify an existing policy. + Requires update privilege on the target policy. + + Here is a summary of the current behavior of Conjur's policy API, recording + the result of a host with [create|update] privilege on a policy branch + attempting to [add new|update existing] annotations to a resource in that + policy branch via a [PUT|POST|PATCH]-based policy load attempt: + - create / add new / PUT : EXPECTED FAIL - 403 on policy load + - create / add new / POST : EXPECTED SUCCESS + - create / add new / PATCH : EXPECTED FAIL - 403 on policy load + - create / update existing / PUT : EXPECTED FAIL - 403 on policy load + - create / update existing / POST : EXPECTED FAIL - 20x on policy load, annot not updated + - create / update existing / PATCH : EXPECTED FAIL - 403 on policy load + - update / add new / PUT : EXPECTED SUCCESS + - update / add new / POST : EXPECTED FAIL - 403 on policy load + - update / add new / PATCH : EXPECTED SUCCESS + - update / update existing / PUT : EXPECTED SUCCESS + - update / update existing / POST : EXPECTED FAIL - 403 on policy load + - update / update existing / PATCH : EXPECTED SUCCESS + + All these outcomes align with our expectations, but one may not align with + user expectations: ( create / update existing / POST ). A user may expect that + a policy load that tries and fails to update the content of a given annotation + should either provide a warning or fail outright. + + How can we update how we handle policy to fail in this case? + + Background: + Given I am the super-user + And I successfully PUT "/policies/cucumber/policy/root" with body: + """ + - !policy hosts + """ + And I successfully PUT "/policies/cucumber/policy/hosts" with body: + """ + - !host + id: annotated + annotations: + description: Already annotated + + - !host to-annotate + """ + And I successfully POST "/policies/cucumber/policy/root" with body: + """ + - !user alice + - !user bob + + - !permit + resource: !policy hosts + privilege: [ read, update ] + role: !user bob + + - !permit + resource: !host hosts/annotated + privilege: [ read ] + role: !user bob + + - !permit + resource: !host hosts/to-annotate + privilege: [ read ] + role: !user bob + + - !permit + resource: !policy hosts + privilege: [ read, create ] + role: !user alice + + - !permit + resource: !host hosts/annotated + privilege: [ read ] + role: !user alice + + - !permit + resource: !host hosts/to-annotate + privilege: [ read ] + role: !user alice + """ + + Scenario: User with create privilege can NOT add new annotations with PUT + When I login as "alice" + And I save my place in the log file + Then I PUT "/policies/cucumber/policy/hosts" with body: + """ + - !host + id: to-annotate + annotations: + description: Success + """ + Then the HTTP response status code is 403 + And The following appears in the log after my savepoint: + """ + CONJ00006E 'cucumber:user:alice' does not have 'update' privilege on cucumber:policy:hosts + """ + + Scenario: User with create privilege can add new annotations with POST + When I login as "alice" + Then I successfully POST "/policies/cucumber/policy/hosts" with body: + """ + - !host + id: to-annotate + annotations: + description: Success + """ + And I successfully GET "/resources/cucumber/host/hosts%2Fto-annotate" + Then the JSON should be: + """ + { + "annotations": [ + { + "name": "description", + "policy": "cucumber:policy:hosts", + "value": "Success" + } + ], + "id": "cucumber:host:hosts/to-annotate", + "owner": "cucumber:policy:hosts", + "permissions": [ + { + "policy": "cucumber:policy:root", + "privilege": "read", + "role": "cucumber:user:bob" + }, + { + "policy": "cucumber:policy:root", + "privilege": "read", + "role": "cucumber:user:alice" + } + ], + "policy": "cucumber:policy:hosts", + "restricted_to": [ + + ] + } + """ + + Scenario: User with create privilege can NOT add new annotations with PATCH + When I login as "alice" + And I save my place in the log file + Then I PATCH "/policies/cucumber/policy/hosts" with body: + """ + - !host + id: to-annotate + annotations: + description: Success + """ + Then the HTTP response status code is 403 + And The following appears in the log after my savepoint: + """ + CONJ00006E 'cucumber:user:alice' does not have 'update' privilege on cucumber:policy:hosts + """ + + Scenario: User with create privilege can NOT update existing annotations with PUT + When I login as "alice" + And I save my place in the log file + Then I PUT "/policies/cucumber/policy/hosts" with body: + """ + - !host + id: annotated + annotations: + description: Success + """ + Then the HTTP response status code is 403 + And The following appears in the log after my savepoint: + """ + CONJ00006E 'cucumber:user:alice' does not have 'update' privilege on cucumber:policy:hosts + """ + + Scenario: User with create privilege CAN NOT update existing annotations with POST, but policy loads successfully + When I login as "alice" + Then I successfully POST "/policies/cucumber/policy/hosts" with body: + """ + - !host + id: annotated + annotations: + description: Success + """ + And I successfully GET "/resources/cucumber/host/hosts%2Fannotated" + Then the JSON should be: + """ + { + "annotations": [ + { + "name": "description", + "policy": "cucumber:policy:hosts", + "value": "Already annotated" + } + ], + "id": "cucumber:host:hosts/annotated", + "owner": "cucumber:policy:hosts", + "permissions": [ + { + "policy": "cucumber:policy:root", + "privilege": "read", + "role": "cucumber:user:bob" + }, + { + "policy": "cucumber:policy:root", + "privilege": "read", + "role": "cucumber:user:alice" + } + ], + "policy": "cucumber:policy:hosts", + "restricted_to": [ + + ] + } + """ + + Scenario: User with create privilege can NOT update existing annotations with PATCH + When I login as "alice" + And I save my place in the log file + Then I PATCH "/policies/cucumber/policy/hosts" with body: + """ + - !host + id: annotated + annotations: + description: Success + """ + Then the HTTP response status code is 403 + And The following appears in the log after my savepoint: + """ + CONJ00006E 'cucumber:user:alice' does not have 'update' privilege on cucumber:policy:hosts + """ + + Scenario: User with update privilege can add new annotations with PUT + When I login as "bob" + Then I successfully PUT "/policies/cucumber/policy/hosts" with body: + """ + - !host + id: to-annotate + annotations: + description: Success + """ + And I successfully GET "/resources/cucumber/host/hosts%2Fto-annotate" + Then the JSON should be: + """ + { + "annotations": [ + { + "name": "description", + "policy": "cucumber:policy:hosts", + "value": "Success" + } + ], + "id": "cucumber:host:hosts/to-annotate", + "owner": "cucumber:policy:hosts", + "permissions": [ + { + "policy": "cucumber:policy:root", + "privilege": "read", + "role": "cucumber:user:bob" + }, + { + "policy": "cucumber:policy:root", + "privilege": "read", + "role": "cucumber:user:alice" + } + ], + "policy": "cucumber:policy:hosts", + "restricted_to": [ + + ] + } + """ + + Scenario: User with update privilege can NOT add new annotations with POST + When I login as "bob" + And I save my place in the log file + Then I POST "/policies/cucumber/policy/hosts" with body: + """ + - !host + id: to-annotate + annotations: + description: Success + """ + Then the HTTP response status code is 403 + And The following appears in the log after my savepoint: + """ + CONJ00006E 'cucumber:user:bob' does not have 'create' privilege on cucumber:policy:hosts + """ + + Scenario: User with update privilege can add new annotations with PATCH + When I login as "bob" + Then I successfully PATCH "/policies/cucumber/policy/hosts" with body: + """ + - !host + id: to-annotate + annotations: + description: Success + """ + And I successfully GET "/resources/cucumber/host/hosts%2Fto-annotate" + Then the JSON should be: + """ + { + "annotations": [ + { + "name": "description", + "policy": "cucumber:policy:hosts", + "value": "Success" + } + ], + "id": "cucumber:host:hosts/to-annotate", + "owner": "cucumber:policy:hosts", + "permissions": [ + { + "policy": "cucumber:policy:root", + "privilege": "read", + "role": "cucumber:user:bob" + }, + { + "policy": "cucumber:policy:root", + "privilege": "read", + "role": "cucumber:user:alice" + } + ], + "policy": "cucumber:policy:hosts", + "restricted_to": [ + + ] + } + """ + + Scenario: User with update privilege can update existing annotations with PUT + When I login as "bob" + Then I successfully PUT "/policies/cucumber/policy/hosts" with body: + """ + - !host + id: annotated + annotations: + description: Success + """ + And I successfully GET "/resources/cucumber/host/hosts%2Fannotated" + Then the JSON should be: + """ + { + "annotations": [ + { + "name": "description", + "policy": "cucumber:policy:hosts", + "value": "Success" + } + ], + "id": "cucumber:host:hosts/annotated", + "owner": "cucumber:policy:hosts", + "permissions": [ + { + "policy": "cucumber:policy:root", + "privilege": "read", + "role": "cucumber:user:bob" + }, + { + "policy": "cucumber:policy:root", + "privilege": "read", + "role": "cucumber:user:alice" + } + ], + "policy": "cucumber:policy:hosts", + "restricted_to": [ + + ] + } + """ + + Scenario: User with update privilege can NOT update existing annotations with POST + When I login as "bob" + And I save my place in the log file + Then I POST "/policies/cucumber/policy/hosts" with body: + """ + - !host + id: annotated + annotations: + description: Success + """ + Then the HTTP response status code is 403 + And The following appears in the log after my savepoint: + """ + CONJ00006E 'cucumber:user:bob' does not have 'create' privilege on cucumber:policy:hosts + """ + + Scenario: User with update privilege can update existing annotations with PATCH + When I login as "bob" + Then I successfully PATCH "/policies/cucumber/policy/hosts" with body: + """ + - !host + id: annotated + annotations: + description: Success + """ + And I successfully GET "/resources/cucumber/host/hosts%2Fannotated" + Then the JSON should be: + """ + { + "annotations": [ + { + "name": "description", + "policy": "cucumber:policy:hosts", + "value": "Success" + } + ], + "id": "cucumber:host:hosts/annotated", + "owner": "cucumber:policy:hosts", + "permissions": [ + { + "policy": "cucumber:policy:root", + "privilege": "read", + "role": "cucumber:user:bob" + }, + { + "policy": "cucumber:policy:root", + "privilege": "read", + "role": "cucumber:user:alice" + } + ], + "policy": "cucumber:policy:hosts", + "restricted_to": [ + + ] + } + """ From 36c8dbe819c86dea54d6f9024f8696cdee5d6760 Mon Sep 17 00:00:00 2001 From: John ODonnell Date: Mon, 14 Aug 2023 16:34:31 -0400 Subject: [PATCH 3/5] Tag new Cucumber tests --- .../features/policy_load_annotations.feature | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/cucumber/api/features/policy_load_annotations.feature b/cucumber/api/features/policy_load_annotations.feature index 2020ee2977..cc2e52c8b5 100644 --- a/cucumber/api/features/policy_load_annotations.feature +++ b/cucumber/api/features/policy_load_annotations.feature @@ -84,6 +84,8 @@ Feature: Updating Policies with Annotations role: !user alice """ + @negative + @acceptance Scenario: User with create privilege can NOT add new annotations with PUT When I login as "alice" And I save my place in the log file @@ -100,6 +102,8 @@ Feature: Updating Policies with Annotations CONJ00006E 'cucumber:user:alice' does not have 'update' privilege on cucumber:policy:hosts """ + @smoke + @acceptance Scenario: User with create privilege can add new annotations with POST When I login as "alice" Then I successfully POST "/policies/cucumber/policy/hosts" with body: @@ -141,6 +145,8 @@ Feature: Updating Policies with Annotations } """ + @negative + @acceptance Scenario: User with create privilege can NOT add new annotations with PATCH When I login as "alice" And I save my place in the log file @@ -157,6 +163,8 @@ Feature: Updating Policies with Annotations CONJ00006E 'cucumber:user:alice' does not have 'update' privilege on cucumber:policy:hosts """ + @negative + @acceptance Scenario: User with create privilege can NOT update existing annotations with PUT When I login as "alice" And I save my place in the log file @@ -173,6 +181,8 @@ Feature: Updating Policies with Annotations CONJ00006E 'cucumber:user:alice' does not have 'update' privilege on cucumber:policy:hosts """ + @negative + @acceptance Scenario: User with create privilege CAN NOT update existing annotations with POST, but policy loads successfully When I login as "alice" Then I successfully POST "/policies/cucumber/policy/hosts" with body: @@ -214,6 +224,8 @@ Feature: Updating Policies with Annotations } """ + @negative + @acceptance Scenario: User with create privilege can NOT update existing annotations with PATCH When I login as "alice" And I save my place in the log file @@ -230,6 +242,8 @@ Feature: Updating Policies with Annotations CONJ00006E 'cucumber:user:alice' does not have 'update' privilege on cucumber:policy:hosts """ + @smoke + @acceptance Scenario: User with update privilege can add new annotations with PUT When I login as "bob" Then I successfully PUT "/policies/cucumber/policy/hosts" with body: @@ -271,6 +285,8 @@ Feature: Updating Policies with Annotations } """ + @negative + @acceptance Scenario: User with update privilege can NOT add new annotations with POST When I login as "bob" And I save my place in the log file @@ -287,6 +303,8 @@ Feature: Updating Policies with Annotations CONJ00006E 'cucumber:user:bob' does not have 'create' privilege on cucumber:policy:hosts """ + @smoke + @acceptance Scenario: User with update privilege can add new annotations with PATCH When I login as "bob" Then I successfully PATCH "/policies/cucumber/policy/hosts" with body: @@ -328,6 +346,8 @@ Feature: Updating Policies with Annotations } """ + @smoke + @acceptance Scenario: User with update privilege can update existing annotations with PUT When I login as "bob" Then I successfully PUT "/policies/cucumber/policy/hosts" with body: @@ -369,6 +389,8 @@ Feature: Updating Policies with Annotations } """ + @negative + @acceptance Scenario: User with update privilege can NOT update existing annotations with POST When I login as "bob" And I save my place in the log file @@ -385,6 +407,8 @@ Feature: Updating Policies with Annotations CONJ00006E 'cucumber:user:bob' does not have 'create' privilege on cucumber:policy:hosts """ + @smoke + @acceptance Scenario: User with update privilege can update existing annotations with PATCH When I login as "bob" Then I successfully PATCH "/policies/cucumber/policy/hosts" with body: From 9141a2dc60c9097eafcbc0db5e0d317f7a4d7a8f Mon Sep 17 00:00:00 2001 From: John ODonnell Date: Thu, 17 Aug 2023 15:15:57 -0400 Subject: [PATCH 4/5] Fail additive policy load requests that update existing resources --- CHANGELOG.md | 3 ++ app/models/loader/create_policy.rb | 2 +- app/models/loader/orchestrate.rb | 8 ++-- .../features/policy_load_annotations.feature | 46 +++---------------- .../api/features/policy_load_modes.feature | 15 ++---- 5 files changed, 21 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22e8522945..751d9edf8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. mitigates the possibility of a web worker becoming starved while waiting for a connection to become available. [cyberark/conjur#2875](https://github.com/cyberark/conjur/pull/2875) +- Additive policy requests submitted via POST are rejected with a 400 status if + they attempt to update an existing resource. + [cyberark/conjur#2888](https://github.com/cyberark/conjur/pull/2888) ### Fixed - Support Authn-IAM regional requests when host value is missing from signed headers. diff --git a/app/models/loader/create_policy.rb b/app/models/loader/create_policy.rb index f3aac3dc67..128a179edc 100644 --- a/app/models/loader/create_policy.rb +++ b/app/models/loader/create_policy.rb @@ -16,7 +16,7 @@ def call @loader.delete_shadowed_and_duplicate_rows - @loader.store_policy_in_db + @loader.store_policy_in_db(reject_duplicates: true) @loader.release_db_connection end diff --git a/app/models/loader/orchestrate.rb b/app/models/loader/orchestrate.rb index f9666d302b..299fe8d218 100644 --- a/app/models/loader/orchestrate.rb +++ b/app/models/loader/orchestrate.rb @@ -120,8 +120,9 @@ def delete_shadowed_and_duplicate_rows end # TODO: consider renaming this method - def store_policy_in_db - eliminate_duplicates_pk + def store_policy_in_db(reject_duplicates: false) + removed_duplicates_count = eliminate_duplicates_pk + raise ApplicationController::BadRequest, "Updating existing resource disallowed in additive policy operation" if removed_duplicates_count.positive? && reject_duplicates insert_new @@ -243,8 +244,9 @@ def eliminate_duplicates_exact end # Delete rows from the new policy which have the same primary keys as existing rows. + # Returns the total number of deleted rows. def eliminate_duplicates_pk - TABLES.each do |table| + TABLES.sum do |table| eliminate_duplicates(table, Array(model_for_table(table).primary_key) + [ :policy_id ]) end end diff --git a/cucumber/api/features/policy_load_annotations.feature b/cucumber/api/features/policy_load_annotations.feature index cc2e52c8b5..c21c547409 100644 --- a/cucumber/api/features/policy_load_annotations.feature +++ b/cucumber/api/features/policy_load_annotations.feature @@ -17,7 +17,7 @@ Feature: Updating Policies with Annotations - create / add new / POST : EXPECTED SUCCESS - create / add new / PATCH : EXPECTED FAIL - 403 on policy load - create / update existing / PUT : EXPECTED FAIL - 403 on policy load - - create / update existing / POST : EXPECTED FAIL - 20x on policy load, annot not updated + - create / update existing / POST : EXPECTED FAIL - 400 on policy load - create / update existing / PATCH : EXPECTED FAIL - 403 on policy load - update / add new / PUT : EXPECTED SUCCESS - update / add new / POST : EXPECTED FAIL - 403 on policy load @@ -26,13 +26,6 @@ Feature: Updating Policies with Annotations - update / update existing / POST : EXPECTED FAIL - 403 on policy load - update / update existing / PATCH : EXPECTED SUCCESS - All these outcomes align with our expectations, but one may not align with - user expectations: ( create / update existing / POST ). A user may expect that - a policy load that tries and fails to update the content of a given annotation - should either provide a warning or fail outright. - - How can we update how we handle policy to fail in this case? - Background: Given I am the super-user And I successfully PUT "/policies/cucumber/policy/root" with body: @@ -183,45 +176,20 @@ Feature: Updating Policies with Annotations @negative @acceptance - Scenario: User with create privilege CAN NOT update existing annotations with POST, but policy loads successfully + Scenario: User with create privilege CAN NOT update existing annotations with POST When I login as "alice" - Then I successfully POST "/policies/cucumber/policy/hosts" with body: + And I save my place in the log file + Then I POST "/policies/cucumber/policy/hosts" with body: """ - !host id: annotated annotations: description: Success """ - And I successfully GET "/resources/cucumber/host/hosts%2Fannotated" - Then the JSON should be: + Then the HTTP response status code is 400 + And The following appears in the log after my savepoint: """ - { - "annotations": [ - { - "name": "description", - "policy": "cucumber:policy:hosts", - "value": "Already annotated" - } - ], - "id": "cucumber:host:hosts/annotated", - "owner": "cucumber:policy:hosts", - "permissions": [ - { - "policy": "cucumber:policy:root", - "privilege": "read", - "role": "cucumber:user:bob" - }, - { - "policy": "cucumber:policy:root", - "privilege": "read", - "role": "cucumber:user:alice" - } - ], - "policy": "cucumber:policy:hosts", - "restricted_to": [ - - ] - } + Updating existing resource disallowed in additive policy operation """ @negative diff --git a/cucumber/api/features/policy_load_modes.feature b/cucumber/api/features/policy_load_modes.feature index 3f0a66ac20..eed60df3ef 100644 --- a/cucumber/api/features/policy_load_modes.feature +++ b/cucumber/api/features/policy_load_modes.feature @@ -172,22 +172,17 @@ Feature: Updating policies @acceptance Scenario: POST cannot update existing policy records - When I successfully POST "/policies/cucumber/policy/dev/db" with body: + When I save my place in the log file + And I POST "/policies/cucumber/policy/dev/db" with body: """ - !variable id: b kind: private key """ - When I successfully GET "/resources/cucumber/variable/dev/db/b" - Then the JSON at "annotations" should be: + Then the HTTP response status code is 400 + And The following appears in the log after my savepoint: """ - [ - { - "name": "conjur/kind", - "policy": "cucumber:policy:dev/db", - "value": "password" - } - ] + Updating existing resource disallowed in additive policy operation """ @negative @acceptance From b1028a102c5c604b460d941acb60976729f4a4b3 Mon Sep 17 00:00:00 2001 From: John ODonnell Date: Fri, 18 Aug 2023 15:42:39 -0400 Subject: [PATCH 5/5] Create and use unique DisallowedPolicyOperation exception --- app/controllers/application_controller.rb | 12 ++++++++++++ app/models/exceptions/disallowed_policy_operation.rb | 11 +++++++++++ app/models/loader/orchestrate.rb | 2 +- .../api/features/policy_load_annotations.feature | 4 ++-- cucumber/api/features/policy_load_modes.feature | 2 +- 5 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 app/models/exceptions/disallowed_policy_operation.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2b53930016..f079873542 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -59,6 +59,7 @@ class UnprocessableEntity < RuntimeError rescue_from Sequel::ForeignKeyConstraintViolation, with: :foreign_key_constraint_violation rescue_from Conjur::PolicyParser::Invalid, with: :policy_invalid rescue_from Exceptions::InvalidPolicyObject, with: :policy_invalid + rescue_from Exceptions::DisallowedPolicyOperation, with: :disallowed_policy_operation rescue_from ArgumentError, with: :argument_error rescue_from ActionController::ParameterMissing, with: :argument_error rescue_from UnprocessableEntity, with: :unprocessable_entity @@ -193,6 +194,17 @@ def policy_invalid e render(json: { error: error }, status: :unprocessable_entity) end + def disallowed_policy_operation e + logger.debug("#{e}\n#{e.backtrace.join("\n")}") + + render(json: { + error: { + code: "disallowed_policy_operation", + message: e.message + } + }, status: :unprocessable_entity) + end + def argument_error e logger.debug("#{e}\n#{e.backtrace.join("\n")}") diff --git a/app/models/exceptions/disallowed_policy_operation.rb b/app/models/exceptions/disallowed_policy_operation.rb new file mode 100644 index 0000000000..84d34f0bbf --- /dev/null +++ b/app/models/exceptions/disallowed_policy_operation.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Exceptions + class DisallowedPolicyOperation < RuntimeError + + def initialize + super("Updating existing resource disallowed in additive policy operation") + end + + end +end diff --git a/app/models/loader/orchestrate.rb b/app/models/loader/orchestrate.rb index 299fe8d218..4b2dda2f59 100644 --- a/app/models/loader/orchestrate.rb +++ b/app/models/loader/orchestrate.rb @@ -122,7 +122,7 @@ def delete_shadowed_and_duplicate_rows # TODO: consider renaming this method def store_policy_in_db(reject_duplicates: false) removed_duplicates_count = eliminate_duplicates_pk - raise ApplicationController::BadRequest, "Updating existing resource disallowed in additive policy operation" if removed_duplicates_count.positive? && reject_duplicates + raise Exceptions::DisallowedPolicyOperation if removed_duplicates_count.positive? && reject_duplicates insert_new diff --git a/cucumber/api/features/policy_load_annotations.feature b/cucumber/api/features/policy_load_annotations.feature index c21c547409..4ecad06154 100644 --- a/cucumber/api/features/policy_load_annotations.feature +++ b/cucumber/api/features/policy_load_annotations.feature @@ -17,7 +17,7 @@ Feature: Updating Policies with Annotations - create / add new / POST : EXPECTED SUCCESS - create / add new / PATCH : EXPECTED FAIL - 403 on policy load - create / update existing / PUT : EXPECTED FAIL - 403 on policy load - - create / update existing / POST : EXPECTED FAIL - 400 on policy load + - create / update existing / POST : EXPECTED FAIL - 422 on policy load - create / update existing / PATCH : EXPECTED FAIL - 403 on policy load - update / add new / PUT : EXPECTED SUCCESS - update / add new / POST : EXPECTED FAIL - 403 on policy load @@ -186,7 +186,7 @@ Feature: Updating Policies with Annotations annotations: description: Success """ - Then the HTTP response status code is 400 + Then the HTTP response status code is 422 And The following appears in the log after my savepoint: """ Updating existing resource disallowed in additive policy operation diff --git a/cucumber/api/features/policy_load_modes.feature b/cucumber/api/features/policy_load_modes.feature index eed60df3ef..ec7a031414 100644 --- a/cucumber/api/features/policy_load_modes.feature +++ b/cucumber/api/features/policy_load_modes.feature @@ -179,7 +179,7 @@ Feature: Updating policies id: b kind: private key """ - Then the HTTP response status code is 400 + Then the HTTP response status code is 422 And The following appears in the log after my savepoint: """ Updating existing resource disallowed in additive policy operation