From f3103eac0f7c16029020d868a918cc538df41284 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 29 Jun 2022 15:14:22 +0200 Subject: [PATCH 01/63] Add registration only for institutional users setting --- app/models/course.rb | 2 +- config/locales/models/en.yml | 1 + config/locales/models/nl.yml | 1 + config/locales/views/courses/en.yml | 2 ++ config/locales/views/courses/nl.yml | 2 ++ 5 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/models/course.rb b/app/models/course.rb index ccdcb94043..9a4265308a 100644 --- a/app/models/course.rb +++ b/app/models/course.rb @@ -55,7 +55,7 @@ class Course < ApplicationRecord has_many :course_labels, dependent: :destroy enum visibility: { visible_for_all: 0, visible_for_institution: 1, hidden: 2 } - enum registration: { open_for_all: 0, open_for_institution: 1, closed: 2 } + enum registration: { open_for_all: 3, open_for_institutional_users: 0, open_for_institution: 1, closed: 2 } # TODO: Remove and use activities? has_many :content_pages, diff --git a/config/locales/models/en.yml b/config/locales/models/en.yml index 3ef3a83b32..623d3b5a85 100644 --- a/config/locales/models/en.yml +++ b/config/locales/models/en.yml @@ -55,6 +55,7 @@ en: open_for_all: Open for everyone open_for_institution: Only open for members of the configured institution closed: Closed + open_for_institutional_users: Open for all users that belong to an institution institution: Institution activity: name: Name diff --git a/config/locales/models/nl.yml b/config/locales/models/nl.yml index 16f8ef964e..6a7335b43c 100644 --- a/config/locales/models/nl.yml +++ b/config/locales/models/nl.yml @@ -56,6 +56,7 @@ nl: open_for_all: Open voor iedereen open_for_institution: Enkel open voor gebruikers van de ingestelde onderwijsinstelling closed: Gesloten + open_for_institutional_users: Open voor alle gebruikers die tot een onderwijsinstelling behoren institution: Onderwijsinstelling activity: name: Naam diff --git a/config/locales/views/courses/en.yml b/config/locales/views/courses/en.yml index 499de39501..1a5d15021c 100644 --- a/config/locales/views/courses/en.yml +++ b/config/locales/views/courses/en.yml @@ -17,6 +17,7 @@ en: registration-open_for_all-help_html: Anyone can register for this course. registration-open_for_institution-help_html: Only members of the configured institution can register for this course. registration-closed-help_html: All new registrations are disabled. + registration-open_for_institutional_users-help_html: Anyone that belongs to an institution can register for this course. description-help_html: A description is optional. Markdown formatting can be used. visibility-label: For whom is this course visible? registration-label: Who can register for this course? @@ -27,6 +28,7 @@ en: registration-open_for_all_html: Everyone registration-open_for_institution_html: Only users of the configured institution registration-closed_html: Nobody + registration-open_for_institutional_users_html: Everyone that belongs to an institution no_institution: "No institution" copy_admins: "Course admins" copy_options: "Copy options" diff --git a/config/locales/views/courses/nl.yml b/config/locales/views/courses/nl.yml index 527d887c87..ae4308b4cb 100644 --- a/config/locales/views/courses/nl.yml +++ b/config/locales/views/courses/nl.yml @@ -17,6 +17,7 @@ nl: registration-open_for_all-help_html: Iedereen kan zich voor deze cursus registreren. registration-open_for_institution-help_html: Enkel gebruikers van de ingestelde onderwijsinstelling kunnen zich voor deze cursus registreren. registration-closed-help_html: Er zijn geen nieuwe registraties mogelijk. + registration-open_for_institutional_users-help_html: Iedereen die tot een instelling behoort kan zich voor deze cursus registreren. description-help_html: Een beschrijving is optioneel en kan in Markdown geschreven worden. visibility-label: Voor wie is deze cursus zichtbaar? registration-label: Wie kan zich registreren voor deze cursus? @@ -27,6 +28,7 @@ nl: registration-open_for_all_html: Iedereen registration-open_for_institution_html: Enkel gebruikers van de ingestelde onderwijsinstelling registration-closed_html: Niemand + registration-open_for_institutional_users_html: Alle gebruikers die tot een onderwijsinstelling behoren no_institution: "Geen onderwijsinstelling" copy_admins: "Cursusbeheerders" copy_options: "Opties voor het kopiëren" From 6510b259713c9498cff368e892138ae655225eb0 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 29 Jun 2022 15:26:48 +0200 Subject: [PATCH 02/63] Add institutional users visibility setting --- app/models/course.rb | 2 +- config/locales/models/en.yml | 1 + config/locales/models/nl.yml | 1 + config/locales/views/courses/en.yml | 2 ++ config/locales/views/courses/nl.yml | 2 ++ 5 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/models/course.rb b/app/models/course.rb index 9a4265308a..cfdc0f5fb2 100644 --- a/app/models/course.rb +++ b/app/models/course.rb @@ -54,7 +54,7 @@ class Course < ApplicationRecord has_many :course_labels, dependent: :destroy - enum visibility: { visible_for_all: 0, visible_for_institution: 1, hidden: 2 } + enum visibility: { visible_for_all: 3, visible_for_institutional_users: 0, visible_for_institution: 1, hidden: 2 } enum registration: { open_for_all: 3, open_for_institutional_users: 0, open_for_institution: 1, closed: 2 } # TODO: Remove and use activities? diff --git a/config/locales/models/en.yml b/config/locales/models/en.yml index 623d3b5a85..8e116f78fb 100644 --- a/config/locales/models/en.yml +++ b/config/locales/models/en.yml @@ -49,6 +49,7 @@ en: featured: Featured course visibilities: visible_for_all: Visible for everyone + visible_for_institutional_users: Visible for all users that belong to an institution visible_for_institution: Only visible for members of the configured institution hidden: Hidden registrations: diff --git a/config/locales/models/nl.yml b/config/locales/models/nl.yml index 6a7335b43c..25fc850125 100644 --- a/config/locales/models/nl.yml +++ b/config/locales/models/nl.yml @@ -50,6 +50,7 @@ nl: featured: Uitgelichte cursus visibilities: visible_for_all: Zichtbaar voor iedereen + visible_for_institutional_users: Zichtbaar voor alle gebruikers die tot een onderwijsinstelling behoren visible_for_institution: Zichtbaar voor gebruikers van de ingestelde onderwijsinstelling hidden: Verborgen registrations: diff --git a/config/locales/views/courses/en.yml b/config/locales/views/courses/en.yml index 1a5d15021c..d1c758b021 100644 --- a/config/locales/views/courses/en.yml +++ b/config/locales/views/courses/en.yml @@ -14,6 +14,7 @@ en: visibility-visible_for_all-help_html: This course is part of the public course overview, and everyone can see the contents. visibility-visible_for_institution-help_html: This course is only accessible for users of the configured institution and via the direct link below. visibility-hidden-help_html: This course will not be part of the public course overview and is only accessible via the direct link below. + visibility-visible_for_institutional_users-help_html: This course is accessible for anyone that belongs to an institution. registration-open_for_all-help_html: Anyone can register for this course. registration-open_for_institution-help_html: Only members of the configured institution can register for this course. registration-closed-help_html: All new registrations are disabled. @@ -25,6 +26,7 @@ en: visibility-visible_for_all_html: Everyone visibility-visible_for_institution_html: Only users of the configured institution visibility-hidden_html: Only registered users + visibility-visible_for_institutional_users_html: Everyone that belongs to an institution registration-open_for_all_html: Everyone registration-open_for_institution_html: Only users of the configured institution registration-closed_html: Nobody diff --git a/config/locales/views/courses/nl.yml b/config/locales/views/courses/nl.yml index ae4308b4cb..2f9f16ec5b 100644 --- a/config/locales/views/courses/nl.yml +++ b/config/locales/views/courses/nl.yml @@ -14,6 +14,7 @@ nl: visibility-visible_for_all-help_html: Deze cursus wordt opgenomen in het publieke cursusoverzicht en iedereen kan de inhoud bekijken. visibility-visible_for_institution-help_html: Deze cursus is enkel toegankelijk voor gebruikers van de ingestelde onderwijsinstelling en via onderstaande directe link. visibility-hidden-help_html: Deze cursus wordt niet opgenomen in het publieke cursusoverzicht en is enkel toegankelijk via onderstaande directe link. + visibility-visible_for_institutional_users-help_html: Iedereen die tot een instelling behoort kan deze cursus bekijken. registration-open_for_all-help_html: Iedereen kan zich voor deze cursus registreren. registration-open_for_institution-help_html: Enkel gebruikers van de ingestelde onderwijsinstelling kunnen zich voor deze cursus registreren. registration-closed-help_html: Er zijn geen nieuwe registraties mogelijk. @@ -25,6 +26,7 @@ nl: visibility-visible_for_all_html: Iedereen visibility-visible_for_institution_html: Enkel gebruikers van de ingestelde onderwijsinstelling visibility-hidden_html: Enkel geregistreerde gebruikers + visibility-visible_for_institutional_users_html: Alle gebruikers die tot een onderwijsinstelling behoren registration-open_for_all_html: Iedereen registration-open_for_institution_html: Enkel gebruikers van de ingestelde onderwijsinstelling registration-closed_html: Niemand From 22e13d42a16d2d11259a5302cdc6502a3ac15d37 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 29 Jun 2022 15:42:03 +0200 Subject: [PATCH 03/63] Add vissibility and registration info translations --- config/locales/views/courses/en.yml | 2 ++ config/locales/views/courses/nl.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/config/locales/views/courses/en.yml b/config/locales/views/courses/en.yml index d1c758b021..904de81a02 100644 --- a/config/locales/views/courses/en.yml +++ b/config/locales/views/courses/en.yml @@ -99,9 +99,11 @@ en: visibility-visible_for_all-info: "This course is visible for everyone: everyone can access this course from the course overview, and the contents are visible for everyone." visibility-visible_for_institution-info: "This course is visible for members of %{institution}: they can access this course from the course overview and see its contents. Members of other institutions need the secret link." visibility-hidden-info: "This course is hidden: it is not listed in the course overview, and users need the secret link to access this course." + visibility-visible_for_institutional_users-info: "This course is accessible for anyone that belongs to an institution: they can access this course from the course overview and see its contents. Personal accounts need the secret link." registration-open_for_all-info: "Registrations are open for everyone: all users can register for this course." registration-open_for_institution-info: "Registrations are open for members of %{institution}: only they can register for this course." registration-closed-info: "Registrations are closed: users can't register until you change the registration settings. Users who are currently registered still have access." + registration-open_for_institutional_users-info: "Registrations are open for users that belong to an institution: only they can register for this course." moderated-info: "This course is moderated. Users who register will have to be explicitly approved by a course admin." subscribe: Register request_registration: Request registration diff --git a/config/locales/views/courses/nl.yml b/config/locales/views/courses/nl.yml index 2f9f16ec5b..4841ca7444 100644 --- a/config/locales/views/courses/nl.yml +++ b/config/locales/views/courses/nl.yml @@ -109,8 +109,10 @@ nl: visibility-visible_for_institution-info: 'Deze cursus is zichtbaar voor leden van %{institution}: voor hen wordt deze cursus opgelijst in het cursusoverzicht en is de inhoud zichtbaar. Anderen kunnen enkel de inhoud van deze cursus zien via de geheime link' visibility-hidden-info: 'Deze cursus is verborgen: ze verschijnt niet in het cursusoverzicht en gebruikers kunnen deze cursus enkel zien via de geheime link.' registration-open_for_all-info: 'De registraties staan open voor iedereen: gebruikers die op de registratie-knop klikken worden geregistreerd.' + visibility-visible_for_institutional_users-info: "Iedereen die tot een instelling behoort kan deze cursus bekijken: voor hen wordt deze cursus opgelijst in het cursusoverzicht en is de inhoud zichtbaar. Anderen kunnen enkel de inhoud van deze cursus zien via de geheime link" registration-open_for_institution-info: 'De registraties staan open voor gebruikers van %{institution}: enkel zij kunnen zich registreren voor deze cursus.' registration-closed-info: 'De registraties zijn gesloten: niemand kan zich nog registreren tot de registratie-instelling wordt aangepast. Gebruikers die geregistreerd zijn behouden hun toegang.' + registration-open_for_institutional_users-info: 'De registraties staan open voor gebruikers die tot een instelling behoren: enkel zij kunnen zich registreren voor deze cursus.' moderated-info: 'Deze cursus is gemodereerd: Gebruikers die zich registreren moeten expliciet goedgekeurd worden door een cursusbeheerder.' subscribe: Registreren request_registration: Registratieverzoek indienen From d2d4abf97d046a85aad35dd5d583bfbbc2c7cf7a Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 29 Jun 2022 16:06:15 +0200 Subject: [PATCH 04/63] Limit visibillity of courses --- app/models/user.rb | 8 ++++++++ app/policies/course_policy.rb | 10 ++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index f25178af9d..9d0c7d5763 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -272,6 +272,14 @@ def repository_admin?(repository) @repository_admin.include?(repository.id) end + def personal? + institution.nil? + end + + def institutional? + institution.present? + end + def attempted_exercises(options) s = submissions.judged s = s.in_course(options[:course]) if options[:course].present? diff --git a/app/policies/course_policy.rb b/app/policies/course_policy.rb index 33b9c85556..5d6097ce35 100644 --- a/app/policies/course_policy.rb +++ b/app/policies/course_policy.rb @@ -7,10 +7,11 @@ def resolve scope elsif user @scope = scope.joins(:course_memberships) - scope.where(visibility: :visible_for_all) - .or(scope.where(institution: user.institution, visibility: :visible_for_institution)) - .or(scope.where(course_memberships: { status: %i[student course_admin], user_id: user.id })) - .distinct + @filtered_scope = scope.where(visibility: :visible_for_all) + .or(scope.where(institution: user.institution, visibility: :visible_for_institution)) + .or(scope.where(course_memberships: { status: %i[student course_admin], user_id: user.id })) + @filtered_scope = @filtered_scope.or(scope.where(visibility: :visible_for_institutional_users)) if user.institutional? + @filtered_scope.distinct else scope.where(visibility: :visible_for_all) end @@ -39,6 +40,7 @@ def copy? user&.zeus? || record.visible_for_all? || (record.visible_for_institution? && record.institution == user&.institution) || + (record.visible_for_institutional_users && user&.institutional?) || record.subscribed_members.include?(user) ) end From f10dfe83cd368aca9168ede0fa25b2df24157440 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Thu, 30 Jun 2022 09:44:32 +0200 Subject: [PATCH 05/63] Limit registration for institutional users --- app/controllers/courses_controller.rb | 18 +++--------------- app/helpers/courses_helper.rb | 2 +- app/models/activity.rb | 2 +- app/models/course.rb | 8 ++++++-- db/seeds.rb | 1 + test/controllers/courses_controller_test.rb | 2 +- 6 files changed, 13 insertions(+), 20 deletions(-) diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb index eee1204eec..22d12f794a 100644 --- a/app/controllers/courses_controller.rb +++ b/app/controllers/courses_controller.rb @@ -342,26 +342,14 @@ def subscribe success_method = method(:signup_succeeded_response) end - case @course.registration - when 'open_for_all' + if @course.open_for_user?(current_user) if try_to_subscribe_current_user status: status success_method.call(format) else subscription_failed_response format end - when 'open_for_institution' - if @course.institution == current_user.institution - if try_to_subscribe_current_user status: status - success_method.call(format) - else - subscription_failed_response format - end - else - format.html { redirect_to(course_url(@course, secret: params[:secret]), alert: I18n.t('courses.registration.closed')) } - format.json { render json: { errors: ['course closed'] }, status: :unprocessable_entity } - end - when 'closed' - format.html { redirect_to(@course, alert: I18n.t('courses.registration.closed')) } + else + format.html { redirect_to(course_url(@course, secret: params[:secret]), alert: I18n.t('courses.registration.closed')) } format.json { render json: { errors: ['course closed'] }, status: :unprocessable_entity } end end diff --git a/app/helpers/courses_helper.rb b/app/helpers/courses_helper.rb index bfd92b3ced..150b7ebdac 100644 --- a/app/helpers/courses_helper.rb +++ b/app/helpers/courses_helper.rb @@ -7,7 +7,7 @@ def registration_action_for(**args) membership = args[:membership] if membership.nil? || membership.unsubscribed? - if course.open_for_all? || (course.open_for_institution? && (course.institution == current_user&.institution || current_user.nil?)) + if course.open_for_user?(current_user) || current_user.nil? if course.moderated link_to t('courses.show.request_registration'), subscribe_course_path(@course, secret: secret), diff --git a/app/models/activity.rb b/app/models/activity.rb index ddd41e9baa..b50b33f36a 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -280,7 +280,7 @@ def accessible?(user, course) return true if user&.member_of? course return false if course.moderated && access_private? - course.open_for_all? || (course.open_for_institution? && course.institution == user&.institution) + course.open_for_user?(user) else return true if user&.repository_admin? repository diff --git a/app/models/course.rb b/app/models/course.rb index cfdc0f5fb2..8c0744011b 100644 --- a/app/models/course.rb +++ b/app/models/course.rb @@ -167,8 +167,8 @@ class Course < ApplicationRecord # Default year & enum values after_initialize do |course| - self.visibility ||= 'visible_for_all' - self.registration ||= 'open_for_all' + self.visibility ||= 'visible_for_institutional_users' + self.registration ||= 'open_for_institutional_users' unless year now = Time.zone.now y = now.year @@ -214,6 +214,10 @@ def all_activities_accessible? activities.where(access: :private).where.not(repository_id: usable_repositories).count.zero? end + def open_for_user?(user) + open_for_all? || (open_for_institution? && institution == user&.institution) || (open_for_institutional_users? && user&.institutional?) + end + def invalidate_subscribed_members_count_cache Rails.cache.delete(format(SUBSCRIBED_MEMBERS_COUNT_CACHE_STRING, id: id)) end diff --git a/db/seeds.rb b/db/seeds.rb index ebb23f073d..1d0795555a 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -151,6 +151,7 @@ def submission_summary(status) courses = [] + courses << Course.create(description: 'This is a test course.', name: 'Open for Institutional users course', year: '2021-2022', registration: 'open_for_institutional_users', visibility: 'visible_for_institutional_users', moderated: false, teacher: 'Prof. Gobelijn') courses << Course.create(description: 'This is a test course.', name: 'Open for All Test Course', year: '2020-2021', registration: 'open_for_all', visibility: 'visible_for_all', moderated: false, teacher: 'Prof. Gobelijn') courses << Course.create(description: 'This is a test course.', name: 'Open for Institution Test Course', year: '2020-2021', registration: 'open_for_institution', visibility: 'visible_for_institution', moderated: false, teacher: 'Prof. Gobelijn', institution: ugent) courses << Course.create(description: 'This is a test course.', name: 'Open Moderated Test Course', year: '2020-2021', registration: 'open_for_all', visibility: 'visible_for_all', moderated: true, teacher: 'Prof. Barabas') diff --git a/test/controllers/courses_controller_test.rb b/test/controllers/courses_controller_test.rb index 1ab409a008..828d6c8f31 100644 --- a/test/controllers/courses_controller_test.rb +++ b/test/controllers/courses_controller_test.rb @@ -158,7 +158,7 @@ def with_users_signed_in(users) test 'should get course page with secret' do add_not_subscribed with_users_signed_in @not_subscribed.compact do |who, user| - %w[visible_for_all visible_for_institution hidden].product(%w[open_for_all open_for_institution closed], [true, false]).each do |v, r, m| + %w[visible_for_all visible_for_institutional_users visible_for_institution hidden].product(%w[open_for_all open_for_institutional_users open_for_institution closed], [true, false]).each do |v, r, m| @course.update(visibility: v, registration: r, moderated: m) get course_url(@course, secret: @course.secret, format: :json) assert_response :success, "#{who} should get registration page" From e55d7870b6c4e50d623c76fab5e784a6c621e891 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Thu, 30 Jun 2022 10:27:27 +0200 Subject: [PATCH 06/63] Fix tests --- app/policies/course_policy.rb | 12 ++++++------ bin/server | 5 ++++- test/fixtures/courses.yml | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/app/policies/course_policy.rb b/app/policies/course_policy.rb index 5d6097ce35..0e5e76355b 100644 --- a/app/policies/course_policy.rb +++ b/app/policies/course_policy.rb @@ -7,11 +7,11 @@ def resolve scope elsif user @scope = scope.joins(:course_memberships) - @filtered_scope = scope.where(visibility: :visible_for_all) - .or(scope.where(institution: user.institution, visibility: :visible_for_institution)) - .or(scope.where(course_memberships: { status: %i[student course_admin], user_id: user.id })) - @filtered_scope = @filtered_scope.or(scope.where(visibility: :visible_for_institutional_users)) if user.institutional? - @filtered_scope.distinct + filtered_scope = scope.where(visibility: :visible_for_all) + filtered_scope = filtered_scope.or(scope.where(institution: user.institution, visibility: :visible_for_institution)) + filtered_scope = filtered_scope.or(scope.where(course_memberships: { status: %i[student course_admin], user_id: user.id })) + filtered_scope = filtered_scope.or(scope.where(visibility: :visible_for_institutional_users)) if user.institutional? + filtered_scope.distinct else scope.where(visibility: :visible_for_all) end @@ -40,7 +40,7 @@ def copy? user&.zeus? || record.visible_for_all? || (record.visible_for_institution? && record.institution == user&.institution) || - (record.visible_for_institutional_users && user&.institutional?) || + (record.visible_for_institutional_users? && user&.institutional?) || record.subscribed_members.include?(user) ) end diff --git a/bin/server b/bin/server index 4ae98c895a..ce5526cbbd 100755 --- a/bin/server +++ b/bin/server @@ -1,4 +1,7 @@ #!/bin/bash bundle install yarn install -bundle exec rails s -p 3000 & bundle exec rails jobs:work & yarn build:css --watch & NODE_ENV=development yarn build:js --watch +bundle exec rails s -p 3000 & +bundle exec rails jobs:work & +yarn build:css --watch & +NODE_ENV=development yarn build:js --watch diff --git a/test/fixtures/courses.yml b/test/fixtures/courses.yml index 031c3afd1e..efb31bbc94 100644 --- a/test/fixtures/courses.yml +++ b/test/fixtures/courses.yml @@ -24,6 +24,6 @@ course1: name: course 1 year: 2021-2022 description: This is the description of course 1 - visibility: 0 # visible for all - registration: 0 # open for all + visibility: 3 # visible for all + registration: 3 # open for all teacher: Prof. Zonnebloem From 080cfc2fea74c80e249bc1e54e56d00a0e6ddbaf Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Thu, 30 Jun 2022 13:58:33 +0200 Subject: [PATCH 07/63] Add tests --- app/models/course.rb | 1 + test/controllers/courses_controller_test.rb | 54 +++++++++++++++++++++ test/fixtures/courses.yml | 1 + 3 files changed, 56 insertions(+) diff --git a/app/models/course.rb b/app/models/course.rb index 8c0744011b..725c9f295d 100644 --- a/app/models/course.rb +++ b/app/models/course.rb @@ -206,6 +206,7 @@ def formatted_attribution def secret_required?(user = nil) return false if visible_for_all? return false if visible_for_institution? && user&.institution == institution + return false if visible_for_institutional_users? && user&.institutional? true end diff --git a/test/controllers/courses_controller_test.rb b/test/controllers/courses_controller_test.rb index 828d6c8f31..8a80d5608d 100644 --- a/test/controllers/courses_controller_test.rb +++ b/test/controllers/courses_controller_test.rb @@ -712,6 +712,60 @@ def with_users_signed_in(users) assert_not response.body.include?(subscribe_course_path(@course, secret: @course.secret)) end + test 'personal user should only see visible_for_institutional_users course with secret and after subscription' do + add_externals + @course.update(visibility: :visible_for_institutional_users) + user = @externals.first + user.update(institution_id: nil) + sign_in user + get course_url(@course) + assert_not response.successful? + get course_url(@course, secret: @course.secret) + assert response.successful? + post subscribe_course_url(@course) + assert_not @course.subscribed_members.include?(user) + post subscribe_course_url(@course, secret: @course.secret) + assert @course.subscribed_members.include?(user) + get course_url(@course) + assert response.successful? + end + + test 'institutional user should always see visible_for_institutional_users course' do + add_externals + @course.update(visibility: :visible_for_institutional_users) + user = @externals.first + user.update(institution: (create :institution)) + sign_in user + get course_url(@course) + assert response.successful? + get course_url(@course, secret: @course.secret) + assert response.successful? + end + + test 'personal user should not be able to subscribe to open_for_institutional_users course' do + add_externals + @course.update(registration: :open_for_institutional_users) + user = @externals.first + user.update(institution: nil) + sign_in user + post subscribe_course_url(@course) + assert_not @course.subscribed_members.include?(user) + post subscribe_course_url(@course, secret: @course.secret) + assert_not @course.subscribed_members.include?(user) + end + + test 'institutional user should be able to subscribe to open_for_institutional_users course' do + add_externals + @course.update(registration: :open_for_institutional_users) + user = @externals.first + user.update(institution: (create :institution)) + sign_in user + post subscribe_course_url(@course) + assert @course.subscribed_members.include?(user) + post subscribe_course_url(@course, secret: @course.secret) + assert @course.subscribed_members.include?(user) + end + test 'should not destroy course as student' do add_students sign_in @students.first diff --git a/test/fixtures/courses.yml b/test/fixtures/courses.yml index efb31bbc94..8e1f9e67b1 100644 --- a/test/fixtures/courses.yml +++ b/test/fixtures/courses.yml @@ -27,3 +27,4 @@ course1: visibility: 3 # visible for all registration: 3 # open for all teacher: Prof. Zonnebloem + secret: "PuDCHk3A7qCEAbxKXPpz" From b550457267329e70a22371721535ac10c224620e Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 13 Jul 2022 15:39:20 +0200 Subject: [PATCH 08/63] Allow personal users --- app/controllers/auth/omniauth_callbacks_controller.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb index 00e048e2ab..538d370660 100644 --- a/app/controllers/auth/omniauth_callbacks_controller.rb +++ b/app/controllers/auth/omniauth_callbacks_controller.rb @@ -52,8 +52,6 @@ def smartschool # ==> Authentication logic. def generic_oauth - return provider_missing! if oauth_provider_id.blank? - # Find the provider for the current institution. If no provider exists yet, # a new one will be created. return redirect_with_flash!(I18n.t('auth.sign_in.errors.institution-creation')) if provider.blank? @@ -366,9 +364,4 @@ def institution_create_failed(errors) .institution_creation_failed .deliver_later end - - def provider_missing! - flash_failure I18n.t('auth.sign_in.errors.missing-provider') - redirect_to sign_in_path - end end From 50dfa17bd08ae1d8ba0ec4b1ab88c2a702948387 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 13 Jul 2022 16:25:56 +0200 Subject: [PATCH 09/63] Allow personal accounts for office 365 --- config/initializers/office365.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/initializers/office365.rb b/config/initializers/office365.rb index 09106322b2..05b8a726f3 100644 --- a/config/initializers/office365.rb +++ b/config/initializers/office365.rb @@ -11,8 +11,8 @@ class Office365 < OmniAuth::Strategies::OAuth2 option :client_options, site: 'https://login.microsoftonline.com/', - authorize_url: '/common/oauth2/authorize', - token_url: '/common/oauth2/token' + authorize_url: '/common/oauth2/v2.0/authorize', + token_url: '/common/oauth2/v2.0/token' DEFAULT_SCOPE = "openid email profile https://outlook.office.com/contacts.read" From a7916e999c3ecb6667f02ceff9eb1aa022f97c1f Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 13 Jul 2022 16:35:07 +0200 Subject: [PATCH 10/63] Don't ask for contact details --- config/initializers/office365.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/office365.rb b/config/initializers/office365.rb index 05b8a726f3..cc499ef64b 100644 --- a/config/initializers/office365.rb +++ b/config/initializers/office365.rb @@ -14,7 +14,7 @@ class Office365 < OmniAuth::Strategies::OAuth2 authorize_url: '/common/oauth2/v2.0/authorize', token_url: '/common/oauth2/v2.0/token' - DEFAULT_SCOPE = "openid email profile https://outlook.office.com/contacts.read" + DEFAULT_SCOPE = "openid email profile" def authorize_params super.tap do |params| From f9920a1571c142d9a6dea273758082ad8415f2e1 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Thu, 14 Jul 2022 09:49:10 +0200 Subject: [PATCH 11/63] Put raw info in log --- config/initializers/office365.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/initializers/office365.rb b/config/initializers/office365.rb index cc499ef64b..3f6316576f 100644 --- a/config/initializers/office365.rb +++ b/config/initializers/office365.rb @@ -49,6 +49,8 @@ def authorize_params end def username + Rails.logger.info "================================================ FIND ME ==============================================================" + Rails.logger.info raw_info raw_info['unique_name'].split('@').first end From 82d01a19b3ca8b9ed7f991c048302a3fc6a5c9cb Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Thu, 14 Jul 2022 10:03:49 +0200 Subject: [PATCH 12/63] Readd contacts to scope to see difference --- config/initializers/office365.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/office365.rb b/config/initializers/office365.rb index 3f6316576f..c384851ab0 100644 --- a/config/initializers/office365.rb +++ b/config/initializers/office365.rb @@ -14,7 +14,7 @@ class Office365 < OmniAuth::Strategies::OAuth2 authorize_url: '/common/oauth2/v2.0/authorize', token_url: '/common/oauth2/v2.0/token' - DEFAULT_SCOPE = "openid email profile" + DEFAULT_SCOPE = "openid email profile https://outlook.office.com/contacts.read" def authorize_params super.tap do |params| From 6d27c92c291f33291e72f1581bdfbabaf0e34323 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Fri, 15 Jul 2022 09:49:39 +0200 Subject: [PATCH 13/63] Fix office365 provider to use new api --- .../auth/omniauth_callbacks_controller.rb | 15 ++++++++++++++- config/initializers/office365.rb | 14 ++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb index 538d370660..66c2dd913f 100644 --- a/app/controllers/auth/omniauth_callbacks_controller.rb +++ b/app/controllers/auth/omniauth_callbacks_controller.rb @@ -152,8 +152,21 @@ def create_identity_from_session!(user) end def find_identity_by_uid + return nil if auth_uid.nil? + # In case of provider without uids, don't return any identity (As it won't be matching a unique user) - Identity.find_by(identifier: auth_uid, provider: provider) if auth_uid.present? + identity = Identity.find_by(identifier: auth_uid, provider: provider) + + return identity unless identity.nil? && provider.sym == :office365 && auth_email.present? + + # This code supports a migration of the office365 oauth api from v1 to v2 + # Try to find the user by the legacy identifier + identity = Identity.find_by(identifier: auth_email.split('@').first, provider: provider) + return nil if identity.nil? + + # Update the identifier to the new uid + identity.update(identifier: auth_uid) + identity end def find_identity_by_user(user) diff --git a/config/initializers/office365.rb b/config/initializers/office365.rb index c384851ab0..3e1e6585d8 100644 --- a/config/initializers/office365.rb +++ b/config/initializers/office365.rb @@ -14,7 +14,7 @@ class Office365 < OmniAuth::Strategies::OAuth2 authorize_url: '/common/oauth2/v2.0/authorize', token_url: '/common/oauth2/v2.0/token' - DEFAULT_SCOPE = "openid email profile https://outlook.office.com/contacts.read" + DEFAULT_SCOPE = "openid email profile" def authorize_params super.tap do |params| @@ -30,14 +30,14 @@ def authorize_params end # These are called after authentication has succeeded. - uid {username} + uid { raw_info['oid'] } info do { username: username, - first_name: raw_info['given_name'], - last_name: raw_info['family_name'], - email: raw_info['upn'], + first_name: raw_info['name'].split(' ').first, + last_name: raw_info['name'].split(' ').drop(1).join(' '), + email: raw_info['email'], institution: raw_info['tid'] } end @@ -49,9 +49,7 @@ def authorize_params end def username - Rails.logger.info "================================================ FIND ME ==============================================================" - Rails.logger.info raw_info - raw_info['unique_name'].split('@').first + raw_info['email'].split('@').first end def decoded_token From 562dfbb4dd61aa606cd13cd028589e6572255adf Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Fri, 15 Jul 2022 10:00:35 +0200 Subject: [PATCH 14/63] Fix bug --- app/controllers/auth/omniauth_callbacks_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb index 66c2dd913f..5030423222 100644 --- a/app/controllers/auth/omniauth_callbacks_controller.rb +++ b/app/controllers/auth/omniauth_callbacks_controller.rb @@ -157,7 +157,7 @@ def find_identity_by_uid # In case of provider without uids, don't return any identity (As it won't be matching a unique user) identity = Identity.find_by(identifier: auth_uid, provider: provider) - return identity unless identity.nil? && provider.sym == :office365 && auth_email.present? + return identity unless identity.nil? && provider.class.sym == :office365 && auth_email.present? # This code supports a migration of the office365 oauth api from v1 to v2 # Try to find the user by the legacy identifier From 4a7f0d3ca5121b0f9297737b62311525f11814e4 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Fri, 15 Jul 2022 10:36:59 +0200 Subject: [PATCH 15/63] Lift restrictions that blocked personal providers --- app/models/provider.rb | 4 ++-- app/models/provider/g_suite.rb | 4 ++-- app/models/provider/lti.rb | 2 +- app/models/provider/office365.rb | 2 +- app/models/provider/oidc.rb | 2 +- app/models/provider/saml.rb | 2 +- app/models/provider/smartschool.rb | 2 +- app/models/provider/surf.rb | 2 +- db/migrate/20220715082910_change_provider_institution_id.rb | 5 +++++ db/schema.rb | 4 ++-- db/seeds.rb | 4 ++++ test/factories/providers.rb | 2 +- test/models/provider_test.rb | 2 +- 13 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 db/migrate/20220715082910_change_provider_institution_id.rb diff --git a/app/models/provider.rb b/app/models/provider.rb index d3cd784d83..9bb2c0f298 100644 --- a/app/models/provider.rb +++ b/app/models/provider.rb @@ -4,7 +4,7 @@ # # id :bigint not null, primary key # type :string(255) default("Provider::Saml"), not null -# institution_id :bigint not null +# institution_id :bigint # identifier :string(255) # certificate :text(16777215) # entity_id :string(255) @@ -24,7 +24,7 @@ class Provider < ApplicationRecord PROVIDERS = [Provider::GSuite, Provider::Lti, Provider::Office365, Provider::Oidc, Provider::Saml, Provider::Smartschool, Provider::Surf].freeze - belongs_to :institution, inverse_of: :providers + belongs_to :institution, inverse_of: :providers, optional: true has_many :identities, inverse_of: :provider, dependent: :destroy diff --git a/app/models/provider/g_suite.rb b/app/models/provider/g_suite.rb index 7531973b7f..7a289adfe4 100644 --- a/app/models/provider/g_suite.rb +++ b/app/models/provider/g_suite.rb @@ -4,7 +4,7 @@ # # id :bigint not null, primary key # type :string(255) default("Provider::Saml"), not null -# institution_id :bigint not null +# institution_id :bigint # identifier :string(255) # certificate :text(16777215) # entity_id :string(255) @@ -22,7 +22,7 @@ class Provider::GSuite < Provider validates :certificate, :entity_id, :sso_url, :slo_url, absence: true validates :authorization_uri, :client_id, :issuer, :jwks_uri, absence: true - validates :identifier, uniqueness: { case_sensitive: false }, presence: true + validates :identifier, uniqueness: { case_sensitive: false } def self.sym :google_oauth2 diff --git a/app/models/provider/lti.rb b/app/models/provider/lti.rb index 0e88611afd..a50a6be45c 100644 --- a/app/models/provider/lti.rb +++ b/app/models/provider/lti.rb @@ -4,7 +4,7 @@ # # id :bigint not null, primary key # type :string(255) default("Provider::Saml"), not null -# institution_id :bigint not null +# institution_id :bigint # identifier :string(255) # certificate :text(16777215) # entity_id :string(255) diff --git a/app/models/provider/office365.rb b/app/models/provider/office365.rb index 510ed03c0e..a0d1931490 100644 --- a/app/models/provider/office365.rb +++ b/app/models/provider/office365.rb @@ -4,7 +4,7 @@ # # id :bigint not null, primary key # type :string(255) default("Provider::Saml"), not null -# institution_id :bigint not null +# institution_id :bigint # identifier :string(255) # certificate :text(16777215) # entity_id :string(255) diff --git a/app/models/provider/oidc.rb b/app/models/provider/oidc.rb index 1849f8cd26..0bad766542 100644 --- a/app/models/provider/oidc.rb +++ b/app/models/provider/oidc.rb @@ -4,7 +4,7 @@ # # id :bigint not null, primary key # type :string(255) default("Provider::Saml"), not null -# institution_id :bigint not null +# institution_id :bigint # identifier :string(255) # certificate :text(16777215) # entity_id :string(255) diff --git a/app/models/provider/saml.rb b/app/models/provider/saml.rb index fb42429ce2..311fb4d484 100644 --- a/app/models/provider/saml.rb +++ b/app/models/provider/saml.rb @@ -4,7 +4,7 @@ # # id :bigint not null, primary key # type :string(255) default("Provider::Saml"), not null -# institution_id :bigint not null +# institution_id :bigint # identifier :string(255) # certificate :text(16777215) # entity_id :string(255) diff --git a/app/models/provider/smartschool.rb b/app/models/provider/smartschool.rb index dd7583a0ce..2e484cbaf1 100644 --- a/app/models/provider/smartschool.rb +++ b/app/models/provider/smartschool.rb @@ -4,7 +4,7 @@ # # id :bigint not null, primary key # type :string(255) default("Provider::Saml"), not null -# institution_id :bigint not null +# institution_id :bigint # identifier :string(255) # certificate :text(16777215) # entity_id :string(255) diff --git a/app/models/provider/surf.rb b/app/models/provider/surf.rb index 772d35bbd4..349af41e6e 100644 --- a/app/models/provider/surf.rb +++ b/app/models/provider/surf.rb @@ -4,7 +4,7 @@ # # id :bigint not null, primary key # type :string(255) default("Provider::Saml"), not null -# institution_id :bigint not null +# institution_id :bigint # identifier :string(255) # certificate :text(16777215) # entity_id :string(255) diff --git a/db/migrate/20220715082910_change_provider_institution_id.rb b/db/migrate/20220715082910_change_provider_institution_id.rb new file mode 100644 index 0000000000..6024abeb8b --- /dev/null +++ b/db/migrate/20220715082910_change_provider_institution_id.rb @@ -0,0 +1,5 @@ +class ChangeProviderInstitutionId < ActiveRecord::Migration[7.0] + def change + change_column_null :providers, :institution_id, true + end +end diff --git a/db/schema.rb b/db/schema.rb index df5371472f..2a0b8fd9c1 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2022_05_24_123721) do +ActiveRecord::Schema[7.0].define(version: 2022_07_15_082910) do create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.string "name", null: false t.string "record_type", null: false @@ -354,7 +354,7 @@ create_table "providers", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.string "type", default: "Provider::Saml", null: false - t.bigint "institution_id", null: false + t.bigint "institution_id" t.string "identifier" t.text "certificate", size: :medium t.string "entity_id" diff --git a/db/seeds.rb b/db/seeds.rb index 288eeee70c..c453543e07 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -91,6 +91,10 @@ def fill_series_with_realistic_submissions(s) # OIDC Provider::Oidc.create institution: vlaanderen, client_id: '12345', issuer: 'https://authenticatie.vlaanderen.be/op' + # Personal providers + Provider::Office365.create identifier: '9188040d-6c67-4c5b-b112-36a304b66dad', institution: nil + Provider::GSuite.create identifier: nil, institution: nil + puts "Creating users (#{Time.now - start})" zeus = User.create username: 'zeus', first_name: 'Zeus', last_name: 'Kronosson', email: 'zeus@ugent.be', permission: :zeus, institution: nil, token: 'zeus' diff --git a/test/factories/providers.rb b/test/factories/providers.rb index f068cf4daa..db77e2e4d9 100644 --- a/test/factories/providers.rb +++ b/test/factories/providers.rb @@ -4,7 +4,7 @@ # # id :bigint not null, primary key # type :string(255) default("Provider::Saml"), not null -# institution_id :bigint not null +# institution_id :bigint # identifier :string(255) # certificate :text(16777215) # entity_id :string(255) diff --git a/test/models/provider_test.rb b/test/models/provider_test.rb index c42c9e76a0..17bcfa2b67 100644 --- a/test/models/provider_test.rb +++ b/test/models/provider_test.rb @@ -4,7 +4,7 @@ # # id :bigint not null, primary key # type :string(255) default("Provider::Saml"), not null -# institution_id :bigint not null +# institution_id :bigint # identifier :string(255) # certificate :text(16777215) # entity_id :string(255) From 148ad025b2617579083b64c6561d2750a59ac142 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Fri, 15 Jul 2022 11:51:05 +0200 Subject: [PATCH 16/63] Add and fix tests --- .../auth/omniauth_callbacks_controller.rb | 2 +- .../omniauth_callbacks_controller_test.rb | 35 +++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb index 5030423222..9c2bfa9d48 100644 --- a/app/controllers/auth/omniauth_callbacks_controller.rb +++ b/app/controllers/auth/omniauth_callbacks_controller.rb @@ -200,7 +200,7 @@ def find_or_create_oauth_provider institution_created provider else - institution_creation_failed institution.errors + institution_create_failed institution.errors nil end end diff --git a/test/controllers/auth/omniauth_callbacks_controller_test.rb b/test/controllers/auth/omniauth_callbacks_controller_test.rb index 21c57c9fdc..f311d443fc 100644 --- a/test/controllers/auth/omniauth_callbacks_controller_test.rb +++ b/test/controllers/auth/omniauth_callbacks_controller_test.rb @@ -339,7 +339,7 @@ def omniauth_path(provider) end test 'oauth login with missing provider' do - OAUTH_PROVIDERS.each do |provider_name| + %i[office365_provider smartschool_provider].each do |provider_name| # Setup. provider = build provider_name, identifier: nil user = build :user, institution: provider.institution @@ -351,11 +351,42 @@ def omniauth_path(provider) follow_redirect! # Assert failed authentication. - assert_redirected_to sign_in_path + assert_redirected_to root_path assert_nil @controller.current_user end end + test 'Can sign up with personal account' do + personal_providers = [ + create(:office365_provider, identifier: '9188040d-6c67-4c5b-b112-36a304b66dad', institution: nil), + create(:gsuite_provider, identifier: nil, institution: nil) + ] + + personal_providers.each do |provider| + # Setup. + user = build :user, institution: nil + identity = build :identity, provider: provider, user: user + omniauth_mock_identity identity + + assert_difference 'User.count', 1 do + assert_difference 'Identity.count', 1 do + # Call the authorization url. + get omniauth_url(provider) + follow_redirect! + end + end + + # Assert successful authentication. + assert_redirected_to root_path + assert_not_nil @controller.current_user + assert_equal @controller.current_user.email, user.email + assert_nil @controller.current_user.institution + + # Cleanup. + sign_out user + end + end + test 'lti redirects to main provider' do main_provider = create :provider provider = create :lti_provider, institution: main_provider.institution, mode: :link From f4925f1ef50a5dafa5eb5251610006ef425c8ecb Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Fri, 15 Jul 2022 13:10:43 +0200 Subject: [PATCH 17/63] Add test for office 365 migration --- .../omniauth_callbacks_controller_test.rb | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/controllers/auth/omniauth_callbacks_controller_test.rb b/test/controllers/auth/omniauth_callbacks_controller_test.rb index f311d443fc..8740ba2ab6 100644 --- a/test/controllers/auth/omniauth_callbacks_controller_test.rb +++ b/test/controllers/auth/omniauth_callbacks_controller_test.rb @@ -387,6 +387,46 @@ def omniauth_path(provider) end end + + + test 'Office 365 identifier should be updated upon login if the identifier still used the old format' do + # Setup. + provider = create :office365_provider + user = create :user, institution: provider.institution + identity = create :identity, provider: provider, user: user, identifier: 'Foo.Bar' + omniauth_mock_identity identity, + info: { + email: 'Foo.Bar@test.com' + }, + uid: 'NEW-UID' + + get omniauth_url(provider) + follow_redirect! + + identity.reload + assert_equal identity.identifier, 'NEW-UID' + + # Cleanup. + sign_out user + + # sign in should still work with changed identifier + omniauth_mock_identity identity, + info: { + email: 'Foo.Bar@test.com' + }, + uid: 'NEW-UID' + + get omniauth_url(provider) + follow_redirect! + + # Assert successful authentication. + assert_redirected_to root_path + assert_equal @controller.current_user, user + + # Cleanup. + sign_out user + end + test 'lti redirects to main provider' do main_provider = create :provider provider = create :lti_provider, institution: main_provider.institution, mode: :link From 224a4a483dbed27ba4b62d8a02bcf61b2e04b152 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Fri, 15 Jul 2022 13:36:25 +0200 Subject: [PATCH 18/63] Only allow identfier based on email log in to work for old identifiers --- .../auth/omniauth_callbacks_controller.rb | 4 ++-- app/models/identity.rb | 13 ++++++----- ...d_identifier_based_on_email_to_identity.rb | 5 +++++ db/schema.rb | 3 ++- .../omniauth_callbacks_controller_test.rb | 22 ++++++++++++++++++- test/factories/identities.rb | 13 ++++++----- test/models/identity_test.rb | 13 ++++++----- 7 files changed, 51 insertions(+), 22 deletions(-) create mode 100644 db/migrate/20220715111838_add_identifier_based_on_email_to_identity.rb diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb index 9c2bfa9d48..7162ac2218 100644 --- a/app/controllers/auth/omniauth_callbacks_controller.rb +++ b/app/controllers/auth/omniauth_callbacks_controller.rb @@ -161,11 +161,11 @@ def find_identity_by_uid # This code supports a migration of the office365 oauth api from v1 to v2 # Try to find the user by the legacy identifier - identity = Identity.find_by(identifier: auth_email.split('@').first, provider: provider) + identity = Identity.find_by(identifier: auth_email.split('@').first, provider: provider, identifier_based_on_email: true) return nil if identity.nil? # Update the identifier to the new uid - identity.update(identifier: auth_uid) + identity.update(identifier: auth_uid, identifier_based_on_email: false) identity end diff --git a/app/models/identity.rb b/app/models/identity.rb index 87d82b499c..f46dbc6608 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -2,12 +2,13 @@ # # Table name: identities # -# id :bigint not null, primary key -# identifier :string(255) not null -# provider_id :bigint not null -# user_id :integer not null -# created_at :datetime not null -# updated_at :datetime not null +# id :bigint not null, primary key +# identifier :string(255) not null +# provider_id :bigint not null +# user_id :integer not null +# created_at :datetime not null +# updated_at :datetime not null +# identifier_based_on_email :boolean default(FALSE), not null # class Identity < ApplicationRecord belongs_to :provider, inverse_of: :identities diff --git a/db/migrate/20220715111838_add_identifier_based_on_email_to_identity.rb b/db/migrate/20220715111838_add_identifier_based_on_email_to_identity.rb new file mode 100644 index 0000000000..1ed84d84ec --- /dev/null +++ b/db/migrate/20220715111838_add_identifier_based_on_email_to_identity.rb @@ -0,0 +1,5 @@ +class AddIdentifierBasedOnEmailToIdentity < ActiveRecord::Migration[7.0] + def change + add_column :identities, :identifier_based_on_email, :boolean, default: false, null: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 2a0b8fd9c1..6279486c93 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2022_07_15_082910) do +ActiveRecord::Schema[7.0].define(version: 2022_07_15_111838) do create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.string "name", null: false t.string "record_type", null: false @@ -296,6 +296,7 @@ t.integer "user_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.boolean "identifier_based_on_email", default: false, null: false t.index ["provider_id", "identifier"], name: "index_identities_on_provider_id_and_identifier", unique: true t.index ["provider_id", "user_id"], name: "index_identities_on_provider_id_and_user_id", unique: true t.index ["user_id"], name: "fk_rails_5373344100" diff --git a/test/controllers/auth/omniauth_callbacks_controller_test.rb b/test/controllers/auth/omniauth_callbacks_controller_test.rb index 8740ba2ab6..cdeffba5de 100644 --- a/test/controllers/auth/omniauth_callbacks_controller_test.rb +++ b/test/controllers/auth/omniauth_callbacks_controller_test.rb @@ -393,7 +393,7 @@ def omniauth_path(provider) # Setup. provider = create :office365_provider user = create :user, institution: provider.institution - identity = create :identity, provider: provider, user: user, identifier: 'Foo.Bar' + identity = create :identity, provider: provider, user: user, identifier: 'Foo.Bar', identifier_based_on_email: true omniauth_mock_identity identity, info: { email: 'Foo.Bar@test.com' @@ -403,6 +403,7 @@ def omniauth_path(provider) get omniauth_url(provider) follow_redirect! + assert_equal @controller.current_user, user identity.reload assert_equal identity.identifier, 'NEW-UID' @@ -425,6 +426,25 @@ def omniauth_path(provider) # Cleanup. sign_out user + + # Should not be able to change it again + omniauth_mock_identity identity, + info: { + email: 'NEW-UID@test.com' + }, + uid: 'NEWER-UID' + + get omniauth_url(provider) + follow_redirect! + + # Assert successful authentication. + assert_redirected_to root_path + assert_not_equal @controller.current_user, user + identity.reload + assert_equal identity.identifier, 'NEW-UID' + + # Cleanup. + sign_out user end test 'lti redirects to main provider' do diff --git a/test/factories/identities.rb b/test/factories/identities.rb index a1cf837c18..ea1e979421 100644 --- a/test/factories/identities.rb +++ b/test/factories/identities.rb @@ -2,12 +2,13 @@ # # Table name: identities # -# id :bigint not null, primary key -# identifier :string(255) not null -# provider_id :bigint not null -# user_id :integer not null -# created_at :datetime not null -# updated_at :datetime not null +# id :bigint not null, primary key +# identifier :string(255) not null +# provider_id :bigint not null +# user_id :integer not null +# created_at :datetime not null +# updated_at :datetime not null +# identifier_based_on_email :boolean default(FALSE), not null # FactoryBot.define do factory :identity do diff --git a/test/models/identity_test.rb b/test/models/identity_test.rb index 88f7f45e9e..25109cfc14 100644 --- a/test/models/identity_test.rb +++ b/test/models/identity_test.rb @@ -2,12 +2,13 @@ # # Table name: identities # -# id :bigint not null, primary key -# identifier :string(255) not null -# provider_id :bigint not null -# user_id :integer not null -# created_at :datetime not null -# updated_at :datetime not null +# id :bigint not null, primary key +# identifier :string(255) not null +# provider_id :bigint not null +# user_id :integer not null +# created_at :datetime not null +# updated_at :datetime not null +# identifier_based_on_email :boolean default(FALSE), not null # require 'test_helper' From 8d447af9d87e6fb54a1477e29a595d14c3d04d15 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Fri, 15 Jul 2022 13:38:30 +0200 Subject: [PATCH 19/63] Fix linting --- test/controllers/auth/omniauth_callbacks_controller_test.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/controllers/auth/omniauth_callbacks_controller_test.rb b/test/controllers/auth/omniauth_callbacks_controller_test.rb index cdeffba5de..8c668cca29 100644 --- a/test/controllers/auth/omniauth_callbacks_controller_test.rb +++ b/test/controllers/auth/omniauth_callbacks_controller_test.rb @@ -387,8 +387,6 @@ def omniauth_path(provider) end end - - test 'Office 365 identifier should be updated upon login if the identifier still used the old format' do # Setup. provider = create :office365_provider From 5177737d27897f18e0cff35c76200aa45ba9e327 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Fri, 15 Jul 2022 13:55:29 +0200 Subject: [PATCH 20/63] Update sign in page --- config/locales/views/auth/en.yml | 2 +- config/locales/views/auth/nl.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/locales/views/auth/en.yml b/config/locales/views/auth/en.yml index 3566747410..aad5846bfc 100644 --- a/config/locales/views/auth/en.yml +++ b/config/locales/views/auth/en.yml @@ -8,7 +8,7 @@ en: institution-search: "Type to search for your institution" ilearn-help: "Users of i-Learn can currently only sign in through the i-Learn website." higher-education: "Higher education" - other-education: "Other educational institutions" + other-education: "Generic sign in methods" other: "Other institutions" blacklist: sgpaulus: Gebruik je sgpaulus-account (voornaam.naam@sgpaulus.eu) om in te loggen op Dodona. diff --git a/config/locales/views/auth/nl.yml b/config/locales/views/auth/nl.yml index 6a80d8260b..c6d37f47ac 100644 --- a/config/locales/views/auth/nl.yml +++ b/config/locales/views/auth/nl.yml @@ -8,7 +8,7 @@ nl: institution-search: "Typ om jouw school te vinden" ilearn-help: "i-Learn gebruikers kunnen momenteel enkel inloggen via de i-Learn website." higher-education: "Hoger onderwijs" - other-education: "Andere onderwijsinstellingen" + other-education: "Generieke login methodes" other: "Andere organisaties" blacklist: sgpaulus: Use your sgpaulus-account (firstname.lastname@sgpaulus.eu) to sign in to Dodona. From 1ede9bfbc003b90c6ff671fd116446cd8bfa3708 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Fri, 15 Jul 2022 14:02:57 +0200 Subject: [PATCH 21/63] Move comment to the correct line --- app/controllers/auth/omniauth_callbacks_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb index 7162ac2218..170797f767 100644 --- a/app/controllers/auth/omniauth_callbacks_controller.rb +++ b/app/controllers/auth/omniauth_callbacks_controller.rb @@ -152,9 +152,9 @@ def create_identity_from_session!(user) end def find_identity_by_uid + # In case of provider without uids, don't return any identity (As it won't be matching a unique user) return nil if auth_uid.nil? - # In case of provider without uids, don't return any identity (As it won't be matching a unique user) identity = Identity.find_by(identifier: auth_uid, provider: provider) return identity unless identity.nil? && provider.class.sym == :office365 && auth_email.present? From 80082318f7b06479d211bd52d6862549af3275f0 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Fri, 15 Jul 2022 14:36:43 +0200 Subject: [PATCH 22/63] Update config/locales/views/auth/nl.yml --- config/locales/views/auth/nl.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/views/auth/nl.yml b/config/locales/views/auth/nl.yml index c6d37f47ac..71306015bb 100644 --- a/config/locales/views/auth/nl.yml +++ b/config/locales/views/auth/nl.yml @@ -8,7 +8,7 @@ nl: institution-search: "Typ om jouw school te vinden" ilearn-help: "i-Learn gebruikers kunnen momenteel enkel inloggen via de i-Learn website." higher-education: "Hoger onderwijs" - other-education: "Generieke login methodes" + other-education: "Generieke loginmethodes" other: "Andere organisaties" blacklist: sgpaulus: Use your sgpaulus-account (firstname.lastname@sgpaulus.eu) to sign in to Dodona. From 0d1e1d44c05cd7de28ea3d24ebbc7798b6103566 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Tue, 2 Aug 2022 15:35:26 +0200 Subject: [PATCH 23/63] Update privacy announcement to also cover personal accounts --- app/views/pages/privacy.nl.html.erb | 40 ++++++++++++++++++----------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/app/views/pages/privacy.nl.html.erb b/app/views/pages/privacy.nl.html.erb index 68d463d339..9ffbf64e08 100644 --- a/app/views/pages/privacy.nl.html.erb +++ b/app/views/pages/privacy.nl.html.erb @@ -6,12 +6,21 @@

