Skip to content

Commit

Permalink
Merge pull request #63 from DFE-Digital/98-integrate-dfe-signin
Browse files Browse the repository at this point in the history
[98] Integrate DfE sign in
  • Loading branch information
steventux authored Aug 1, 2023
2 parents 2906cac + a999910 commit 8d36b3b
Show file tree
Hide file tree
Showing 28 changed files with 437 additions and 8 deletions.
6 changes: 6 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
BYPASS_DSI=true
HOSTING_ENVIRONMENT=local
DFE_SIGN_IN_CLIENT_ID=checkchildrensbarredlist
DFE_SIGN_IN_REDIRECT_URL=http://localhost:3000/auth/dfe/callback
DFE_SIGN_IN_SECRET=override-locally
DFE_SIGN_IN_ISSUER=https://dev-oidc.signin.education.gov.uk
6 changes: 6 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ gem "sidekiq"
# Feature switching
gem "govuk_feature_flags", github: "DFE-Digital/govuk_feature_flags", branch: "main"

# Authentication
gem "omniauth-oauth2", "~> 1.8"
gem "omniauth_openid_connect"
gem "omniauth-rails_csrf_protection"

group :development, :test do
gem "debug", platforms: %i[mri mingw x64_mingw]
end
Expand All @@ -65,6 +70,7 @@ group :test do
end

