Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
c6eebbd
Initial plan
Copilot Feb 12, 2026
b5e2ea7
Replace super_user checks with ActionPolicy calls
Copilot Feb 12, 2026
68c4a06
Memoize member? checks in policies for performance
Copilot Feb 12, 2026
77765ca
Optimize member? queries to use exists? for better performance
Copilot Feb 12, 2026
d86cda4
Replace usage of helper in favor of policies
maebeale Feb 13, 2026
5018c20
Change perms on show page buttons
maebeale Feb 13, 2026
e764292
Cleanup
maebeale Feb 13, 2026
e049311
Remove auth stuff from workshop search service
maebeale Feb 13, 2026
0d5173e
Remove default true on workshop logs for org
maebeale Feb 13, 2026
578b4bf
Cleanup organization show data
maebeale Feb 13, 2026
672b276
Change workshop_logs index so logs can be passed in (from organizatio…
maebeale Feb 13, 2026
07de02a
Fix workshop and logs display
maebeale Feb 13, 2026
e8602d4
Update navbar
maebeale Feb 13, 2026
ecbcba3
Replace admin? helper calls with allowed_to? policy checks in views
Copilot Feb 13, 2026
b2ad1c7
Fix policy actions for promote links - use :manage? instead of :destroy?
Copilot Feb 13, 2026
8382c40
Add persisted? check to all destroy? policies
Copilot Feb 13, 2026
d493dd4
Add test for destroy? policy with persisted check
Copilot Feb 13, 2026
f382c86
Send object not class for instance method policies
maebeale Feb 13, 2026
7516967
rubocop
maebeale Feb 13, 2026
dc76246
Tighten up workshop_variation_idea to better match workshop_variation…
maebeale Feb 13, 2026
a8974d6
Revert workshop service change -- leave some auth stuff there
maebeale Feb 13, 2026
ec61276
Fix Hidden text to Unpublished
maebeale Feb 13, 2026
4a93afe
Policy check cancel button route
maebeale Feb 13, 2026
c26734e
Change color of logout text link
maebeale Feb 13, 2026
088f670
Only check person policy if there's a person (and don't autogenerate …
maebeale Feb 13, 2026
726e1b5
Change action_policy_draper setup in order to support calls to decora…
maebeale Feb 13, 2026
e8a7de8
Don't check persisted in view level since that's now in the policy
maebeale Feb 13, 2026
db77b57
Remove remaining calls to
maebeale Feb 13, 2026
4879447
Fix user show and event edit specs
maebeale Feb 13, 2026
415f385
Stub index? policy check in event form view spec
maebeale Feb 13, 2026
d5b6b16
Fix broken tests after policy refactor
maebeale Feb 13, 2026
b87c439
Scope organizations in people form via affiliated policy scope
maebeale Feb 13, 2026
5b2ef68
Rename all_workshop_logs to affiliated_workshop_logs
maebeale Feb 13, 2026
d52f47e
Use named :active policy scope for users in workshop logs controller
maebeale Feb 13, 2026
e0af64e
Default show_hidden_badge to true and remove redundant args
maebeale Feb 14, 2026
abb3b0e
Replace link_to method: :delete with data: { turbo_method: :delete }
maebeale Feb 14, 2026
d79ca39
Filter navbar organization links to active organization_people only
maebeale Feb 14, 2026
fa1f873
Gate MiniProfiler behind manage? policy instead of super_user check
maebeale Feb 14, 2026
37509e7
Fix incorrect allowed_to? calls in events and workshop logs show views
maebeale Feb 14, 2026
890bfed
Restyle user edit form, toggle lock button, and edit layout
maebeale Feb 14, 2026
7fda4b8
Rename :active user policy scope to :colleagues
maebeale Feb 14, 2026
3294244
Return organization colleagues in :colleagues user policy scope
maebeale Feb 14, 2026
44a2da4
Rename Super User labels to Admin for consistency
maebeale Feb 14, 2026
f0c8aaf
Fix edit button policy check in workshop_variation_ideas show view
maebeale Feb 14, 2026
970a27d
Replace super_user checks with policy checks in scaffold templates
maebeale Feb 14, 2026
d8996a3
Merge branch 'main' into copilot/refactor-super-user-checks-again
maebeale Feb 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
class ApplicationController < ActionController::Base
prepend ActionPolicy::Draper
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i changed the installation of action policy from lib to initializer, and made it so the allowed_to? call is available from views as well as controllers


before_action :authenticate_user! # ensures only logged-in users can access pages
before_action :set_current_user # for AhoyTrackable in models
before_action :preload_current_user_associations

before_action do
if current_user && current_user.super_user
if current_user && allowed_to?(:manage?, with: ApplicationPolicy)
Rack::MiniProfiler.authorize_request
end
end
Expand Down
3 changes: 0 additions & 3 deletions app/controllers/events_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ def new
def edit
authorize! @event
set_form_variables
unless @event.created_by == current_user || current_user&.super_user?
redirect_to events_path, alert: "You are not authorized to edit this event."
end
end

def create
Expand Down
6 changes: 1 addition & 5 deletions app/controllers/monthly_reports_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,7 @@ def show
@answers = @monthly_report.report_form_field_answers

if @monthly_report
if current_user&.super_user? || (@monthly_report.organization && current_user.organization_ids.include?(@monthly_report.organization.id))
render :show
else
redirect_to root_path, error: "You do not have permission to view this page."
end
render :show
else
redirect_to root_path, error: "Unable to find that Workshop Log."
end
Expand Down
8 changes: 1 addition & 7 deletions app/controllers/people_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,9 @@ def set_form_variables
@all_sectors = Sector.published.order(:name)
@current_sector_ids = @person.sectorable_items.pluck(:sector_id)

organizations = if current_user&.super_user?
Organization.active
else
current_user.organizations
end
@organizations_array = organizations.order(:name).pluck(:name, :id)
@organizations_array = authorized_scope(Organization.all, as: :affiliated).order(:name).pluck(:name, :id)
end


# Only allow a list of trusted parameters through.
def person_params
params.require(:person).permit(
Expand Down
3 changes: 1 addition & 2 deletions app/controllers/quotes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ def destroy

# Optional hooks for setting variables for forms or index
def set_form_variables
workshops = current_user&.super_user? ? Workshop.all : Workshop.active
@workshops = workshops.order(:title)
@workshops = authorized_scope(Workshop.all).order(:title)
end

private
Expand Down
10 changes: 4 additions & 6 deletions app/controllers/workshop_logs_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def set_index_variables # needs to not be private
Arel.sql("DISTINCT EXTRACT(YEAR FROM COALESCE(date, created_at, NOW()))")
).sort.reverse

scoped_users = current_user&.super_user? ? User.active : User.where(id: current_user.id)
scoped_users = authorized_scope(User.all, as: :colleagues)
@people = scoped_users.or(User.where(id: @workshop_logs_unpaginated.pluck(:user_id)))
.includes(:workshop_logs, :person)
.joins(:workshop_logs)
Expand All @@ -136,11 +136,9 @@ def set_form_variables
@workshop = Workshop.new
end

workshops = Workshop.includes(:windows_type)
unless current_user&.super_user?
workshops = workshops.published
end
@workshops = workshops.or(Workshop.where(id: @workshop_log.workshop_id).includes(:windows_type))
workshops = authorized_scope(Workshop.all)
@workshops = workshops.or(Workshop.where(id: @workshop_log.workshop_id))
.includes(:windows_type)
.distinct
.order(title: :asc)

Expand Down
11 changes: 10 additions & 1 deletion app/controllers/workshop_variation_ideas_controller.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
class WorkshopVariationIdeasController < ApplicationController
include AhoyTracking
before_action :set_workshop_variation_idea, only: [ :show, :edit, :update, :destroy ]

def index
Expand All @@ -13,6 +14,14 @@ def index

def show
authorize! @workshop_variation_idea
track_view(@workshop_variation_idea)

@workshop = (@workshop_variation_idea.workshop || Workshop.where(id: params[:workshop_id]).last)&.decorate
@bookmark = current_user.bookmarks.find_by(bookmarkable: @workshop)
@new_bookmark = @workshop.bookmarks.build
@quotes = @workshop.quotes
@workshop_variation_ideas = WorkshopVariationIdea.where(workshop: @workshop)
@sectors = @workshop.sectors
end

def new
Expand Down Expand Up @@ -69,7 +78,7 @@ def destroy
private

def set_workshop_variation_idea
@workshop_variation_idea = WorkshopVariationIdea.find(params[:id])
@workshop_variation_idea = WorkshopVariationIdea.find(params[:id]).decorate
end

def set_form_variables
Expand Down
2 changes: 1 addition & 1 deletion app/helpers/title_display_helper.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module TitleDisplayHelper
def title_with_badges(record, font_size: "text-lg", record_title: nil,
show_hidden_badge: false, display_windows_type: false)
show_hidden_badge: true, display_windows_type: false)
fragments = []