Wie is de verwerkingsverantwoordelijke en hoe kan je ons contacteren?

-

Dodona verwerkt persoonsgegevens in opdracht van respectievelijk de school, onderwijs- of - onderzoeksinstelling die jou dit platform aanbiedt.

-

Voor vragen, opmerkingen of suggesties over de verwerking van jouw persoonsgegevens kan je in eerste - instantie terecht bij je school, onderwijs- of onderzoeksinstelling. Uiteraard kan je ook rechtstreeks +

Indien je dodona gebruikt in functie van een bedrijf, school, onderwijs- of onderzoeksinstelling zijn zij de + verwerkingsverantwoordelijke en kan je in eerste instantie bij hen terecht voor vragen, opmerkingen of + suggesties over de verwerking van jouw persoonsgegevens.

+ +

In alle andere gevallen is de verwerkingsverantwoordelijke:

+

+ Universiteit Gent
+ Campus Ufo, Rectoraat
+ Sint-Pietersnieuwstraat 25
+ 9000 Gent
+

+ +

Voor vragen, opmerkingen of suggesties over de verwerking van jouw persoonsgegevens kan je contact opnemen met het Dodona-team of de - Data Protection Officer van de UGent contacteren. + Data Protection Officer van de UGent.

Welke persoonsgegevens worden verwerkt?

@@ -25,10 +34,11 @@ gegevens met betrekking tot iemands seksueel gedrag of seksuele gerichtheid.

Doeleinde en rechtsgrond van de verwerking van jouw persoonsgegevens

-

Persoonsgegevens worden door Dodona verwerkt om jouw school, onderwijs- of onderzoeksinstelling te helpen in - de uitvoering en ondersteuning van haar onderwijsopdracht en om de correcte werking van Dodona te waarborgen - en te verbeteren. Deze onderwijsopdracht kan berusten op het algemeen belang, op een wettelijke verplichting - of op het gerechtvaardigd belang van jouw school, onderwijs- of onderzoeksinstelling.

+

Persoonsgegevens worden door Dodona verwerkt om jouw voortgang doorheen jouw leertraject te kunnen opvolgen, + om jouw school, onderwijs- of onderzoeksinstelling te helpen in de uitvoering en ondersteuning van haar + onderwijsopdracht en om de correcte werking van Dodona te waarborgen en te verbeteren. Deze onderwijsopdracht + kan berusten op het algemeen belang, op een wettelijke verplichting of op het gerechtvaardigd belang van jouw + school, onderwijs- of onderzoeksinstelling.

De persoonsgegevens uit Dodona kunnen gebruikt worden in het kader van wetenschappelijk onderzoek aan de Universiteit Gent door het Dodona team, en dit met het oog op het publiceren van wetenschappelijke artikels over Dodona. Dit onderzoek is in eerste instantie bedoeld om de werking van en de gebruikerservaring binnen @@ -51,9 +61,9 @@ zodoende de voortgang van gebruikers doorheen hun leertraject te kunnen blijven opvolgen en om betere dienstverlening aan gebruikers te kunnen aanbieden. Dodona gebruikt historische data enkel om een betere ondersteuning te ontwikkelen voor gebruikers en om technische problemen op te sporen en te remediëren. Op - vraag van een individuele gebruiker kan de verwerking van zijn/haar gegevens stopgezet worden, na afstemming - met de respectievelijke school, onderwijs- of onderzoeksinstelling en indien dit in overeenstemming is met de - bepalingen uit “Welke rechten heb je in het kader van de verwerking van jouw persoonsgegevens?”.