group :test, :development do
gem "dotenv-rails"
gem "factory_bot_rails"
gem "launchy"
gem "pry-byebug"
Expand Down
88 changes: 88 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,12 @@ GEM
tzinfo (~> 2.0)
addressable (2.8.4)
public_suffix (>= 2.0.2, < 6.0)
aes_key_wrap (1.1.0)
ast (2.4.2)
attr_required (1.0.1)
backport (1.2.0)
benchmark (0.2.1)
bindata (2.4.15)
bindex (0.8.1)
bootsnap (1.16.0)
msgpack (~> 1.2)
Expand Down Expand Up @@ -107,13 +110,23 @@ GEM
irb (>= 1.5.0)
reline (>= 0.3.1)
diff-lcs (1.5.0)
dotenv (2.8.1)
dotenv-rails (2.8.1)
dotenv (= 2.8.1)
railties (>= 3.2)
e2mmap (0.1.0)
erubi (1.12.0)
factory_bot (6.2.1)
activesupport (>= 5.0.0)
factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0)
railties (>= 5.0.0)
faraday (2.7.10)
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-follow_redirects (0.3.0)
faraday (>= 1, < 3)
faraday-net_http (3.0.2)
ferrum (0.13)
addressable (~> 2.5)
concurrent-ruby (~> 1.1)
Expand All @@ -134,6 +147,7 @@ GEM
temple (>= 0.8.2)
thor
tilt
hashie (5.0.0)
html-attributes-utils (1.0.2)
activesupport (>= 6.1.4.4)
i18n (1.14.1)
Expand All @@ -145,6 +159,13 @@ GEM
jsbundling-rails (1.1.2)
railties (>= 6.0.0)
json (2.6.3)
json-jwt (1.16.3)
activesupport (>= 4.2)
aes_key_wrap
bindata
faraday (~> 2.0)
faraday-follow_redirects
jwt (2.7.1)
kramdown (2.4.0)
rexml
kramdown-parser-gfm (1.1.0)
Expand All @@ -167,6 +188,7 @@ GEM
mini_portile2 (2.8.4)
minitest (5.19.0)
msgpack (1.7.1)
multi_xml (0.6.0)
net-imap (0.3.6)
date
net-protocol
Expand All @@ -180,7 +202,40 @@ GEM
nokogiri (1.15.3)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
oauth2 (2.0.9)
faraday (>= 0.17.3, < 3.0)
jwt (>= 1.0, < 3.0)
multi_xml (~> 0.5)
rack (>= 1.2, < 4)
snaky_hash (~> 2.0)
version_gem (~> 1.1)
okcomputer (1.18.4)
omniauth (2.1.1)
hashie (>= 3.4.6)
rack (>= 2.2.3)
rack-protection
omniauth-oauth2 (1.8.0)
oauth2 (>= 1.4, < 3)
omniauth (~> 2.0)
omniauth-rails_csrf_protection (1.0.1)
actionpack (>= 4.2)
omniauth (~> 2.0)
omniauth_openid_connect (0.7.1)
omniauth (>= 1.9, < 3)
openid_connect (~> 2.2)
openid_connect (2.2.0)
activemodel
attr_required (>= 1.0.0)
faraday (~> 2.0)
faraday-follow_redirects
json-jwt (>= 1.16)
net-smtp
rack-oauth2 (~> 2.2)
swd (~> 2.0)
tzinfo
validate_email
validate_url
webfinger (~> 2.0)
pagy (6.0.4)
parallel (1.23.0)
parser (3.2.2.3)
Expand All @@ -204,6 +259,15 @@ GEM
nio4r (~> 2.0)
racc (1.7.1)
rack (2.2.7)
rack-oauth2 (2.2.0)
activesupport
attr_required
faraday (~> 2.0)
faraday-follow_redirects
json-jwt (>= 1.11.0)
rack (>= 2.1.0)
rack-protection (3.0.6)
rack
rack-test (2.1.0)
rack (>= 1.3)
rails (7.0.6)
Expand Down Expand Up @@ -301,6 +365,7 @@ GEM
rubocop-capybara (~> 2.17)
rubocop-factory_bot (~> 2.22)
ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5)
sentry-rails (5.10.0)
railties (>= 5.0)
sentry-ruby (~> 5.10.0)
Expand All @@ -313,6 +378,9 @@ GEM
connection_pool (>= 2.3.0)
rack (>= 2.2.4)
redis-client (>= 0.14.0)
snaky_hash (2.0.1)
hashie
version_gem (~> 1.1, >= 1.1.1)
solargraph (0.49.0)
backport (~> 1.2)
benchmark
Expand All @@ -332,6 +400,11 @@ GEM
solargraph-rails (1.1.0)
activesupport
solargraph
swd (2.0.2)
activesupport (>= 3)
attr_required (>= 0.0.5)
faraday (~> 2.0)
faraday-follow_redirects
syntax_tree (6.1.1)
prettier_print (>= 1.2.0)
syntax_tree-haml (4.0.3)
Expand All @@ -349,6 +422,13 @@ GEM
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.4.2)
validate_email (0.1.6)
activemodel (>= 3.0)
mail (>= 2.2.5)
validate_url (1.0.15)
activemodel (>= 3.0.0)
public_suffix
version_gem (1.1.3)
view_component (3.3.0)
activesupport (>= 5.2.0, < 8.0)
concurrent-ruby (~> 1.0)
Expand All @@ -358,6 +438,10 @@ GEM
activemodel (>= 6.0.0)
bindex (>= 0.4.0)
railties (>= 6.0.0)
webfinger (2.1.2)
activesupport
faraday (~> 2.0)
faraday-follow_redirects
webrick (1.8.1)
websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0)
Expand All @@ -376,13 +460,17 @@ DEPENDENCIES
cssbundling-rails
cuprite
debug
dotenv-rails
factory_bot_rails
govuk-components
govuk_design_system_formbuilder
govuk_feature_flags!
jsbundling-rails
launchy
okcomputer
omniauth-oauth2 (~> 1.8)
omniauth-rails_csrf_protection
omniauth_openid_connect
pg (~> 1.1)
prettier_print
propshaft
Expand Down
34 changes: 32 additions & 2 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
class ApplicationController < ActionController::Base
default_form_builder(GOVUKDesignSystemFormBuilder::FormBuilder)

before_action :authenticate, unless: -> { FeatureFlags::FeatureFlag.active?("service_open") }
before_action :http_basic_authenticate, unless: -> { FeatureFlags::FeatureFlag.active?(:service_open) }
before_action :authenticate_dsi_user!, if: -> { FeatureFlags::FeatureFlag.active?(:service_open) }
before_action :handle_expired_session!, if: -> { FeatureFlags::FeatureFlag.active?(:service_open) }

def authenticate
def http_basic_authenticate
valid_credentials = [
{
username: ENV.fetch("SUPPORT_USERNAME", "support"),
Expand All @@ -15,4 +17,32 @@ def authenticate
valid_credentials.include?({ username:, password: })
end
end

def current_dsi_user
@current_dsi_user ||= DsiUser.find(session[:dsi_user_id]) if session[:dsi_user_id]
end
helper_method :current_dsi_user

def authenticate_dsi_user!
if current_dsi_user.blank?
flash[:warning] = "You need to sign in to continue."
redirect_to sign_in_path
end
end

def dsi_user_signed_in?
!!current_dsi_user
end

def handle_expired_session!
if session[:dsi_user_session_expiry].nil?
redirect_to sign_out_path
return
end

if Time.zone.at(session[:dsi_user_session_expiry]).past?
flash[:warning] = "Your session has expired. Please sign in again."
redirect_to sign_out_path
end
end
end
16 changes: 16 additions & 0 deletions app/controllers/omniauth_callbacks_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

class OmniauthCallbacksController < ApplicationController
protect_from_forgery except: :dfe_bypass
skip_before_action :authenticate_dsi_user!
skip_before_action :handle_expired_session!

def dfe
@dsi_user = DsiUser.create_or_update_from_dsi(request.env["omniauth.auth"])
session[:dsi_user_id] = @dsi_user.id
session[:dsi_user_session_expiry] = 2.hours.from_now.to_i

redirect_to root_path
end
alias_method :dfe_bypass, :dfe
end
7 changes: 7 additions & 0 deletions app/controllers/sign_in_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class SignInController < ApplicationController
skip_before_action :authenticate_dsi_user!
skip_before_action :handle_expired_session!

def new
end
end
7 changes: 7 additions & 0 deletions app/controllers/sign_out_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class SignOutController < ApplicationController
skip_before_action :handle_expired_session!

def new
session[:dsi_user_id] = nil if dsi_user_signed_in?
end
end
7 changes: 7 additions & 0 deletions app/lib/dfe_sign_in.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require "hosting_environment"

class DfESignIn
def self.bypass?
HostingEnvironment.test_environment? && ENV["BYPASS_DSI"] == "true"
end
end
21 changes: 21 additions & 0 deletions app/lib/hosting_environment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true
module HostingEnvironment
TEST_ENVIRONMENTS = %w[local test preproduction review].freeze
PRODUCTION_URL = I18n.t("service.url")

def self.host
ENV.fetch("HOSTING_DOMAIN")
end

def self.environment_name
ENV.fetch("HOSTING_ENVIRONMENT", "unknown-environment")
end

def self.test_environment?
TEST_ENVIRONMENTS.include?(HostingEnvironment.environment_name)
end

def self.production?
environment_name == "production"
end
end
13 changes: 13 additions & 0 deletions app/models/dsi_user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class DsiUser < ApplicationRecord
def self.create_or_update_from_dsi(dsi_payload)
dsi_user = find_or_initialize_by(email: dsi_payload.info.fetch(:email))

dsi_user.update!(
first_name: dsi_payload.info.first_name,
last_name: dsi_payload.info.last_name,
uid: dsi_payload.uid
)

dsi_user
end
end
8 changes: 7 additions & 1 deletion app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@
<%= govuk_skip_link %>
<%= govuk_header(service_name: t("service.name")) %>
<%= govuk_header(service_name: t("service.name")) do |header|
if current_dsi_user
header.with_navigation_item(href: "/sign-out", text: "Sign out")
else
header.with_navigation_item(href: "/sign-in", text: "Sign in")
end
end %>

<div class="govuk-width-container">
<%= govuk_phase_banner(tag: { text: "Beta" }) do %>
Expand Down
26 changes: 26 additions & 0 deletions app/views/sign_in/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-l">Sign in</h1>

<p class="govuk-body">
You can use this service if you’re:
</p>
<ul class="govuk-list govuk-list--bullet">
<li>
waiting for the result of a DBS check for a new employee
</li>
<li>
checking the status of an employee who worked in a school or college less than 3 months ago
</li>
</ul>

<%= form_with url: (DfESignIn.bypass? ? "/auth/developer" : "/auth/dfe") do |f| %>
<button type="submit" class="govuk-button govuk-button--start">
<%= DfESignIn.bypass? ? "Sign in with DSI bypass" : "Sign in" %>
<svg class="govuk-button__start-icon" xmlns="http://www.w3.org/2000/svg" width="17.5" height="19" viewBox="0 0 33 40" aria-hidden="true" focusable="false">
<path fill="currentColor" d="M0 0h13l20 20-20 20H0l20-20z"></path>
</svg>
</button>
<% end %>
</div>
</div>
7 changes: 7 additions & 0 deletions app/views/sign_out/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-xl">You are now signed out</h1>

<%= govuk_button_link_to "Continue", root_path %>
</div>
</div>
Loading

0 comments on commit 8d36b3b

Please sign in to comment.