Skip to content

Commit

Permalink
Call DSI API for user roles on successful authentication
Browse files Browse the repository at this point in the history
DfE SignIn API will return 404 if the user isn't found for the given API call.
Checks the roles in the response for a valid authorised role code.
  • Loading branch information
steventux committed Sep 5, 2023
1 parent d02dd62 commit 53432b4
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 1 deletion.
4 changes: 4 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ DFE_SIGN_IN_CLIENT_ID=checkrecordteacher
DFE_SIGN_IN_REDIRECT_URL=http://check.localhost:3000/check-records/auth/dfe/callback
DFE_SIGN_IN_SECRET=override-locally
DFE_SIGN_IN_ISSUER=https://dev-oidc.signin.education.gov.uk
DFE_SIGN_IN_API_BASE_URL=https://dev-api.signin.education.gov.uk
DFE_SIGN_IN_API_SECRET=override-locally
DFE_SIGN_IN_API_AUDIENCE=signin.education.gov.uk
DFE_SIGN_IN_API_ROLE_CODE=override-locally
GOVUK_NOTIFY_API_KEY=override-locally
HOSTING_DOMAIN=http://localhost:3000
HOSTING_ENVIRONMENT_NAME=local
Expand Down
4 changes: 4 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ DFE_SIGN_IN_CLIENT_ID=checkrecordteacher
DFE_SIGN_IN_ISSUER=test
DFE_SIGN_IN_REDIRECT_URL=test
DFE_SIGN_IN_SECRET=override-locally
DFE_SIGN_IN_API_BASE_URL=https://dev-api.signin.education.gov.uk
DFE_SIGN_IN_API_SECRET=override-locally
DFE_SIGN_IN_API_AUDIENCE=signin.education.gov.uk
DFE_SIGN_IN_API_ROLE_CODE=override-locally
GOVUK_NOTIFY_API_KEY=override-locally
HOSTING_DOMAIN=http://qualifications.localhost
HOSTING_ENVIRONMENT_NAME=local
Expand Down
18 changes: 17 additions & 1 deletion app/controllers/check_records/omniauth_callbacks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,23 @@ class CheckRecords::OmniauthCallbacksController < ApplicationController
protect_from_forgery except: :dfe_bypass

def dfe
@dsi_user = DsiUser.create_or_update_from_dsi(request.env["omniauth.auth"])
auth = request.env["omniauth.auth"]

unless CheckRecords::DfESignIn.bypass?
begin
DfESignInApi::GetUserAccessToService.new(
org_id: auth.extra.raw_info.organisation.id,
user_id: auth.uid,
).call
rescue DfESignInApi::InvalidTokenError
return redirect_to check_records_sign_out_path
rescue DfESignInApi::UserNotAuthorisedError
flash[:warning] = "User not permitted to access this service"
return redirect_to check_records_sign_out_path
end
end

@dsi_user = DsiUser.create_or_update_from_dsi(auth)
session[:dsi_user_id] = @dsi_user.id
session[:dsi_user_session_expiry] = 2.hours.from_now.to_i

Expand Down
33 changes: 33 additions & 0 deletions app/lib/dfe_sign_in_api/client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require "jwt"

module DfESignInApi
class Client
TIMEOUT_IN_SECONDS = 5

def client
@client ||=
Faraday.new(
url: ENV.fetch("DFE_SIGN_IN_API_BASE_URL"),
request: {
timeout: TIMEOUT_IN_SECONDS
}
) do |faraday|
faraday.request :authorization, "Bearer", jwt
faraday.request :json
faraday.response :json
faraday.adapter Faraday.default_adapter
end
end

def jwt
@jwt ||= JWT.encode(
{
iss: ENV.fetch("DFE_SIGN_IN_CLIENT_ID"),
aud: ENV.fetch("DFE_SIGN_IN_API_AUDIENCE"),
},
ENV.fetch("DFE_SIGN_IN_API_SECRET"),
"HS256",
)
end
end
end
47 changes: 47 additions & 0 deletions app/lib/dfe_sign_in_api/get_user_access_to_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module DfESignInApi
class GetUserAccessToService
attr_reader :org_id, :user_id

def initialize(org_id:, user_id:)
@org_id = org_id
@user_id = user_id
end

def call
response = client.get(endpoint)

case response.status
when 200
if response.body.key?("roles")
response.body["roles"].map { |role| role["code"] }.include?(authorised_role_code)
else
raise DfESignInApi::UserNotAuthorisedError, "Response does not contain appropriate role"
end
when 404
raise DfESignInApi::UserNotAuthorisedError
when 401
raise DfESignInApi::InvalidTokenError
when 500
raise StandardError, "Internal Server Error"
end
end

private

def client
@client ||= Client.new.client
end

def endpoint
"/services/#{service_id}/organisations/#{org_id}/users/#{user_id}"
end

def service_id
ENV["DFE_SIGN_IN_CLIENT_ID"]
end

def authorised_role_code
ENV["DFE_SIGN_IN_API_ROLE_CODE"]
end
end
end
3 changes: 3 additions & 0 deletions app/lib/dfe_sign_in_api/invalid_token_error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module DfESignInApi
class InvalidTokenError < StandardError; end
end
3 changes: 3 additions & 0 deletions app/lib/dfe_sign_in_api/user_not_authorised_error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module DfESignInApi
class UserNotAuthorisedError < StandardError; end
end
19 changes: 19 additions & 0 deletions spec/support/system/check_records/authentication_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,24 @@ def given_dsi_auth_is_mocked
email: "[email protected]",
first_name: "Test",
last_name: "User"
},
extra: {
raw_info: {
organisation: {
id: org_id,
}
}
}
}
)

stub_request(
:get,
"#{ENV.fetch("DFE_SIGN_IN_API_BASE_URL")}/services/checkrecordteacher/organisations/#{org_id}/users/123456",
).to_return_json(
status: 200,
body: { "roles" => [{ "code" => ENV["DFE_SIGN_IN_API_ROLE_CODE"] }] },
)
end

def when_i_visit_the_sign_in_page
Expand All @@ -28,5 +43,9 @@ def when_i_visit_the_sign_in_page
def and_click_the_dsi_sign_in_button
click_button "Sign in with DSI"
end

def org_id
"12345678-1234-1234-1234-123456789012"
end
end
end

0 comments on commit 53432b4

Please sign in to comment.