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

Feature: SOFIA-local accounts with login (with optional 2fa) #925

Open
wants to merge 12 commits into
base: staging
Choose a base branch
from
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
source 'https://rubygems.org'

gem 'active_model_otp', '~> 2.3', '>= 2.3.1'
gem 'bcrypt', '~> 3.1.20'
gem 'bootsnap', '~> 1.18.4'
gem 'browser', '~> 5.3.1'
Expand All @@ -17,6 +18,7 @@ gem 'net-imap', require: false
gem 'net-pop', require: false
gem 'net-smtp', require: false
gem 'omniauth', '~> 2.1.2'
gem 'omniauth-identity', '~> 3.0', '>= 3.0.9'
gem 'omniauth-oauth2', '~> 1.8.0'
gem 'omniauth-rails_csrf_protection', '~> 1.0', '>= 1.0.2'
gem 'paper_trail', '~> 16.0.0'
Expand All @@ -29,6 +31,7 @@ gem 'rails', '~> 7.1.0'
gem 'rails-i18n', '~> 7.0.10'
gem 'redis-rails', '~> 5.0.2'
gem 'rest-client', '~> 2.1.0'
gem 'rqrcode', '~> 2.2'
gem 'sassc-rails', '~> 2.1.2'
gem 'sentry-rails', '~> 5.22', '>= 5.22.1'
gem 'sentry-ruby', '~> 5.22', '>= 5.22.1'
Expand Down
15 changes: 15 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ GEM
erubi (~> 1.11)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
active_model_otp (2.3.4)
activemodel
rotp (~> 6.3.0)
activejob (7.1.5.1)
activesupport (= 7.1.5.1)
globalid (>= 0.3.6)
Expand Down Expand Up @@ -113,6 +116,7 @@ GEM
capistrano (>= 3.9.0)
capistrano-bundler
sidekiq (>= 6.0)
chunky_png (1.4.0)
coderay (1.1.3)
colorize (0.8.1)
concurrent-ruby (1.3.4)
Expand Down Expand Up @@ -296,6 +300,9 @@ GEM
hashie (>= 3.4.6)
rack (>= 2.2.3)
rack-protection
omniauth-identity (3.0.9)
bcrypt
omniauth
omniauth-oauth2 (1.8.0)
oauth2 (>= 1.4, < 3)
omniauth (~> 2.0)
Expand Down Expand Up @@ -423,6 +430,11 @@ GEM
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
rexml (3.4.0)
rotp (6.3.0)
rqrcode (2.2.0)
chunky_png (~> 1.0)
rqrcode_core (~> 1.0)
rqrcode_core (1.2.0)
rspec (3.12.0)
rspec-core (~> 3.12.0)
rspec-expectations (~> 3.12.0)
Expand Down Expand Up @@ -574,6 +586,7 @@ PLATFORMS
x86_64-linux

DEPENDENCIES
active_model_otp (~> 2.3, >= 2.3.1)
awesome_print
bcrypt (~> 3.1.20)
better_errors
Expand Down Expand Up @@ -604,6 +617,7 @@ DEPENDENCIES
net-pop
net-smtp
omniauth (~> 2.1.2)
omniauth-identity (~> 3.0, >= 3.0.9)
omniauth-oauth2 (~> 1.8.0)
omniauth-rails_csrf_protection (~> 1.0, >= 1.0.2)
paper_trail (~> 16.0.0)
Expand All @@ -620,6 +634,7 @@ DEPENDENCIES
rb-readline
redis-rails (~> 5.0.2)
rest-client (~> 2.1.0)
rqrcode (~> 2.2)
rspec-rails
rubocop (~> 1.50.2)
rubocop-performance
Expand Down
14 changes: 14 additions & 0 deletions app/assets/stylesheets/application.scss
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,20 @@ a {
}
}

.sofia-account-input {
min-width: 18rem;
}

.qr-code {
max-width: 20rem;
}

main {
// Wanneer er veel activiteiten zijn, gaat de quote onderaan de
// pagina onder de footer, dus voeg padding toe met de hoogte van de footer
padding-bottom: 48px;
}

.footer {
position: absolute;
bottom: 0;
Expand Down
6 changes: 6 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
class ApplicationController < ActionController::Base
include Pundit::Authorization

protect_from_forgery with: :exception, prepend: true

before_action :set_sentry_context
before_action :set_paper_trail_whodunnit
before_action :set_layout_flag
Expand Down Expand Up @@ -34,4 +36,8 @@ def set_layout_flag
@show_navigationbar = true
@show_extras = true
end

def normalize_error_messages(full_messages)
full_messages.map(&:downcase).join(', ')
end
end
57 changes: 56 additions & 1 deletion app/controllers/callbacks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,62 @@
sign_in(:user, user)
redirect_to user.roles.any? ? root_path : user_path(user.id)
else
redirect_to root_path, flash: { error: 'Authentication failed' }
redirect_to root_path, flash: { error: 'Inloggen gefaald.' }

Check warning on line 9 in app/controllers/callbacks_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/callbacks_controller.rb#L9

Added line #L9 was not covered by tests
end
end

def identity
user = User.from_omniauth_inspect(request.env['omniauth.auth'])

if user.persisted?
if user.deactivated
render(json: { state: 'password_prompt', error_message: 'Uw account is gedeactiveerd, dus inloggen is niet mogelijk.' })

Check warning on line 18 in app/controllers/callbacks_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/callbacks_controller.rb#L18

Added line #L18 was not covered by tests
else
check_identity_with_user(user, SofiaAccount.find_by(user_id: user.id))
end
else
render(json: { state: 'password_prompt', error_message: 'Inloggen mislukt. De ingevulde gegevens zijn incorrect.' })

Check warning on line 23 in app/controllers/callbacks_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/callbacks_controller.rb#L23

Added line #L23 was not covered by tests
end
end

def check_identity_with_user(user, sofia_account)
if sofia_account&.otp_enabled
check_identity_with_otp(user, params[:verification_code])
elsif sofia_account
# no OTP enabled
sign_in(:user, user)
render(json: { state: 'logged_in', redirect_url: user.roles.any? ? root_path : user_path(user.id) })
else
# sofia_account does not exist, should not be possible
render(json: { state: 'password_prompt', error_message: 'Inloggen mislukt door een error. Herlaad de pagina en probeer het nog

Check warning on line 36 in app/controllers/callbacks_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/callbacks_controller.rb#L36

Added line #L36 was not covered by tests
een keer. <br/><i>Werkt het na een paar keer proberen nog steeds niet?
Neem dan contact op met de ICT-commissie.</i>' })
end
end

def check_identity_with_otp(user, one_time_password)
if !one_time_password
# OTP code not present, so request it
render(json: { state: 'otp_prompt' })
elsif sofia_account.authenticate_otp(one_time_password)
# OTP code correct
sign_in(:user, user)
render(json: { state: 'logged_in', redirect_url: user.roles.any? ? root_path : user_path(user.id) })

Check warning on line 49 in app/controllers/callbacks_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/callbacks_controller.rb#L48-L49

Added lines #L48 - L49 were not covered by tests
else
# OTP code incorrect
render(json: { state: 'otp_prompt', error_message: 'Inloggen mislukt. De authenticatiecode is incorrect.' })

Check warning on line 52 in app/controllers/callbacks_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/callbacks_controller.rb#L52

Added line #L52 was not covered by tests
end
end

def failure
error_message = 'Inloggen mislukt.'
if request.env['omniauth.error.strategy'].instance_of? OmniAuth::Strategies::Identity
error_message << if request.env['omniauth.error.type'].to_s == 'invalid_credentials'
' De ingevulde gegevens zijn incorrect.'
else
" #{request.env['omniauth.error.type']}"

Check warning on line 62 in app/controllers/callbacks_controller.rb

View check run for this annotation

Codecov / codecov/patch

app/controllers/callbacks_controller.rb#L62

Added line #L62 was not covered by tests
end
end
render(json: { state: 'password_prompt', error_message: })
end
end
Loading
Loading