From 4befcf757e1361dea854bc3221ed8cb7d6c76f2c Mon Sep 17 00:00:00 2001
From: Felix Eckhofer <felix@eckhofer.com>
Date: Sat, 21 Dec 2024 02:02:26 +0100
Subject: [PATCH] Restrict signups to shiftcoordinator role

---
 app/controllers/application_controller.rb                 | 6 ++++++
 app/controllers/assignments_controller.rb                 | 1 +
 app/controllers/candidates_controller.rb                  | 2 ++
 app/helpers/application_helper.rb                         | 1 +
 app/javascript/controllers/session_controller.js          | 4 ++++
 app/views/assignments/_user_avatar.html.erb               | 2 +-
 app/views/candidates/_user_avatar.html.erb                | 2 +-
 app/views/sessions/_session.html.erb                      | 1 +
 .../20241220201657_add_shiftcoordinator_to_users.rb       | 5 +++++
 db/schema.rb                                              | 8 ++++----
 10 files changed, 26 insertions(+), 6 deletions(-)
 create mode 100644 db/migrate/20241220201657_add_shiftcoordinator_to_users.rb

diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index fde1425..a470bde 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -6,4 +6,10 @@ class ApplicationController < ActionController::Base
   def configure_permitted_parameters
     devise_parameter_sanitizer.permit(:sign_up, keys: [:invitation_token])
   end
+
+  def authorize_shiftcoordinator
+    unless current_user.shiftcoordinator?
+      render plain: 'Forbidden', status: :forbidden
+    end
+  end
 end
diff --git a/app/controllers/assignments_controller.rb b/app/controllers/assignments_controller.rb
index 878c06c..6270499 100644
--- a/app/controllers/assignments_controller.rb
+++ b/app/controllers/assignments_controller.rb
@@ -1,6 +1,7 @@
 require 'icalendar/tzinfo'
 
 class AssignmentsController < ApplicationController
+  before_action :authorize_shiftcoordinator, except: [:index, :by_user]
   before_action :set_session, :set_users
 
   def index
diff --git a/app/controllers/candidates_controller.rb b/app/controllers/candidates_controller.rb
index 0907937..94bff98 100644
--- a/app/controllers/candidates_controller.rb
+++ b/app/controllers/candidates_controller.rb
@@ -1,6 +1,8 @@
 require 'icalendar/tzinfo'
 
 class CandidatesController < ApplicationController
+  before_action :authorize_shiftcoordinator, except: [:create, :destroy_self]
+
   def create
     @session = Session.find_by(ref_id: params[:session_ref_id])
     @conference = Conference.find_by(slug: params[:conference_slug])
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 6a2eda1..2dd6bf2 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -2,6 +2,7 @@ module ApplicationHelper
   def body_data_attributes
     attributes = {}
     attributes[:loggedin_uid] = current_user.id if user_signed_in?
+    attributes[:is_shiftcoordinator] = 1 if current_user&.shiftcoordinator?
     { data: attributes }
   end
 end
diff --git a/app/javascript/controllers/session_controller.js b/app/javascript/controllers/session_controller.js
index e58fcfd..a3a9872 100644
--- a/app/javascript/controllers/session_controller.js
+++ b/app/javascript/controllers/session_controller.js
@@ -14,6 +14,7 @@ export default class extends Controller {
   connect() {
     const uid = document.body.dataset.loggedinUid;
     const isCandidate = this.candidatesTarget.querySelector(`a.candidate[data-candidate-uid="${uid}"]`);
+    const isShiftcoordinator = document.body.dataset.isShiftcoordinator;
 
     this.element.querySelectorAll('.only-loggedin').forEach(el => {
       hide(el, !uid);
@@ -27,6 +28,9 @@ export default class extends Controller {
     this.element.querySelectorAll('.only-no-candidate').forEach(el => {
       hide(el, isCandidate);
     });
+    this.element.querySelectorAll('.only-shiftcoordinator').forEach(el => {
+      hide(el, !isShiftcoordinator);
+    });
   }
 
   submitWithPrompt(event) {
diff --git a/app/views/assignments/_user_avatar.html.erb b/app/views/assignments/_user_avatar.html.erb
index 8f1cc9b..a22856e 100644
--- a/app/views/assignments/_user_avatar.html.erb
+++ b/app/views/assignments/_user_avatar.html.erb
@@ -1,7 +1,7 @@
 <% user = assignment.user %>
 <span class="inline-flex items-center gap-x-0.5 rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10" style="background-color: <%= user.avatar_color %>" title="<%= user.name %>">
   <span style="color: <%= user.text_color %>"><%= user.name %></span>
-  <button type="button" class="group relative -mr-1 size-3.5 rounded-sm hover:bg-gray-500/20">
+  <button type="button" class="only-shiftcoordinator hidden group relative -mr-1 size-3.5 rounded-sm hover:bg-gray-500/20">
     <%= link_to conference_session_assignment_path(assignment.session.conference, assignment.session, assignment), data: { turbo_method: :delete, confirm: 'Are you sure?' }, method: :delete do %>
       <span class="sr-only">Remove</span>
       <svg viewBox="0 0 14 14" class="size-3.5 stroke-gray-600/50 group-hover:stroke-gray-600/75" style="stroke: <%= user.text_color %>">
diff --git a/app/views/candidates/_user_avatar.html.erb b/app/views/candidates/_user_avatar.html.erb
index 8d2c0c8..382cef8 100644
--- a/app/views/candidates/_user_avatar.html.erb
+++ b/app/views/candidates/_user_avatar.html.erb
@@ -2,7 +2,7 @@
 <% session = candidate.session %>
 <span class="gap-x-0.5 rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10" style="background-color: <%= user.avatar_color %>" title="<%= user.name %>">
   <span style="color: <%= user.text_color %>"><%= user.name %></span>
-  <button type="button" class="group relative -mr-1 size-3.5 rounded-sm hover:bg-gray-500/20">
+  <button type="button" class="only-shiftcoordinator hidden group relative -mr-1 size-3.5 rounded-sm hover:bg-gray-500/20">
     <%= link_to conference_session_assignments_path(session.conference, session, user_id: user.id), class: "candidate", data: { turbo_method: :post, turbo_frame: dom_id(session), candidate_uid: user.id } do %>
       <span class="sr-only">Add</span>
       <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" stroke-width="1" class="size-3.5 stroke-gray-600/50 group-hover:stroke-gray-600/75 fill-gray-600/50" style="stroke: <%= user.text_color %>; fill: <%= user.text_color %>">
diff --git a/app/views/sessions/_session.html.erb b/app/views/sessions/_session.html.erb
index c220a44..c4df7da 100644
--- a/app/views/sessions/_session.html.erb
+++ b/app/views/sessions/_session.html.erb
@@ -83,6 +83,7 @@
     </ul>
     <% end %>
     </div>
+    <div class="only-shiftcoordinator hidden">
     <hr>
     <small>unassigned (<%= unassigned_users.length %>)</small>
     <%= render partial: 'assignments/filteredlist', locals: { session: session, users: [] } %>
diff --git a/db/migrate/20241220201657_add_shiftcoordinator_to_users.rb b/db/migrate/20241220201657_add_shiftcoordinator_to_users.rb
new file mode 100644
index 0000000..228d1eb
--- /dev/null
+++ b/db/migrate/20241220201657_add_shiftcoordinator_to_users.rb
@@ -0,0 +1,5 @@
+class AddShiftcoordinatorToUsers < ActiveRecord::Migration[7.1]
+  def change
+    add_column :users, :shiftcoordinator, :boolean, null: false, default: false
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index d37ec25..8a993e7 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.1].define(version: 2024_12_20_161958) do
+ActiveRecord::Schema[7.1].define(version: 2024_12_20_212328) do
   create_table "assignments", force: :cascade do |t|
     t.integer "user_id", null: false
     t.integer "session_id", null: false
@@ -270,15 +270,15 @@ ActiveRecord::Schema[7.1].define(version: 2024_12_20_161958) do
 
   create_table "users", force: :cascade do |t|
     t.string "name"
-    t.string "email", default: "", null: false
+    t.string "email"
     t.datetime "created_at", null: false
     t.datetime "updated_at", null: false
     t.string "avatar_color"
     t.string "telegram_username"
-    t.string "password_digest"
     t.string "encrypted_password", default: "", null: false
     t.datetime "remember_created_at"
-    t.index ["email"], name: "index_users_on_email", unique: true
+    t.boolean "shiftcoordinator", default: false, null: false
+    t.string "invitation_token"
   end
 
   add_foreign_key "assignments", "sessions"
-- 
GitLab