Commit 74fd713a authored by Etienne Baqué's avatar Etienne Baqué

Merge branch 'vij-member-approval-service' into 'master'

Add member activation service

See merge request gitlab-org/gitlab!73483
parents a40517e8 5fcbee2d
# frozen_string_literal: true
# Members added to groups and projects after the root group user cap has been reached
# will be added in an `awaiting` state.
#
# Root Group owners may activate those members at their discretion via this service, either
# individually or all awaiting members.
#
# User facing terminology differs to what we use in the backend:
#
# - activate => approve
# - awaiting => pending
module Members
class ActivateService
include BaseServiceUtility
def initialize(group, user: nil, activate_all: false, current_user:)
@group = group
@user = user
@current_user = current_user
@activate_all = activate_all
end
def execute
return error(_('No group provided')) unless group
return error(_('You do not have permission to approve a member'), :forbidden) unless allowed?
if activate_memberships
log_event
success
else
error(_('No memberships found'), :bad_request)
end
end
private
attr_reader :current_user, :group, :user, :activate_all
def activate_memberships
memberships_found = false
memberships = activate_all ? awaiting_memberships : user_memberships
memberships.find_each do |member|
memberships_found = true
member.activate
end
memberships_found
end
# rubocop: disable CodeReuse/ActiveRecord
def user_memberships
awaiting_memberships.where(user_id: user.id)
end
# rubocop: enable CodeReuse/ActiveRecord
def awaiting_memberships
::Member.in_hierarchy(group).awaiting
end
def allowed?
can?(current_user, :admin_group_member, group)
end
def log_event
log_params = {
group: group.id,
approved_by: current_user.id
}.tap do |params|
params[:message] = activate_all ? 'Approved all pending group members' : 'Group member access approved'
params[:user] = user.id unless activate_all
end
Gitlab::AppLogger.info(log_params)
end
end
end
# frozen_string_literal: true
FactoryBot.modify do
factory :group_member do
trait :awaiting do
after(:create) do |member|
member.wait
end
end
end
end
# frozen_string_literal: true
FactoryBot.modify do
factory :project_member do
trait :awaiting do
after(:create) do |member|
member.wait
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Members::ActivateService do
describe '#execute' do
let_it_be(:current_user) { create(:user) }
let_it_be(:user) { create(:user) }
let_it_be(:root_group) { create(:group) }
let_it_be(:project) { create(:project, group: root_group) }
let_it_be(:sub_group) { create(:group, parent: root_group) }
let(:group) { root_group }
let(:activate_all) { false }
subject(:execute) { described_class.new(group, user: user, current_user: current_user, activate_all: activate_all).execute }
context 'when unauthorized' do
it 'returns an access error' do
result = execute
expect(result[:status]).to eq :error
expect(result[:message]).to eq 'You do not have permission to approve a member'
end
end
context 'when no group is provided' do
let(:group) { nil }
it 'returns an error' do
result = execute
expect(result[:status]).to eq :error
expect(result[:message]).to eq 'No group provided'
end
end
shared_examples 'successful user activation' do
before do
expect(member.awaiting?).to be true
end
it 'activates the member' do
expect(execute[:status]).to eq :success
expect(member.reload.active?).to be true
end
it 'logs the approval in application logs' do
expect(Gitlab::AppLogger).to receive(:info).with(
message: "Group member access approved",
group: group.id,
user: user.id,
approved_by: current_user.id
)
execute
end
end
context 'when authorized' do
before do
group.add_owner(current_user)
end
context 'when activating an individual user' do
context 'when user is an awaiting member of a root group' do
it_behaves_like 'successful user activation' do
let(:member) { create(:group_member, :awaiting, group: root_group, user: user) }
end
end
context 'when user is an awaiting member of a sub-group' do
let(:group) { sub_group }
it_behaves_like 'successful user activation' do
let(:member) { create(:group_member, :awaiting, group: sub_group, user: user) }
end
end
context 'when user is an awaiting member of a project' do
it_behaves_like 'successful user activation' do
let(:member) { create(:project_member, :awaiting, project: project, user: user) }
end
end
context 'when user is not an awaiting member' do
it 'returns an error' do
result = execute
expect(result[:status]).to eq :error
expect(result[:message]).to eq 'No memberships found'
end
end
end
context 'when activating all awaiting members' do
let!(:group_members) { create_list(:group_member, 5, :awaiting, group: group) }
let!(:sub_group_members) { create_list(:group_member, 5, :awaiting, group: sub_group) }
let!(:project_members) { create_list(:project_member, 5, :awaiting, project: project) }
let(:user) { nil }
let(:activate_all) { true }
it 'activates all awaiting group members' do
execute
group_members.each do |member|
expect(member.reload.active?).to be true
end
end
it 'activates all awaiting sub_group members' do
execute
sub_group_members.each do |member|
expect(member.reload.active?).to be true
end
end
it 'activates all awaiting project members' do
execute
project_members.each do |member|
expect(member.reload.active?).to be true
end
end
it 'logs the approval in application logs' do
expect(Gitlab::AppLogger).to receive(:info).with(
message: "Approved all pending group members",
group: group.id,
approved_by: current_user.id
)
execute
end
end
end
end
end
......@@ -23326,6 +23326,9 @@ msgstr ""
msgid "No forks are available to you."
msgstr ""
msgid "No group provided"
msgstr ""
msgid "No grouping"
msgstr ""
......@@ -23377,6 +23380,9 @@ msgstr ""
msgid "No members found"
msgstr ""
msgid "No memberships found"
msgstr ""
msgid "No merge requests found"
msgstr ""
......@@ -39470,6 +39476,9 @@ msgstr ""
msgid "You do not have permission to access dora metrics."
msgstr ""
msgid "You do not have permission to approve a member"
msgstr ""
msgid "You do not have permission to leave this %{namespaceType}."
msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment