Skip to content

Commit

Permalink
Allow managers to create and manage event participations of their man…
Browse files Browse the repository at this point in the history
…ageds

Most of the changes here can use the new for_self_or_manageds mechanism.
But when a condition combines her_own_or_something_else, then we must
manually add the manager option, to make sure that the manager only
inherits the her_own part, not the something_else part.

Refs hitobito/hitobito#1967
  • Loading branch information
carlobeltrame committed Apr 14, 2023
1 parent 9878c76 commit 06ab255
Show file tree
Hide file tree
Showing 10 changed files with 330 additions and 2 deletions.
19 changes: 19 additions & 0 deletions app/abilities/youth/event/application_ability.rb
@@ -0,0 +1,19 @@
# encoding: utf-8

# Copyright (c) 2023, Pfadibewegung Schweiz. This file is part of
# hitobito_youth and licensed under the Affero General Public License version 3
# or later. See the COPYING file at the top-level directory or at
# https://github.com/hitobito/hitobito_youth.

module Youth::Event::ApplicationAbility
extend ActiveSupport::Concern

included do
on(Event::Application) do
for_self_or_manageds do
# abilities which managers inherit from their managed children
permission(:any).may(:show_priorities, :show_approval).her_own
end
end
end
end
19 changes: 19 additions & 0 deletions app/abilities/youth/event/invitation_ability.rb
@@ -0,0 +1,19 @@
# encoding: utf-8

# Copyright (c) 2023, Pfadibewegung Schweiz. This file is part of
# hitobito_youth and licensed under the Affero General Public License version 3
# or later. See the COPYING file at the top-level directory or at
# https://github.com/hitobito/hitobito_youth.

module Youth::Event::InvitationAbility
extend ActiveSupport::Concern

included do
on(Event::Invitation) do
for_self_or_manageds do
# abilities which managers inherit from their managed children
permission(:any).may(:decline).own_invitation
end
end
end
end
29 changes: 28 additions & 1 deletion app/abilities/youth/event/participation_ability.rb
@@ -1,6 +1,6 @@
# encoding: utf-8

# Copyright (c) 2012-2014, Pfadibewegung Schweiz. This file is part of
# Copyright (c) 2012-2023, Pfadibewegung Schweiz. This file is part of
# hitobito_youth and licensed under the Affero General Public License version 3
# or later. See the COPYING file at the top-level directory or at
# https://github.com/hitobito/hitobito_youth.
Expand All @@ -10,6 +10,16 @@ module Youth::Event::ParticipationAbility

included do
on(Event::Participation) do
# abilities which managers inherit from their managed children
permission(:any).may(:show).her_own_or_manager_or_for_participations_read_events
permission(:any).may(:show_details, :print).
her_own_or_manager_or_for_participations_full_events
for_self_or_manageds do
permission(:any).may(:create).her_own_if_application_possible
permission(:any).may(:destroy).her_own_if_application_cancelable
general(:create).at_least_one_group_not_deleted
end

permission(:any).may(:cancel, :absent, :assign, :attend).for_participations_full_events
permission(:group_full).may(:cancel, :reject, :absent, :assign, :attend).in_same_group
permission(:group_and_below_full).
Expand All @@ -26,6 +36,14 @@ module Youth::Event::ParticipationAbility
end
end

def her_own_or_manager_or_for_participations_read_events
her_own_or_for_participations_read_events || manager
end

def her_own_or_manager_or_for_participations_full_events
her_own_or_for_participations_full_events || manager
end

def person_in_same_layer
person.nil? || permission_in_layers?(person.layer_group_ids)
end
Expand All @@ -45,8 +63,17 @@ def if_application
event.supports_applications && participation.application_id?
end

def participant_can_show_event?
participation.person &&
(AbilityWithoutManagerAbilities.new(participation.person).can? :show, participation.event)
end

private

def manager
contains_any?([user.id], person.managers.pluck(:id))
end

def event
participation.event
end
Expand Down
19 changes: 19 additions & 0 deletions app/abilities/youth/event/participation_contact_data_ability.rb
@@ -0,0 +1,19 @@
# encoding: utf-8

# Copyright (c) 2023, Pfadibewegung Schweiz. This file is part of
# hitobito_youth and licensed under the Affero General Public License version 3
# or later. See the COPYING file at the top-level directory or at
# https://github.com/hitobito/hitobito_youth.

module Youth::Event::ParticipationContactDataAbility
extend ActiveSupport::Concern

included do
on(Event::ParticipationContactData) do
for_self_or_manageds do
# abilities which managers inherit from their managed children
permission(:any).may(:show, :update).her_own
end
end
end
end
9 changes: 8 additions & 1 deletion app/abilities/youth/event_ability.rb
@@ -1,6 +1,6 @@
# encoding: utf-8