+ vraag van een individuele gebruiker kan de verwerking van zijn/haar gegevens stopgezet worden, indien dit in overeenstemming is met de + bepalingen uit “Welke rechten heb je in het kader van de verwerking van jouw persoonsgegevens?”. + Indien van toepassing zal dit verzoek eerst worden afgestemd met het bedrijf, de school, onderwijs- of onderzoeksinstelling van de gebruiker.

Logbestanden worden door Dodona slechts zeven dagen bewaard, waarna ze automatisch gewist worden. Deze logbestanden worden gedurende deze 7 dagen enkel geraadpleegd om technische problemen op te sporen en te remediëren.

@@ -66,9 +76,9 @@

Welke rechten heb je in het kader van de verwerking van jouw persoonsgegevens?

Bij de verwerking van jouw persoonsgegevens door Dodona kan je in bepaalde omstandigheden verschillende rechten uitoefenen zoals bijvoorbeeld het recht op inzage, aanpassing en verwijdering van de persoonsgegevens - die van jou verwerkt worden. Voor het uitoefenen van deze rechten kan je je wenden tot jouw school, onderwijs- - of onderzoeksinstelling, die vervolgens in overleg en met bijstand van het Dodona-team je vraag zal - behandelen. Indien je meent dat onvoldoende gevolg werd gegeven aan jouw vraag, kan je je wenden tot de + die van jou verwerkt worden. Voor het uitoefenen van deze rechten kan je je wenden tot het Dodona-team die je vraag zal + behandelen, indien van toepassing zal dit in overleg met jouw bedrijf, school, onderwijs- of onderzoeksinstelling gebeuren. + Indien je meent dat onvoldoende gevolg werd gegeven aan jouw vraag, kan je je wenden tot de Vlaamse toezichthouder:

Vlaamse Toezichtcommissie voor de verwerking van persoonsgegevens
From daf062013ab0d7d77612cbe817e565483273b395 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Tue, 2 Aug 2022 16:01:15 +0200 Subject: [PATCH 24/63] Update english version of the privacy statement --- app/views/pages/privacy.en.html.erb | 30 +++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/app/views/pages/privacy.en.html.erb b/app/views/pages/privacy.en.html.erb index 65e08d451d..9b72b73fdf 100644 --- a/app/views/pages/privacy.en.html.erb +++ b/app/views/pages/privacy.en.html.erb @@ -6,10 +6,18 @@

Who is the data controller, and how can you contact us?

-

Dodona processes personal data on behalf of the school, educational or research institution that offers you - this platform.

-

Questions, comments or suggestions about the processing of your personal data should primarily be addressed - by your school, educational or research institution. Of course, you can also directly contact +

If you are using dodona as a member of a company, school, educational or research institution, they are the data controller. + You can contact them with questions, comments or suggestions about the processing of your personal data.

+ +

In every other case, the data controller is:

+

+ Ghent University
+ Rectorate
+ Sint-Pietersnieuwstraat 25
+ 9000 Ghent
+

+ +

Questions, comments or suggestions about the processing of your personal data can be addressed to the Dodona team or the Data Protection Officer of Ghent University.

@@ -23,7 +31,8 @@ data, or data relating to a person’s sexual behavior or sexual orientation.

Goals and justifications of the processing of your personal data

-

Personal data is processed by Dodona to assist your school, educational or research institution in the +

Personal data is processed by Dodona in order to enable continuous monitoring of your progress throughout + your learning process, to assist your school, educational or research institution in the implementation and support of their educational assignment and to guarantee and improve the correct functioning of Dodona. This educational assignment can be based on public interest, a legal obligation or the legitimate interest of your school, educational or research institution.

@@ -45,9 +54,10 @@

Dodona does not delete any personal data supplied to the platform on its own initiative, in order to enable continuous monitoring of users' progress throughout their learning process and to provide improved user experiences. Dodona uses historical data only to develop better user support and to detect and remedy - technical issues. At the request of an individual user, the processing of his/her data can be stopped after - coordination with your school, educational or research institution and if this is in accordance with the - provisions in "What rights do you have with regard to the processing of your personal data?".

+ technical issues. At the request of an individual user, the processing of his/her data can be stopped if this + is in accordance with the provisions in "What rights do you have with regard to the processing of your personal data?". + If relevant we will first coordinate your request with your company, school, educational or research institution. +

Log files are only stored by Dodona for seven days, after which they are automatically deleted. These log files are only consulted to detect and remedy technical issues.

@@ -58,8 +68,8 @@

What rights do you have with regard to the processing of your personal data?

When Dodona processes your personal data, you can exercise various rights in certain circumstances, such as the right to access, modify and delete your personal data that is processed by Dodona. To exercise these - rights, you can address your school, educational or research institution, which will then deal with your - requests in consultation with and with the assistance of the Dodona team. If you believe that your requests + rights, you can address the Dodona team. If relevant we will first coordinate your request with your company, + school, educational or research institution. If you believe that your requests have not been adequately handled, you can turn to the Flemish regulator:

Vlaamse Toezichtcommissie voor de verwerking van persoonsgegevens
From 146fa1355be8b9e713de811195001a889116b201 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 3 Aug 2022 10:11:59 +0200 Subject: [PATCH 25/63] Update dutch privacy statement --- app/views/pages/privacy.nl.html.erb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/views/pages/privacy.nl.html.erb b/app/views/pages/privacy.nl.html.erb index 9ffbf64e08..0d544786b0 100644 --- a/app/views/pages/privacy.nl.html.erb +++ b/app/views/pages/privacy.nl.html.erb @@ -6,7 +6,7 @@

Wie is de verwerkingsverantwoordelijke en hoe kan je ons contacteren?

-

Indien je dodona gebruikt in functie van een bedrijf, school, onderwijs- of onderzoeksinstelling zijn zij de +

Indien je inlogt in dodona met een account die behoort tot een school, onderwijs- of onderzoeksinstelling zijn zij de verwerkingsverantwoordelijke en kan je in eerste instantie bij hen terecht voor vragen, opmerkingen of suggesties over de verwerking van jouw persoonsgegevens.

@@ -34,8 +34,12 @@ gegevens met betrekking tot iemands seksueel gedrag of seksuele gerichtheid.

Doeleinde en rechtsgrond van de verwerking van jouw persoonsgegevens

-

Persoonsgegevens worden door Dodona verwerkt om jouw voortgang doorheen jouw leertraject te kunnen opvolgen, - om jouw school, onderwijs- of onderzoeksinstelling te helpen in de uitvoering en ondersteuning van haar +

Bij het aanmaken van een dodona account wordt jouw expliciete instemming met deze privacyverklaring gevraagd. + Dat wil zeggen dat je akkoord gaat met alle verwerking van jouw data die beschreven staan in deze verklaring. + Je kan te allen tijde deze toestemming intrekken. Dit gebeurt volgens de bepalingen uit “Welke rechten heb je + in het kader van de verwerking van jouw persoonsgegevens?” +

+

Persoonsgegevens worden door Dodona verwerkt om jouw school, onderwijs- of onderzoeksinstelling te helpen in de uitvoering en ondersteuning van haar onderwijsopdracht en om de correcte werking van Dodona te waarborgen en te verbeteren. Deze onderwijsopdracht kan berusten op het algemeen belang, op een wettelijke verplichting of op het gerechtvaardigd belang van jouw school, onderwijs- of onderzoeksinstelling.

@@ -63,7 +67,7 @@ ondersteuning te ontwikkelen voor gebruikers en om technische problemen op te sporen en te remediëren. Op vraag van een individuele gebruiker kan de verwerking van zijn/haar gegevens stopgezet worden, indien dit in overeenstemming is met de bepalingen uit “Welke rechten heb je in het kader van de verwerking van jouw persoonsgegevens?”. - Indien van toepassing zal dit verzoek eerst worden afgestemd met het bedrijf, de school, onderwijs- of onderzoeksinstelling van de gebruiker.

+ Indien van toepassing zal dit verzoek eerst worden afgestemd met de school, onderwijs- of onderzoeksinstelling van de gebruiker.

Logbestanden worden door Dodona slechts zeven dagen bewaard, waarna ze automatisch gewist worden. Deze logbestanden worden gedurende deze 7 dagen enkel geraadpleegd om technische problemen op te sporen en te remediëren.

@@ -77,7 +81,7 @@

Bij de verwerking van jouw persoonsgegevens door Dodona kan je in bepaalde omstandigheden verschillende rechten uitoefenen zoals bijvoorbeeld het recht op inzage, aanpassing en verwijdering van de persoonsgegevens die van jou verwerkt worden. Voor het uitoefenen van deze rechten kan je je wenden tot het Dodona-team die je vraag zal - behandelen, indien van toepassing zal dit in overleg met jouw bedrijf, school, onderwijs- of onderzoeksinstelling gebeuren. + behandelen, indien van toepassing zal dit in overleg met jouw school, onderwijs- of onderzoeksinstelling gebeuren. Indien je meent dat onvoldoende gevolg werd gegeven aan jouw vraag, kan je je wenden tot de Vlaamse toezichthouder:

From f3c5ee00f7a4fcdcdf27cc8d6d0836b840995489 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 3 Aug 2022 13:02:25 +0200 Subject: [PATCH 26/63] Change the name of google workspace to google --- app/models/provider/g_suite.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/provider/g_suite.rb b/app/models/provider/g_suite.rb index 7a289adfe4..a197562a25 100644 --- a/app/models/provider/g_suite.rb +++ b/app/models/provider/g_suite.rb @@ -33,7 +33,7 @@ def self.logo end def self.readable_name - 'Google Workspace' + 'Google' end def self.extract_institution_name(auth_hash) From 3ec2615604f5512e7b0fc7dae44fa5246c595c1c Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 3 Aug 2022 14:49:43 +0200 Subject: [PATCH 27/63] Remove visible for institutional users setting --- app/models/course.rb | 5 ++-- app/policies/course_policy.rb | 11 +++---- config/locales/models/en.yml | 1 - config/locales/models/nl.yml | 1 - config/locales/views/courses/en.yml | 3 -- config/locales/views/courses/nl.yml | 3 -- test/controllers/courses_controller_test.rb | 32 +-------------------- test/fixtures/courses.yml | 2 +- 8 files changed, 8 insertions(+), 50 deletions(-) diff --git a/app/models/course.rb b/app/models/course.rb index 725c9f295d..8ab82f33d4 100644 --- a/app/models/course.rb +++ b/app/models/course.rb @@ -54,7 +54,7 @@ class Course < ApplicationRecord has_many :course_labels, dependent: :destroy - enum visibility: { visible_for_all: 3, visible_for_institutional_users: 0, visible_for_institution: 1, hidden: 2 } + enum visibility: { visible_for_all: 0, visible_for_institution: 1, hidden: 2 } enum registration: { open_for_all: 3, open_for_institutional_users: 0, open_for_institution: 1, closed: 2 } # TODO: Remove and use activities? @@ -167,7 +167,7 @@ class Course < ApplicationRecord # Default year & enum values after_initialize do |course| - self.visibility ||= 'visible_for_institutional_users' + self.visibility ||= 'visible_for_all' self.registration ||= 'open_for_institutional_users' unless year now = Time.zone.now @@ -206,7 +206,6 @@ def formatted_attribution def secret_required?(user = nil) return false if visible_for_all? return false if visible_for_institution? && user&.institution == institution - return false if visible_for_institutional_users? && user&.institutional? true end diff --git a/app/policies/course_policy.rb b/app/policies/course_policy.rb index 0e5e76355b..59b754cc99 100644 --- a/app/policies/course_policy.rb +++ b/app/policies/course_policy.rb @@ -7,12 +7,10 @@ def resolve scope elsif user @scope = scope.joins(:course_memberships) - filtered_scope = scope.where(visibility: :visible_for_all) - filtered_scope = filtered_scope.or(scope.where(institution: user.institution, visibility: :visible_for_institution)) - filtered_scope = filtered_scope.or(scope.where(course_memberships: { status: %i[student course_admin], user_id: user.id })) - filtered_scope = filtered_scope.or(scope.where(visibility: :visible_for_institutional_users)) if user.institutional? - filtered_scope.distinct - else + scope.where(visibility: :visible_for_all) + .or(scope.where(institution: user.institution, visibility: :visible_for_institution)) + .or(scope.where(course_memberships: { status: %i[student course_admin], user_id: user.id })) + .distinct scope.where(visibility: :visible_for_all) end end @@ -40,7 +38,6 @@ def copy? user&.zeus? || record.visible_for_all? || (record.visible_for_institution? && record.institution == user&.institution) || - (record.visible_for_institutional_users? && user&.institutional?) || record.subscribed_members.include?(user) ) end diff --git a/config/locales/models/en.yml b/config/locales/models/en.yml index 8e116f78fb..623d3b5a85 100644 --- a/config/locales/models/en.yml +++ b/config/locales/models/en.yml @@ -49,7 +49,6 @@ en: featured: Featured course visibilities: visible_for_all: Visible for everyone - visible_for_institutional_users: Visible for all users that belong to an institution visible_for_institution: Only visible for members of the configured institution hidden: Hidden registrations: diff --git a/config/locales/models/nl.yml b/config/locales/models/nl.yml index 25fc850125..6a7335b43c 100644 --- a/config/locales/models/nl.yml +++ b/config/locales/models/nl.yml @@ -50,7 +50,6 @@ nl: featured: Uitgelichte cursus visibilities: visible_for_all: Zichtbaar voor iedereen - visible_for_institutional_users: Zichtbaar voor alle gebruikers die tot een onderwijsinstelling behoren visible_for_institution: Zichtbaar voor gebruikers van de ingestelde onderwijsinstelling hidden: Verborgen registrations: diff --git a/config/locales/views/courses/en.yml b/config/locales/views/courses/en.yml index 904de81a02..7ef26dfcaa 100644 --- a/config/locales/views/courses/en.yml +++ b/config/locales/views/courses/en.yml @@ -14,7 +14,6 @@ en: visibility-visible_for_all-help_html: This course is part of the public course overview, and everyone can see the contents. visibility-visible_for_institution-help_html: This course is only accessible for users of the configured institution and via the direct link below. visibility-hidden-help_html: This course will not be part of the public course overview and is only accessible via the direct link below. - visibility-visible_for_institutional_users-help_html: This course is accessible for anyone that belongs to an institution. registration-open_for_all-help_html: Anyone can register for this course. registration-open_for_institution-help_html: Only members of the configured institution can register for this course. registration-closed-help_html: All new registrations are disabled. @@ -26,7 +25,6 @@ en: visibility-visible_for_all_html: Everyone visibility-visible_for_institution_html: Only users of the configured institution visibility-hidden_html: Only registered users - visibility-visible_for_institutional_users_html: Everyone that belongs to an institution registration-open_for_all_html: Everyone registration-open_for_institution_html: Only users of the configured institution registration-closed_html: Nobody @@ -99,7 +97,6 @@ en: visibility-visible_for_all-info: "This course is visible for everyone: everyone can access this course from the course overview, and the contents are visible for everyone." visibility-visible_for_institution-info: "This course is visible for members of %{institution}: they can access this course from the course overview and see its contents. Members of other institutions need the secret link." visibility-hidden-info: "This course is hidden: it is not listed in the course overview, and users need the secret link to access this course." - visibility-visible_for_institutional_users-info: "This course is accessible for anyone that belongs to an institution: they can access this course from the course overview and see its contents. Personal accounts need the secret link." registration-open_for_all-info: "Registrations are open for everyone: all users can register for this course." registration-open_for_institution-info: "Registrations are open for members of %{institution}: only they can register for this course." registration-closed-info: "Registrations are closed: users can't register until you change the registration settings. Users who are currently registered still have access." diff --git a/config/locales/views/courses/nl.yml b/config/locales/views/courses/nl.yml index 4841ca7444..f97994a44f 100644 --- a/config/locales/views/courses/nl.yml +++ b/config/locales/views/courses/nl.yml @@ -14,7 +14,6 @@ nl: visibility-visible_for_all-help_html: Deze cursus wordt opgenomen in het publieke cursusoverzicht en iedereen kan de inhoud bekijken. visibility-visible_for_institution-help_html: Deze cursus is enkel toegankelijk voor gebruikers van de ingestelde onderwijsinstelling en via onderstaande directe link. visibility-hidden-help_html: Deze cursus wordt niet opgenomen in het publieke cursusoverzicht en is enkel toegankelijk via onderstaande directe link. - visibility-visible_for_institutional_users-help_html: Iedereen die tot een instelling behoort kan deze cursus bekijken. registration-open_for_all-help_html: Iedereen kan zich voor deze cursus registreren. registration-open_for_institution-help_html: Enkel gebruikers van de ingestelde onderwijsinstelling kunnen zich voor deze cursus registreren. registration-closed-help_html: Er zijn geen nieuwe registraties mogelijk. @@ -26,7 +25,6 @@ nl: visibility-visible_for_all_html: Iedereen visibility-visible_for_institution_html: Enkel gebruikers van de ingestelde onderwijsinstelling visibility-hidden_html: Enkel geregistreerde gebruikers - visibility-visible_for_institutional_users_html: Alle gebruikers die tot een onderwijsinstelling behoren registration-open_for_all_html: Iedereen registration-open_for_institution_html: Enkel gebruikers van de ingestelde onderwijsinstelling registration-closed_html: Niemand @@ -109,7 +107,6 @@ nl: visibility-visible_for_institution-info: 'Deze cursus is zichtbaar voor leden van %{institution}: voor hen wordt deze cursus opgelijst in het cursusoverzicht en is de inhoud zichtbaar. Anderen kunnen enkel de inhoud van deze cursus zien via de geheime link' visibility-hidden-info: 'Deze cursus is verborgen: ze verschijnt niet in het cursusoverzicht en gebruikers kunnen deze cursus enkel zien via de geheime link.' registration-open_for_all-info: 'De registraties staan open voor iedereen: gebruikers die op de registratie-knop klikken worden geregistreerd.' - visibility-visible_for_institutional_users-info: "Iedereen die tot een instelling behoort kan deze cursus bekijken: voor hen wordt deze cursus opgelijst in het cursusoverzicht en is de inhoud zichtbaar. Anderen kunnen enkel de inhoud van deze cursus zien via de geheime link" registration-open_for_institution-info: 'De registraties staan open voor gebruikers van %{institution}: enkel zij kunnen zich registreren voor deze cursus.' registration-closed-info: 'De registraties zijn gesloten: niemand kan zich nog registreren tot de registratie-instelling wordt aangepast. Gebruikers die geregistreerd zijn behouden hun toegang.' registration-open_for_institutional_users-info: 'De registraties staan open voor gebruikers die tot een instelling behoren: enkel zij kunnen zich registreren voor deze cursus.' diff --git a/test/controllers/courses_controller_test.rb b/test/controllers/courses_controller_test.rb index 8a80d5608d..ad7eb0c931 100644 --- a/test/controllers/courses_controller_test.rb +++ b/test/controllers/courses_controller_test.rb @@ -158,7 +158,7 @@ def with_users_signed_in(users) test 'should get course page with secret' do add_not_subscribed with_users_signed_in @not_subscribed.compact do |who, user| - %w[visible_for_all visible_for_institutional_users visible_for_institution hidden].product(%w[open_for_all open_for_institutional_users open_for_institution closed], [true, false]).each do |v, r, m| + %w[visible_for_all visible_for_institution hidden].product(%w[open_for_all open_for_institutional_users open_for_institution closed], [true, false]).each do |v, r, m| @course.update(visibility: v, registration: r, moderated: m) get course_url(@course, secret: @course.secret, format: :json) assert_response :success, "#{who} should get registration page" @@ -712,36 +712,6 @@ def with_users_signed_in(users) assert_not response.body.include?(subscribe_course_path(@course, secret: @course.secret)) end - test 'personal user should only see visible_for_institutional_users course with secret and after subscription' do - add_externals - @course.update(visibility: :visible_for_institutional_users) - user = @externals.first - user.update(institution_id: nil) - sign_in user - get course_url(@course) - assert_not response.successful? - get course_url(@course, secret: @course.secret) - assert response.successful? - post subscribe_course_url(@course) - assert_not @course.subscribed_members.include?(user) - post subscribe_course_url(@course, secret: @course.secret) - assert @course.subscribed_members.include?(user) - get course_url(@course) - assert response.successful? - end - - test 'institutional user should always see visible_for_institutional_users course' do - add_externals - @course.update(visibility: :visible_for_institutional_users) - user = @externals.first - user.update(institution: (create :institution)) - sign_in user - get course_url(@course) - assert response.successful? - get course_url(@course, secret: @course.secret) - assert response.successful? - end - test 'personal user should not be able to subscribe to open_for_institutional_users course' do add_externals @course.update(registration: :open_for_institutional_users) diff --git a/test/fixtures/courses.yml b/test/fixtures/courses.yml index 8e1f9e67b1..1da0126d5e 100644 --- a/test/fixtures/courses.yml +++ b/test/fixtures/courses.yml @@ -24,7 +24,7 @@ course1: name: course 1 year: 2021-2022 description: This is the description of course 1 - visibility: 3 # visible for all + visibility: 0 # visible for all registration: 3 # open for all teacher: Prof. Zonnebloem secret: "PuDCHk3A7qCEAbxKXPpz" From 9b0ff8d5e7ea2821bd27cd0ca776a57a83a7c2dc Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 3 Aug 2022 15:14:29 +0200 Subject: [PATCH 28/63] Make texts clearer --- config/locales/views/courses/en.yml | 4 ++-- config/locales/views/courses/nl.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config/locales/views/courses/en.yml b/config/locales/views/courses/en.yml index 7ef26dfcaa..5fc0833aaa 100644 --- a/config/locales/views/courses/en.yml +++ b/config/locales/views/courses/en.yml @@ -17,7 +17,7 @@ en: registration-open_for_all-help_html: Anyone can register for this course. registration-open_for_institution-help_html: Only members of the configured institution can register for this course. registration-closed-help_html: All new registrations are disabled. - registration-open_for_institutional_users-help_html: Anyone that belongs to an institution can register for this course. + registration-open_for_institutional_users-help_html: Anyone that belongs to an institution can register for this course. Users that signed in using a private account, such as a personal gmail or outlook account, cannot register. description-help_html: A description is optional. Markdown formatting can be used. visibility-label: For whom is this course visible? registration-label: Who can register for this course? @@ -100,7 +100,7 @@ en: registration-open_for_all-info: "Registrations are open for everyone: all users can register for this course." registration-open_for_institution-info: "Registrations are open for members of %{institution}: only they can register for this course." registration-closed-info: "Registrations are closed: users can't register until you change the registration settings. Users who are currently registered still have access." - registration-open_for_institutional_users-info: "Registrations are open for users that belong to an institution: only they can register for this course." + registration-open_for_institutional_users-info: "Registrations are open for users that belong to an institution: only they can register for this course. Registrations are closed for users who signed in using a private account, such as a personal gmail or outlook account." moderated-info: "This course is moderated. Users who register will have to be explicitly approved by a course admin." subscribe: Register request_registration: Request registration diff --git a/config/locales/views/courses/nl.yml b/config/locales/views/courses/nl.yml index f97994a44f..f77d0c0d97 100644 --- a/config/locales/views/courses/nl.yml +++ b/config/locales/views/courses/nl.yml @@ -17,7 +17,7 @@ nl: registration-open_for_all-help_html: Iedereen kan zich voor deze cursus registreren. registration-open_for_institution-help_html: Enkel gebruikers van de ingestelde onderwijsinstelling kunnen zich voor deze cursus registreren. registration-closed-help_html: Er zijn geen nieuwe registraties mogelijk. - registration-open_for_institutional_users-help_html: Iedereen die tot een instelling behoort kan zich voor deze cursus registreren. + registration-open_for_institutional_users-help_html: Iedereen die tot een instelling behoort kan zich voor deze cursus registreren. Gebruikers die zijn ingelogd met een privaat account, zoals een persoonlijk gmail of outlook account, kunnen niet registreren. description-help_html: Een beschrijving is optioneel en kan in Markdown geschreven worden. visibility-label: Voor wie is deze cursus zichtbaar? registration-label: Wie kan zich registreren voor deze cursus? @@ -107,7 +107,7 @@ nl: visibility-visible_for_institution-info: 'Deze cursus is zichtbaar voor leden van %{institution}: voor hen wordt deze cursus opgelijst in het cursusoverzicht en is de inhoud zichtbaar. Anderen kunnen enkel de inhoud van deze cursus zien via de geheime link' visibility-hidden-info: 'Deze cursus is verborgen: ze verschijnt niet in het cursusoverzicht en gebruikers kunnen deze cursus enkel zien via de geheime link.' registration-open_for_all-info: 'De registraties staan open voor iedereen: gebruikers die op de registratie-knop klikken worden geregistreerd.' - registration-open_for_institution-info: 'De registraties staan open voor gebruikers van %{institution}: enkel zij kunnen zich registreren voor deze cursus.' + registration-open_for_institution-info: 'De registraties staan open voor gebruikers van %{institution}: enkel zij kunnen zich registreren voor deze cursus. De registraties zijn gesloten voor gebruikers die zijn ingelogd met een privaat account, zoals een persoonlijk gmail of outlook account.' registration-closed-info: 'De registraties zijn gesloten: niemand kan zich nog registreren tot de registratie-instelling wordt aangepast. Gebruikers die geregistreerd zijn behouden hun toegang.' registration-open_for_institutional_users-info: 'De registraties staan open voor gebruikers die tot een instelling behoren: enkel zij kunnen zich registreren voor deze cursus.' moderated-info: 'Deze cursus is gemodereerd: Gebruikers die zich registreren moeten expliciet goedgekeurd worden door een cursusbeheerder.' From 276e39a45565fee6a972784b3aa03d71d4487a3e Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 3 Aug 2022 15:20:54 +0200 Subject: [PATCH 29/63] Fix bugs --- app/policies/course_policy.rb | 1 + config/locales/views/courses/nl.yml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/policies/course_policy.rb b/app/policies/course_policy.rb index 59b754cc99..33b9c85556 100644 --- a/app/policies/course_policy.rb +++ b/app/policies/course_policy.rb @@ -11,6 +11,7 @@ def resolve .or(scope.where(institution: user.institution, visibility: :visible_for_institution)) .or(scope.where(course_memberships: { status: %i[student course_admin], user_id: user.id })) .distinct + else scope.where(visibility: :visible_for_all) end end diff --git a/config/locales/views/courses/nl.yml b/config/locales/views/courses/nl.yml index f77d0c0d97..4cee542aca 100644 --- a/config/locales/views/courses/nl.yml +++ b/config/locales/views/courses/nl.yml @@ -107,9 +107,9 @@ nl: visibility-visible_for_institution-info: 'Deze cursus is zichtbaar voor leden van %{institution}: voor hen wordt deze cursus opgelijst in het cursusoverzicht en is de inhoud zichtbaar. Anderen kunnen enkel de inhoud van deze cursus zien via de geheime link' visibility-hidden-info: 'Deze cursus is verborgen: ze verschijnt niet in het cursusoverzicht en gebruikers kunnen deze cursus enkel zien via de geheime link.' registration-open_for_all-info: 'De registraties staan open voor iedereen: gebruikers die op de registratie-knop klikken worden geregistreerd.' - registration-open_for_institution-info: 'De registraties staan open voor gebruikers van %{institution}: enkel zij kunnen zich registreren voor deze cursus. De registraties zijn gesloten voor gebruikers die zijn ingelogd met een privaat account, zoals een persoonlijk gmail of outlook account.' + registration-open_for_institution-info: 'De registraties staan open voor gebruikers van %{institution}: enkel zij kunnen zich registreren voor deze cursus.' registration-closed-info: 'De registraties zijn gesloten: niemand kan zich nog registreren tot de registratie-instelling wordt aangepast. Gebruikers die geregistreerd zijn behouden hun toegang.' - registration-open_for_institutional_users-info: 'De registraties staan open voor gebruikers die tot een instelling behoren: enkel zij kunnen zich registreren voor deze cursus.' + registration-open_for_institutional_users-info: 'De registraties staan open voor gebruikers die tot een instelling behoren: enkel zij kunnen zich registreren voor deze cursus. De registraties zijn gesloten voor gebruikers die zijn ingelogd met een privaat account, zoals een persoonlijk gmail of outlook account.' moderated-info: 'Deze cursus is gemodereerd: Gebruikers die zich registreren moeten expliciet goedgekeurd worden door een cursusbeheerder.' subscribe: Registreren request_registration: Registratieverzoek indienen From 990cec4dde5528f03b14f5f724fc85a704f18221 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 3 Aug 2022 16:11:59 +0200 Subject: [PATCH 30/63] Update english privacy stetement --- app/views/pages/privacy.en.html.erb | 13 ++++++++----- app/views/pages/privacy.nl.html.erb | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/views/pages/privacy.en.html.erb b/app/views/pages/privacy.en.html.erb index 9b72b73fdf..a5a1d5d896 100644 --- a/app/views/pages/privacy.en.html.erb +++ b/app/views/pages/privacy.en.html.erb @@ -6,7 +6,7 @@

Who is the data controller, and how can you contact us?

-

If you are using dodona as a member of a company, school, educational or research institution, they are the data controller. +

If you are log in to Dodona using an account that belongs to a school, educational or research institution, they are the data controller. You can contact them with questions, comments or suggestions about the processing of your personal data.

In every other case, the data controller is:

@@ -31,8 +31,11 @@ data, or data relating to a person’s sexual behavior or sexual orientation.

Goals and justifications of the processing of your personal data

-

Personal data is processed by Dodona in order to enable continuous monitoring of your progress throughout - your learning process, to assist your school, educational or research institution in the +

In order to create an account on Dodona, you have to explicitly consent to this privacy statement. This means + that you agree to all forms of data processing that are outlined in this privacy statement. You can withdraw this + consent at any time. This should happen in accordance with the provisions in "What rights do you have with regard + to the processing of your personal data?"

+

Personal data is processed by Dodona in order to assist your school, educational or research institution in the implementation and support of their educational assignment and to guarantee and improve the correct functioning of Dodona. This educational assignment can be based on public interest, a legal obligation or the legitimate interest of your school, educational or research institution.

@@ -56,7 +59,7 @@ experiences. Dodona uses historical data only to develop better user support and to detect and remedy technical issues. At the request of an individual user, the processing of his/her data can be stopped if this is in accordance with the provisions in "What rights do you have with regard to the processing of your personal data?". - If relevant we will first coordinate your request with your company, school, educational or research institution. + If relevant we will first coordinate your request with your school, educational or research institution.

Log files are only stored by Dodona for seven days, after which they are automatically deleted. These log files are only consulted to detect and remedy technical issues.

@@ -68,7 +71,7 @@

What rights do you have with regard to the processing of your personal data?

When Dodona processes your personal data, you can exercise various rights in certain circumstances, such as the right to access, modify and delete your personal data that is processed by Dodona. To exercise these - rights, you can address the Dodona team. If relevant we will first coordinate your request with your company, + rights, you can address the Dodona team. If relevant we will first coordinate your request with your school, educational or research institution. If you believe that your requests have not been adequately handled, you can turn to the Flemish regulator:

diff --git a/app/views/pages/privacy.nl.html.erb b/app/views/pages/privacy.nl.html.erb index 0d544786b0..6e352b85b0 100644 --- a/app/views/pages/privacy.nl.html.erb +++ b/app/views/pages/privacy.nl.html.erb @@ -6,7 +6,7 @@

Wie is de verwerkingsverantwoordelijke en hoe kan je ons contacteren?

-

Indien je inlogt in dodona met een account die behoort tot een school, onderwijs- of onderzoeksinstelling zijn zij de +

Indien je inlogt in Dodona met een account die behoort tot een school, onderwijs- of onderzoeksinstelling zijn zij de verwerkingsverantwoordelijke en kan je in eerste instantie bij hen terecht voor vragen, opmerkingen of suggesties over de verwerking van jouw persoonsgegevens.

From 3142b238655321f538c50c96b92e8d8f34ac3d08 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 3 Aug 2022 16:21:51 +0200 Subject: [PATCH 31/63] Update data pages --- app/views/pages/data.en.html.erb | 2 +- app/views/pages/data.nl.html.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/pages/data.en.html.erb b/app/views/pages/data.en.html.erb index cab3407abc..a79db8950c 100644 --- a/app/views/pages/data.en.html.erb +++ b/app/views/pages/data.en.html.erb @@ -9,7 +9,7 @@

Personal data

-

When signing into Dodona, we exclusively use the authentication system of your school or institution. Examples of these are Smartschool and Office 365 for secondary education and SAML for higher education. This means that under no circumstances, Dodona gets access to your password. What we do get access to when signing in are your name, username and email address. We need these data to identify you correctly and to be able to contact you in case of any problems. Next to that, we also store the institution you used to sign in, your preferred language and your local timezone. Your timezone is needed to display any deadlines set by courses correctly.

+

When signing into Dodona, we exclusively use external authentication systems. Examples of these are Office 365, google, Smartschool (for secondary education only) and SAML (for higher education only). This means that under no circumstances, Dodona gets access to your password. What we do get access to when signing in are your name, username and email address. We need these data to identify you correctly and to be able to contact you in case of any problems. Next to that, we also store the institution you used to sign in, your preferred language and your local timezone. Your timezone is needed to display any deadlines set by courses correctly.

You can check these data for yourself on your profile page. Your profile is also visible for the teachers of the courses you subscribed to.

<%= image_tag "outline-account_circle.svg", class: "img-fluid", style: "width:90%" %>
diff --git a/app/views/pages/data.nl.html.erb b/app/views/pages/data.nl.html.erb index 8c96114139..816feba7a2 100644 --- a/app/views/pages/data.nl.html.erb +++ b/app/views/pages/data.nl.html.erb @@ -9,7 +9,7 @@

Persoonlijke gegevens

-

Om aan te melden bij Dodona gebruiken we uitsluitend het loginsysteem van jouw school, onderwijs- of onderzoeksinstelling. Voorbeelden hiervan zijn Smartschool en Office 365 voor het secundair onderwijs en SAML voor het hoger onderwijs. Dodona krijgt hierdoor op geen enkel moment jouw wachtwoord te zien. Wat we wel meekrijgen bij het inloggen zijn je naam, gebruikersnaam en e-mailadres. Deze gegevens hebben we nodig om je correct te identificeren en om je te contacteren in geval van problemen. Daarnaast houden we ook bij via welke school je bent ingelogd, wat je voorkeurstaal is en in welke tijdzone je je bevindt. Dit laatste is nodig om eventuele deadlines correct weer te geven.

+

Om aan te melden bij Dodona gebruiken we uitsluitend het externe loginsystemen. Voorbeelden hiervan zijn Office 365, google, Smartschool (alleen voor het secundair onderwijs) en SAML (alleen voor het hoger onderwijs). Dodona krijgt hierdoor op geen enkel moment jouw wachtwoord te zien. Wat we wel meekrijgen bij het inloggen zijn je naam, gebruikersnaam en e-mailadres. Deze gegevens hebben we nodig om je correct te identificeren en om je te contacteren in geval van problemen. Daarnaast houden we ook bij via welke school je bent ingelogd, wat je voorkeurstaal is en in welke tijdzone je je bevindt. Dit laatste is nodig om eventuele deadlines correct weer te geven.

Deze gegevens kan je zelf bekijken op je profielpagina. Daarnaast kunnen ook alle lesgevers van de cursussen waarvoor je bent ingeschreven je profiel bekijken.

<%= image_tag "outline-account_circle.svg", class: "img-fluid", style: "width:90%" %>
From 316086828843722d70abfede877fbece0c9fd897 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 3 Aug 2022 16:25:19 +0200 Subject: [PATCH 32/63] Fix unwanted change --- app/views/pages/privacy.en.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/pages/privacy.en.html.erb b/app/views/pages/privacy.en.html.erb index a5a1d5d896..fb962a3c38 100644 --- a/app/views/pages/privacy.en.html.erb +++ b/app/views/pages/privacy.en.html.erb @@ -35,7 +35,7 @@ that you agree to all forms of data processing that are outlined in this privacy statement. You can withdraw this consent at any time. This should happen in accordance with the provisions in "What rights do you have with regard to the processing of your personal data?"

-

Personal data is processed by Dodona in order to assist your school, educational or research institution in the +

Personal data is processed by Dodona to assist your school, educational or research institution in the implementation and support of their educational assignment and to guarantee and improve the correct functioning of Dodona. This educational assignment can be based on public interest, a legal obligation or the legitimate interest of your school, educational or research institution.

From 85edbde85f879268ce8fd085a16d1ad3b49245d1 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 3 Aug 2022 16:26:15 +0200 Subject: [PATCH 33/63] Fix unwanted change --- app/views/pages/privacy.nl.html.erb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/pages/privacy.nl.html.erb b/app/views/pages/privacy.nl.html.erb index 6e352b85b0..9d503a4f10 100644 --- a/app/views/pages/privacy.nl.html.erb +++ b/app/views/pages/privacy.nl.html.erb @@ -39,10 +39,10 @@ Je kan te allen tijde deze toestemming intrekken. Dit gebeurt volgens de bepalingen uit “Welke rechten heb je in het kader van de verwerking van jouw persoonsgegevens?”

-

Persoonsgegevens worden door Dodona verwerkt om jouw school, onderwijs- of onderzoeksinstelling te helpen in de uitvoering en ondersteuning van haar - onderwijsopdracht en om de correcte werking van Dodona te waarborgen en te verbeteren. Deze onderwijsopdracht - kan berusten op het algemeen belang, op een wettelijke verplichting of op het gerechtvaardigd belang van jouw - school, onderwijs- of onderzoeksinstelling.

+

Persoonsgegevens worden door Dodona verwerkt om jouw school, onderwijs- of onderzoeksinstelling te helpen in + de uitvoering en ondersteuning van haar onderwijsopdracht en om de correcte werking van Dodona te waarborgen + en te verbeteren. Deze onderwijsopdracht kan berusten op het algemeen belang, op een wettelijke verplichting + of op het gerechtvaardigd belang van jouw school, onderwijs- of onderzoeksinstelling.

De persoonsgegevens uit Dodona kunnen gebruikt worden in het kader van wetenschappelijk onderzoek aan de Universiteit Gent door het Dodona team, en dit met het oog op het publiceren van wetenschappelijke artikels over Dodona. Dit onderzoek is in eerste instantie bedoeld om de werking van en de gebruikerservaring binnen From d9d635e5a644cec9c29e483460682c01dee6fc9b Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 3 Aug 2022 16:27:18 +0200 Subject: [PATCH 34/63] Improve sentence --- app/views/pages/privacy.nl.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/pages/privacy.nl.html.erb b/app/views/pages/privacy.nl.html.erb index 9d503a4f10..092e3397cc 100644 --- a/app/views/pages/privacy.nl.html.erb +++ b/app/views/pages/privacy.nl.html.erb @@ -35,7 +35,7 @@

Doeleinde en rechtsgrond van de verwerking van jouw persoonsgegevens

Bij het aanmaken van een dodona account wordt jouw expliciete instemming met deze privacyverklaring gevraagd. - Dat wil zeggen dat je akkoord gaat met alle verwerking van jouw data die beschreven staan in deze verklaring. + Dat wil zeggen dat je akkoord gaat met alle vormen van verwerking van jouw data die beschreven staan in deze verklaring. Je kan te allen tijde deze toestemming intrekken. Dit gebeurt volgens de bepalingen uit “Welke rechten heb je in het kader van de verwerking van jouw persoonsgegevens?”

From bd288b1a29f2619d58ed92fc6a690946390876b0 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Thu, 4 Aug 2022 10:14:32 +0200 Subject: [PATCH 35/63] Force new users to accept the privacy policy before creating an account --- app/controllers/application_controller.rb | 2 +- .../auth/omniauth_callbacks_controller.rb | 54 ++++++++++++++++--- app/views/auth/privacy_prompt.html.erb | 19 +++++++ app/views/pages/_privacy_disclaimer.html.erb | 10 ---- app/views/pages/home.html.erb | 1 - config/locales/views/auth/en.yml | 3 ++ config/locales/views/auth/nl.yml | 3 ++ config/locales/views/pages/en.yml | 2 - config/locales/views/pages/nl.yml | 2 - config/routes.rb | 2 + .../omniauth_callbacks_controller_test.rb | 24 +++++++++ 11 files changed, 100 insertions(+), 22 deletions(-) create mode 100644 app/views/auth/privacy_prompt.html.erb delete mode 100644 app/views/pages/_privacy_disclaimer.html.erb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index e27d7474be..4b1e94e939 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -12,7 +12,7 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :null_session before_action :store_current_location, - except: %i[media sign_in institution_not_supported], + except: %i[media sign_in institution_not_supported privacy_prompt accept_privacy_policy], unless: -> { devise_controller? || remote_request? } before_action :skip_session, diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb index 00e048e2ab..5989cd19cd 100644 --- a/app/controllers/auth/omniauth_callbacks_controller.rb +++ b/app/controllers/auth/omniauth_callbacks_controller.rb @@ -47,6 +47,14 @@ def smartschool generic_oauth end + # ==> Privacy agreement acceptance before new account creation + + def privacy_prompt; end + + def accept_privacy_policy + sign_in_new_user_from_session! + end + private # ==> Authentication logic. @@ -96,10 +104,8 @@ def try_login! return redirect_to_known_provider!(user) if user.present? # No existing user was found - # Create a new user - user = User.new institution: provider&.institution - # Create a new identity for the newly created user - identity = user.identities.build identifier: auth_uid, provider: provider + # Redirect to privacy prompt before we create a new user + return redirect_to_privacy_prompt end # Validation. @@ -111,6 +117,12 @@ def try_login! user.update_from_provider(auth_hash, provider) return redirect_with_errors!(user) if user.errors.any? + sign_in!(user) + end + + # ==> Utilities. + + def sign_in!(user) # If the session contains credentials for another identity, add this identity to the signed in user create_identity_from_session!(user) @@ -122,7 +134,37 @@ def try_login! redirect_to_target!(user) end - # ==> Utilities. + def redirect_to_privacy_prompt + session[:new_user_identifier] = auth_hash.uid + session[:new_user_email] = auth_hash.info.email + session[:new_user_first_name] = auth_hash.info.first_name + session[:new_user_last_name] = auth_hash.info.last_name + session[:new_user_provider_id] = provider&.id + + redirect_to privacy_prompt_path + end + + def sign_in_new_user_from_session! + identifier = session.delete(:new_user_identifier) + email = session.delete(:new_user_email) + first_name = session.delete(:new_user_first_name) + last_name = session.delete(:new_user_last_name) + provider_id = session.delete(:new_user_provider_id) + + provider = Provider.find(provider_id) + + # Create a new user + user = User.new institution: provider&.institution, email: email, first_name: first_name, last_name: last_name, + username: identifier + + # Create a new identity for the newly created user + user.identities.build identifier: identifier, provider: provider + user.save + + return redirect_with_errors!(user) if user.errors.any? + + sign_in!(user) + end def create_identity_from_session!(user) # Find the original provider and uid in the session. @@ -319,7 +361,7 @@ def auth_redirect_params end def auth_target - return nil if auth_hash.extra[:target].blank? + return nil if auth_hash.blank? || auth_hash.extra[:target].blank? "#{auth_hash.extra[:target]}?#{auth_redirect_params.to_param}" end diff --git a/app/views/auth/privacy_prompt.html.erb b/app/views/auth/privacy_prompt.html.erb new file mode 100644 index 0000000000..1b7dbf1313 --- /dev/null +++ b/app/views/auth/privacy_prompt.html.erb @@ -0,0 +1,19 @@ +
+
+
+
+
+ +
+
+ <%= t ".text_html", your_data: data_url, privacy_statement: privacy_url %> +
+
+
+ <%= link_to privacy_prompt_path, method: :post, class: "btn btn-primary" do %> + <%= t ".button" %> + <% end %> +
+
+
+
diff --git a/app/views/pages/_privacy_disclaimer.html.erb b/app/views/pages/_privacy_disclaimer.html.erb deleted file mode 100644 index d09ab5ab99..0000000000 --- a/app/views/pages/_privacy_disclaimer.html.erb +++ /dev/null @@ -1,10 +0,0 @@ -
-
-
- -
-
- <%= t ".text_html", your_data: data_url, privacy_statement: privacy_url %> -
-
-
diff --git a/app/views/pages/home.html.erb b/app/views/pages/home.html.erb index 0e1e245c04..4f766691ba 100644 --- a/app/views/pages/home.html.erb +++ b/app/views/pages/home.html.erb @@ -5,7 +5,6 @@
<% if @subscribed_courses.empty? %> <%= render "getting_started_card" %> - <%= render 'privacy_disclaimer' %> <% else %>
<% unless @favorite_courses.empty? %> diff --git a/config/locales/views/auth/en.yml b/config/locales/views/auth/en.yml index 3566747410..9330af81a0 100644 --- a/config/locales/views/auth/en.yml +++ b/config/locales/views/auth/en.yml @@ -22,3 +22,6 @@ en: providers_title: "Link accounts" "contact_support_html": "If this isn't you or you keep having issues? Fill in the %{form} and we will assist you as soon as possible." contact_form: "contact form" + privacy_prompt: + text_html: "Your privacy is important to us. On the your data page we explain in clear and understandable language what data we collect and how we use it. Our privacy statement contains the legally binding version." + button: Accept the privacy policy diff --git a/config/locales/views/auth/nl.yml b/config/locales/views/auth/nl.yml index 6a80d8260b..ff201ddbb2 100644 --- a/config/locales/views/auth/nl.yml +++ b/config/locales/views/auth/nl.yml @@ -22,3 +22,6 @@ nl: providers_title: "Accounts koppelen" "contact_support_html": "Ben jij dit niet of blijf je problemen hebben? Vul het %{form} in en we helpen je zo snel mogelijk verder." contact_form: "contactformulier" + privacy_prompt: + text_html: "Jouw privacy is belangrijk voor ons. Op de jouw data pagina leggen we in mensentaal uit welke data we verzamelen en hoe we die gebruiken. De juridisch bindende versie kan je in onze privacyverklaring vinden." + button: Accepteer de privacyverklaring diff --git a/config/locales/views/pages/en.yml b/config/locales/views/pages/en.yml index 39cd520bae..af075bf119 100644 --- a/config/locales/views/pages/en.yml +++ b/config/locales/views/pages/en.yml @@ -63,8 +63,6 @@ en: create_contact: mail_sent: "Your message has been sent. Thanks for getting in touch." captcha_failed: HCaptcha could not be verified; please try again. - privacy_disclaimer: - text_html: "Your privacy is important to us. On the your data page we explain in clear and understandable language what data we collect and how we use it. Our privacy statement contains the legally binding version." support: support_dodona: Support Dodona title_l1: Learn to code for free diff --git a/config/locales/views/pages/nl.yml b/config/locales/views/pages/nl.yml index 387f2e15f4..131692b984 100644 --- a/config/locales/views/pages/nl.yml +++ b/config/locales/views/pages/nl.yml @@ -63,8 +63,6 @@ nl: create_contact: captcha_failed: HCaptcha kon niet geverifieerd worden, probeer opnieuw. mail_sent: "Je bericht werd verstuurd. Bedankt om contact op te nemen." - privacy_disclaimer: - text_html: "Jouw privacy is belangrijk voor ons. Op de jouw data pagina leggen we in mensentaal uit welke data we verzamelen en hoe we die gebruiken. De juridisch bindende versie kan je in onze privacyverklaring vinden." support: support_dodona: Steun Dodona title_l1: Gratis leren programmeren diff --git a/config/routes.rb b/config/routes.rb index cfa62dc6c7..402bd03131 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -18,6 +18,8 @@ devise_scope :user do get '/sign_in' => 'authentication#sign_in', as: 'sign_in' delete '/sign_out' => 'authentication#destroy', as: 'sign_out' + get '/privacy_prompt' => 'omniauth_callbacks#privacy_prompt' + post '/privacy_prompt' => 'omniauth_callbacks#accept_privacy_policy' end get '/users/saml/metadata' => 'saml#metadata' diff --git a/test/controllers/auth/omniauth_callbacks_controller_test.rb b/test/controllers/auth/omniauth_callbacks_controller_test.rb index 21c57c9fdc..9383b23915 100644 --- a/test/controllers/auth/omniauth_callbacks_controller_test.rb +++ b/test/controllers/auth/omniauth_callbacks_controller_test.rb @@ -90,6 +90,11 @@ def omniauth_path(provider) assert_difference 'Identity.count', 1 do get omniauth_url(provider) follow_redirect! + + # assert privacy prompt before successful sign in + assert_redirected_to privacy_prompt_path + assert_nil @controller.current_user + post privacy_prompt_path end end @@ -115,6 +120,11 @@ def omniauth_path(provider) assert_difference 'Identity.count', 1 do get omniauth_url(provider) follow_redirect! + + # assert privacy prompt before successful sign in + assert_redirected_to privacy_prompt_path + assert_nil @controller.current_user + post privacy_prompt_path end end @@ -141,6 +151,11 @@ def omniauth_path(provider) assert_difference 'Institution.count', 1 do get omniauth_url(provider) follow_redirect! + + # assert privacy prompt before successful sign in + assert_redirected_to privacy_prompt_path + assert_nil @controller.current_user + post privacy_prompt_path end end end @@ -276,6 +291,11 @@ def omniauth_path(provider) assert_difference 'User.count', +1 do get omniauth_url(provider) follow_redirect! + + # assert privacy prompt before successful sign in + assert_redirected_to privacy_prompt_path + assert_nil @controller.current_user + post privacy_prompt_path end assert_equal @controller.current_user.username, user.username @@ -315,6 +335,10 @@ def omniauth_path(provider) assert_difference 'User.count', 0 do get omniauth_url(second_provider) follow_redirect! + + assert_redirected_to privacy_prompt_path + assert_nil @controller.current_user + post privacy_prompt_path end assert_redirected_to root_path From a368b37172a91f4e848f23ff34b9d11170c0781c Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Thu, 4 Aug 2022 10:35:16 +0200 Subject: [PATCH 36/63] Fix misnamed method --- app/controllers/auth/omniauth_callbacks_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb index 5989cd19cd..2c6fcd513f 100644 --- a/app/controllers/auth/omniauth_callbacks_controller.rb +++ b/app/controllers/auth/omniauth_callbacks_controller.rb @@ -231,7 +231,7 @@ def find_or_create_oauth_provider institution_created provider else - institution_creation_failed institution.errors + institution_create_failed institution.errors nil end end From 15a91fd98178fe9b5c8588837ec672b03761f8e3 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Thu, 4 Aug 2022 10:40:46 +0200 Subject: [PATCH 37/63] Fix flanders oidc test --- test/integration/auth/vlaanderen/oidc_vlaanderen_test.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/integration/auth/vlaanderen/oidc_vlaanderen_test.rb b/test/integration/auth/vlaanderen/oidc_vlaanderen_test.rb index d52b3aa3e7..55f81b75cd 100644 --- a/test/integration/auth/vlaanderen/oidc_vlaanderen_test.rb +++ b/test/integration/auth/vlaanderen/oidc_vlaanderen_test.rb @@ -179,6 +179,11 @@ def stub_keys!(kid = nil) assert_equal 'authorization_code', parameters[:grant_type].first end + # assert privacy prompt before successful sign in + assert_redirected_to privacy_prompt_path + assert_nil @controller.current_user + post privacy_prompt_path + # Validate that the user is correctly logged in. current_user = @controller.current_user assert_equal id_token_body[:given_name], current_user.first_name From d92504e1c322509337016bdeb7482902b46bef97 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Thu, 4 Aug 2022 11:36:08 +0200 Subject: [PATCH 38/63] Fix show privacy prompt --- app/controllers/auth/omniauth_callbacks_controller.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb index 2c6fcd513f..5ce8e4085c 100644 --- a/app/controllers/auth/omniauth_callbacks_controller.rb +++ b/app/controllers/auth/omniauth_callbacks_controller.rb @@ -49,7 +49,9 @@ def smartschool # ==> Privacy agreement acceptance before new account creation - def privacy_prompt; end + def privacy_prompt + render 'auth/privacy_prompt' + end def accept_privacy_policy sign_in_new_user_from_session! From eee496a23616ff7c74d40c482f000490c48c7061 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Thu, 4 Aug 2022 13:09:22 +0200 Subject: [PATCH 39/63] Update text and provide a decline option --- app/views/auth/privacy_prompt.html.erb | 5 ++++- config/locales/views/auth/en.yml | 5 +++-- config/locales/views/auth/nl.yml | 5 +++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/views/auth/privacy_prompt.html.erb b/app/views/auth/privacy_prompt.html.erb index 1b7dbf1313..a9fadfa319 100644 --- a/app/views/auth/privacy_prompt.html.erb +++ b/app/views/auth/privacy_prompt.html.erb @@ -10,8 +10,11 @@
+ <%= link_to root_path, class: "btn btn-danger" do %> + <%= t ".decline_button" %> + <% end %> <%= link_to privacy_prompt_path, method: :post, class: "btn btn-primary" do %> - <%= t ".button" %> + <%= t ".accept_button" %> <% end %>
diff --git a/config/locales/views/auth/en.yml b/config/locales/views/auth/en.yml index 9330af81a0..9a3b4252d3 100644 --- a/config/locales/views/auth/en.yml +++ b/config/locales/views/auth/en.yml @@ -23,5 +23,6 @@ en: "contact_support_html": "If this isn't you or you keep having issues? Fill in the %{form} and we will assist you as soon as possible." contact_form: "contact form" privacy_prompt: - text_html: "Your privacy is important to us. On the your data page we explain in clear and understandable language what data we collect and how we use it. Our privacy statement contains the legally binding version." - button: Accept the privacy policy + text_html: "In order to use Dodona, you need to accept our privacy statement. On the your data page we explain in clear and understandable language what data we collect and how we use it. Our privacy statement contains the legally binding version." + accept_button: Accept the privacy statement + decline_button: Decline diff --git a/config/locales/views/auth/nl.yml b/config/locales/views/auth/nl.yml index ff201ddbb2..940c11a3b2 100644 --- a/config/locales/views/auth/nl.yml +++ b/config/locales/views/auth/nl.yml @@ -23,5 +23,6 @@ nl: "contact_support_html": "Ben jij dit niet of blijf je problemen hebben? Vul het %{form} in en we helpen je zo snel mogelijk verder." contact_form: "contactformulier" privacy_prompt: - text_html: "Jouw privacy is belangrijk voor ons. Op de jouw data pagina leggen we in mensentaal uit welke data we verzamelen en hoe we die gebruiken. De juridisch bindende versie kan je in onze privacyverklaring vinden." - button: Accepteer de privacyverklaring + text_html: "Om Dodona te gebruiken moet je onze privacyverklaring accepteren. Op de jouw data pagina leggen we in mensentaal uit welke data we verzamelen en hoe we die gebruiken. De juridisch bindende versie kan je in onze privacyverklaring vinden." + accept_button: Accepteer de privacyverklaring + decline_button: Weigeren From 7665d43b750232b4ef6ce3bcd9df4b21f4a7afbd Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Thu, 4 Aug 2022 14:02:43 +0200 Subject: [PATCH 40/63] Fix lti auth tests --- app/controllers/auth/omniauth_callbacks_controller.rb | 3 ++- test/controllers/lti_controller_test.rb | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb index 5ce8e4085c..9ee3c9afe5 100644 --- a/app/controllers/auth/omniauth_callbacks_controller.rb +++ b/app/controllers/auth/omniauth_callbacks_controller.rb @@ -142,6 +142,7 @@ def redirect_to_privacy_prompt session[:new_user_first_name] = auth_hash.info.first_name session[:new_user_last_name] = auth_hash.info.last_name session[:new_user_provider_id] = provider&.id + session[:new_user_auth_target] = auth_target redirect_to privacy_prompt_path end @@ -341,7 +342,7 @@ def redirect_to_target!(user) # ==> Shorthands. def target_path(user) - auth_target || after_sign_in_path_for(user) + auth_target || session.delete(:new_user_auth_target) || after_sign_in_path_for(user) end def auth_hash diff --git a/test/controllers/lti_controller_test.rb b/test/controllers/lti_controller_test.rb index 6829bdb5ec..2529dbfefb 100644 --- a/test/controllers/lti_controller_test.rb +++ b/test/controllers/lti_controller_test.rb @@ -142,6 +142,11 @@ def teardown state: params[:state] } + # assert privacy prompt before successful sign in + assert_redirected_to privacy_prompt_path + assert_nil @controller.current_user + post privacy_prompt_path + assert_response :found target_uri = URI.parse(@response.header['Location']) params = URI.decode_www_form(target_uri.query).to_h.symbolize_keys @@ -171,6 +176,11 @@ def teardown state: params[:state] } + # assert privacy prompt before successful sign in + assert_redirected_to privacy_prompt_path + assert_nil @controller.current_user + post privacy_prompt_path + assert_response :redirect target_uri = URI.parse(@response.header['Location']) params = URI.decode_www_form(target_uri.query).to_h.symbolize_keys From f3016a19a6ed585c84c94d490891762eaf77da6a Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Fri, 5 Aug 2022 12:03:40 +0200 Subject: [PATCH 41/63] Apply suggestions from code review Co-authored-by: Niko Strijbol --- app/views/pages/data.en.html.erb | 2 +- app/views/pages/data.nl.html.erb | 2 +- app/views/pages/privacy.nl.html.erb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/pages/data.en.html.erb b/app/views/pages/data.en.html.erb index a79db8950c..db6b28beaa 100644 --- a/app/views/pages/data.en.html.erb +++ b/app/views/pages/data.en.html.erb @@ -9,7 +9,7 @@

Personal data

-

When signing into Dodona, we exclusively use external authentication systems. Examples of these are Office 365, google, Smartschool (for secondary education only) and SAML (for higher education only). This means that under no circumstances, Dodona gets access to your password. What we do get access to when signing in are your name, username and email address. We need these data to identify you correctly and to be able to contact you in case of any problems. Next to that, we also store the institution you used to sign in, your preferred language and your local timezone. Your timezone is needed to display any deadlines set by courses correctly.

+

