Skip to content

Commit 6a61cb0

Browse files
committed
Allow external users to configure CodeHarbor links
Previously, only a user themselves was able to change the CodeHarbor link. Now, we modify the behavior, so that admins can change the links, too. Furthermore, this commit fixes the incorrect styling in case of errors, the incorrect bootstrap tooltips and locales shown on the page. Closes #1457
1 parent 4743562 commit 6a61cb0

File tree

18 files changed

+103
-50
lines changed

18 files changed

+103
-50
lines changed

app/assets/stylesheets/base.css.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,9 @@ html[data-bs-theme="light"] {
200200
}
201201
}
202202
}
203+
204+
.input-group {
205+
.field_with_errors {
206+
flex: 1 1 auto;
207+
}
208+
}

app/controllers/codeharbor_links_controller.rb

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@ class CodeharborLinksController < ApplicationController
44
include CommonBehavior
55
before_action :verify_codeharbor_activation
66
before_action :set_codeharbor_link, only: %i[edit update destroy]
7+
before_action :set_user_and_authorize
78

89
def new
910
base_url = CodeOcean::Config.new(:code_ocean).read[:codeharbor][:url] || ''
10-
@codeharbor_link = CodeharborLink.new(push_url: "#{base_url}/import_task",
11-
check_uuid_url: "#{base_url}/import_uuid_check")
11+
@codeharbor_link = CodeharborLink.new(
12+
push_url: "#{base_url}/import_task",
13+
check_uuid_url: "#{base_url}/import_uuid_check",
14+
user: @user
15+
)
1216
authorize!
1317
end
1418

@@ -18,24 +22,26 @@ def edit
1822

1923
def create
2024
@codeharbor_link = CodeharborLink.new(codeharbor_link_params)
21-
@codeharbor_link.user = current_user
25+
@codeharbor_link.user = @user
2226
authorize!
23-
create_and_respond(object: @codeharbor_link, path: -> { @codeharbor_link.user })
27+
create_and_respond(object: @codeharbor_link, path: -> { @user })
2428
end
2529

2630
def update
2731
authorize!
28-
update_and_respond(object: @codeharbor_link, params: codeharbor_link_params, path: @codeharbor_link.user)
32+
update_and_respond(object: @codeharbor_link, params: codeharbor_link_params, path: @user)
2933
end
3034

3135
def destroy
32-
destroy_and_respond(object: @codeharbor_link, path: @codeharbor_link.user)
36+
destroy_and_respond(object: @codeharbor_link, path: @user)
3337
end
3438

3539
private
3640

3741
def authorize!
38-
authorize @codeharbor_link
42+
raise Pundit::NotAuthorizedError if @codeharbor_link.present? && @user.present? && @codeharbor_link.user != @user
43+
44+
authorize(@codeharbor_link)
3945
end
4046

4147
def verify_codeharbor_activation
@@ -47,6 +53,16 @@ def set_codeharbor_link
4753
authorize!
4854
end
4955

56+
def set_user_and_authorize
57+
if params[:external_user_id]
58+
@user = ExternalUser.find(params[:external_user_id])
59+
else
60+
@user = InternalUser.find(params[:internal_user_id])
61+
end
62+
params[:user_id] = @user.id_with_type # for the breadcrumbs
63+
authorize(@user, :change_codeharbor_link?)
64+
end
65+
5066
def codeharbor_link_params
5167
params.require(:codeharbor_link).permit(:push_url, :check_uuid_url, :api_key)
5268
end

app/models/codeharbor_link.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
# frozen_string_literal: true
22

33
class CodeharborLink < ApplicationRecord
4+
include Creation
5+
46
validates :push_url, presence: true
57
validates :check_uuid_url, presence: true
68
validates :api_key, presence: true
79

8-
belongs_to :user, class_name: 'InternalUser'
9-
1010
def to_s
1111
"#{model_name.human} #{id}"
1212
end
13+
14+
def self.parent_resource
15+
User
16+
end
1317
end

app/models/user.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ def self.find_by_id_with_type(id_with_type)
8080
end
8181
end
8282

83+
def self.find_by(*args, id: nil)
84+
# Allow `User.find_by(id: 'e123')` and `User.find_by(id: 'i123')`
85+
# Inherited classes keep their original behavior.
86+
return super unless self == User
87+
88+
find_by_id_with_type(id)
89+
end
90+
8391
def self.ransackable_attributes(auth_object)
8492
if auth_object.present? && auth_object.admin?
8593
%w[name email external_id consumer_id platform_admin id]

app/policies/codeharbor_link_policy.rb

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,16 @@
33
class CodeharborLinkPolicy < ApplicationPolicy
44
CODEHARBOR_CONFIG = CodeOcean::Config.new(:code_ocean).read[:codeharbor]
55

6-
def index?
7-
no_one
6+
%i[index? show?].each do |action|
7+
define_method(action) { no_one }
88
end
99

10-
def show?
11-
no_one
10+
%i[new? create?].each do |action|
11+
define_method(action) { enabled? && (admin? || teacher?) && (@user.admin? || @user.teacher?) }
1212
end
1313