# --- Hidden badge ---
Expand Down
12 changes: 10 additions & 2 deletions app/models/organization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ class Organization < ApplicationRecord
has_many :organization_people, dependent: :restrict_with_error
has_many :people, through: :organization_people
has_many :users, through: :people
has_many :reports, through: :users
has_many :workshop_logs, through: :users
has_many :reports
has_many :workshop_logs
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

old data can't be counted on to always have the proper organization_people records, so, making this a direct association for now


has_many :categorizable_items, dependent: :destroy, inverse_of: :categorizable, as: :categorizable
has_many :sectorable_items, as: :sectorable, dependent: :destroy
# has_many through
has_many :categories, through: :categorizable_items
has_many :sectors, through: :sectorable_items
has_many :workshops, through: :users

# Asset associations
has_one_attached :logo, dependent: :purge do |attachable|
Expand Down Expand Up @@ -82,6 +83,13 @@ def self.search_by_params(params)
organizations
end

def affiliated_workshop_logs
direct = WorkshopLog.where(organization_id: id)
legacy = WorkshopLog.where(organization_id: nil)
.where(user_id: users.select(:id))
direct.or(legacy).distinct
end

# Methods
def led_by?(user)
return false unless leader
Expand Down
1 change: 1 addition & 0 deletions app/models/report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class Report < ApplicationRecord
where("EXTRACT(YEAR FROM COALESCE(reports.date, reports.created_at)) = ?", year.to_i)
end }
scope :ordered_by_date, -> { order(Arel.sql("COALESCE(reports.date, reports.created_at) DESC")) }
scope :workshop_logs, -> { where(type: "WorkshopLog") }

