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

Création d'un type d'utilisateur avec des droits académiques #1371

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
6 changes: 6 additions & 0 deletions .env.local
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
APLYPRO_ACADEMIC_OIDC_CLIENT_ID=academic client id
APLYPRO_ACADEMIC_OIDC_CLIENT_SECRET=academic client secret
APLYPRO_ACADEMIC_OIDC_HOST=localhost:1080
APLYPRO_ACADEMIC_OIDC_ISSUER=localhost:3001/realms/aplypro-academic
APLYPRO_ACADEMIC_OIDC_REDIRECT_URI=http://localhost:3000/auth/academic/callback
APLYPRO_ACADEMIC_OIDC_SCOPE=openid
APLYPRO_ASP_FILENAME=aplypro_test_dev
APLYPRO_ASP_FTP_DROP_FOLDER=depot
APLYPRO_ASP_FTP_HOST=asp-mock
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/test.env
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
APLYPRO_ACADEMIC_OIDC_CLIENT_ID=academic client id
APLYPRO_ACADEMIC_OIDC_CLIENT_SECRET=academic client secret
APLYPRO_ACADEMIC_OIDC_HOST=academic ci oidc host
APLYPRO_ACADEMIC_OIDC_ISSUER=academic ci issuer
APLYPRO_ACADEMIC_OIDC_REDIRECT_URI=academic ci redirect uri
APLYPRO_ACADEMIC_OIDC_SCOPE=academic ci scope
APLYPRO_ASP_FILENAME=aplypro_test_ci
APLYPRO_ASP_FTP_DROP_FOLDER=ci depot
APLYPRO_ASP_FTP_HOST=ci-asp-mock
Expand Down
46 changes: 46 additions & 0 deletions app/controllers/academic/application_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# frozen_string_literal: true

module Academic
class ApplicationController < ActionController::Base
include UserLogger
include PageTitle

layout "application"

before_action :authenticate_academic_user!, except: :login
before_action :log_user,
:set_overrides,
:infer_page_title

helper_method :current_user, :current_establishment

def home; end

def login; end

def logout
sign_out(current_academic_user)

redirect_to after_sign_out_path_for(:academic_user)
end

protected

def after_sign_out_path_for(_resource)
new_academic_user_session_path
end

def current_user
current_academic_user
end

def current_establishment
nil
end

def set_overrides
@inhibit_nav = true
@logout_path = :destroy_academic_user_session
end
end
end
11 changes: 11 additions & 0 deletions app/controllers/academic/users_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

module Academic
class UsersController < ApplicationController
before_action :infer_page_title

def select_academy
@academic_user = current_user
end
end
end
7 changes: 7 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class ApplicationController < ActionController::Base
before_action :authenticate_user!,
:log_user,
:redirect_asp_users!,
:redirect_academic_users!,
:check_maintenance,
:check_current_establishment

Expand All @@ -24,6 +25,8 @@ def after_sign_out_path_for(resource_or_scope)
new_user_session_path
when :asp_user
new_asp_user_session_path
when :academic_user
new_academic_user_session_path
end
end

Expand Down Expand Up @@ -52,6 +55,10 @@ def redirect_asp_users!
redirect_to asp_schoolings_path and return if asp_user_signed_in?
end

def redirect_academic_users!
redirect_to academic_home_path and return if academic_user_signed_in?
end

private

def check_current_establishment
Expand Down
23 changes: 13 additions & 10 deletions app/controllers/concerns/developer_oidc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,28 @@ def role(attrs)

def static_info(attrs)
{
"credentials" => {
"token" => "dev token"
credentials: {
token: "dev token"
},
"uid" => attrs["info"]["email"],
"info" => {
"name" => "Developer Account",
"email" => attrs["info"]["email"]
uid: attrs["info"]["email"],
info: {
name: "Developer Account",
email: attrs["info"]["email"]
}
}
end

def provider_info(attrs)
{ "provider" => provider(attrs) }
{ provider: provider(attrs) }
end

def extra_info(attrs)
uai = attrs["info"]["uai"]

info = role(attrs) == :dir ? responsibility_hash(attrs, uai) : authorised_hash(attrs, uai)
if attrs["info"]["uai"].nil?
info = { AplyproAcademieResp: attrs["info"]["academy_code"] }
else
uai = attrs["info"]["uai"]
info = role(attrs) == :dir ? responsibility_hash(attrs, uai) : authorised_hash(attrs, uai)
end

{
extra: {
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/concerns/page_title.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ def infer_page_title(attrs = {})
private

def page_title_key
asp = "asp" if controller_path.include?("asp/")
["pages", "titles", asp, controller_name, action_name].join(".")
namespace = controller_path.split("/").first if controller_path.include?("/")
["pages", "titles", namespace, controller_name, action_name].join(".")
end

def extract_title_data(data)
Expand Down
63 changes: 48 additions & 15 deletions app/controllers/users/omniauth_callbacks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,30 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController

rescue_from IdentityMappers::Errors::Error, ActiveRecord::RecordInvalid, with: :authentication_failure

def asp
@asp_login = true
@asp_user = ASP::User.from_oidc(auth_hash).tap(&:save!)

sign_in(:asp_user, @asp_user)
def developer
oidcize_dev_hash(auth_hash)

redirect_to asp_schoolings_path
oidc
end

def developer
def academic_developer
oidcize_dev_hash(auth_hash)

academic
end

def masa
oidc
end

def fim
oidc
end

def oidc
parse_identity

@user.save!
@user = User.from_oidc(auth_hash).tap(&:save!)

add_auth_breadcrumb(data: { user_id: @user.id }, message: "Successfully parsed user")

Expand All @@ -42,12 +47,36 @@ def oidc
choose_redirect_page!
end

def masa
oidc
def academic # rubocop:disable Metrics/AbcSize
parse_identity

@academic_login = true
@academic_user = Academic::User.from_oidc(auth_hash).tap(&:save!)

add_auth_breadcrumb(data: { user_id: @academic_user.id }, message: "Successfully parsed academic user")

@academies = @mapper.aplypro_academies

raise IdentityMappers::Errors::NoLimitedAccessError if @academies.empty?

sign_in(:academic_user, @academic_user)

if @academies.many?
redirect_to academic_user_select_academy_path(@academic_user)
else
@academic_user.update!(selected_academy: @academies.first)

redirect_to academic_home_path, notice: t("auth.success")
end
end

def fim
oidc
def asp
@asp_login = true
@asp_user = ASP::User.from_oidc(auth_hash).tap(&:save!)

sign_in(:asp_user, @asp_user)

redirect_to asp_schoolings_path
end

def failure
Expand All @@ -63,6 +92,8 @@ def authentication_failure(error)

if defined? @asp_login
fail_asp_user
elsif defined? @academic_login
fail_academic_user
else
fail_user
end
Expand All @@ -78,6 +109,10 @@ def fail_asp_user
redirect_to new_asp_user_session_path
end

def fail_academic_user
redirect_to new_academic_user_session_path
end

private

def check_access!
Expand All @@ -96,10 +131,8 @@ def parse_identity
data = auth_hash
raw = data.extra.raw_info

@user = User.from_oidc(data)

@mapper = case data.provider.to_sym
when :fim
when :fim, :academic
IdentityMappers::Fim.new(raw)
when :masa
IdentityMappers::Cas.new(raw)
Expand Down
32 changes: 32 additions & 0 deletions app/models/academic/user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

module Academic
class User < User
devise :authenticatable

validates :uid, :provider, :name, :email, presence: true

class << self
# ideally all these methods would live in some OIDC-factory but I
# can't figure out a pattern I like quite yet
def from_oidc(attrs)
# we can't use find_or_create because a bunch of fields are mandatory
User.find_or_initialize_by(uid: attrs["uid"], provider: attrs["provider"]).tap do |user|
user.token = attrs["credentials"]["token"]
user.secret = "nope"
user.name = attrs["info"]["name"]
user.email = attrs["info"]["email"]
user.oidc_attributes = attrs
end
end
end

def academies
establishments.distinct.pluck(:academy_code)
end

def to_s
name
end
end
end
4 changes: 4 additions & 0 deletions app/models/concerns/identity_mappers/fim.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,9 @@ def responsibility_uais
def aplypro_responsibilities
Array(attributes["AplyproResp"]).compact
end

def aplypro_academies
Array(attributes["AplyproAcademieResp"]).compact
end
end
end
3 changes: 3 additions & 0 deletions app/views/academic/application/home.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.home.fr-container
.fr-grid-row.fr-grid-row--center
%p Bonjour
14 changes: 14 additions & 0 deletions app/views/academic/application/login.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.fr-grid-row.fr-grid-row--gutters#login-page
.fr-col-md-6.fr-col-12
= render 'shared/connection_panel',
name: t("omniauth.fim.title"),
description: t("omniauth.fim.description"),
button: t("omniauth.fim.button"),
path: "/auth/academic"

.fr-col-md-6.fr-col-12
= render 'shared/connection_panel',
name: t("omniauth.developer.title"),
description: t("omniauth.developer.description"),
button: t("omniauth.developer.button"),
path: "/auth/academic_developer"
12 changes: 12 additions & 0 deletions app/views/academic/users/select_academy.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.fr-grid-row
.fr-col-md-7
%p
Veuillez sélectionner l'académie que vous désirez piloter dans la liste ci-dessous.

.fr-select-group.fr-col-md-7
= form_with url: academic_user_select_academy_path, builder: DsfrFormBuilder do |form|
.fr-input-group
= form.label :selected_academy, "Académie", class: 'fr-label'
= form.select :selected_academy, @academic_user.academies, {}, { class: 'fr-select' }

= form.submit "Continuez avec cette académie", class: 'fr-btn'
2 changes: 1 addition & 1 deletion app/views/asp/application/login.html.haml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.fr-grid-row.fr-grid-row--gutters#login-page
.fr-col-md-6.fr-col-12
= render '/home/connection_panel',
= render '/shared/connection_panel',
name: "Agence de Services et Paiements (ASP)",
description: "Vous êtes un agent de l'ASP",
button: "Connexion",
Expand Down
4 changes: 2 additions & 2 deletions app/views/home/login.html.haml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
.fr-grid-row.fr-grid-row--gutters#login-page
- User::OMNIAUTH_PROVIDERS.each do |provider|
.fr-col-md-6.fr-col-12
= render 'connection_panel',
= render 'shared/connection_panel',
name: t("omniauth.#{provider}.title"),
description: t("omniauth.#{provider}.description"),
button: t("omniauth.#{provider}.button"),
path: "/users/auth/#{provider}"

.fr-col-md-6.fr-col-12
= render 'connection_panel',
= render 'shared/connection_panel',
name: t("omniauth.mer.title"),
description: t("omniauth.mer.description"),
button: t("omniauth.mer.button"),
Expand Down
Loading
Loading