diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml index 2e4c2995..25ccb622 100644 --- a/.haml-lint_todo.yml +++ b/.haml-lint_todo.yml @@ -78,6 +78,7 @@ linters: - "app/views/admin/resources/show.html.haml" - "app/views/admin/roles/_form.html.haml" - "app/views/admin/roles/_users.html.haml" + - "app/views/admin/roles/_users_with_org_admin_role.haml" - "app/views/admin/roles/index.html.haml" - "app/views/admin/roles/show.html.haml" - "app/views/admin/rooms/_form.html.haml" diff --git a/app/controllers/admin/organizations_controller.rb b/app/controllers/admin/organizations_controller.rb index 087bf7f8..d125c5cf 100644 --- a/app/controllers/admin/organizations_controller.rb +++ b/app/controllers/admin/organizations_controller.rb @@ -1,6 +1,7 @@ module Admin class OrganizationsController < Admin::BaseController load_and_authorize_resource :organization + before_action :verify_user, only: [:assign_org_admins, :unassign_org_admins] def index @organizations = Organization.all @@ -43,8 +44,49 @@ module Admin end end + def assign_org_admins + if @user.has_role? 'organization_admin', @organization + flash[:error] = "User #{@user.email} already has the role organization admin" + elsif @user.add_role 'organization_admin', @organization + flash[:notice] = "Successfully added role organization admin to user #{@user.email}" + else + flash[:error] = "Coud not add role organization admin to #{@user.email}" + end + + redirect_to admins_admin_organization_path(@organization) + end + + def unassign_org_admins + if @user.remove_role 'organization_admin', @organization + flash[:notice] = "Successfully removed role organization admin from user #{@user.email}" + else + flash[:error] = "Could not remove role organization admin from user #{@user.email}" + end + + redirect_to admins_admin_organization_path(@organization) + end + + def admins + @role = @organization.roles.first + @users = @role.users + render 'show_org_admins' + end + private + def user_params + params.require(:user).permit(:email) + end + + def verify_user + @user = User.find_by(email: user_params[:email]) + unless @user + redirect_to admins_admin_organization_path(@organization), + error: 'Could not find user. Please provide a valid email!' + return + end + end + def organization_params params.require(:organization).permit(:name, :description, :picture) end diff --git a/app/models/admin_ability.rb b/app/models/admin_ability.rb index 39183543..cfe17d1d 100644 --- a/app/models/admin_ability.rb +++ b/app/models/admin_ability.rb @@ -28,7 +28,7 @@ class AdminAbility conference.registration_open? && !conference.registration_limit_exceeded? || conference.program.speakers.confirmed.include?(user) end - can :index, Organization + can [:index, :admins], Organization can :index, Ticket can :manage, TicketPurchase, user_id: user.id can [:new, :create], Payment, user_id: user.id @@ -96,13 +96,11 @@ class AdminAbility org_ids_for_organization_admin = Organization.with_role(:organization_admin, user).pluck(:id) conf_ids_for_organization_admin = Conference.where(organization_id: org_ids_for_organization_admin).pluck(:id) - can [:read, :update, :destroy], Organization, id: org_ids_for_organization_admin + can [:read, :update, :destroy, :assign_org_admins, :unassign_org_admins, :admins], Organization, id: org_ids_for_organization_admin can :new, Conference can :manage, Conference, organization_id: org_ids_for_organization_admin can [:index, :show], Role - can [:edit, :update], Role do |role| - role.resource_type == 'Organization' && (org_ids_for_organization_admin.include? role.resource_id) - end + signed_in_with_organizer_role(user, conf_ids_for_organization_admin) end diff --git a/app/views/admin/organizations/_users_with_org_admin_role.haml b/app/views/admin/organizations/_users_with_org_admin_role.haml new file mode 100644 index 00000000..e107868c --- /dev/null +++ b/app/views/admin/organizations/_users_with_org_admin_role.haml @@ -0,0 +1,25 @@ +.page-header + %h3 Users (#{users.length}) +- if users.present? + %table.table.table-striped.table-bordered.table-hover.datatable#users + %thead + %th Name + %th Email + - if ( can? :unassign_org_admins, organization ) + %th + Actions + %tbody + - users.each do |user| + %tr + %td= user.name + %td= user.email + - if ( can? :unassign_org_admins, organization ) + %td + = link_to 'Remove from organization admin', + unassign_org_admins_admin_organization_path(organization.id, + role.name, + user: {email: user.email}), + method: :delete, + class: 'btn btn-danger' +- else + %h5 No users found! diff --git a/app/views/admin/organizations/index.html.haml b/app/views/admin/organizations/index.html.haml index aa682c57..baf9bc80 100644 --- a/app/views/admin/organizations/index.html.haml +++ b/app/views/admin/organizations/index.html.haml @@ -25,6 +25,8 @@ = organization.conferences.past.count %td .btn-group + = link_to 'Admins', admins_admin_organization_path(organization), + method: :get, class: 'btn btn-success' = link_to 'Edit', edit_admin_organization_path(organization), method: :get, class: 'btn btn-primary' = link_to 'Delete', admin_organization_path(organization), diff --git a/app/views/admin/organizations/show_org_admins.haml b/app/views/admin/organizations/show_org_admins.haml new file mode 100644 index 00000000..7fe1f943 --- /dev/null +++ b/app/views/admin/organizations/show_org_admins.haml @@ -0,0 +1,24 @@ +.row + .col-md-12 + .page-header + %h2 + Organization admins for #{@organization.name} + .text-muted + = @role.description + +.row.col-md-3 + - if ( can? :assign_org_admins, @organization ) + = semantic_form_for :user, + url: assign_org_admins_admin_organization_path(@organization, + @role.name), method: :post do |u| + + = u.label 'Add user by email: ' + .input-group + = u.input :email, label: false, placeholder: "User's email" + .input-group-btn + = u.submit 'Add', id: 'user-add', class: 'btn btn-primary' + +.row + .col-md-12 + = render partial: 'users_with_org_admin_role', + locals: { users: @users, organization: @organization, role: @role } diff --git a/config/routes.rb b/config/routes.rb index 523a076d..3b955953 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -20,7 +20,13 @@ Osem::Application.routes.draw do end namespace :admin do - resources :organizations + resources :organizations do + member do + get :admins + post :assign_org_admins + delete :unassign_org_admins + end + end resources :users do member do patch :toggle_confirmation diff --git a/spec/controllers/admin/organizations_controller_spec.rb b/spec/controllers/admin/organizations_controller_spec.rb index abe5005b..801cce83 100644 --- a/spec/controllers/admin/organizations_controller_spec.rb +++ b/spec/controllers/admin/organizations_controller_spec.rb @@ -167,5 +167,32 @@ describe Admin::OrganizationsController do end end end + + describe 'POST #assign_org_admins' do + let(:org_admin_role) { Role.find_by(name: 'organization_admin', resource: organization) } + + before do + post :assign_org_admins, id: organization.id, + user: { email: user.email } + end + + it 'assigns organization_admin role' do + expect(user.roles).to eq [org_admin_role] + end + end + + describe 'DELETE #unassign_org_admins' do + let(:org_admin_role) { Role.find_by(name: 'organization_admin', resource: organization) } + let!(:org_admin_user) { create(:user, role_ids: [org_admin_role.id]) } + + before do + delete :unassign_org_admins, id: organization.id, + user: { email: org_admin_user.email } + end + + it 'unassigns organization_admin role' do + expect(org_admin_user.reload.roles).to eq [] + end + end end end diff --git a/spec/controllers/admin/roles_controller_spec.rb b/spec/controllers/admin/roles_controller_spec.rb index d5cdc67e..dcc81313 100644 --- a/spec/controllers/admin/roles_controller_spec.rb +++ b/spec/controllers/admin/roles_controller_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' describe Admin::RolesController do - let(:conference) { create(:conference) } let(:organizer_role) { Role.find_by(name: 'organizer', resource: conference) } let(:cfp_role) { Role.find_by(name: 'cfp', resource: conference) } diff --git a/spec/features/roles_spec.rb b/spec/features/roles_spec.rb index ebdeacaf..ba9e5d4c 100644 --- a/spec/features/roles_spec.rb +++ b/spec/features/roles_spec.rb @@ -98,6 +98,51 @@ feature Role do end end + context 'organization_admin' do + let!(:organization) { create(:organization) } + let!(:org_admin_role) { Role.find_by(name: 'organization_admin', resource: organization) } + let!(:organization_admin) { create(:user, role_ids: [org_admin_role.id]) } + let(:user_with_no_role) { create :user } + let!(:other_organization) { create(:organization) } + + before do + sign_in organization_admin + visit admin_organizations_path + end + + context 'for the organization it belongs to' do + scenario 'successfully adds role organization_admin' do + click_link('Admins', href: admins_admin_organization_path(organization.id)) + + fill_in 'user_email', with: user_with_no_role.email + click_button 'Add' + user_with_no_role.reload + + expect(user_with_no_role.has_role?('organization_admin', organization)).to eq true + end + + scenario 'successfully removes role organization_admin' do + click_link('Admins', href: admins_admin_organization_path(organization.id)) + + first('tr').find('.btn-danger').click + expect(organization_admin.has_role?('organization_admin', organization)).to eq false + end + end + + context 'for the organizations it does not belong to' do + scenario 'does not successfully add role organization_admin' do + click_link('Admins', href: admins_admin_organization_path(other_organization.id)) + + expect(page.has_field?('user_email')).to eq false + end + + scenario 'does not successfully removes role organization_admin' do + click_link('Admins', href: admins_admin_organization_path(other_organization.id)) + expect(page.has_css?('.btn-danger')).to eq false + end + end + end + context 'organizer' do Role.all.each.map(&:name).each do |role| it_behaves_like 'successfully', role, 'organizer' diff --git a/spec/models/admin_ability_spec.rb b/spec/models/admin_ability_spec.rb index 805dd621..f148b2ae 100644 --- a/spec/models/admin_ability_spec.rb +++ b/spec/models/admin_ability_spec.rb @@ -63,7 +63,7 @@ describe 'User with admin role' do it{ should_not be_able_to(:update, Role.find_by(name: 'organization_admin', resource: other_organization)) } it{ should_not be_able_to(:edit, Role.find_by(name: 'organization_admin', resource: other_organization)) } - it{ should_not be_able_to(:show, Role.find_by(name: 'organization_admin', resource: other_organization)) } + it{ should be_able_to(:admins, organization) } it{ should_not be_able_to(:new, User.new) } it{ should_not be_able_to(:create, User.new) } @@ -127,6 +127,8 @@ describe 'User with admin role' do let(:other_organization) { create(:organization) } let(:other_conference) { create(:conference, organization: other_organization) } + it{ should be_able_to(:assign_org_admins, organization) } + it{ should be_able_to(:unassign_org_admins, organization) } it{ should be_able_to(:manage, my_conference) } it{ should be_able_to(:read, organization) } it{ should be_able_to(:update, organization) } @@ -137,6 +139,8 @@ describe 'User with admin role' do it{ should_not be_able_to(:create, Conference.new(organization_id: other_organization.id)) } it{ should_not be_able_to(:new, Organization.new) } it{ should_not be_able_to(:create, Organization.new) } + + it_behaves_like 'user with any role' end context 'when user has the role organizer' do @@ -214,6 +218,9 @@ describe 'User with admin role' do it{ should be_able_to(:manage, resource) } + it{ should_not be_able_to(:assign_org_admins, organization) } + it{ should_not be_able_to(:unassign_org_admins, organization) } + %w[organizer cfp info_desk volunteers_coordinator].each do |role| it{ should be_able_to(:toggle_user, Role.find_by(name: role, resource: my_conference)) } it{ should be_able_to(:edit, Role.find_by(name: role, resource: my_conference)) } @@ -299,6 +306,8 @@ describe 'User with admin role' do it{ should be_able_to(:index, resource) } it{ should be_able_to(:show, resource) } it{ should be_able_to(:update, resource) } + it{ should_not be_able_to(:assign_org_admins, organization) } + it{ should_not be_able_to(:unassign_org_admins, organization) } it_behaves_like 'user with any role' it_behaves_like 'user with non-organizer role', 'cfp' @@ -366,6 +375,8 @@ describe 'User with admin role' do it{ should be_able_to(:index, resource) } it{ should be_able_to(:show, resource) } it{ should be_able_to(:update, resource) } + it{ should_not be_able_to(:assign_org_admins, organization) } + it{ should_not be_able_to(:unassign_org_admins, organization) } it_behaves_like 'user with any role' it_behaves_like 'user with non-organizer role', 'info_desk' @@ -433,6 +444,8 @@ describe 'User with admin role' do it{ should be_able_to(:index, resource) } it{ should be_able_to(:show, resource) } it{ should be_able_to(:update, resource) } + it{ should_not be_able_to(:assign_org_admins, organization) } + it{ should_not be_able_to(:unassign_org_admins, organization) } it 'should be_able to :manage Vposition' it 'should be_able to :manage Vday' @@ -509,6 +522,9 @@ describe 'User with admin role' do it{ should_not be_able_to(:edit, my_self_organized_track) } it{ should_not be_able_to(:update, my_self_organized_track) } + it{ should_not be_able_to(:assign_org_admins, organization) } + it{ should_not be_able_to(:unassign_org_admins, organization) } + it_behaves_like 'user with any role' it_behaves_like 'user with non-organizer role', 'track_organizer' end