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

Adds OIDC code workflow #2595

Merged
merged 3 commits into from
Jul 14, 2022
Merged

Adds OIDC code workflow #2595

merged 3 commits into from
Jul 14, 2022

Conversation

jvanderhoof
Copy link
Contributor

@jvanderhoof jvanderhoof commented Jul 5, 2022

Desired Outcome

This PR adds support for OIDC's code-based authorization flow. This functionality allows Conjur to use an external
OIDC Provider (Okta, Keycloak), to authenticate users and redirect them to Conjur on success. Conjur will resolve the identified user to a Conjur User ID and return and Authorization Token.

Implemented Changes

The architecture of this feature is different from that of the other authenticators. The intention of this architecture is to simplify testing and reduce developer cognitive load.

  • Authenticator Specific Constructs:
    • Authenticator Data Object - contains the configuration data specific to an authenticator type.
    • Strategy - implements the lower level details (the "how") an authenticator verifies an external identity.
    • Resolve Identity - functionality to an externally identified identity with an internal identity.
    • Client - handles the low level network specifics of verifying an identity with an external identity provider.
  • Generic Authenticator Constructs:
    • Authentication Handler - a generic handler which calls the authenticator specific objects to orchestrate the authentication flow.

Planned Future Work

Connected Issue/Story

Jira Epic

Definition of Done

At least 1 todo must be completed in the sections below for the PR to be
merged.

Changelog

  • The CHANGELOG has been updated, or
  • This PR does not include user-facing changes and doesn't require a
    CHANGELOG update

Test coverage

  • This PR includes new unit and integration tests to go with the code
    changes, or
  • The changes in this PR do not require tests

Documentation

  • Docs (e.g. READMEs) were updated in this PR
  • A follow-up issue to update official docs has been filed here: insert issue ID
  • This PR does not require updating any documentation

Behavior

  • This PR changes product behavior and has been reviewed by a PO, or
  • These changes are part of a larger initiative that will be reviewed later, or
  • No behavior was changed with this PR

Security

  • Security architect has reviewed the changes in this PR,
  • These changes are part of a larger initiative with a separate security review, or
  • There are no security aspects to these changes

@jvanderhoof jvanderhoof force-pushed the adds-oidc-code-workflow branch from f18eec8 to 0553203 Compare July 11, 2022 19:29
)
end

def log_audit_failure(account, service_id, client_ip, type, error)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method log_audit_failure has 5 arguments (exceeds 4 allowed). Consider refactoring.

)
end

def call(parameters:, request_ip:)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method call has a Cognitive Complexity of 15 (exceeds 5 allowed). Consider refactoring.

)
end

def call(parameters:, request_ip:)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method call has 35 lines of code (exceeds 25 allowed). Consider refactoring.

handle_error(e)
end

def handle_error(err)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method handle_error has 30 lines of code (exceeds 25 allowed). Consider refactoring.

module AuthnOidc
module V2
class ResolveIdentity
def call(identity:, account:, allowed_roles:)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method call has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.

@jvanderhoof jvanderhoof force-pushed the adds-oidc-code-workflow branch 2 times, most recently from edab389 to e2c0ecc Compare July 12, 2022 15:46
@jvanderhoof jvanderhoof marked this pull request as ready for review July 12, 2022 15:47
@jvanderhoof jvanderhoof requested a review from a team as a code owner July 12, 2022 15:47
mFelgate
mFelgate previously approved these changes Jul 12, 2022
Copy link
Contributor

@mFelgate mFelgate left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm (other then the error handling haha)

@@ -4,6 +4,25 @@ class AuthenticateController < ApplicationController
include BasicAuthenticator
include AuthorizeResource

def authenticate_okta
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be named something closer to authenticate_oidc_v2 as we use it for other services as well?

webservice = @resource_repository.where(
Sequel.like(
:resource_id,
"#{account}:webservice:conjur/#{type}/#{service_id}%"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this function is trying to find a specific webservice but wouldn't the % at the end allow it to pickup other rows from the db we don't necessarily want? Or am I missing something with this function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch @telday!

).all
)

# TODO: Add an error message
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to address this before merging or leave for later

