Commit 0d8410f4 authored by Mark Lapierre's avatar Mark Lapierre 🌴 Committed by Rémy Coutable

Add E2E test protected branch specific user access

Makes configuring projected branch roles compatible with adding a
user or group when using EE.

Adds some login convenience methods.

Allows projects and groups to add members.
parent 214441d1
......@@ -29,4 +29,4 @@
= yield :push_access_levels
.card-footer
= f.submit 'Protect', class: 'btn-success btn', disabled: true
= f.submit 'Protect', class: 'btn-success btn', disabled: true, data: { qa_selector: 'protect_button' }
......@@ -61,6 +61,7 @@ module QA
autoload :KubernetesCluster, 'qa/resource/kubernetes_cluster'
autoload :User, 'qa/resource/user'
autoload :ProjectMilestone, 'qa/resource/project_milestone'
autoload :Members, 'qa/resource/members'
autoload :Wiki, 'qa/resource/wiki'
autoload :File, 'qa/resource/file'
autoload :Fork, 'qa/resource/fork'
......
......@@ -20,6 +20,23 @@ module QA
end
end
end
private
def select_allowed(action, allowed)
click_element :"allowed_to_#{action}_select"
allowed[:roles] = QA::Resource::ProtectedBranch::Roles::NO_ONE unless allowed.key?(:roles)
within_element(:"allowed_to_#{action}_dropdown") do
click_on allowed[:roles]
allowed[:users].each { |user| click_on user.username } if allowed.key?(:users)
allowed[:groups].each { |group| click_on group.name } if allowed.key?(:groups)
end
# Click the select element again to close the dropdown
click_element :protected_branch_select
end
end
end
end
......
......@@ -44,7 +44,7 @@ module QA
def sign_in_using_credentials(user = nil)
# Don't try to log-in if we're already logged-in
return if Page::Main::Menu.perform { |menu| menu.has_personal_area?(wait: 0) }
return if Page::Main::Menu.perform(&:signed_in?)
using_wait_time 0 do
set_initial_password_if_present
......@@ -75,10 +75,7 @@ module QA
end
def sign_in_using_ldap_credentials(user)
# Log out if already logged in
Page::Main::Menu.perform do |menu|
menu.sign_out if menu.has_personal_area?(wait: 0)
end
Page::Main::Menu.perform(&:sign_out_if_signed_in)
using_wait_time 0 do
set_initial_password_if_present
......@@ -149,7 +146,7 @@ module QA
end
def sign_out_and_sign_in_as(user:)
Menu.perform(&:sign_out)
Menu.perform(&:sign_out_if_signed_in)
has_sign_in_tab?
sign_in_using_credentials(user)
end
......
......@@ -55,6 +55,10 @@ module QA
within_top_menu { click_element :admin_area_link }
end
def signed_in?
has_personal_area?(wait: 0)
end
def sign_out
within_user_menu do
click_element :sign_out_link
......@@ -62,7 +66,7 @@ module QA
end
def sign_out_if_signed_in
sign_out if has_personal_area?(wait: 0)
sign_out if signed_in?
end
def click_settings_link
......
......@@ -25,6 +25,10 @@ module QA
element :protected_branches_list
end
view 'app/views/projects/protected_branches/shared/_create_protected_branch.html.haml' do
element :protect_button
end
def select_branch(branch_name)
click_element :protected_branch_select
......@@ -33,40 +37,31 @@ module QA
end
end
def allow_no_one_to_push
go_to_allow(:push, 'No one')
end
def allow_devs_and_maintainers_to_push
go_to_allow(:push, 'Developers + Maintainers')
def select_allowed_to_merge(allowed)
select_allowed(:merge, allowed)
end
# @deprecated
alias_method :allow_devs_and_masters_to_push, :allow_devs_and_maintainers_to_push
def allow_no_one_to_merge
go_to_allow(:merge, 'No one')
def select_allowed_to_push(allowed)
select_allowed(:push, allowed)
end
def allow_devs_and_maintainers_to_merge
go_to_allow(:merge, 'Developers + Maintainers')
end
# @deprecated
alias_method :allow_devs_and_masters_to_merge, :allow_devs_and_maintainers_to_merge
def protect_branch
click_on 'Protect'
click_element :protect_button
end
private
def go_to_allow(action, text)
def select_allowed(action, allowed)
click_element :"allowed_to_#{action}_select"
allowed[:roles] = Resource::ProtectedBranch::Roles::NO_ONE unless allowed.key?(:roles)
within_element(:"allowed_to_#{action}_dropdown") do
click_on text
click_on allowed[:roles]
end
# Click the select element again to close the dropdown
click_element :protected_branch_select
end
end
end
......
......@@ -3,6 +3,8 @@
module QA
module Resource
class Group < Base
include Members
attr_accessor :path, :description
attribute :sandbox do
......@@ -48,19 +50,10 @@ module QA
super
end
def add_member(user, access_level = '30')
# 30 = developer access
post Runtime::API::Request.new(api_client, api_members_path).url, { user_id: user.id, access_level: access_level }
end
def api_get_path
"/groups/#{CGI.escape("#{sandbox.path}/#{path}")}"
end
def api_members_path
"#{api_get_path}/members"
end
def api_post_path
'/groups'
end
......
# frozen_string_literal: true
module QA
module Resource
#
# Included in Resource::Project and Resource::Group to allow changes to
# project/group membership
#
module Members
def add_member(user, access_level = AccessLevel::DEVELOPER)
post Runtime::API::Request.new(api_client, api_members_path).url, { user_id: user.id, access_level: access_level }
end
def api_members_path
"#{api_get_path}/members"
end
class AccessLevel
NO_ACCESS = 0
GUEST = 10
REPORTER = 20
DEVELOPER = 30
MAINTAINER = 40
OWNER = 50
end
end
end
end
......@@ -6,6 +6,7 @@ module QA
module Resource
class Project < Base
include Events::Project
include Members
attr_writer :initialize_with_readme
attr_writer :visibility
......@@ -75,11 +76,6 @@ module QA
super
end
def add_member(user, access_level = '30')
# 30 = developer access
post Runtime::API::Request.new(api_client, api_members_path).url, { user_id: user.id, access_level: access_level }
end
def api_get_path
"/projects/#{CGI.escape(path_with_namespace)}"
end
......@@ -112,6 +108,10 @@ module QA
post_body
end
def share_with_group(invitee, access_level = Resource::Members::AccessLevel::DEVELOPER)
post Runtime::API::Request.new(api_client, "/projects/#{id}/share").url, { group_id: invitee.id, group_access: access_level }
end
private
def transform_api_resource(api_resource)
......
......@@ -3,7 +3,7 @@
module QA
module Resource
class ProtectedBranch < Base
attr_accessor :branch_name, :allow_to_push, :allow_to_merge, :protected
attr_accessor :branch_name, :allowed_to_push, :allowed_to_merge, :protected
attribute :project do
Project.fabricate_via_api! do |resource|
......@@ -25,8 +25,12 @@ module QA
def initialize
@branch_name = 'test/branch'
@allow_to_push = true
@allow_to_merge = true
@allowed_to_push = {
roles: Resource::ProtectedBranch::Roles::DEVS_AND_MAINTAINERS
}
@allowed_to_merge = {
roles: Resource::ProtectedBranch::Roles::DEVS_AND_MAINTAINERS
}
@protected = false
end
......@@ -35,29 +39,14 @@ module QA
project.wait_for_push_new_branch @branch_name
# The upcoming process will make it access the Protected Branches page,
# select the already created branch and protect it according
# to `allow_to_push` variable.
return branch unless @protected
project.visit!
Page::Project::Menu.perform(&:go_to_repository_settings)
Page::Project::Settings::Repository.perform do |setting|
setting.expand_protected_branches do |page|
page.select_branch(branch_name)
if allow_to_push
page.allow_devs_and_maintainers_to_push
else
page.allow_no_one_to_push
end
if allow_to_merge
page.allow_devs_and_maintainers_to_merge
else
page.allow_no_one_to_merge
end
page.select_allowed_to_merge(allowed_to_merge)
page.select_allowed_to_push(allowed_to_push)
page.wait(reload: false) do
!page.first('.btn-success').disabled?
......@@ -79,6 +68,12 @@ module QA
def api_delete_path
"/projects/#{@project.api_resource[:id]}/protected_branches/#{@branch_name}"
end
class Roles
NO_ONE = 'No one'
DEVS_AND_MAINTAINERS = 'Developers + Maintainers'
MAINTAINERS = 'Maintainers'
end
end
end
end
......@@ -17,16 +17,11 @@ module QA
Page::Main::Login.perform(&:sign_in_using_credentials)
end
after do
# We need to clear localStorage because we're using it for the dropdown,
# and capybara doesn't do this for us.
# https://github.com/teamcapybara/capybara/issues/1702
Capybara.execute_script 'localStorage.clear()'
end
context 'when developers and maintainers are allowed to push to a protected branch' do
it 'user with push rights successfully pushes to the protected branch' do
create_protected_branch(allow_to_push: true)
create_protected_branch(allowed_to_push: {
roles: Resource::ProtectedBranch::Roles::DEVS_AND_MAINTAINERS
})
push = push_new_file(branch_name)
......@@ -36,18 +31,19 @@ module QA
context 'when developers and maintainers are not allowed to push to a protected branch' do
it 'user without push rights fails to push to the protected branch' do
create_protected_branch(allow_to_push: false)
create_protected_branch(allowed_to_push: {
roles: Resource::ProtectedBranch::Roles::NO_ONE
})
expect { push_new_file(branch_name) }.to raise_error(QA::Git::Repository::RepositoryCommandError, /remote: GitLab: You are not allowed to push code to protected branches on this project\.([\s\S]+)\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/)
end
end
def create_protected_branch(allow_to_push:)
def create_protected_branch(allowed_to_push:)
Resource::ProtectedBranch.fabricate! do |resource|
resource.branch_name = branch_name
resource.project = project
resource.allow_to_push = allow_to_push
resource.protected = true
resource.allowed_to_push = allowed_to_push
end
end
......
# frozen_string_literal: true
module QA
context 'Create' do
describe 'Restricted protected branch push and merge' do
let(:user_developer) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) }
let(:user_maintainer) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_2, Runtime::Env.gitlab_qa_password_2) }
let(:branch_name) { 'protected-branch' }
let(:commit_message) { 'Protected push commit message' }
shared_examples 'only user with access pushes and merges' do
it 'unselected maintainer user fails to push' do
expect { push_new_file(branch_name, as_user: user_maintainer) }.to raise_error(
QA::Git::Repository::RepositoryCommandError,
/remote: GitLab: You are not allowed to push code to protected branches on this project\.([\s\S]+)\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/)
end
it 'selected developer user pushes and merges' do
push = push_new_file(branch_name, as_user: user_developer)
expect(push.output).to match(/remote: To create a merge request for protected-branch, visit/)
Resource::MergeRequest.fabricate_via_api! do |merge_request|
merge_request.project = project
merge_request.target_new_branch = false
merge_request.source_branch = branch_name
merge_request.no_preparation = true
end.visit!
Page::MergeRequest::Show.perform(&:merge!)
expect(page).to have_content('The changes were merged')
end
end
context 'when only one user is allowed to merge and push to a protected branch' do
let(:project) do
Resource::Project.fabricate! do |resource|
resource.name = 'user-with-access-to-protected-branch'
resource.initialize_with_readme = true
end
end
before do
project.add_member(user_developer, Resource::Members::AccessLevel::DEVELOPER)
project.add_member(user_maintainer, Resource::Members::AccessLevel::MAINTAINER)
login
Resource::ProtectedBranch.fabricate! do |protected_branch|
protected_branch.branch_name = branch_name
protected_branch.project = project
protected_branch.allowed_to_merge = {
users: [user_developer]
}
protected_branch.allowed_to_push = {
users: [user_developer]
}
end
end
it_behaves_like 'only user with access pushes and merges'
end
context 'when only one group is allowed to merge and push to a protected branch' do
let(:group) do
Resource::Group.fabricate_via_api! do |group|
group.path = "access-to-protected-branch-#{SecureRandom.hex(8)}"
end
end
let(:project) do
Resource::Project.fabricate! do |resource|
resource.name = 'group-with-access-to-protected-branch'
resource.initialize_with_readme = true
end
end
before do
login
group.add_member(user_developer, Resource::Members::AccessLevel::DEVELOPER)
project.share_with_group(group, Resource::Members::AccessLevel::DEVELOPER)
project.add_member(user_maintainer, Resource::Members::AccessLevel::MAINTAINER)
Resource::ProtectedBranch.fabricate! do |protected_branch|
protected_branch.branch_name = branch_name
protected_branch.project = project
protected_branch.allowed_to_merge = {
groups: [group]
}
protected_branch.allowed_to_push = {
groups: [group]
}
end
end
it_behaves_like 'only user with access pushes and merges'
end
def login(as_user: Runtime::User)
Page::Main::Menu.perform(&:sign_out_if_signed_in)
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform do |login|
login.sign_in_using_credentials(as_user)
end
end
def push_new_file(branch, as_user: user)
Resource::Repository::Push.fabricate! do |push|
push.repository_http_uri = project.repository_http_location.uri
push.branch_name = branch_name
push.new_branch = false
push.user = as_user
end
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