When signing into Dodona, we exclusively use external authentication systems. Examples of these are Office 365, Google, Smartschool (for secondary education only) and SAML (for higher education only). This means that under no circumstances, Dodona gets access to your password. What we do get access to when signing in are your name, username and email address. We need these data to identify you correctly and to be able to contact you in case of any problems. Next to that, we also store the institution you used to sign in, your preferred language and your local timezone. Your timezone is needed to display any deadlines set by courses correctly.

You can check these data for yourself on your profile page. Your profile is also visible for the teachers of the courses you subscribed to.

<%= image_tag "outline-account_circle.svg", class: "img-fluid", style: "width:90%" %>
diff --git a/app/views/pages/data.nl.html.erb b/app/views/pages/data.nl.html.erb index 816feba7a2..3b76d04e6b 100644 --- a/app/views/pages/data.nl.html.erb +++ b/app/views/pages/data.nl.html.erb @@ -9,7 +9,7 @@

Persoonlijke gegevens

-

Om aan te melden bij Dodona gebruiken we uitsluitend het externe loginsystemen. Voorbeelden hiervan zijn Office 365, google, Smartschool (alleen voor het secundair onderwijs) en SAML (alleen voor het hoger onderwijs). Dodona krijgt hierdoor op geen enkel moment jouw wachtwoord te zien. Wat we wel meekrijgen bij het inloggen zijn je naam, gebruikersnaam en e-mailadres. Deze gegevens hebben we nodig om je correct te identificeren en om je te contacteren in geval van problemen. Daarnaast houden we ook bij via welke school je bent ingelogd, wat je voorkeurstaal is en in welke tijdzone je je bevindt. Dit laatste is nodig om eventuele deadlines correct weer te geven.

+

Om aan te melden bij Dodona gebruiken we uitsluitend het externe loginsystemen. Voorbeelden hiervan zijn Office 365, Google, Smartschool (alleen voor het secundair onderwijs) en SAML (alleen voor het hoger onderwijs). Dodona krijgt hierdoor op geen enkel moment jouw wachtwoord te zien. Wat we wel meekrijgen bij het inloggen zijn je naam, gebruikersnaam en e-mailadres. Deze gegevens hebben we nodig om je correct te identificeren en om je te contacteren in geval van problemen. Daarnaast houden we ook bij via welke school je bent ingelogd, wat je voorkeurstaal is en in welke tijdzone je je bevindt. Dit laatste is nodig om eventuele deadlines correct weer te geven.

Deze gegevens kan je zelf bekijken op je profielpagina. Daarnaast kunnen ook alle lesgevers van de cursussen waarvoor je bent ingeschreven je profiel bekijken.

<%= image_tag "outline-account_circle.svg", class: "img-fluid", style: "width:90%" %>
diff --git a/app/views/pages/privacy.nl.html.erb b/app/views/pages/privacy.nl.html.erb index 092e3397cc..1ddf51b208 100644 --- a/app/views/pages/privacy.nl.html.erb +++ b/app/views/pages/privacy.nl.html.erb @@ -34,7 +34,7 @@ gegevens met betrekking tot iemands seksueel gedrag of seksuele gerichtheid.

Doeleinde en rechtsgrond van de verwerking van jouw persoonsgegevens

-

Bij het aanmaken van een dodona account wordt jouw expliciete instemming met deze privacyverklaring gevraagd. +

Bij het aanmaken van een Dodona-account wordt jouw expliciete instemming met deze privacyverklaring gevraagd. Dat wil zeggen dat je akkoord gaat met alle vormen van verwerking van jouw data die beschreven staan in deze verklaring. Je kan te allen tijde deze toestemming intrekken. Dit gebeurt volgens de bepalingen uit “Welke rechten heb je in het kader van de verwerking van jouw persoonsgegevens?” From ed4297bd190532bb75c06a4ab36277b5caa1cf5e Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Fri, 5 Aug 2022 14:28:11 +0200 Subject: [PATCH 42/63] Apply suggestions from code review Co-authored-by: Niko Strijbol --- config/locales/views/auth/nl.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/locales/views/auth/nl.yml b/config/locales/views/auth/nl.yml index 940c11a3b2..b3c24faa80 100644 --- a/config/locales/views/auth/nl.yml +++ b/config/locales/views/auth/nl.yml @@ -23,6 +23,6 @@ nl: "contact_support_html": "Ben jij dit niet of blijf je problemen hebben? Vul het %{form} in en we helpen je zo snel mogelijk verder." contact_form: "contactformulier" privacy_prompt: - text_html: "Om Dodona te gebruiken moet je onze privacyverklaring accepteren. Op de jouw data pagina leggen we in mensentaal uit welke data we verzamelen en hoe we die gebruiken. De juridisch bindende versie kan je in onze privacyverklaring vinden." - accept_button: Accepteer de privacyverklaring + text_html: "Om Dodona te gebruiken moet je onze privacyverklaring accepteren. Op de pagina jouw data leggen we in mensentaal uit welke data we verzamelen en hoe we die gebruiken. De juridisch bindende versie kan je in onze privacyverklaring vinden." + accept_button: Privacyverklaring accepteren decline_button: Weigeren From 1a124a6e6453fd624143e4db0787c749327c4dfd Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Mon, 8 Aug 2022 08:58:37 +0200 Subject: [PATCH 43/63] Apply suggestions from code review Co-authored-by: Niko Strijbol --- config/locales/views/courses/en.yml | 4 ++-- config/locales/views/courses/nl.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config/locales/views/courses/en.yml b/config/locales/views/courses/en.yml index 5fc0833aaa..2c7c5ebdc1 100644 --- a/config/locales/views/courses/en.yml +++ b/config/locales/views/courses/en.yml @@ -17,7 +17,7 @@ en: registration-open_for_all-help_html: Anyone can register for this course. registration-open_for_institution-help_html: Only members of the configured institution can register for this course. registration-closed-help_html: All new registrations are disabled. - registration-open_for_institutional_users-help_html: Anyone that belongs to an institution can register for this course. Users that signed in using a private account, such as a personal gmail or outlook account, cannot register. + registration-open_for_institutional_users-help_html: Anyone that belongs to an institution can register for this course. Users that signed in using a private account, such as a personal Gmail or Outlook account, cannot register. description-help_html: A description is optional. Markdown formatting can be used. visibility-label: For whom is this course visible? registration-label: Who can register for this course? @@ -100,7 +100,7 @@ en: registration-open_for_all-info: "Registrations are open for everyone: all users can register for this course." registration-open_for_institution-info: "Registrations are open for members of %{institution}: only they can register for this course." registration-closed-info: "Registrations are closed: users can't register until you change the registration settings. Users who are currently registered still have access." - registration-open_for_institutional_users-info: "Registrations are open for users that belong to an institution: only they can register for this course. Registrations are closed for users who signed in using a private account, such as a personal gmail or outlook account." + registration-open_for_institutional_users-info: "Registrations are open for users that belong to an institution: only they can register for this course. Registrations are closed for users who signed in using a private account, such as a personal Gmail or Outlook account." moderated-info: "This course is moderated. Users who register will have to be explicitly approved by a course admin." subscribe: Register request_registration: Request registration diff --git a/config/locales/views/courses/nl.yml b/config/locales/views/courses/nl.yml index 4cee542aca..f42f64bc2e 100644 --- a/config/locales/views/courses/nl.yml +++ b/config/locales/views/courses/nl.yml @@ -17,7 +17,7 @@ nl: registration-open_for_all-help_html: Iedereen kan zich voor deze cursus registreren. registration-open_for_institution-help_html: Enkel gebruikers van de ingestelde onderwijsinstelling kunnen zich voor deze cursus registreren. registration-closed-help_html: Er zijn geen nieuwe registraties mogelijk. - registration-open_for_institutional_users-help_html: Iedereen die tot een instelling behoort kan zich voor deze cursus registreren. Gebruikers die zijn ingelogd met een privaat account, zoals een persoonlijk gmail of outlook account, kunnen niet registreren. + registration-open_for_institutional_users-help_html: Iedereen die tot een instelling behoort kan zich voor deze cursus registreren. Gebruikers die zijn ingelogd met een privéaccount, zoals een persoonlijk Gmail- of Outlook-account, kunnen niet registreren. description-help_html: Een beschrijving is optioneel en kan in Markdown geschreven worden. visibility-label: Voor wie is deze cursus zichtbaar? registration-label: Wie kan zich registreren voor deze cursus? @@ -109,7 +109,7 @@ nl: registration-open_for_all-info: 'De registraties staan open voor iedereen: gebruikers die op de registratie-knop klikken worden geregistreerd.' registration-open_for_institution-info: 'De registraties staan open voor gebruikers van %{institution}: enkel zij kunnen zich registreren voor deze cursus.' registration-closed-info: 'De registraties zijn gesloten: niemand kan zich nog registreren tot de registratie-instelling wordt aangepast. Gebruikers die geregistreerd zijn behouden hun toegang.' - registration-open_for_institutional_users-info: 'De registraties staan open voor gebruikers die tot een instelling behoren: enkel zij kunnen zich registreren voor deze cursus. De registraties zijn gesloten voor gebruikers die zijn ingelogd met een privaat account, zoals een persoonlijk gmail of outlook account.' + registration-open_for_institutional_users-info: 'De registraties staan open voor gebruikers die tot een instelling behoren: enkel zij kunnen zich registreren voor deze cursus. De registraties zijn gesloten voor gebruikers die zijn ingelogd met een privéaccount, zoals een persoonlijk Gmail- of Outlook-account.' moderated-info: 'Deze cursus is gemodereerd: Gebruikers die zich registreren moeten expliciet goedgekeurd worden door een cursusbeheerder.' subscribe: Registreren request_registration: Registratieverzoek indienen From 429980d04fdb687fc27c4217591fcad065b987cc Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Fri, 12 Aug 2022 17:05:28 +0200 Subject: [PATCH 44/63] Update config/locales/views/courses/en.yml Co-authored-by: Bart Mesuere --- config/locales/views/courses/en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/views/courses/en.yml b/config/locales/views/courses/en.yml index 2c7c5ebdc1..d808fcc0ff 100644 --- a/config/locales/views/courses/en.yml +++ b/config/locales/views/courses/en.yml @@ -100,7 +100,7 @@ en: registration-open_for_all-info: "Registrations are open for everyone: all users can register for this course." registration-open_for_institution-info: "Registrations are open for members of %{institution}: only they can register for this course." registration-closed-info: "Registrations are closed: users can't register until you change the registration settings. Users who are currently registered still have access." - registration-open_for_institutional_users-info: "Registrations are open for users that belong to an institution: only they can register for this course. Registrations are closed for users who signed in using a private account, such as a personal Gmail or Outlook account." + registration-open_for_institutional_users-info: "Registrations are open for users that belong to any institution. Registrations are closed for users who signed in using a private account, such as a personal Gmail or Outlook account." moderated-info: "This course is moderated. Users who register will have to be explicitly approved by a course admin." subscribe: Register request_registration: Request registration From e611f71590e2109adfa76469f9cdd7b62ae5e43a Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Fri, 12 Aug 2022 17:05:47 +0200 Subject: [PATCH 45/63] Update config/locales/views/courses/nl.yml Co-authored-by: Bart Mesuere --- config/locales/views/courses/nl.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/views/courses/nl.yml b/config/locales/views/courses/nl.yml index f42f64bc2e..db65763953 100644 --- a/config/locales/views/courses/nl.yml +++ b/config/locales/views/courses/nl.yml @@ -17,7 +17,7 @@ nl: registration-open_for_all-help_html: Iedereen kan zich voor deze cursus registreren. registration-open_for_institution-help_html: Enkel gebruikers van de ingestelde onderwijsinstelling kunnen zich voor deze cursus registreren. registration-closed-help_html: Er zijn geen nieuwe registraties mogelijk. - registration-open_for_institutional_users-help_html: Iedereen die tot een instelling behoort kan zich voor deze cursus registreren. Gebruikers die zijn ingelogd met een privéaccount, zoals een persoonlijk Gmail- of Outlook-account, kunnen niet registreren. + registration-open_for_institutional_users-help_html: Iedereen die tot een instelling behoort kan zich voor deze cursus registreren. Gebruikers die zijn ingelogd met een privéaccount, zoals een persoonlijk Gmail- of Outlook-account, kunnen zich niet registreren. description-help_html: Een beschrijving is optioneel en kan in Markdown geschreven worden. visibility-label: Voor wie is deze cursus zichtbaar? registration-label: Wie kan zich registreren voor deze cursus? From 0482cbc6467c5117cdcab7df303f4084621a8ff8 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Fri, 12 Aug 2022 17:06:12 +0200 Subject: [PATCH 46/63] Apply suggestions from code review Co-authored-by: Bart Mesuere --- config/locales/views/courses/en.yml | 2 +- config/locales/views/courses/nl.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/locales/views/courses/en.yml b/config/locales/views/courses/en.yml index d808fcc0ff..e4d7cc81c9 100644 --- a/config/locales/views/courses/en.yml +++ b/config/locales/views/courses/en.yml @@ -17,7 +17,7 @@ en: registration-open_for_all-help_html: Anyone can register for this course. registration-open_for_institution-help_html: Only members of the configured institution can register for this course. registration-closed-help_html: All new registrations are disabled. - registration-open_for_institutional_users-help_html: Anyone that belongs to an institution can register for this course. Users that signed in using a private account, such as a personal Gmail or Outlook account, cannot register. + registration-open_for_institutional_users-help_html: Anyone that belongs to an institution can register for this course. Users that signed in using a private account, such as a personal Gmail or Outlook account, cannot register. description-help_html: A description is optional. Markdown formatting can be used. visibility-label: For whom is this course visible? registration-label: Who can register for this course? diff --git a/config/locales/views/courses/nl.yml b/config/locales/views/courses/nl.yml index db65763953..c8f43ed09d 100644 --- a/config/locales/views/courses/nl.yml +++ b/config/locales/views/courses/nl.yml @@ -109,7 +109,7 @@ nl: registration-open_for_all-info: 'De registraties staan open voor iedereen: gebruikers die op de registratie-knop klikken worden geregistreerd.' registration-open_for_institution-info: 'De registraties staan open voor gebruikers van %{institution}: enkel zij kunnen zich registreren voor deze cursus.' registration-closed-info: 'De registraties zijn gesloten: niemand kan zich nog registreren tot de registratie-instelling wordt aangepast. Gebruikers die geregistreerd zijn behouden hun toegang.' - registration-open_for_institutional_users-info: 'De registraties staan open voor gebruikers die tot een instelling behoren: enkel zij kunnen zich registreren voor deze cursus. De registraties zijn gesloten voor gebruikers die zijn ingelogd met een privéaccount, zoals een persoonlijk Gmail- of Outlook-account.' + registration-open_for_institutional_users-info: 'De registraties staan open voor gebruikers die tot een instelling behoren. De registraties zijn gesloten voor gebruikers die zijn ingelogd met een privéaccount, zoals een persoonlijk Gmail- of Outlook-account.' moderated-info: 'Deze cursus is gemodereerd: Gebruikers die zich registreren moeten expliciet goedgekeurd worden door een cursusbeheerder.' subscribe: Registreren request_registration: Registratieverzoek indienen From d6ff809ea9aa5fd433004c12f23568868d70c257 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Tue, 16 Aug 2022 14:43:02 +0200 Subject: [PATCH 47/63] Rework login sceen --- .../auth/authentication_controller.rb | 2 ++ app/models/provider/g_suite.rb | 9 ++++++- app/views/auth/sign_in.html.erb | 24 +++++++++++++++++++ config/locales/views/auth/en.yml | 4 +++- config/locales/views/auth/nl.yml | 4 +++- 5 files changed, 40 insertions(+), 3 deletions(-) diff --git a/app/controllers/auth/authentication_controller.rb b/app/controllers/auth/authentication_controller.rb index 6088416fab..abf4ec2583 100644 --- a/app/controllers/auth/authentication_controller.rb +++ b/app/controllers/auth/authentication_controller.rb @@ -38,6 +38,8 @@ def sign_in Provider::Surf ] + @private_providers = Provider.where(institution: nil) + @providers = Provider.all @title = I18n.t('auth.sign_in.sign_in') @oauth_providers = apply_scopes(@providers diff --git a/app/models/provider/g_suite.rb b/app/models/provider/g_suite.rb index a197562a25..a9c76d3164 100644 --- a/app/models/provider/g_suite.rb +++ b/app/models/provider/g_suite.rb @@ -33,7 +33,7 @@ def self.logo end def self.readable_name - 'Google' + 'Google Workspace' end def self.extract_institution_name(auth_hash) @@ -46,4 +46,11 @@ def self.extract_institution_name(auth_hash) Provider.extract_institution_name(auth_hash) end end + + def readable_name + # We want to display gmail for private accounts + return 'Gmail' if institution.nil? + + super.readable_name + end end diff --git a/app/views/auth/sign_in.html.erb b/app/views/auth/sign_in.html.erb index a8a7333648..047c922852 100644 --- a/app/views/auth/sign_in.html.erb +++ b/app/views/auth/sign_in.html.erb @@ -60,6 +60,30 @@

+ + +