Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cucumber test for AuthnOIDC with Identity #2997

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions ci/test_suites/authenticators_oidc/secrets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,22 @@ ci:
OKTA_USERNAME: !var ci/okta/user/assigned/username
OKTA_PASSWORD: !var ci/okta/user/assigned/password

IDENTITY_CLIENT_ID: !var ci/identity/app/client-id
IDENTITY_CLIENT_SECRET: !var ci/identity/app/client-secret
IDENTITY_PROVIDER_URI: !var ci/identity/app/provider-uri
IDENTITY_USERNAME: !var ci/identity/user/assigned/username
IDENTITY_PASSWORD: !var ci/identity/user/assigned/password

development:
OKTA_CLIENT_ID: !var dev/okta/app/client-id
OKTA_CLIENT_SECRET: !var dev/okta/app/client-secret
OKTA_PROVIDER_URI: !var dev/okta/app/provider-uri
OKTA_USERNAME: !var dev/okta/user/assigned/username
OKTA_PASSWORD: !var dev/okta/user/assigned/password

IDENTITY_CLIENT_ID: !var dev/identity/app/client-id
IDENTITY_CLIENT_SECRET: !var dev/identity/app/client-secret
IDENTITY_PROVIDER_URI: !var dev/identity/app/provider-uri
# Identity integration tests depend on the following variables.
# IDENTITY_USERNAME: myUsername
# IDENTITY_PASSWORD: myP@ssword!
5 changes: 5 additions & 0 deletions ci/test_suites/authenticators_oidc/test
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ function _hydrate_all_env_args() {
"OKTA_PROVIDER_URI=${OKTA_PROVIDER_URI}oauth2/default"
"OKTA_USERNAME=$OKTA_USERNAME"
"OKTA_PASSWORD=$OKTA_PASSWORD"
"IDENTITY_CLIENT_ID=$IDENTITY_CLIENT_ID"
"IDENTITY_CLIENT_SECRET=$IDENTITY_CLIENT_SECRET"
"IDENTITY_PROVIDER_URI=$IDENTITY_PROVIDER_URI"
"IDENTITY_USERNAME=$IDENTITY_USERNAME"
"IDENTITY_PASSWORD=$IDENTITY_PASSWORD"
)
}

Expand Down
51 changes: 51 additions & 0 deletions cucumber/authenticators_oidc/features/authn_oidc_identity.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
@authenticators_oidc
Feature: OIDC Authenticator V2 - Users can authenticate with Identity using OIDC

Background:
Given the following environment variables are available:
| context_variable | environment_variable | default_value |
| oidc_provider_uri | IDENTITY_PROVIDER_URI | |
| oidc_client_id | IDENTITY_CLIENT_ID | |
| oidc_client_secret | IDENTITY_CLIENT_SECRET | |
| oidc_redirect_url | IDENTITY_REDIRECT | http://localhost:3000/authn-oidc/identity/cucumber/authenticate |
| oidc_username | IDENTITY_USERNAME | |
| oidc_password | IDENTITY_PASSWORD | |

And I load a policy and enable an oidc user into group "conjur/authn-oidc/identity/users":
"""
- !policy
id: conjur/authn-oidc/identity
body:
- !webservice
annotations:
description: Authentication service for Identity, based on Open ID Connect.

- !variable provider-uri
- !variable client-id
- !variable client-secret
- !variable claim-mapping
- !variable state
- !variable nonce
- !variable redirect-uri

- !group users

- !permit
role: !group users
privilege: [ read, authenticate ]
resource: !webservice
"""
And I set the following conjur variables:
| variable_id | context_variable | default_value |
| conjur/authn-oidc/identity/provider-uri | oidc_provider_uri | |
| conjur/authn-oidc/identity/client-id | oidc_client_id | |
| conjur/authn-oidc/identity/client-secret | oidc_client_secret | |
| conjur/authn-oidc/identity/claim-mapping | | preferred_username |
| conjur/authn-oidc/identity/redirect-uri | oidc_redirect_url | |

@smoke
Scenario: Authenticating with Conjur using Identity
Given I retrieve OIDC configuration from the provider endpoint for "identity"
And I authenticate and fetch a code from Identity
When I authenticate via OIDC with code and service_id "identity"
Then the OIDC user has been authorized by conjur
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,4 @@ Feature: OIDC Authenticator V2 - Users can authenticate with Okta using OIDC
Given I retrieve OIDC configuration from the provider endpoint for "okta"
And I authenticate and fetch a code from Okta
When I authenticate via OIDC with code and service_id "okta"
Then the okta user has been authorized by conjur
Then the OIDC user has been authorized by conjur
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,113 @@
@scenario_context.add(:redirect_uri, provider['redirect_uri'])
end

Given(/^I authenticate and fetch a code from Identity/) do
# A request to /Security/StartAuthentication begins the login process,
# and returns a list of authentication mechanisms to engage with.

host = URI(@scenario_context.get(:redirect_uri)).host
resp = start_auth_request(host, @scenario_context.get(:oidc_username))
resp_h = JSON.parse(resp.body)

if resp_h["Result"]["PodFqdn"]
resp = start_auth_request(resp_h["Result"]["PodFqdn"], @scenario_context.get(:oidc_username))
resp_h = JSON.parse(resp.body)
end

raise "Failed to retrieve OIDC code status: #{resp.code}" unless resp_h["success"]

session_id = resp_h["Result"]["SessionId"]
challenges = resp_h["Result"]["Challenges"]

# Usually, we would iterate through MFA challenges sequentially.
# For our purposes, though, we want to make sure to use the Password
# and Mobile Authenticator mechanisms.