@logger.warn(@authentication_error.new(err))
case err
when Errors::Authentication::Security::RoleNotAuthorizedOnResource
puts "Errors::Authentication::Security::RoleNotAuthorizedOnResource"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity why are we printing the error class here?

We could we have a puts err.class before the switch statement to eliminate most of the print statements

end
end

def callback(code:)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method callback has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.

end
end

def callback(code:)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method callback has 31 lines of code (exceeds 25 allowed). Consider refactoring.


private

def load_authenticator(type:, account:, id:)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method load_authenticator has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.

require 'cucumber/policy/features/support/client'
require 'json'

Then('the list of authenticators contains the service-id {string}') do |service_id|
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar blocks of code found in 2 locations. Consider refactoring.

end


Then('the list of authenticators does not contain the service-id {string}') do |service_id|
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar blocks of code found in 2 locations. Consider refactoring.

@jvanderhoof jvanderhoof force-pushed the adds-oidc-code-workflow branch 5 times, most recently from 2c99ef1 to 171eb1f Compare July 14, 2022 16:34
def oidc_authenticate_code_redirect
# TODO: need a mechanism for an authenticator strategy to define the required
# params. This will likely need to be done via the Handler.
params.permit!
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parameters should be whitelisted for mass assignment

gem 'json_spec', '~> 1.1'
gem 'faye-websocket'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gems should be sorted in an alphabetical order within their section of the Gemfile. Gem faye-websocket should appear before json_spec.

::Authentication::LogAuditEvent.new.call(
authentication_params:
Authentication::AuthenticatorInput.new(
authenticator_name: "#{type}",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer to_s over string interpolation.

::Authentication::LogAuditEvent.new.call(
authentication_params:
Authentication::AuthenticatorInput.new(
authenticator_name: "#{type}",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer to_s over string interpolation.

raise ApplicationController::BadRequest

when Errors::Conjur::RequestedResourceNotFound
raise ApplicationController::RecordNotFound.new(err.message)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Provide an exception class and message as arguments to raise.

This commit includes all work related to support for the code-based OIDC authentication workflow.
@jvanderhoof jvanderhoof force-pushed the adds-oidc-code-workflow branch from 5c3d401 to e0d8366 Compare July 14, 2022 18:54
@@ -88,6 +88,7 @@ group :development, :test do
gem 'debase', '~> 0.2.5.beta2'
gem 'faye-websocket'
gem 'json_spec', '~> 1.1'
gem 'faye-websocket'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gem faye-websocket requirements already given on line 89 of the Gemfile.


def call(parameters:, request_ip:)
raise Errors::Authentication::RequestBody::MissingRequestParam, parameters[:code] unless parameters[:code]
raise Errors::Authentication::RequestBody::MissingRequestParam, parameters[:state] unless parameters[:state]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add empty line after guard clause.

@@ -0,0 +1,41 @@

module Authentication
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary blank line at the beginning of the source.

attr_reader :provider_uri, :client_id, :client_secret, :claim_mapping, :nonce, :state, :account
attr_reader :service_id, :redirect_uri, :response_type

def initialize(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid parameter lists longer than 7 parameters. [12/7]

id_token,
discovery_information.jwks
)
rescue Exception => e
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid rescuing the Exception class. Perhaps you meant to rescue StandardError?

@codeclimate
Copy link

codeclimate bot commented Jul 14, 2022

Code Climate has analyzed commit e0d8366 and detected 124 issues on this pull request.

Here's the issue category breakdown:

Category Count
Complexity 76
Duplication 5
Bug Risk 4
Style 38
Security 1

The test coverage on the diff in this pull request is 90.5% (50% is the threshold).

This pull request will bring the total coverage in the repository to 89.9% (-1.5% change).

View more on Code Climate.

@jvanderhoof jvanderhoof requested review from mFelgate and telday July 14, 2022 20:48
@smoke
Scenario: List readable authenticators

Given I load a policy:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this, and the variable setting should be the background.

Then I can add a secret to variable resource "conjur/authn-oidc/okta/claim-mapping"
Then I can add a secret to variable resource "conjur/authn-oidc/okta/nonce"
Then I can add a secret to variable resource "conjur/authn-oidc/okta/state"
When I log in as user "admin"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these should be separate Scenario

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants