Commit cb57c24b authored by Stan Hu's avatar Stan Hu

Merge branch '4354-lock-memberships-to-ldap-sync-part-1' into 'master'

Add global LDAP membership lock setting

Closes #4354

See merge request gitlab-org/gitlab-ee!14007
parents f320e9ed 9a61f6ee
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddLdapMembershipLock < ActiveRecord::Migration[5.1]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default(:application_settings, :lock_memberships_to_ldap, :boolean, default: false)
end
def down
remove_column(:application_settings, :lock_memberships_to_ldap)
end
end
......@@ -227,6 +227,7 @@ ActiveRecord::Schema.define(version: 20190611161641) do
t.text "encrypted_lets_encrypt_private_key_iv"
t.boolean "dns_rebinding_protection_enabled", default: true, null: false
t.boolean "default_project_deletion_protection", default: false, null: false
t.boolean "lock_memberships_to_ldap", default: false, null: false
t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id", using: :btree
t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id", using: :btree
t.index ["usage_stats_set_by_user_id"], name: "index_application_settings_on_usage_stats_set_by_user_id", using: :btree
......
......@@ -183,6 +183,15 @@ group, as opposed to the full DN.
1. [Restart GitLab][restart] for the changes to take effect.
## Global group memberships lock
"Lock memberships to LDAP synchronization" setting allows instance administrators
to lock down user abilities to invite new members to a group. When enabled following happens:
1. Only administrator can manage memberships of any group including access levels.
2. Users are not allowed to share project with other groups or invite members to a project created in a group.
## Adjusting LDAP user sync schedule
> Introduced in GitLab Enterprise Edition Starter.
......
......@@ -37,6 +37,7 @@ module EE
:geo_status_timeout,
:geo_node_allowed_ips,
:help_text,
:lock_memberships_to_ldap,
:pseudonymizer_enabled,
:repository_size_limit,
:shared_runners_minutes,
......
......@@ -139,11 +139,11 @@ module EE
override :membership_locked?
def membership_locked?
if @project.group && @project.group.membership_lock
true
else
false
end
group = @project.group
return false unless group
group.membership_lock? || ::Gitlab::CurrentSettings.lock_memberships_to_ldap?
end
def group_project_templates_count(group_id)
......
......@@ -82,6 +82,7 @@ module EE
elasticsearch_shards: 5,
elasticsearch_url: ENV['ELASTIC_URL'] || 'http://localhost:9200',
email_additional_text: nil,
lock_memberships_to_ldap: false,
mirror_capacity_threshold: Settings.gitlab['mirror_capacity_threshold'],
mirror_max_capacity: Settings.gitlab['mirror_max_capacity'],
mirror_max_delay: Settings.gitlab['mirror_max_delay'],
......
......@@ -306,11 +306,12 @@ module EE
end
def group_ldap_synced?
if group
group.ldap_synced?
else
false
end
group&.ldap_synced?
end
override :allowed_to_share_with_group?
def allowed_to_share_with_group?
super && !(group && ::Gitlab::CurrentSettings.lock_memberships_to_ldap?)
end
def reference_issue_tracker?
......
......@@ -14,8 +14,11 @@ module EE
end
condition(:can_owners_manage_ldap, scope: :global) do
::Gitlab::CurrentSettings.current_application_settings
.allow_group_owners_to_manage_ldap
::Gitlab::CurrentSettings.allow_group_owners_to_manage_ldap?
end
condition(:memberships_locked_to_ldap, scope: :global) do
::Gitlab::CurrentSettings.lock_memberships_to_ldap?
end
condition(:security_dashboard_feature_disabled) do
......@@ -79,6 +82,12 @@ module EE
rule { ldap_synced & (admin | (can_owners_manage_ldap & owner)) }.enable :override_group_member
rule { memberships_locked_to_ldap & ~admin }.policy do
prevent :admin_group_member
prevent :update_group_member
prevent :override_group_member
end
rule { developer }.policy do
enable :read_group_security_dashboard
end
......
......@@ -4,6 +4,15 @@
.form-group
= form.label :allow_group_owners_to_manage_ldap, _('LDAP settings'), class: 'label-bold'
.form-check
= form.check_box :lock_memberships_to_ldap, class: 'form-check-input'
= form.label :lock_memberships_to_ldap, class: 'form-check-label' do
= _('Lock memberships to LDAP synchronization')
%span.form-text.text-muted
= _('If checked, new group memberships and permissions can only be added via LDAP synchronization')
= link_to icon('question-circle'), help_page_path('administration/auth/ldap-ee', anchor: 'global-group-memberships-lock')
.form-check
= form.check_box :allow_group_owners_to_manage_ldap, class: 'form-check-input'
= form.label :allow_group_owners_to_manage_ldap, class: 'form-check-label' do
......
---
title: Provide application-wide LDAP membership lock setting
merge_request: 4354
author:
type: added
......@@ -35,6 +35,7 @@ describe Admin::ApplicationSettingsController do
slack_app_secret: 'slack_app_secret',
slack_app_verification_token: 'slack_app_verification_token',
allow_group_owners_to_manage_ldap: false,
lock_memberships_to_ldap: true,
geo_node_allowed_ips: '0.0.0.0/0, ::/0'
}
......
......@@ -3,13 +3,13 @@
require 'spec_helper'
describe ProjectsHelper do
describe '#project_incident_management_setting' do
set(:project) { create(:project) }
let(:project) { create(:project) }
before do
helper.instance_variable_set(:@project, project)
end
before do
helper.instance_variable_set(:@project, project)
end
describe '#project_incident_management_setting' do
context 'when incident_management_setting exists' do
let(:project_incident_management_setting) do
create(:project_incident_management_setting, project: project)
......@@ -45,11 +45,9 @@ describe ProjectsHelper do
end
describe '#can_import_members?' do
let(:project) { create(:project) }
let(:owner) { project.owner }
before do
helper.instance_variable_set(:@project, project)
allow(helper).to receive(:current_user) { owner }
end
......@@ -63,4 +61,39 @@ describe ProjectsHelper do
expect(helper.can_import_members?).to eq true
end
end
describe '#membership_locked?' do
let(:project) { build_stubbed(:project, group: group) }
let(:group) { nil }
context 'when project has no group' do
let(:project) { Project.new }
it 'is false' do
expect(helper).not_to be_membership_locked
end
end
context 'with group_membership_lock enabled' do
let(:group) { build_stubbed(:group, membership_lock: true) }
it 'is true' do
expect(helper).to be_membership_locked
end
end
context 'with global LDAP membership lock enabled' do
before do
stub_application_setting(lock_memberships_to_ldap: true)
end
context 'and group membership_lock disabled' do
let(:group) { build_stubbed(:group, membership_lock: false) }
it 'is true' do
expect(helper).to be_membership_locked
end
end
end
end
end
......@@ -2101,6 +2101,34 @@ describe Project do
end
end
describe '#allowed_to_share_with_group?' do
context 'for group related project' do
subject(:project) { build_stubbed(:project, namespace: group, group: group) }
let(:group) { build_stubbed :group }
context 'with lock_memberships_to_ldap application setting enabled' do
before do
stub_application_setting(lock_memberships_to_ldap: true)
end
it { is_expected.not_to be_allowed_to_share_with_group }
end
end
context 'personal project' do
subject(:project) { build_stubbed(:project, namespace: namespace) }
let(:namespace) { build_stubbed :namespace }
context 'with lock_memberships_to_ldap application setting enabled' do
before do
stub_application_setting(lock_memberships_to_ldap: true)
end
it { is_expected.to be_allowed_to_share_with_group }
end
end
end
# Despite stubbing the current node as the primary or secondary, the
# behaviour for EE::Project#lfs_http_url_to_repo() is to call
# Project#lfs_http_url_to_repo() which does not have a Geo context.
......
......@@ -174,6 +174,28 @@ describe GroupPolicy do
it { is_expected.to be_allowed(:override_group_member) }
it { is_expected.to be_allowed(:admin_ldap_group_links) }
end
context 'when memberships locked to LDAP' do
before do
stub_application_setting(allow_group_owners_to_manage_ldap: true)
stub_application_setting(lock_memberships_to_ldap: true)
end
context 'admin' do
let(:current_user) { admin }
it { is_expected.to be_allowed(:override_group_member) }
it { is_expected.to be_allowed(:update_group_member) }
end
context 'owner' do
let(:current_user) { owner }
it { is_expected.not_to be_allowed(:admin_group_member) }
it { is_expected.not_to be_allowed(:override_group_member) }
it { is_expected.not_to be_allowed(:update_group_member) }
end
end
end
describe 'create_jira_connect_subscription' do
......
......@@ -6880,6 +6880,9 @@ msgstr ""
msgid "If checked, group owners can manage LDAP group links and LDAP member overrides"
msgstr ""
msgid "If checked, new group memberships and permissions can only be added via LDAP synchronization"
msgstr ""
msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
msgstr ""
......@@ -7882,6 +7885,9 @@ msgstr ""
msgid "Lock %{issuableDisplayName}"
msgstr ""
msgid "Lock memberships to LDAP synchronization"
msgstr ""
msgid "Lock not found"
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