def self.search(params)
logs = is_a?(ActiveRecord::Relation) ? self : all
Expand Down
7 changes: 0 additions & 7 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,6 @@ def self.search_by_params(params)
results
end

# TODO Remove once all view's use ActionPolicy
def admin?
super_user
end
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed this just for you, @jmilljr24

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will sleep well tonight ☺️




def active_for_authentication?
super && !inactive?
end
Expand Down
10 changes: 6 additions & 4 deletions app/models/workshop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,12 @@ def self.mentionable_rich_text_fields
source: :category # needs to be after has_many :categorizable_items
has_many :categories, through: :categorizable_items
has_many :category_types, through: :categories
has_many :organizations, through: :user
has_many :quotes, through: :quotable_item_quotes
has_many :resources, through: :workshop_resources, source: :resource
has_many :sectors, through: :sectorable_items


# Images
has_one_attached :thumbnail # old paperclip -- TODO convert these to AvatarImage records
has_one_attached :header # old paperclip -- TODO convert these to MainImage records
Expand Down Expand Up @@ -138,7 +140,7 @@ def self.mentionable_rich_text_fields
scope :created_by_id, ->(created_by_id) { where(user_id: created_by_id) }
scope :legacy, -> { where(legacy: true) }
scope :title, ->(title) { where("workshops.title like ?", "%#{ title }%") }
scope :order_by_date, ->(sort_order = "asc") {
scope :order_by_date, ->(sort_order = "asc") do
order(Arel.sql(<<~SQL.squish))
COALESCE(
STR_TO_DATE(
Expand All @@ -148,14 +150,14 @@ def self.mentionable_rich_text_fields
DATE(workshops.created_at)
) #{sort_order == "asc" ? "ASC" : "DESC"}
SQL
}
end
scope :title, ->(title) { where("workshops.title like ?", "%#{ title }%") }
scope :windows_type_ids, ->(windows_type_ids) { where(windows_type_id: windows_type_ids) }
scope :with_bookmarks_count, -> {
scope :with_bookmarks_count, -> do
left_joins(:bookmarks)
.select("workshops.*, COUNT(bookmarks.id) AS bookmarks_count")
.group("workshops.id")
}
end

# Search Cop
include SearchCop
Expand Down
6 changes: 5 additions & 1 deletion app/policies/application_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ class ApplicationPolicy < ActionPolicy::Base
authorize :user, optional: true, allow_nil: true

default_rule :manage?
alias_rule :index?, :show?, :new?, :create?, :edit?, :update?, :destroy?, to: :manage?
alias_rule :index?, :show?, :new?, :create?, :edit?, :update?, to: :manage?

def manage?
admin?
end

def destroy?
record.persisted? && manage?
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moving the persistence checks for destroy out of views and into policies so we can minimize logic in views

end

private
# Define shared methods useful for most policies.

Expand Down
2 changes: 1 addition & 1 deletion app/policies/bookmark_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def update?
end

def destroy?
admin? || owner?
record.persisted? && (admin? || owner?)
end

# Scoping
Expand Down
2 changes: 1 addition & 1 deletion app/policies/category_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def index? = admin?
def show? = admin?
def create? = admin?
def update? = admin?
def destroy? = admin?
def destroy? = record.persisted? && admin?

def tags_index?
true
Expand Down
2 changes: 1 addition & 1 deletion app/policies/category_type_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ def index? = admin?
def show? = admin?
def create? = admin?
def update? = admin?
def destroy? = admin?
def destroy? = record.persisted? && admin?
end
15 changes: 15 additions & 0 deletions app/policies/event_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,21 @@ def register?
authenticated? && record.published?
end

def edit?
admin? || owner?
end

def update?
admin? || owner?
end

private

def owner?
return false unless authenticated?
record.created_by == user
end

relation_scope do |relation|
next relation if admin?
if authenticated? # logged in users can see events they are registered for even if registration is closed
Expand Down
2 changes: 1 addition & 1 deletion app/policies/event_registration_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class EventRegistrationPolicy < ApplicationPolicy

def index? = admin?
def create? = admin? || owner?
def destroy? = admin? || owner?
def destroy? = record.persisted? && (admin? || owner?)


relation_scope do |relation|
Expand Down
32 changes: 32 additions & 0 deletions app/policies/monthly_report_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
class MonthlyReportPolicy < ApplicationPolicy
def index?
authenticated?
end

def show?
admin? || owner? || member?
end

def update?
admin? || owner? || member?
end

def destroy?
record.persisted? && (admin? || owner? || member?)
end

private

def member?
@member ||= begin
return false unless authenticated?
return false unless record.organization
record.organization.organization_people.exists?(person_id: user.person_id)
end
end

relation_scope do |relation|
next relation if admin?
relation.where(user_id: user.id)
end
end
2 changes: 1 addition & 1 deletion app/policies/organization_obligation_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ def index? = admin?
def show? = admin?
def create? = admin?
def update? = admin?
def destroy? = admin?
def destroy? = record.persisted? && admin?


# Scoping
Expand Down
2 changes: 1 addition & 1 deletion app/policies/organization_person_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ class OrganizationPersonPolicy < ApplicationPolicy
# See https://actionpolicy.evilmartians.io/#/writing_policies
#
def destroy?
admin? # we don't allow users to edit their own
record.persisted? && admin? # we don't allow users to edit their own
end

# Scoping
Expand Down
22 changes: 22 additions & 0 deletions app/policies/organization_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,33 @@ def show?
admin? || (authenticated? && record.published?)
end

def show_workshop_logs?
admin? || member?
end


# Scoping
# See https://actionpolicy.evilmartians.io/#/scoping

relation_scope do |relation|
next relation if admin?
relation.published
end

relation_scope(:affiliated) do |relation|
next relation.active if admin?
next relation.none unless user&.person_id

relation.joins(:organization_people)
.where(organization_people: { person_id: user.person_id })
end

private

def member?
@member ||= begin
return false unless user&.person_id
record.organization_people.exists?(person_id: user.person_id)
end
end
end
2 changes: 1 addition & 1 deletion app/policies/organization_status_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ def index? = admin?
def show? = admin?
def create? = admin?
def update? = admin?
def destroy? = admin?
def destroy? = record.persisted? && admin?

#
# Scoping
Expand Down
2 changes: 1 addition & 1 deletion app/policies/primary_asset_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ def show? = admin?
def create? = authenticated?
def edit? = admin?
def update? = authenticated?
def destroy? = admin?
def destroy? = record.persisted? && admin?
end
Loading