# Copyright (c) 2012-2014, Pfadibewegung Schweiz. This file is part of
# Copyright (c) 2012-2023, Pfadibewegung Schweiz. This file is part of
# hitobito_youth and licensed under the Affero General Public License version 3
# or later. See the COPYING file at the top-level directory or at
# https://github.com/hitobito/hitobito_youth.
Expand All @@ -10,6 +10,13 @@ module Youth::EventAbility

included do
on(Event::Course) do

for_self_or_manageds do
# abilities which managers inherit from their managed children
class_side(:list_available).if_any_role
permission(:any).may(:show).in_same_layer_or_globally_visible_or_participating
end

permission(:any).
may(:index_revoked_participations, :list_tentatives).
for_participations_full_events
Expand Down
3 changes: 3 additions & 0 deletions lib/hitobito_youth/wagon.rb
Expand Up @@ -53,7 +53,10 @@ class Wagon < Rails::Engine
GroupAbility.include Youth::GroupAbility
EventAbility.include Youth::EventAbility
PersonAbility.include Youth::PersonAbility
Event::ApplicationAbility.include Youth::Event::ApplicationAbility
Event::InvitationAbility.include Youth::Event::InvitationAbility
Event::ParticipationAbility.include Youth::Event::ParticipationAbility
Event::ParticipationContactDataAbility.include Youth::Event::ParticipationContactDataAbility
PersonReadables.prepend(Youth::PersonReadables)
PersonLayerWritables.prepend(Youth::PersonLayerWritables)

Expand Down
43 changes: 43 additions & 0 deletions spec/abilities/event/application_ability_spec.rb
@@ -0,0 +1,43 @@
# encoding: utf-8

# Copyright (c) 2023, Pfadibewegung Schweiz. This file is part of
# hitobito_youth and licensed under the Affero General Public License version 3
# or later. See the COPYING file at the top-level directory or at
# https://github.com/hitobito/hitobito_youth.

require 'spec_helper'

describe Event::ApplicationAbility do

let(:applicant) { Fabricate(:person) }
let(:manager) { Fabricate(:person) }
let!(:manager_relation) { PeopleManager.create(manager: manager, managed: applicant) }

let(:course) { Fabricate(:course) }
let(:application) { Fabricate(:event_application, priority_1: course, priority_2: nil) }
let!(:participation) { Fabricate(:event_participation, event: course, application: application, person: applicant) }

subject { Ability.new(user) }

[:show_priorities, :show_approval].each do |ability|
context ability.to_s do

context 'for self' do
let(:user) { applicant }
it { is_expected.to be_able_to ability, application }
end

context 'for manager' do
let(:user) { manager }
it { is_expected.to be_able_to ability, application }
end

context 'for unrelated user' do
let(:user) { Fabricate(:person) }
it { is_expected.not_to be_able_to ability, application }
end

end
end

end
40 changes: 40 additions & 0 deletions spec/abilities/event/invitation_ability_spec.rb
@@ -0,0 +1,40 @@
# encoding: utf-8

# Copyright (c) 2023, Pfadibewegung Schweiz. This file is part of
# hitobito_youth and licensed under the Affero General Public License version 3
# or later. See the COPYING file at the top-level directory or at
# https://github.com/hitobito/hitobito_youth.

require 'spec_helper'

describe Event::InvitationAbility do

let(:invitee) { Fabricate(:person) }
let(:manager) { Fabricate(:person) }
let!(:manager_relation) { PeopleManager.create(manager: manager, managed: invitee) }

let(:course) { Fabricate(:course) }
let(:invitation) { Fabricate(:event_invitation, event: course, person: invitee) }

subject { Ability.new(user) }

context 'decline' do

context 'for self' do
let(:user) { invitee }
it { is_expected.to be_able_to :decline, invitation }
end

context 'for manager' do
let(:user) { manager }
it { is_expected.to be_able_to :decline, invitation }
end

context 'for unrelated user' do
let(:user) { Fabricate(:person) }
it { is_expected.not_to be_able_to :decline, invitation }
end

end

end
102 changes: 102 additions & 0 deletions spec/abilities/event/participation_ability_spec.rb
Expand Up @@ -130,4 +130,106 @@ def build(attrs)
end
end
end

context 'manager inherited permissions' do

let(:participant) { Fabricate(:person) }
let(:manager) { Fabricate(:person) }
let!(:manager_relation) { PeopleManager.create(manager: manager, managed: participant) }

let(:course) { Fabricate(:course) }
let(:participation) { Fabricate(:event_participation, event: course, person: participant) }

subject { Ability.new(user) }

context 'for self' do
let(:user) { participant }

context 'if event applications are not possible / cancelable' do
it { is_expected.to be_able_to :show, participation }
it { is_expected.to be_able_to :show_details, participation }
it { is_expected.not_to be_able_to :create, participation }
it { is_expected.not_to be_able_to :destroy, participation }
end

context 'if event application is possible / cancelable' do
before { course.update(state: :application_open, applications_cancelable: true) }
it { is_expected.to be_able_to :show, participation }
it { is_expected.to be_able_to :show_details, participation }
it { is_expected.to be_able_to :create, participation }
it { is_expected.to be_able_to :destroy, participation }

context 'but event is in archived group' do
before { course.groups.each(&:archive!) }
it { is_expected.not_to be_able_to :create, participation }
end
end

context 'as event leader' do
let!(:leader_role) { Fabricate(Event::Role::Leader.name, participation: participation) }
let(:other_participation) { Fabricate(:event_participation, event: course) }
it { is_expected.to be_able_to :show, other_participation }
it { is_expected.to be_able_to :show_details, other_participation }
end
end

context 'for manager' do
let(:user) { manager }

context 'if event applications are not possible / cancelable' do
it { is_expected.to be_able_to :show, participation }
it { is_expected.to be_able_to :show_details, participation }
it { is_expected.not_to be_able_to :create, participation }
it { is_expected.not_to be_able_to :destroy, participation }
end

context 'if event application is possible / cancelable' do
before { course.update(state: :application_open, applications_cancelable: true) }
it { is_expected.to be_able_to :show, participation }
it { is_expected.to be_able_to :show_details, participation }
it { is_expected.to be_able_to :create, participation }
it { is_expected.to be_able_to :destroy, participation }

context 'but event is in archived group' do
before { course.groups.each(&:archive!) }
it { is_expected.not_to be_able_to :create, participation }
end

context 'of event leader' do
let!(:leader_role) { Fabricate(Event::Role::Leader.name, participation: participation) }
let(:other_participation) { Fabricate(:event_participation, event: course) }
it 'does not inherit show permissions on participants' do
is_expected.not_to be_able_to :show, other_participation
end
it 'does not inherit show_details permissions on participants' do
is_expected.not_to be_able_to :show_details, other_participation
end
end
end
end

context 'for unrelated user' do
let(:user) { Fabricate(:person) }
context 'if event applications are not possible / cancelable' do
it { is_expected.not_to be_able_to :show, participation }
it { is_expected.not_to be_able_to :show_details, participation }
it { is_expected.not_to be_able_to :create, participation }
it { is_expected.not_to be_able_to :destroy, participation }
end

context 'if event application is possible / cancelable' do
before { course.update(state: :application_open, applications_cancelable: true) }
it { is_expected.not_to be_able_to :show, participation }
it { is_expected.not_to be_able_to :show_details, participation }
it { is_expected.not_to be_able_to :create, participation }
it { is_expected.not_to be_able_to :destroy, participation }

context 'but event is in archived group' do
before { course.groups.each(&:archive!) }
it { is_expected.not_to be_able_to :create, participation }
end
end
end

end
end
49 changes: 49 additions & 0 deletions spec/abilities/event/participation_contact_data_ability_spec.rb
@@ -0,0 +1,49 @@
# encoding: utf-8

# Copyright (c) 2023, Pfadibewegung Schweiz. This file is part of
# hitobito_youth and licensed under the Affero General Public License version 3
# or later. See the COPYING file at the top-level directory or at
# https://github.com/hitobito/hitobito_youth.

require 'spec_helper'

describe Event::ParticipationContactDataAbility do

let(:participant) { Fabricate(:person) }
let(:manager) { Fabricate(:person) }
let!(:manager_relation) { PeopleManager.create(manager: manager, managed: participant) }

let(:course) { Fabricate(:course) }
let(:participation) { Fabricate(:event_participation, event: course, person: participant) }
let(:attributes) do
h = ActiveSupport::HashWithIndifferentAccess.new
h.merge({ first_name: 'John', last_name: 'Gonzales',
email: 'somebody@example.com',
nickname: '' })
end
let(:participation_contact_data) { Event::ParticipationContactData.new(course, participant, attributes) }

subject { Ability.new(user) }

[:show, :update].each do |ability|
context ability.to_s do

context 'for self' do
let(:user) { participant }
it { is_expected.to be_able_to ability, participation_contact_data }
end

context 'for manager' do
let(:user) { manager }
it { is_expected.to be_able_to ability, participation_contact_data }
end

context 'for unrelated user' do
let(:user) { Fabricate(:person) }
it { is_expected.not_to be_able_to ability, participation_contact_data }
end

end
end

end

0 comments on commit 06ab255

Please sign in to comment.