14-
def new?
15-
enabled? && (teacher? || admin?)
16-
end
17-
18-
def create?
19-
enabled? && (teacher? || admin?)
20-
end
21-
22-
def edit?
23-
enabled? && owner?
24-
end
25-
26-
def update?
27-
enabled? && owner?
28-
end
29-
30-
def destroy?
31-
enabled? && owner?
14+
%i[destroy? update? edit?].each do |action|
15+
define_method(action) { enabled? && (admin? || owner?) }
3216
end
3317

3418
def enabled?
@@ -38,6 +22,6 @@ def enabled?
3822
private
3923

4024
def owner?
41-
@record.reload.user == @user
25+
@record.user == @user
4226
end
4327
end

app/policies/external_user_policy.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ def tag_statistics?
1717
admin?
1818
end
1919

20+
def change_codeharbor_link?
21+
admin? || @record == @user
22+
end
23+
2024
class Scope < Scope
2125
def resolve
2226
if @user.admin?

app/policies/internal_user_policy.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ def show?
1313
admin? || @record == @user || teacher_in_study_group?
1414
end
1515

16+
def change_codeharbor_link?
17+
admin? || @record == @user
18+
end
19+
1620
class Scope < Scope
1721
def resolve
1822
if @user.admin?
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
= form_for(@codeharbor_link) do |f|
1+
= form_for([@user, @codeharbor_link]) do |f|
22
= render('shared/form_errors', object: @codeharbor_link)
33
.mb-3
44
= f.label(:push_url, class: 'form-label')
5-
= f.text_field :push_url, data: {toggle: 'tooltip', placement: 'bottom'}, title: t('codeharbor_link.info.push_url'), class: 'form-control'
5+
= f.text_field :push_url, data: {'bs-toggle': 'tooltip', 'bs-placement': 'bottom'}, title: t('codeharbor_link.info.push_url'), class: 'form-control'
66
.mb-3
77
= f.label(:check_uuid_url, class: 'form-label')
8-
= f.text_field :check_uuid_url, data: {toggle: 'tooltip', placement: 'bottom'}, title: t('codeharbor_link.info.check_uuid_url'), class: 'form-control'
8+
= f.text_field :check_uuid_url, data: {'bs-toggle': 'tooltip', 'bs-placement': 'bottom'}, title: t('codeharbor_link.info.check_uuid_url'), class: 'form-control'
99
.mb-3
1010
= f.label(:api_key, class: 'form-label')
1111
.input-group
12-
= f.text_field(:api_key, data: {toggle: 'tooltip', placement: 'bottom'}, title: t('codeharbor_link.info.api_key'), class: 'form-control api_key')
12+
= f.text_field(:api_key, data: {'bs-toggle': 'tooltip', 'bs-placement': 'bottom'}, title: t('codeharbor_link.info.api_key'), class: 'form-control api_key')
1313
.input-group-btn
1414
= button_tag t('codeharbor_link.generate'), type: 'button', class: 'generate-api_key btn btn-default'
1515
.actions
1616
= render('shared/submit_button', f:, object: @codeharbor_link)
1717
- if @codeharbor_link.persisted?
18-
= link_to(t('codeharbor_link.delete'), codeharbor_link_path(@codeharbor_link), data: {confirm: t('shared.confirm_destroy')}, method: :delete, class: 'btn btn-danger float-end')
18+
= link_to(t('codeharbor_link.delete'), polymorphic_path([@codeharbor_link.user, @codeharbor_link]), data: {confirm: t('shared.confirm_destroy')}, method: :delete, class: 'btn btn-danger float-end')

app/views/external_users/show.html.slim

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ h1 = @user.displayname
3131
- else
3232
= t('users.show.no_groups')
3333

34+
- if @user == current_user || current_user.admin?
35+
= row(label: 'codeharbor_link.profile_label', value: @user.codeharbor_link.nil? ? link_to(t('codeharbor_link.new'), polymorphic_path([@user, CodeharborLink], action: :new), class: 'btn btn-secondary') : link_to(t('codeharbor_link.edit'), polymorphic_path([@user, @user.codeharbor_link], action: :edit), class: 'btn btn-secondary'))
36+
3437
h4.mt-4 = link_to(t('.exercise_statistics'), statistics_external_user_path(@user)) if policy(@user).statistics?
3538

3639
- if current_user.admin?

app/views/internal_users/show.html.slim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,5 @@ h1
2828
- else
2929
= t('users.show.no_groups')
3030

31-
- if @user == current_user
32-
= row(label: 'codeharbor_link.profile_label', value: @user.codeharbor_link.nil? ? link_to(t('codeharbor_link.new'), new_codeharbor_link_path, class: 'btn btn-secondary') : link_to(t('codeharbor_link.edit'), edit_codeharbor_link_path(@user.codeharbor_link), class: 'btn btn-secondary'))
31+
- if @user == current_user || current_user.admin?
32+
= row(label: 'codeharbor_link.profile_label', value: @user.codeharbor_link.nil? ? link_to(t('codeharbor_link.new'), polymorphic_path([@user, CodeharborLink], action: :new), class: 'btn btn-secondary') : link_to(t('codeharbor_link.edit'), polymorphic_path([@user, @user.codeharbor_link], action: :edit), class: 'btn btn-secondary'))

0 commit comments

Comments
 (0)