Commit d9612da7 authored by Kerri Miller's avatar Kerri Miller

Merge branch...

Merge branch '271635-new-project-access-tokens-do-not-show-in-active-project-access-tokens' into 'master'

Skip SSO/GMA validation when creating project access token

See merge request gitlab-org/gitlab!46257
parents 8ddee1f1 f9df9d2f
...@@ -23,7 +23,7 @@ module Projects ...@@ -23,7 +23,7 @@ module Projects
redirect_to namespace_project_settings_access_tokens_path, notice: _("Your new project access token has been created.") redirect_to namespace_project_settings_access_tokens_path, notice: _("Your new project access token has been created.")
else else
render :index redirect_to namespace_project_settings_access_tokens_path, alert: _("Failed to create new project access token: %{token_response_message}") % { token_response_message: token_response.message }
end end
end end
......
...@@ -15,13 +15,20 @@ module ResourceAccessTokens ...@@ -15,13 +15,20 @@ module ResourceAccessTokens
user = create_user user = create_user
return error(user.errors.full_messages.to_sentence) unless user.persisted? return error(user.errors.full_messages.to_sentence) unless user.persisted?
return error("Failed to provide maintainer access") unless provision_access(resource, user)
member = create_membership(resource, user)
unless member.persisted?
delete_failed_user(user)
return error("Could not provision maintainer access to project access token")
end
token_response = create_personal_access_token(user) token_response = create_personal_access_token(user)
if token_response.success? if token_response.success?
success(token_response.payload[:personal_access_token]) success(token_response.payload[:personal_access_token])
else else
delete_failed_user(user)
error(token_response.message) error(token_response.message)
end end
end end
...@@ -43,6 +50,10 @@ module ResourceAccessTokens ...@@ -43,6 +50,10 @@ module ResourceAccessTokens
Users::CreateService.new(current_user, default_user_params).execute(skip_authorization: true) Users::CreateService.new(current_user, default_user_params).execute(skip_authorization: true)
end end
def delete_failed_user(user)
DeleteUserWorker.perform_async(current_user.id, user.id, hard_delete: true, skip_authorization: true)
end
def default_user_params def default_user_params
{ {
name: params[:name] || "#{resource.name.to_s.humanize} bot", name: params[:name] || "#{resource.name.to_s.humanize} bot",
...@@ -88,7 +99,7 @@ module ResourceAccessTokens ...@@ -88,7 +99,7 @@ module ResourceAccessTokens
Gitlab::Auth.resource_bot_scopes Gitlab::Auth.resource_bot_scopes
end end
def provision_access(resource, user) def create_membership(resource, user)
resource.add_user(user, :maintainer, expires_at: params[:expires_at]) resource.add_user(user, :maintainer, expires_at: params[:expires_at])
end end
......
---
title: Skip GMA and SSO validation when creating project access tokens for project bots
merge_request: 46257
author:
type: fixed
...@@ -7,8 +7,8 @@ module EE ...@@ -7,8 +7,8 @@ module EE
prepended do prepended do
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
validate :sso_enforcement, if: :group validate :sso_enforcement, if: :group, unless: :project_bot
validate :gma_enforcement, if: :group validate :gma_enforcement, if: :group, unless: :project_bot
before_destroy :delete_member_branch_protection before_destroy :delete_member_branch_protection
end end
...@@ -17,6 +17,10 @@ module EE ...@@ -17,6 +17,10 @@ module EE
source&.group source&.group
end end
def project_bot
user&.project_bot?
end
def delete_member_branch_protection def delete_member_branch_protection
if user.present? && project.present? if user.present? && project.present?
project.protected_branches.merge_access_by_user(user).destroy_all # rubocop: disable Cop/DestroyAll project.protected_branches.merge_access_by_user(user).destroy_all # rubocop: disable Cop/DestroyAll
......
...@@ -21,23 +21,67 @@ RSpec.describe ProjectMember do ...@@ -21,23 +21,67 @@ RSpec.describe ProjectMember do
stub_licensed_features(group_saml: true) stub_licensed_features(group_saml: true)
end end
it 'allows adding the project member' do it 'allows adding a user linked to the GMA account as project member' do
user = create(:user, :group_managed, managing_group: group) user = create(:user, :group_managed, managing_group: group)
member = entity.add_developer(user) member = entity.add_developer(user)
expect(member).to be_valid expect(member).to be_valid
end end
it 'does not add the the project member' do it 'does not allow adding a user not linked to the GMA account as project member' do
member = entity.add_developer(create(:user)) member = entity.add_developer(create(:user))
expect(member).not_to be_valid expect(member).not_to be_valid
expect(member.errors.messages[:user]).to include('is not in the group enforcing Group Managed Account') expect(member.errors.messages[:user]).to include('is not in the group enforcing Group Managed Account')
end end
it 'allows adding a project bot' do
member = entity.add_developer(create(:user, :project_bot))
expect(member).to be_valid
end
end
context "for SSO enforced groups" do
let(:group) { create(:group, :private) }
let!(:saml_provider) { create(:saml_provider, group: group, enforced_sso: true) }
let(:identity) { create(:group_saml_identity, saml_provider: saml_provider) }
before do
stub_licensed_features(group_saml: true)
end
it 'allows adding a user linked to the SAML account as project member' do
sso_user = identity.user
member = entity.add_developer(sso_user)
expect(member).to be_valid
end
it 'does not allow adding a user not linked to the SAML account as a project member' do
member = entity.add_developer(create(:user))
expect(member).not_to be_valid
expect(member.errors.messages[:user]).to include('is not linked to a SAML account')
end
it 'allows adding a project bot' do
member = entity.add_developer(create(:user, :project_bot))
expect(member).to be_valid
end
end end
context 'enforced group managed account disabled' do context 'enforced group managed account disabled' do
it 'allows adding the group member' do it 'allows adding any user as project member' do
member = entity.add_developer(create(:user))
expect(member).to be_valid
end
end
context 'enforced SSO disabled' do
it 'allows adding any user as project member' do
member = entity.add_developer(create(:user)) member = entity.add_developer(create(:user))
expect(member).to be_valid expect(member).to be_valid
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ResourceAccessTokens::CreateService do
subject { described_class.new(user, resource).execute }
let_it_be(:user) { create(:user) }
shared_examples 'token creation succeeds' do
let(:resource) { create(:project, group: group)}
before do
resource.add_maintainer(user)
end
it 'does not cause an error' do
response = subject
expect(response.error?).to be false
end
it 'adds the project bot as a member' do
expect { subject }.to change { resource.members.count }.by(1)
end
it 'creates a project bot user' do
expect { subject }.to change { User.bots.count }.by(1)
end
end
describe '#execute' do
context 'with enforced group managed account enabled' do
let(:group) { create(:group_with_managed_accounts, :private) }
let(:user) { create(:user, :group_managed, managing_group: group) }
before do
stub_feature_flags(group_managed_accounts: true)
stub_licensed_features(group_saml: true)
end
it_behaves_like 'token creation succeeds'
end
context "for SAML enabled groups" do
let(:group) { create(:group, :private) }
let!(:saml_provider) { create(:saml_provider, group: group, enforced_sso: true) }
let(:identity) { create(:group_saml_identity, saml_provider: saml_provider) }
let(:user) { identity.user }
before do
stub_licensed_features(group_saml: true)
end
it_behaves_like 'token creation succeeds'
end
end
end
...@@ -11072,6 +11072,9 @@ msgstr "" ...@@ -11072,6 +11072,9 @@ msgstr ""
msgid "Failed to create import label for jira import." msgid "Failed to create import label for jira import."
msgstr "" msgstr ""
msgid "Failed to create new project access token: %{token_response_message}"
msgstr ""
msgid "Failed to create repository" msgid "Failed to create repository"
msgstr "" msgstr ""
......
...@@ -11,16 +11,15 @@ RSpec.describe ResourceAccessTokens::CreateService do ...@@ -11,16 +11,15 @@ RSpec.describe ResourceAccessTokens::CreateService do
describe '#execute' do describe '#execute' do
# Created shared_examples as it will easy to include specs for group bots in https://gitlab.com/gitlab-org/gitlab/-/issues/214046 # Created shared_examples as it will easy to include specs for group bots in https://gitlab.com/gitlab-org/gitlab/-/issues/214046
shared_examples 'fails when user does not have the permission to create a Resource Bot' do shared_examples 'token creation fails' do
before_all do let(:resource) { create(:project)}
resource.add_developer(user)
end
it 'returns error' do it 'does not add the project bot as a member' do
response = subject expect { subject }.not_to change { resource.members.count }
end
expect(response.error?).to be true it 'immediately destroys the bot user if one was created', :sidekiq_inline do
expect(response.message).to eq("User does not have permission to create #{resource_type} Access Token") expect { subject }.not_to change { User.bots.count }
end end
end end
...@@ -154,24 +153,36 @@ RSpec.describe ResourceAccessTokens::CreateService do ...@@ -154,24 +153,36 @@ RSpec.describe ResourceAccessTokens::CreateService do
context 'when invalid scope is passed' do context 'when invalid scope is passed' do
let_it_be(:params) { { scopes: [:invalid_scope] } } let_it_be(:params) { { scopes: [:invalid_scope] } }
it 'returns error' do it_behaves_like 'token creation fails'
it 'returns the scope error message' do
response = subject response = subject
expect(response.error?).to be true expect(response.error?).to be true
expect(response.errors).to include("Scopes can only contain available scopes")
end end
end end
end end
end
context 'when access provisioning fails' do context "when access provisioning fails" do
before do let_it_be(:bot_user) { create(:user, :project_bot) }
allow(resource).to receive(:add_user).and_return(nil) let(:unpersisted_member) { build(:project_member, source: resource, user: bot_user) }
end
it 'returns error' do before do
response = subject allow_next_instance_of(ResourceAccessTokens::CreateService) do |service|
allow(service).to receive(:create_user).and_return(bot_user)
allow(service).to receive(:create_membership).and_return(unpersisted_member)
end
end
expect(response.error?).to be true it_behaves_like 'token creation fails'
it 'returns the provisioning error message' do
response = subject
expect(response.error?).to be true
expect(response.errors).to include("Could not provision maintainer access to project access token")
end
end end
end end
end end
...@@ -180,7 +191,16 @@ RSpec.describe ResourceAccessTokens::CreateService do ...@@ -180,7 +191,16 @@ RSpec.describe ResourceAccessTokens::CreateService do
let_it_be(:resource_type) { 'project' } let_it_be(:resource_type) { 'project' }
let_it_be(:resource) { project } let_it_be(:resource) { project }
it_behaves_like 'fails when user does not have the permission to create a Resource Bot' context 'when user does not have permission to create a resource bot' do
it_behaves_like 'token creation fails'
it 'returns the permission error message' do
response = subject
expect(response.error?).to be true
expect(response.errors).to include("User does not have permission to create #{resource_type} Access Token")
end
end
context 'user with valid permission' do context 'user with valid permission' do
before_all do before_all do
......
...@@ -73,7 +73,23 @@ RSpec.shared_examples 'project access tokens available #create' do ...@@ -73,7 +73,23 @@ RSpec.shared_examples 'project access tokens available #create' do
end end
end end
it { expect(subject).to render_template(:index) } it 'does not create the token' do
expect { subject }.not_to change { PersonalAccessToken.count }
end
it 'does not add the project bot as a member' do
expect { subject }.not_to change { Member.count }
end
it 'does not create the project bot user' do
expect { subject }.not_to change { User.count }
end
it 'shows a failure alert' do
subject
expect(response.flash[:alert]).to match("Failed to create new project access token: Failed!")
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