From 1a7ec5c242521fc05e73781d4bba307e57b855af Mon Sep 17 00:00:00 2001 From: Steve Laing Date: Mon, 4 Sep 2023 11:07:55 +0100 Subject: [PATCH] Call DSI API for user roles on successful authentication The DfE SignIn API will return 404 if the user roles are not found for the specified organisation. --- .env.development | 3 + .../omniauth_callbacks_controller.rb | 18 +++++- app/lib/check_records/dfe_sign_in.rb | 62 +++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/.env.development b/.env.development index 2fdb4a96..d1b70eb1 100644 --- a/.env.development +++ b/.env.development @@ -4,6 +4,9 @@ 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 GOVUK_NOTIFY_API_KEY=override-locally HOSTING_DOMAIN=http://localhost:3000 HOSTING_ENVIRONMENT_NAME=local diff --git a/app/controllers/check_records/omniauth_callbacks_controller.rb b/app/controllers/check_records/omniauth_callbacks_controller.rb index b79ff8ba..28d85bc1 100644 --- a/app/controllers/check_records/omniauth_callbacks_controller.rb +++ b/app/controllers/check_records/omniauth_callbacks_controller.rb @@ -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 + CheckRecords::DfESignIn::Client.new.get_user_access_to_service( + auth.extra.raw_info.organisation.id, + auth.uid + ) + rescue CheckRecords::DfESignIn::Client::InvalidTokenError + return redirect_to check_records_sign_out_path + rescue CheckRecords::DfESignIn::Client::UserNotFoundError + 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 diff --git a/app/lib/check_records/dfe_sign_in.rb b/app/lib/check_records/dfe_sign_in.rb index 884411d6..0c101372 100644 --- a/app/lib/check_records/dfe_sign_in.rb +++ b/app/lib/check_records/dfe_sign_in.rb @@ -1,4 +1,5 @@ require "hosting_environment" +require "jwt" module CheckRecords class DfESignIn @@ -13,5 +14,66 @@ def self.bypass_active? def self.review_app? HostingEnvironment.review? end + + class Client + class InvalidTokenError < StandardError; end + class UserNotFoundError < StandardError; end + + TIMEOUT_IN_SECONDS = 5 + + def get_user_access_to_service(org_id, user_id) + endpoint = "/services/#{service_id}/organisations/#{org_id}/users/#{user_id}" + response = client.get(endpoint) + + handle_response(response) + end + + 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 handle_response(response) + case response.status + when 200 + if response.body["success"] == false + raise InvalidTokenError, response.body["message"] + else + response.body + end + when 404 + raise UserNotFoundError + when 401 + raise InvalidTokenError + when 500 + raise StandardError, "DFE Sign-in API Internal Server Error" + end + end + + def service_id + ENV["DFE_SIGN_IN_CLIENT_ID"] + 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 end