Skip to content

Commit 514b044

Browse files
Feature: SOFIA-local accounts with login (with optional 2fa) (#925)
* added local accounts with login and 2fa * added tests for logging in * added translation for forgot password page * fixed some stuff * finished tests * rubocop autofixes * fixes css linting warning * merge? * fixed schema * fixed bunch of rubocop linting * Fixed all rubocop linting errors * fix tests * change check for association * improve the lint * improve schema.rb * change schema to use new migration date * use build in way for checking mails * change Null constraints * make majority of coderabbits its suggestions * fix some more code rabbit suggeestions * fix tests and lint * fixx tests --------- Co-authored-by: Lodewiges <[email protected]>
1 parent e4ec396 commit 514b044

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2936
-126
lines changed

Gemfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
source 'https://rubygems.org'
22

3+
gem 'active_model_otp', '~> 2.3', '>= 2.3.1'
34
gem 'bcrypt', '~> 3.1.20'
45
gem 'bootsnap', '~> 1.19.0'
56
gem 'browser', '~> 6.2.0'
@@ -19,6 +20,7 @@ gem 'net-imap', '~> 0.5.12'
1920
gem 'net-pop', '~> 0.1.2'
2021
gem 'net-smtp', '~> 0.5.1'
2122
gem 'omniauth', '~> 2.1.4'
23+
gem 'omniauth-identity', '~> 3.1', '>= 3.1.2'
2224
gem 'omniauth-oauth2', '~> 1.8.0'
2325
gem 'omniauth-rails_csrf_protection', '~> 1.0'
2426
gem 'paper_trail', '~> 17.0.0'
@@ -31,6 +33,7 @@ gem 'rails', '~> 7.2.3'
3133
gem 'rails-i18n', '~> 7.0.10'
3234
gem 'redis', '~> 5.0'
3335
gem 'rest-client', '~> 2.1.0'
36+
gem 'rqrcode', '~> 2.2'
3437
gem 'sentry-rails', '~> 6.1', '>= 6.1.1'
3538
gem 'sentry-ruby', '~> 6.1', '>= 6.1.1'
3639
gem 'sentry-sidekiq', '~> 6.1', '>= 6.1.1'

Gemfile.lock

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ GEM
4747
erubi (~> 1.11)
4848
rails-dom-testing (~> 2.2)
4949
rails-html-sanitizer (~> 1.6)
50+
active_model_otp (2.3.4)
51+
activemodel
52+
rotp (~> 6.3.0)
5053
activejob (7.2.3)
5154
activesupport (= 7.2.3)
5255
globalid (>= 0.3.6)
@@ -111,10 +114,11 @@ GEM
111114
capistrano-bundler
112115
sidekiq (>= 7.0)
113116
cgi (0.5.0)
117+
chunky_png (1.4.0)
114118
coderay (1.1.3)
115119
colorize (1.1.0)
116120
concurrent-ruby (1.3.5)
117-
connection_pool (2.5.4)
121+
connection_pool (2.5.5)
118122
crass (1.0.6)
119123
cssbundling-rails (1.4.3)
120124
railties (>= 6.0.0)
@@ -278,6 +282,7 @@ GEM
278282
multi_json (1.17.0)
279283
multi_xml (0.7.2)
280284
bigdecimal (~> 3.1)
285+
mutex_m (0.3.0)
281286
nenv (0.3.0)
282287
net-http (0.6.0)
283288
uri
@@ -315,6 +320,11 @@ GEM
315320
logger
316321
rack (>= 2.2.3)
317322
rack-protection
323+
omniauth-identity (3.1.5)
324+
bcrypt (~> 3.1)
325+
mutex_m (~> 0.1)
326+
omniauth (>= 1)
327+
version_gem (~> 1.1, >= 1.1.9)
318328
omniauth-oauth2 (1.8.0)
319329
oauth2 (>= 1.4, < 3)
320330
omniauth (~> 2.0)
@@ -413,7 +423,7 @@ GEM
413423
rb-inotify (0.11.1)
414424
ffi (~> 1.0)
415425
rb-readline (0.5.5)
416-
rdoc (6.15.1)
426+
rdoc (6.16.1)
417427
erb
418428
psych (>= 4.0.0)
419429
tsort
@@ -434,7 +444,12 @@ GEM
434444
http-cookie (>= 1.0.2, < 2.0)
435445
mime-types (>= 1.16, < 4.0)
436446
netrc (~> 0.8)
447+
rotp (6.3.0)
437448
rouge (4.6.1)
449+
rqrcode (2.2.0)
450+
chunky_png (~> 1.0)
451+
rqrcode_core (~> 1.0)
452+
rqrcode_core (1.2.0)
438453
rspec (3.13.2)
439454
rspec-core (~> 3.13.0)
440455
rspec-expectations (~> 3.13.0)
@@ -444,7 +459,7 @@ GEM
444459
rspec-expectations (3.13.5)
445460
diff-lcs (>= 1.2.0, < 2.0)
446461
rspec-support (~> 3.13.0)
447-
rspec-mocks (3.13.6)
462+
rspec-mocks (3.13.7)
448463
diff-lcs (>= 1.2.0, < 2.0)
449464
rspec-support (~> 3.13.0)
450465
rspec-rails (8.0.2)
@@ -594,6 +609,7 @@ PLATFORMS
594609
x86_64-linux
595610

596611
DEPENDENCIES
612+
active_model_otp (~> 2.3, >= 2.3.1)
597613
awesome_print (~> 1.9.2)
598614
bcrypt (~> 3.1.20)
599615
better_errors (~> 2.10.1)
@@ -626,6 +642,7 @@ DEPENDENCIES
626642
net-pop (~> 0.1.2)
627643
net-smtp (~> 0.5.1)
628644
omniauth (~> 2.1.4)
645+
omniauth-identity (~> 3.1, >= 3.1.2)
629646
omniauth-oauth2 (~> 1.8.0)
630647
omniauth-rails_csrf_protection (~> 1.0)
631648
paper_trail (~> 17.0.0)
@@ -642,6 +659,7 @@ DEPENDENCIES
642659
rb-readline (~> 0.5.5)
643660
redis (~> 5.0)
644661
rest-client (~> 2.1.0)
662+
rqrcode (~> 2.2)
645663
rspec-rails (~> 8.0.2)
646664
rubocop (~> 1.81.7)
647665
rubocop-factory_bot (~> 2.28.0)

app/assets/stylesheets/application.scss

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,20 @@ a {
8181
}
8282
}
8383

84+
.sofia-account-input {
85+
min-width: 18rem;
86+
}
87+
88+
.qr-code {
89+
max-width: 20rem;
90+
}
91+
92+
main {
93+
// Wanneer er veel activiteiten zijn, gaat de quote onderaan de
94+
// pagina onder de footer, dus voeg padding toe met de hoogte van de footer
95+
padding-bottom: 48px;
96+
}
97+
8498
.btn-login {
8599
width: 100%
86100
}

app/controllers/application_controller.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
class ApplicationController < ActionController::Base
22
include Pundit::Authorization
33

4+
protect_from_forgery with: :exception, prepend: true
5+
46
before_action :set_sentry_context
57
before_action :set_paper_trail_whodunnit
68
before_action :set_layout_flag
@@ -34,4 +36,8 @@ def set_layout_flag
3436
@show_navigationbar = true
3537
@show_extras = true
3638
end
39+
40+
def normalize_error_messages(full_messages)
41+
full_messages.map(&:downcase).join(', ')
42+
end
3743
end

app/controllers/callbacks_controller.rb

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,66 @@ class CallbacksController < Devise::OmniauthCallbacksController
22
def amber_oauth2
33
user = User.from_omniauth(request.env['omniauth.auth'])
44

5-
if user.persisted?
5+
if user&.persisted?
66
sign_in(:user, user)
77
redirect_to user.roles.any? ? root_path : user_path(user.id)
88
else
9-
redirect_to root_path, flash: { error: 'Authentication failed' }
9+
redirect_to root_path, flash: { error: 'Inloggen gefaald.' }
1010
end
1111
end
12+
13+
def identity
14+
user = User.from_omniauth_inspect(request.env['omniauth.auth'])
15+
16+
if user&.persisted?
17+
if user.deactivated
18+
render(json: { state: 'password_prompt', error_message: 'Uw account is gedeactiveerd, dus inloggen is niet mogelijk.' })
19+
else
20+
check_identity_with_user(user, SofiaAccount.find_by(user_id: user.id))
21+
end
22+
else
23+
render(json: { state: 'password_prompt', error_message: 'Inloggen mislukt. De ingevulde gegevens zijn incorrect.' })
24+
end
25+
end
26+
27+
def check_identity_with_user(user, sofia_account)
28+
if sofia_account&.otp_enabled
29+
check_identity_with_otp(sofia_account, user)
30+
elsif sofia_account
31+
# no OTP enabled
32+
sign_in(:user, user)
33+
render(json: { state: 'logged_in', redirect_url: user.roles.any? ? root_path : user_path(user.id) })
34+
else
35+
# sofia_account does not exist, should not be possible
36+
render(json: { state: 'password_prompt', error_message: 'Inloggen mislukt door een error. Herlaad de pagina en probeer het nog
37+
een keer. <br/><i>Werkt het na een paar keer proberen nog steeds niet?
38+
Neem dan contact op met de ICT-commissie.</i>' })
39+
end
40+
end
41+
42+
def check_identity_with_otp(sofia_account, user)
43+
if params[:verification_code].blank?
44+
# OTP code not present, so request it
45+
render(json: { state: 'otp_prompt' })
46+
elsif sofia_account.authenticate_otp(params[:verification_code])
47+
# OTP code correct
48+
sign_in(:user, user)
49+
render(json: { state: 'logged_in', redirect_url: user.roles.any? ? root_path : user_path(user.id) })
50+
else
51+
# OTP code incorrect
52+
render(json: { state: 'otp_prompt', error_message: 'Inloggen mislukt. De authenticatiecode is incorrect.' })
53+
end
54+
end
55+
56+
def failure
57+
error_message = 'Inloggen mislukt.'
58+
if request.env['omniauth.error.strategy'].instance_of? OmniAuth::Strategies::Identity
59+
error_message << if request.env['omniauth.error.type'].to_s == 'invalid_credentials'
60+
' De ingevulde gegevens zijn incorrect.'
61+
else
62+
' Er is een onverwachte fout opgetreden.'
63+
end
64+
end
65+
render(json: { state: 'password_prompt', error_message: })
66+
end
1267
end

0 commit comments

Comments
 (0)