Class: CurrentScope::RoleAssignmentsController

Inherits:
ApplicationController show all
Defined in:
app/controllers/current_scope/role_assignments_controller.rb

Overview

Sets (or clears) a subject's single org-wide role.

Instance Method Summary collapse

Instance Method Details

#createObject



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'app/controllers/current_scope/role_assignments_controller.rb', line 4

def create
  subjects = locate_subjects()
  if subjects.empty?
    redirect_back_or_to subjects_path, alert: "No subjects selected."
    return
  end

  clearing = params[:role_id].blank?

  # One transaction for the whole bulk action: the UI presents it as a single
  # operation, so a failure partway must not leave only the first subjects
  # changed. All-or-nothing across assignments and their audit events. Count
  # only the subjects that ACTUALLY changed so the notice can't over-report
  # (a re-set to the same role, or a clear on a subject with no role, is a
  # no-op and shouldn't be counted).
  #
  # Lock full-access holders inside the same transaction as the writes so
  # concurrent clears cannot both observe "another holder remains" and then
  # both proceed to zero holders.
  changed = 0
  refused = false
  RoleAssignment.transaction do
    lock_full_access_org_holders!

    if would_remove_last_full_access_holders?(subjects, clearing: clearing)
      refused = true
    else
      subjects.each do |subject|
        assignment = RoleAssignment.find_or_initialize_by(subject: subject)
        prior_role = assignment.role # nil for a brand-new assignment
        did = clearing ? clear_org_role(subject, assignment, prior_role) : set_org_role(subject, assignment, prior_role)
        changed += 1 if did
      end
    end
  end

  if refused
    redirect_back_or_to subjects_path,
                        alert: "Refusing to remove the last full-access org-wide assignment — " \
                               "it would lock everyone out of this UI. Grant full access to " \
                               "another subject first, then retry."
    return
  end

  # Return to wherever the action was invoked (the subjects page or a role's
  # members page); falls back to subjects when there's no referrer.
  redirect_back_or_to subjects_path, notice: org_notice(clearing, changed)
rescue ActiveRecord::RecordNotFound, NameError
  redirect_back_or_to subjects_path, alert: "Couldn't set the org-wide role — a subject or role is no longer available."
end

#destroyObject

Remove ONE org-wide assignment by id — the path the members page uses to clean up an orphaned assignment whose subject was deleted (the subject-keyed clear on create can't target a subject that no longer resolves).



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'app/controllers/current_scope/role_assignments_controller.rb', line 58

def destroy
  refused = false
  RoleAssignment.transaction do
    # Lock FA state BEFORE the target assignment so order matches create/
    # role demote/delete (FA roles → FA holders → target row). Locking the
    # assignment first inverted that order and could deadlock.
    lock_full_access_org_holders!
    assignment = RoleAssignment.lock.find(params[:id])
    subject = resolve_subject(assignment)
    role_name = assignment.role.name

    if last_full_access_org_assignment?(assignment)
      refused = true
    else
      assignment.destroy!
      Event.record!(event: "org_role.removed", target: subject || assignment, details: { role: role_name })
    end
  end

  if refused
    redirect_back_or_to subjects_path,
                        alert: "Refusing to remove the last full-access org-wide assignment — " \
                               "it would lock everyone out of this UI. Grant full access to " \
                               "another subject first, then retry."
    return
  end

  redirect_back_or_to subjects_path, notice: "Org-wide role removed."
rescue ActiveRecord::RecordNotFound
  redirect_back_or_to subjects_path, notice: "That org-wide role was already removed."
end