Skip to content
Snippets Groups Projects
Verified Commit 7a71e994 authored by Suraj Tripathi's avatar Suraj Tripathi :one: Committed by GitLab
Browse files

Added membership_modified_by_admin_event

- Added feature enabled and setting enabled check on EventStore
- Added trigger for events
- Added create_service spec
- Added spec for ApplyPendingMemberApprovals worker
- Added spec for saas

EE: true
Changelog: added
parent addd64cd
No related branches found
No related tags found
2 merge requests!170053Security patch upgrade alert: Only expose to admins 17-4,!163631Added event and worker to apply outstanding member promotions
......@@ -377,6 +377,8 @@
- 1
- - gitlab_subscriptions_add_on_purchases_refresh_user_assignments
- 1
- - gitlab_subscriptions_member_management_apply_pending_member_approvals
- 1
- - gitlab_subscriptions_refresh_seats
- 1
- - gitlab_subscriptions_trials_apply_trial
......
# frozen_string_literal: true
module Members
class MembershipModifiedByAdminEvent < ::Gitlab::EventStore::Event
def schema
{
'type' => 'object',
'required' => %w[member_user_id],
'properties' => {
'member_user_id' => { 'type' => 'integer' }
}
}
end
end
end
......@@ -4,6 +4,7 @@ module EE
module Members
module CreateService
include ::Gitlab::Utils::StrongMemoize
include ::GitlabSubscriptions::MemberManagement::PromotionManagementUtils
extend ::Gitlab::Utils::Override
override :initialize
......@@ -205,6 +206,39 @@ def result(pass_back = {})
super(pass_back)
end
override :publish_event!
def publish_event!
super
return unless should_publish_admin_events?
members.each do |member|
next unless member_eligible_for_admin_event?(member)
::Gitlab::EventStore.publish(
::Members::MembershipModifiedByAdminEvent.new(data: {
member_user_id: member.user_id
})
)
end
end
def should_publish_admin_events?
promotion_management_applicable? &&
current_user&.can_admin_all_resources? &&
at_least_one_member_created?
end
def member_eligible_for_admin_event?(member)
member.persisted? &&
member.errors.empty? &&
member.user_id.present? &&
promotion_management_required_for_role?(
new_access_level: member.access_level,
member_role_id: member.member_role_id
)
end
end
end
end
......@@ -1542,6 +1542,15 @@
:weight: 1
:idempotent: true
:tags: []
- :name: gitlab_subscriptions_member_management_apply_pending_member_approvals
:worker_name: GitlabSubscriptions::MemberManagement::ApplyPendingMemberApprovalsWorker
:feature_category: :seat_cost_management
:has_external_dependencies: false
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent: true
:tags: []
- :name: gitlab_subscriptions_refresh_seats
:worker_name: GitlabSubscriptions::RefreshSeatsWorker
:feature_category: :seat_cost_management
......
# frozen_string_literal: true
module GitlabSubscriptions
module MemberManagement
class ApplyPendingMemberApprovalsWorker
include Gitlab::EventStore::Subscriber
include GitlabSubscriptions::MemberManagement::PromotionManagementUtils
feature_category :seat_cost_management
data_consistency :always
urgency :low
idempotent!
deduplicate :until_executed
def handle_event(event)
return unless promotion_management_applicable?
member_user = User.find_by_id(event.data[:member_user_id])
return unless member_user.present?
return unless ::Members::MemberApproval.pending_member_approvals_for_user(member_user.id).exists?
::GitlabSubscriptions::MemberManagement::ProcessUserBillablePromotionService
.new(nil, member_user, :approved, true).execute
end
end
end
end
......@@ -66,6 +66,12 @@ def configure!(store)
to: ::ContainerRegistry::ImagePushedEvent,
delay: 1.minute,
if: ->(event) { ::AppSec::ContainerScanning::ScanImageWorker.dispatch?(event) }
store.subscribe ::GitlabSubscriptions::MemberManagement::ApplyPendingMemberApprovalsWorker,
to: ::Members::MembershipModifiedByAdminEvent,
if: ->(_) {
::Gitlab::CurrentSettings.enable_member_promotion_management? &&
::Feature.enabled?(:member_promotion_management, :instance, type: :wip)
}
register_threat_insights_subscribers(store)
register_security_policy_subscribers(store)
......
......@@ -52,7 +52,8 @@
Search::Zoekt::OrphanedRepoEvent,
Search::Zoekt::RepoMarkedAsToDeleteEvent,
Search::Zoekt::TaskFailedEvent,
Security::PolicyDeletedEvent
Security::PolicyDeletedEvent,
::Members::MembershipModifiedByAdminEvent
])
end
end
......
......@@ -602,6 +602,16 @@
allow(License).to receive(:current).and_return(license)
end
shared_examples 'does not trigger event' do
it 'does not publish a MembershipModifiedByAdminEvent' do
expect(Gitlab::EventStore)
.not_to receive(:publish)
.with(an_instance_of(::Members::MembershipModifiedByAdminEvent))
execute_service
end
end
context 'when feature is disabled' do
before do
stub_feature_flags(member_promotion_management: false)
......@@ -657,6 +667,95 @@
})
end
end
it_behaves_like 'does not trigger event'
end
context 'when admin modifies members', :enable_admin_mode do
let_it_be(:user) { create(:admin) }
shared_examples 'triggers event' do |call_count = 1|
it 'publishes MembershipModifiedByAdminEvent' do
allow(Gitlab::EventStore).to receive(:publish).and_call_original
expect(Gitlab::EventStore)
.to receive(:publish)
.with(an_instance_of(::Members::MembershipModifiedByAdminEvent))
.and_call_original
.exactly(call_count).times
execute_service
end
end
context 'when adding user to billable DEVELOPER role' do
before do
params[:access_level] = Gitlab::Access::DEVELOPER
end
it_behaves_like 'triggers event', 2
context 'when one member has error' do
before do
root_ancestor.add_owner(project_users[0])
end
it_behaves_like 'triggers event', 1
end
context 'when feature is disabled' do
before do
stub_feature_flags(member_promotion_management: false)
end
it_behaves_like 'does not trigger event'
end
context 'when setting is disabled' do
before do
stub_application_setting(enable_member_promotion_management: false)
end
it_behaves_like 'does not trigger event'
end
context 'when license is not Ultimate' do
let(:license) { create(:license, plan: License::STARTER_PLAN) }
it_behaves_like 'does not trigger event'
end
context 'when new user not present in system is invited with email' do
let(:invites) { ['new_user@example.com'] }
it_behaves_like 'does not trigger event'
end
end
context 'when adding user to a non billable GUEST role' do
before do
params[:access_level] = Gitlab::Access::GUEST
end
it_behaves_like 'does not trigger event'
end
end
context 'when current_user is nil' do
let(:user) { nil }
before do
params[:skip_authorization] = true
end
it_behaves_like 'does not trigger event'
end
context 'when saas', :saas do
before_all do
root_ancestor.add_owner(user)
end
it_behaves_like 'does not trigger event'
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSubscriptions::MemberManagement::ApplyPendingMemberApprovalsWorker, feature_category: :seat_cost_management do
let_it_be(:group) { create(:group) }
let_it_be(:member_user) { create(:user) }
let(:user_id) { member_user.id }
let(:modified_by_admin_event) do
::Members::MembershipModifiedByAdminEvent.new(
data: { member_user_id: user_id }
)
end
let_it_be(:license) { create(:license, plan: License::ULTIMATE_PLAN) }
let!(:member_approval) do
create(:member_approval, user: member_user, member_namespace: group, member: nil, old_access_level: nil)
end
before do
stub_application_setting(enable_member_promotion_management: true)
allow(License).to receive(:current).and_return(license)
end
it_behaves_like 'subscribes to event' do
let(:event) { modified_by_admin_event }
end
it 'has the `until_executed` deduplicate strategy' do
expect(described_class.get_deduplicate_strategy).to eq(:until_executed)
end
describe '#handle_event' do
shared_examples 'does not perform any action' do
it do
expect(::GitlabSubscriptions::MemberManagement::ProcessUserBillablePromotionService).not_to receive(:new)
consume_event(subscriber: described_class, event: modified_by_admin_event)
end
end
context 'when member_user exists and has pending approvals' do
it 'applies pending promotion' do
expect do
consume_event(subscriber: described_class, event: modified_by_admin_event)
end.to change { member_approval.reload.status }
end
end
context 'when member_user does not exist' do
let(:user_id) { non_existing_record_id }
it_behaves_like 'does not perform any action'
end
context 'when member_user has no pending approvals' do
let!(:member_approval) { nil }
it_behaves_like 'does not perform any action'
end
context 'when feature is disabled' do
before do
stub_feature_flags(member_promotion_management: false)
end
it_behaves_like 'does not perform any action'
end
context 'when setting is disabled' do
before do
stub_application_setting(enable_member_promotion_management: false)
end
it_behaves_like 'does not perform any action'
end
context 'when license is not Ultimate' do
let(:license) { create(:license, plan: License::STARTER_PLAN) }
it_behaves_like 'does not perform any action'
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment