diff --git a/CHANGELOG-EE b/CHANGELOG-EE index e96037a4d26f11fcf9a5b4c34fd3e46a7068918a..a1be3c73e24daa6644b87c0a60c78483aa75bdc0 100644 --- a/CHANGELOG-EE +++ b/CHANGELOG-EE @@ -7,6 +7,9 @@ v 8.6.0 (unreleased) - [Elastic] Removing repository and wiki index after removing project - [Elastic] Update index on push to wiki +v 8.5.3 + - Prevent LDAP from downgrading a group's last owner + v 8.5.2 - Update LDAP groups asynchronously - Fix an issue when weight text was displayed in Issuable collapsed sidebar diff --git a/lib/gitlab/ldap/access.rb b/lib/gitlab/ldap/access.rb index 8fe73ce1d80ae6496e95d26c3a59446a6af3ffd9..2024fecc58e8f36c5aa37bc5520db83402228953 100644 --- a/lib/gitlab/ldap/access.rb +++ b/lib/gitlab/ldap/access.rb @@ -175,7 +175,14 @@ def update_ldap_group_links active_group_links = group.ldap_group_links.where(cn: cns_with_access) if active_group_links.any? - group.add_users([user.id], active_group_links.maximum(:group_access), skip_notification: true) + max_access = active_group_links.maximum(:group_access) + + # Ensure we don't leave a group without an owner + if max_access < Gitlab::Access::OWNER && group.last_owner?(user) + logger.warn "#{self.class.name}: LDAP group sync cannot demote #{user.name} (#{user.id}) from group #{group.name} (#{group.id}) as this is the group's last owner" + else + group.add_users([user.id], max_access, skip_notification: true) + end elsif group.last_owner?(user) logger.warn "#{self.class.name}: LDAP group sync cannot remove #{user.name} (#{user.id}) from group #{group.name} (#{group.id}) as this is the group's last owner" else diff --git a/spec/lib/gitlab/ldap/access_spec.rb b/spec/lib/gitlab/ldap/access_spec.rb index 371d42bf57f7660518804135b2edaacc7eb99a52..2d2c8fc54b44e7b33177cf632d868ad2781773ff 100644 --- a/spec/lib/gitlab/ldap/access_spec.rb +++ b/spec/lib/gitlab/ldap/access_spec.rb @@ -384,33 +384,50 @@ end end - context "existing access as master for group-1, not allowed" do + context 'existing access as master for group-1, not allowed via LDAP' do before do gitlab_group_1.group_members.masters.create(user_id: user.id) gitlab_group_1.ldap_group_links.create(cn: 'ldap-group1', group_access: Gitlab::Access::MASTER, provider: 'ldapmain') allow(access).to receive_messages(cns_with_access: ['ldap-group2']) end - it "removes user from gitlab_group_1" do + it 'removes user from gitlab_group_1' do expect { access.update_ldap_group_links }.to \ change{ gitlab_group_1.members.where(user_id: user).any? }.from(true).to(false) end end - context "existing access as owner for group-1 with no other owner, not allowed" do + context 'existing access as owner for group-1 with no other owner, not allowed via LDAP' do before do gitlab_group_1.group_members.owners.create(user_id: user.id) gitlab_group_1.ldap_group_links.create(cn: 'ldap-group1', group_access: Gitlab::Access::OWNER, provider: 'ldapmain') allow(access).to receive_messages(cns_with_access: ['ldap-group2']) end - it "does not remove the user from gitlab_group_1 since it's the last owner" do - expect { access.update_ldap_group_links }.not_to \ - change{ gitlab_group_1.has_owner?(user) } + # Note: Don't use `has_owner?` or `has_master?` in this expectation. + # It leads to false negatives. + it 'does not remove the user from gitlab_group_1 since its the last owner' do + expect { access.update_ldap_group_links }.not_to \ + change { gitlab_group_1.members.owners.where(user_id: user).any? } + end + end + + context 'existing access as owner for group-1 with no other owner, allowed via ldap-group1 as DEVELOPER' do + before do + gitlab_group_1.group_members.owners.create(user_id: user.id) + gitlab_group_1.ldap_group_links.create(cn: 'ldap-group1', group_access: Gitlab::Access::DEVELOPER, provider: 'ldapmain') + allow(access).to receive_messages(cns_with_access: ['ldap-group1']) + end + + # Note: Don't use `has_owner?` or `has_master?` in this expectation. + # It leads to false negatives. + it 'does not remove the user from gitlab_group_1 since its the last owner' do + expect { access.update_ldap_group_links }.not_to \ + change { gitlab_group_1.members.owners.where(user_id: user).any? } end end - context "existing access as owner for group-1 while other owners present, not allowed" do + context 'existing access as owner for group-1 while other owners present, not allowed via LDAP' do before do owner2 = create(:user) # a 2nd owner gitlab_group_1.group_members.owners.create([{ user_id: user.id }, { user_id: owner2.id }]) @@ -418,7 +435,7 @@ allow(access).to receive_messages(cns_with_access: ['ldap-group2']) end - it "removes user from gitlab_group_1" do + it 'removes user from gitlab_group_1' do expect { access.update_ldap_group_links }.to \ change{ gitlab_group_1.members.where(user_id: user).any? }.from(true).to(false) end