password_mechanism = challenges[0]["Mechanisms"].detect { |m| m["PromptSelectMech"] == "Password" }
mobile_auth_mechanism = challenges[1]["Mechanisms"].detect { |m| m["PromptSelectMech"] == "Mobile Authenticator" }

# Advance Password-based authentication handshake.

password_body = JSON.generate({
"Action": "Answer",
"Answer": @scenario_context.get(:oidc_password),
"MechanismId": password_mechanism["MechanismId"],
"SessionId": session_id
})
resp = advance_auth_request(host, password_body)
resp_h = JSON.parse(resp.body)
raise "Failed to advance authentication: #{resp_h['Message']}" unless resp_h["success"]

# Begin temporary block
#
# Engaging with a Mobile Auth and polling for out-of-band authentication
# success is included temporarily, and is required for users that are required
# to perform MFA. Before merging, and service account should be made available
# for running this test in CI, and the account should not be bound by MFA.

# Advance Mobile Authenticator-based authentication handshake.

mobile_auth_body = JSON.generate({
"Action": "StartOOB",
"MechanismId": mobile_auth_mechanism["MechanismId"],
"SessionId": session_id
})
resp = advance_auth_request(host, mobile_auth_body)
resp_h = JSON.parse(resp.body)
raise "Failed to advance authentication: #{resp_h['Message']}" unless resp_h["success"]

puts "Dev env users: select #{resp_h['Result']['GeneratedAuthValue']} in your Identity notification"

# For 30 seconds, Poll for out-of-band authentication success.

poll_body = JSON.generate({
"Action": "Poll",
"MechanismId": mobile_auth_mechanism["MechanismId"],
"SessionId": session_id
})

token = ""
current = Time.current
while Time.current < current + 30
resp = advance_auth_request(host, poll_body)
resp_h = JSON.parse(resp.body)

next unless resp_h["Result"]["Summary"] == "LoginSuccess"

cookies = resp.get_fields('Set-Cookie')
token_cookie = cookies.detect { |c| c.start_with?(".ASPXAUTH") }
token = token_cookie.split('; ')[0].split('=')[1]
end
if token == ""
raise "Failed to advance authentication: please reattempt"
end

# End temporary block
#
# Make request to /Authorization endpoint with bearer token.

target = URI("#{@scenario_context.get(:redirect_uri)}&state=test-state")
resp = nil
until target.to_s.include?("localhost:3000/authn-oidc/identity/cucumber")
http = Net::HTTP.new(target.host, 443)
http.use_ssl = true
req = Net::HTTP::Get.new(target.request_uri)
req['Accept'] = '*/*'
req['Authorization'] = "Bearer #{token}"
resp = http.request(req)

target = URI(resp['location'].to_s)
end

if resp.is_a?(Net::HTTPRedirection)
parse_oidc_code(resp['location']).each do |key, value|
@scenario_context.set(key, value)
end
else
raise "Failed to retrieve OIDC code status: #{resp.code}"
end
end

Given(/^I authenticate and fetch a code from Okta/) do
uri = URI("https://#{URI(@scenario_context.get(:redirect_uri)).host}/api/v1/authn")
body = JSON.generate({ username: @scenario_context.get(:oidc_username), password: @scenario_context.get(:oidc_password) })
Expand Down Expand Up @@ -176,7 +283,7 @@
)
end

Then(/^the okta user has been authorized by conjur/) do
Then(/^the OIDC user has been authorized by conjur/) do
username = @scenario_context.get(:oidc_username)
expect(retrieved_access_token.username).to eq(username)
end
Expand Down
27 changes: 27 additions & 0 deletions cucumber/authenticators_oidc/features/support/authn_oidc_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,33 @@ def invalid_id_token
"invalididtoken"
end

def identity_request(uri, body)
http = Net::HTTP.new(uri.host, 443)
http.use_ssl = true

req = Net::HTTP::Post.new(uri.request_uri)
req['Accept'] = '*/*'
req['Content-Type'] = 'application/json'
req.body = body

http.request(req)
end

def start_auth_request(host, username)
body = JSON.generate({
"User": username,
"Version": "1.0"
})

uri = URI("https://#{host}/Security/StartAuthentication")
identity_request(uri, body)
end

def advance_auth_request(host, body)
uri = URI("https://#{host}/Security/AdvanceAuthentication")
identity_request(uri, body)
end

private

def parse_oidc_id_token
Expand Down
12 changes: 12 additions & 0 deletions dev/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ services:
# See https://github.com/DatabaseCleaner/database_cleaner#safeguards
DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL: "true"
BUNDLE_GEMFILE: /src/conjur-server/Gemfile
# Adding the following envvars allows users to run Cucumber tests for
# AuthnOIDC V2 with Okta and Identity from the dev environment.
OKTA_CLIENT_ID: ${OKTA_CLIENT_ID}
OKTA_CLIENT_SECRET: ${OKTA_CLIENT_SECRET}
OKTA_PROVIDER_URI: ${OKTA_PROVIDER_URI}oauth2/default
OKTA_USERNAME: ${OKTA_USERNAME}
OKTA_PASSWORD: ${OKTA_PASSWORD}
IDENTITY_CLIENT_ID: ${IDENTITY_CLIENT_ID}
IDENTITY_CLIENT_SECRET: ${IDENTITY_CLIENT_SECRET}
IDENTITY_PROVIDER_URI: ${IDENTITY_PROVIDER_URI}
IDENTITY_USERNAME: ${IDENTITY_USERNAME}
IDENTITY_PASSWORD: ${IDENTITY_PASSWORD}
cap_add:
- SYSLOG
ports:
Expand Down
Loading