Commit 5ec9214d authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch 'qa-193-ldap-group-sync' into 'master'

E2E QA Tests - LDAP Admin user sync and Group sync with group cn and user filter

See merge request gitlab-org/gitlab-ee!8234
parents 4ef33e00 1e254c1b
......@@ -108,7 +108,7 @@
= link_to group_group_members_path(@group) do
.nav-icon-container
= sprite_icon('users')
%span.nav-item-name
%span.nav-item-name.qa-group-members-item
= _('Members')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(path: 'group_members#index', html_options: { class: "fly-out-top-item" } ) do
......
- if Gitlab::Auth::LDAP::Config.group_sync_enabled? && can?(current_user, :admin_ldap_group_links, @group)
= nav_link(path: 'ldap_group_links#index') do
= link_to group_ldap_group_links_path(@group), title: 'LDAP Group' do
= link_to group_ldap_group_links_path(@group), title: 'LDAP Group', class: 'qa-ldap-synchronization-link' do
%span
LDAP Synchronization
......
......@@ -7,7 +7,7 @@
= icon("refresh spin")
Pending sync…
- else
= link_to sync_group_ldap_path(@group), method: :put, class: 'btn' do
= link_to sync_group_ldap_path(@group), method: :put, class: 'btn qa-sync-now-button' do
= icon("refresh")
Sync now
- if @group.ldap_sync_ready? && @group.ldap_sync_last_successful_update_at
......
......@@ -25,7 +25,7 @@
= f.label :cn, class: 'col-form-label col-sm-2' do
LDAP Group cn
.col-sm-10
= f.hidden_field :cn, placeholder: 'Ex. QA group', class: 'xxlarge ajax-ldap-groups-select input-mn-300'
= f.hidden_field :cn, placeholder: 'Ex. QA group', class: 'xxlarge ajax-ldap-groups-select input-mn-300 qa-ldap-group-cn-select'
.form-text.text-muted
Synchronize #{group.name}'s members with this LDAP group.
%br
......@@ -36,7 +36,7 @@
= f.label :filter, class: 'col-form-label col-sm-2' do
LDAP User filter
.col-sm-10
= f.text_field :filter, placeholder: 'Ex. (&(objectCategory=person)(objectClass=developer))', class: 'form-control xxlarge input-mn-300'
= f.text_field :filter, placeholder: 'Ex. (&(objectCategory=person)(objectClass=developer))', class: 'form-control xxlarge input-mn-300 qa-ldap-user-filter-text-field'
.form-text.text-muted
- ldap_link = link_to 'LDAP Search Filter Syntax', 'https://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx'
This query must use valid #{ldap_link}. Synchronize #{group.name}'s members with this LDAP user filter.
......@@ -54,4 +54,4 @@
You can manage permission levels for individual group members in the Members tab.
.form-actions
= f.submit 'Add synchronization', class: 'btn btn-success'
= f.submit 'Add synchronization', class: 'btn btn-success qa-add-sync-button'
......@@ -329,6 +329,7 @@ module QA
module Page
autoload :Logging, 'qa/support/page/logging'
end
autoload :Api, 'qa/support/api'
end
end
......
......@@ -17,9 +17,11 @@ module QA
module Group
autoload :Menu, 'qa/ee/page/group/menu'
autoload :SamlSSOSignIn, 'qa/ee/page/group/saml_sso_sign_in'
autoload :Members, 'qa/ee/page/group/members'
module Settings
autoload :SamlSSO, 'qa/ee/page/group/settings/saml_sso'
autoload :LDAPSync, 'qa/ee/page/group/settings/ldap_sync'
end
end
......
# frozen_string_literal: true
module QA
module EE
module Page
module Group
class Members < QA::Page::Base
view 'ee/app/views/groups/group_members/_sync_button.html.haml' do
element :sync_now_button
end
def click_sync_now
click_element :sync_now_button
end
end
end
end
end
end
......@@ -7,12 +7,14 @@ module QA
class Menu < ::QA::Page::Base
view 'ee/app/views/groups/ee/_settings_nav.html.haml' do
element :group_saml_sso_link
element :ldap_synchronization_link
end
view 'app/views/layouts/nav/sidebar/_group.html.haml' do
element :group_sidebar
element :group_sidebar_submenu
element :group_settings_item
element :group_members_item
end
view 'ee/app/views/layouts/nav/ee/_epic_link.html.haml' do
......@@ -27,6 +29,20 @@ module QA
end
end
def go_to_ldap_sync_settings
hover_settings do
within_submenu do
click_element(:ldap_synchronization_link)
end
end
end
def go_to_members
within_sidebar do
click_element(:group_members_item)
end
end
def go_to_group_epics
within_sidebar do
click_element(:group_epics_link)
......
# frozen_string_literal: true
module QA
module EE
module Page
module Group
module Settings
class LDAPSync < ::QA::Page::Base
include QA::Page::Component::Select2
view 'ee/app/views/ldap_group_links/_form.html.haml' do
element :ldap_group_cn_select
element :add_sync_button
element :ldap_user_filter_text_field
end
def set_sync_method(method)
choose method
end
def set_group_cn(group_cn)
click_element :ldap_group_cn_select
search_and_select(group_cn)
end
def set_user_filter(user_filter)
fill_element :ldap_user_filter_text_field, user_filter
end
def click_add_sync_button
click_element :add_sync_button
end
end
end
end
end
end
end
......@@ -3,7 +3,12 @@ module QA
module Component
module Select2
def select_item(item_text)
find('ul.select2-result-sub > li', text: item_text).click
find('.select2-result-label', text: item_text).click
end
def search_and_select(item_text)
find('.select2-input').set(item_text)
select_item(item_text)
end
end
end
......
......@@ -68,6 +68,12 @@ module QA
end
end
def has_admin_area_link?(wait: Capybara.default_max_wait_time)
using_wait_time(wait) do
page.has_selector?(element_selector_css(:admin_area_link))
end
end
private
def within_top_menu
......
# frozen_string_literal: true
require 'airborne'
require 'active_support/core_ext/object/deep_dup'
require 'capybara/dsl'
module QA
module Resource
module ApiFabricator
include Airborne
include Capybara::DSL
HTTP_STATUS_OK = 200
......@@ -43,6 +41,7 @@ module QA
private
include Support::Api
attr_writer :api_resource, :api_response
def resource_web_url(resource)
......@@ -84,10 +83,6 @@ module QA
end
end
def parse_body(response)
JSON.parse(response.body, symbolize_names: true)
end
def process_api_response(parsed_response)
self.api_response = parsed_response
self.api_resource = transform_api_resource(parsed_response.deep_dup)
......
......@@ -2,6 +2,7 @@
require 'forwardable'
require 'capybara/dsl'
require 'active_support/core_ext/array/extract_options'
module QA
module Resource
......
......@@ -51,6 +51,10 @@ module QA
"/groups/#{CGI.escape("#{sandbox.path}/#{path}")}"
end
def api_members_path
"#{api_get_path}/members"
end
def api_post_path
'/groups'
end
......
......@@ -6,7 +6,8 @@ module QA
module Resource
class User < Base
attr_reader :unique_id
attr_writer :username, :password
attr_writer :username, :password, :name, :email
attr_accessor :provider, :extern_uid
def initialize
@unique_id = SecureRandom.hex(8)
......@@ -73,7 +74,7 @@ module QA
username: username,
name: name,
skip_confirmation: true
}
}.merge(ldap_post_body)
end
def self.fabricate_or_use(username, password)
......@@ -89,6 +90,15 @@ module QA
private
def ldap_post_body
return {} unless extern_uid && provider
{
extern_uid: extern_uid,
provider: provider
}
end
def fetch_id(username)
users = parse_body(api_get_from("/users?username=#{username}"))
......
......@@ -7,7 +7,7 @@ module QA
extend self
attr_writer :personal_access_token
attr_writer :personal_access_token, :ldap_username, :ldap_password
# The environment variables used to indicate if the environment under test
# supports the given feature
......@@ -94,11 +94,11 @@ module QA
end
def ldap_username
ENV['GITLAB_LDAP_USERNAME']
@ldap_username ||= ENV['GITLAB_LDAP_USERNAME']
end
def ldap_password
ENV['GITLAB_LDAP_PASSWORD']
@ldap_password ||= ENV['GITLAB_LDAP_PASSWORD']
end
def sandbox_name
......
# frozen_string_literal: true
module QA
context 'Manage', :orchestrated, :ldap_tls, :ldap_no_tls do
describe 'LDAP Group sync' do
include Support::Api
before(:all) do
create_admin_personal_access_token
end
context 'using group cn method' do
let(:ldap_users) do
[
{
name: 'ENG User 1',
username: 'enguser1',
email: 'enguser1@example.org',
provider: 'ldapmain',
extern_uid: 'uid=enguser1,ou=people,ou=global groups,dc=example,dc=org'
},
{
name: 'ENG User 2',
username: 'enguser2',
email: 'enguser2@example.org',
provider: 'ldapmain',
extern_uid: 'uid=enguser2,ou=people,ou=global groups,dc=example,dc=org'
},
{
name: 'ENG User 3',
username: 'enguser3',
email: 'enguser3@example.org',
provider: 'ldapmain',
extern_uid: 'uid=enguser3,ou=people,ou=global groups,dc=example,dc=org'
}
]
end
let(:owner_user) { 'enguser1' }
let(:sync_users) { ['ENG User 2', 'ENG User 3'] }
before do
create_users_via_api(ldap_users)
group = create_group_and_add_user_via_api(owner_user, 'Synched-engineering-group')
signin_and_visit_group_as_user(owner_user, group)
EE::Page::Group::Menu.perform(&:go_to_ldap_sync_settings)
EE::Page::Group::Settings::LDAPSync.perform do |page|
page.set_sync_method('LDAP Group cn')
page.set_group_cn('Engineering')
page.click_add_sync_button
end
EE::Page::Group::Menu.perform(&:go_to_members)
end
it 'has LDAP users synced' do
verify_users_synced(sync_users)
end
end
context 'user filter method' do
let(:ldap_users) do
[
{
name: 'HR User 1',
username: 'hruser1',
email: 'hruser1@example.org',
provider: 'ldapmain',
extern_uid: 'uid=hruser1,ou=people,ou=global groups,dc=example,dc=org'
},
{
name: 'HR User 2',
username: 'hruser2',
email: 'hruser2@example.org',
provider: 'ldapmain',
extern_uid: 'uid=hruser2,ou=people,ou=global groups,dc=example,dc=org'
},
{
name: 'HR User 3',
username: 'hruser3',
email: 'hruser3@example.org',
provider: 'ldapmain',
extern_uid: 'uid=hruser3,ou=people,ou=global groups,dc=example,dc=org'
}
]
end
let(:owner_user) { 'hruser1' }
let(:sync_users) { ['HR User 2', 'HR User 3'] }
before do
create_users_via_api(ldap_users)
group = create_group_and_add_user_via_api(owner_user, 'Synched-human-resources-group')
signin_and_visit_group_as_user(owner_user, group)
EE::Page::Group::Menu.perform(&:go_to_ldap_sync_settings)
EE::Page::Group::Settings::LDAPSync.perform do |page|
page.set_user_filter('(&(objectClass=person)(cn=HR*))')
page.click_add_sync_button
end
EE::Page::Group::Menu.perform(&:go_to_members)
end
it 'has LDAP users synced' do
verify_users_synced(sync_users)
end
end
def create_users_via_api(users)
users.each do |user|
Resource::User.fabricate_via_api! do |resource|
resource.username = user[:username]
resource.name = user[:name]
resource.email = user[:email]
resource.extern_uid = user[:extern_uid]
resource.provider = user[:provider]
end
end
end
def add_user_to_group_via_api(user, group)
api_client = Runtime::API::Client.new(:gitlab)
response = get Runtime::API::Request.new(api_client, "/users?username=#{user}").url
post Runtime::API::Request.new(api_client, group.api_members_path).url, { user_id: parse_body(response).first[:id], access_level: '50' }
end
def create_admin_personal_access_token
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_admin_credentials)
Runtime::Env.personal_access_token = Resource::PersonalAccessToken.fabricate!.access_token
Page::Main::Menu.perform(&:sign_out)
end
def create_group_and_add_user_via_api(user_name, group_name)
group = Resource::Group.fabricate_via_api! do |resource|
resource.path = "#{group_name}-#{SecureRandom.hex(4)}"
end
add_user_to_group_via_api(user_name, group)
group
end
def signin_and_visit_group_as_user(user_name, group)
Runtime::Env.ldap_username = user_name
Runtime::Env.ldap_password = 'password'
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform do |login_page|
login_page.sign_in_using_credentials
end
group.visit!
end
def verify_users_synced(expected_users)
EE::Page::Group::Members.perform do |page|
page.click_sync_now
users_synchronised = page.with_retry(reload: true) do
expected_users.map { |user| page.has_content?(user) }.all?
end
expect(users_synchronised).to be_truthy
end
end
end
end
end
# frozen_string_literal: true
module QA
context 'Manage', :orchestrated, :ldap_no_tls, :ldap_tls do
describe 'LDAP admin sync' do
it 'Syncs admin users' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform do |login_page|
Runtime::Env.ldap_username = 'adminuser1'
Runtime::Env.ldap_password = 'password'
login_page.sign_in_using_credentials
end
Page::Main::Menu.perform do |menu|
expect(menu).to have_personal_area
# The ldap_sync_worker_cron job is set to run every minute
admin_synchronised = menu.wait(max: 80, time: 1, reload: true) do
menu.has_admin_area_link?
end
expect(admin_synchronised).to be_truthy
end
end
end
end
end
module QA
module Support
module Api
def post(url, payload)
RestClient::Request.execute(
method: :post,
url: url,
payload: payload,
verify_ssl: false)
rescue RestClient::ExceptionWithResponse => e
e.response
end
def get(url)
RestClient::Request.execute(
method: :get,
url: url,
verify_ssl: false)
rescue RestClient::ExceptionWithResponse => e
e.response
end
def parse_body(response)
JSON.parse(response.body, symbolize_names: true)
end
end
end
end
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