Commit 5955e1d9 authored by Steve Abrams's avatar Steve Abrams

Deploy tokens as API auth method

Add deploy tokens as an authentication strategy for
API endpoints and add permissions to deploy tokens
on the project policy for package access.
Enables deploy tokens for Maven packages.
parent 9089200d
......@@ -522,12 +522,14 @@ class Project < ApplicationRecord
def self.public_or_visible_to_user(user = nil, min_access_level = nil)
min_access_level = nil if user&.admin?
if user
return public_to_user unless user
if user.is_a?(DeployToken)
user.projects
else
where('EXISTS (?) OR projects.visibility_level IN (?)',
user.authorizations_for_projects(min_access_level: min_access_level),
Gitlab::VisibilityLevel.levels_for_user(user))
else
public_to_user
end
end
......
# frozen_string_literal: true
# Include this module if we want to pass something else than the user to
# check policies. This defines several methods which the policy checker
# would call and check.
# Include this module to have an object respond to user messages without being
# a user.
#
# Use Case 1:
# Pass something else than the user to check policies. This defines several
# methods which the policy checker would call and check.
#
# Use Case 2:
# Access the API with non-user object such as deploy tokens. This defines
# several methods which the API auth flow would call.
module PolicyActor
extend ActiveSupport::Concern
......@@ -37,6 +44,30 @@ module PolicyActor
def alert_bot?
false
end
def deactivated?
false
end
def confirmation_required_on_sign_in?
false
end
def can?(action, subject = :global)
Ability.allowed?(self, action, subject)
end
def preferred_language
nil
end
def requires_ldap_check?
false
end
def try_obtain_ldap_lease
nil
end
end
PolicyActor.prepend_if_ee('EE::PolicyActor')
......@@ -84,6 +84,16 @@ class ProjectPolicy < BasePolicy
project.merge_requests_allowing_push_to_user(user).any?
end
desc "Deploy token with read_package_registry scope"
condition(:read_package_registry_deploy_token) do
user.is_a?(DeployToken) && user.has_access_to?(project) && user.read_package_registry
end
desc "Deploy token with write_package_registry scope"
condition(:write_package_registry_deploy_token) do
user.is_a?(DeployToken) && user.has_access_to?(project) && user.write_package_registry
end
with_scope :subject
condition(:forking_allowed) do
@subject.feature_available?(:forking, @user)
......@@ -530,6 +540,16 @@ class ProjectPolicy < BasePolicy
prevent :destroy_design
end
rule { read_package_registry_deploy_token }.policy do
enable :read_package
enable :read_project
end
rule { write_package_registry_deploy_token }.policy do
enable :create_package
enable :read_project
end
private
def team_member?
......
---
title: Deploy token authentication for API with Maven endpoints
merge_request: 30332
author:
type: added
......@@ -207,9 +207,9 @@ Enter a project name or hit enter to use the directory name as project name.
The next step is to add the GitLab Package Registry as a Maven remote. If a
project is private or you want to upload Maven artifacts to GitLab,
credentials will need to be provided for authorization too. Support is available
for [personal access tokens](#authenticating-with-a-personal-access-token) and
[CI job tokens](#authenticating-with-a-ci-job-token) only.
[Deploy tokens](../../project/deploy_tokens/index.md) and regular username/password
for [personal access tokens](#authenticating-with-a-personal-access-token),
[CI job tokens](#authenticating-with-a-ci-job-token), and
[deploy tokens](../../project/deploy_tokens/index.md) only. Regular username/password
credentials do not work.
### Authenticating with a personal access token
......@@ -324,6 +324,59 @@ repositories {
}
```
### Authenticating with a deploy token
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213566) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.0.
To authenticate with a [deploy token](./../../project/deploy_tokens/index.md),
set the scope to `api` when creating one, and add it to your Maven or Gradle configuration
files.
#### Authenticating with a deploy token in Maven
Add a corresponding section to your
[`settings.xml`](https://maven.apache.org/settings.html) file:
```xml
<settings>
<servers>
<server>
<id>gitlab-maven</id>
<configuration>
<httpHeaders>
<property>
<name>Deploy-Token</name>
<value>REPLACE_WITH_YOUR_DEPLOY_TOKEN</value>
</property>
</httpHeaders>
</configuration>
</server>
</servers>
</settings>
```
#### Authenticating with a deploy token in Gradle
To authenticate with a deploy token, add a repositories section to your
[`build.gradle`](https://docs.gradle.org/current/userguide/tutorial_using_tasks.html)
file:
```groovy
repositories {
maven {
url "https://<gitlab-url>/api/v4/groups/<group>/-/packages/maven"
name "GitLab"
credentials(HttpHeaderCredentials) {
name = 'Deploy-Token'
value = '<deploy-token>'
}
authentication {
header(HttpHeaderAuthentication)
}
}
}
```
## Configuring your project to use the GitLab Maven repository URL
To download and upload packages from GitLab, you need a `repository` and
......@@ -397,7 +450,7 @@ project's ID can be used for uploading.
### Group level Maven endpoint
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/8798) in GitLab Premium 11.7.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/8798) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.7.
If you rely on many packages, it might be inefficient to include the `repository` section
with a unique URL for each package. Instead, you can use the group level endpoint for
......@@ -460,7 +513,7 @@ For retrieving artifacts, you can use either the
### Instance level Maven endpoint
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/8274) in GitLab Premium 11.7.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/8274) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.7.
If you rely on many packages, it might be inefficient to include the `repository` section
with a unique URL for each package. Instead, you can use the instance level endpoint for
......
......@@ -84,7 +84,7 @@ module API
requires :path, type: String, desc: 'Package path'
requires :file_name, type: String, desc: 'Package file name'
end
route_setting :authentication, job_token_allowed: true
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
get 'packages/maven/*path/:file_name', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
file_name, format = extract_format(params[:file_name])
......@@ -125,7 +125,7 @@ module API
requires :path, type: String, desc: 'Package path'
requires :file_name, type: String, desc: 'Package file name'
end
route_setting :authentication, job_token_allowed: true
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
get ':id/-/packages/maven/*path/:file_name', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
file_name, format = extract_format(params[:file_name])
......@@ -170,7 +170,7 @@ module API
requires :path, type: String, desc: 'Package path'
requires :file_name, type: String, desc: 'Package file name'
end
route_setting :authentication, job_token_allowed: true
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
get ':id/packages/maven/*path/:file_name', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
authorize_read_package!(user_project)
......@@ -201,7 +201,7 @@ module API
requires :path, type: String, desc: 'Package path'
requires :file_name, type: String, desc: 'Package file name', regexp: Gitlab::Regex.maven_file_name_regex
end
route_setting :authentication, job_token_allowed: true
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
put ':id/packages/maven/*path/:file_name/authorize', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
authorize_upload!
......@@ -224,7 +224,7 @@ module API
optional 'file.sha1', type: String, desc: %q(sha1 checksum of the file (generated by Workhorse))
optional 'file.sha256', type: String, desc: %q(sha256 checksum of the file (generated by Workhorse))
end
route_setting :authentication, job_token_allowed: true
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
put ':id/packages/maven/*path/:file_name', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
authorize_upload!
......
......@@ -8,7 +8,8 @@ module EE
override :find_user_from_sources
def find_user_from_sources
find_user_from_bearer_token ||
deploy_token_from_request ||
find_user_from_bearer_token ||
find_user_from_job_token ||
find_user_from_warden
end
......
......@@ -11,10 +11,19 @@ describe API::MavenPackages do
let_it_be(:jar_file) { package.package_files.with_file_name_like('%.jar').first }
let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
let_it_be(:job) { create(:ci_build, user: user) }
let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) }
let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) }
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:headers) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
let(:headers_with_token) { headers.merge('Private-Token' => personal_access_token.token) }
let(:headers_with_deploy_token) do
headers.merge(
Gitlab::Auth::AuthFinders::DEPLOY_TOKEN_HEADER => deploy_token.token
)
end
let(:version) { '1.0-SNAPSHOT' }
before do
......@@ -78,6 +87,28 @@ describe API::MavenPackages do
end
end
shared_examples 'downloads with a deploy token' do
it 'allows download with deploy token' do
download_file(
package_file.file_name,
{},
Gitlab::Auth::AuthFinders::DEPLOY_TOKEN_HEADER => deploy_token.token
)
expect(response).to have_gitlab_http_status(:ok)
expect(response.media_type).to eq('application/octet-stream')
end
end
shared_examples 'downloads with a job token' do
it 'allows download with job token' do
download_file(package_file.file_name, job_token: job.token)
expect(response).to have_gitlab_http_status(:ok)
expect(response.media_type).to eq('application/octet-stream')
end
end
describe 'GET /api/v4/packages/maven/*path/:file_name' do
context 'a public project' do
subject { download_file(package_file.file_name) }
......@@ -123,12 +154,9 @@ describe API::MavenPackages do
expect(response).to have_gitlab_http_status(:forbidden)
end
it 'allows download with job token' do
download_file(package_file.file_name, job_token: job.token)
it_behaves_like 'downloads with a job token'
expect(response).to have_gitlab_http_status(:ok)
expect(response.media_type).to eq('application/octet-stream')
end
it_behaves_like 'downloads with a deploy token'
end
context 'private project' do
......@@ -161,12 +189,9 @@ describe API::MavenPackages do
expect(response).to have_gitlab_http_status(:forbidden)
end
it 'allows download with job token' do
download_file(package_file.file_name, job_token: job.token)
it_behaves_like 'downloads with a job token'
expect(response).to have_gitlab_http_status(:ok)
expect(response.media_type).to eq('application/octet-stream')
end
it_behaves_like 'downloads with a deploy token'
end
it 'rejects request if feature is not in the license' do
......@@ -254,12 +279,9 @@ describe API::MavenPackages do
expect(response).to have_gitlab_http_status(:not_found)
end
it 'allows download with job token' do
download_file(package_file.file_name, job_token: job.token)
it_behaves_like 'downloads with a job token'
expect(response).to have_gitlab_http_status(:ok)
expect(response.media_type).to eq('application/octet-stream')
end
it_behaves_like 'downloads with a deploy token'
end
context 'private project' do
......@@ -292,12 +314,9 @@ describe API::MavenPackages do
expect(response).to have_gitlab_http_status(:not_found)
end
it 'allows download with job token' do
download_file(package_file.file_name, job_token: job.token)
it_behaves_like 'downloads with a job token'
expect(response).to have_gitlab_http_status(:ok)
expect(response.media_type).to eq('application/octet-stream')
end
it_behaves_like 'downloads with a deploy token'
end
it 'rejects request if feature is not in the license' do
......@@ -375,12 +394,9 @@ describe API::MavenPackages do
expect(response).to have_gitlab_http_status(:not_found)
end
it 'allows download with job token' do
download_file(package_file.file_name, job_token: job.token)
it_behaves_like 'downloads with a job token'
expect(response).to have_gitlab_http_status(:ok)
expect(response.media_type).to eq('application/octet-stream')
end
it_behaves_like 'downloads with a deploy token'
end
it 'rejects request if feature is not in the license' do
......@@ -452,6 +468,12 @@ describe API::MavenPackages do
expect(response).to have_gitlab_http_status(:ok)
end
it 'authorizes upload with deploy token' do
authorize_upload({}, headers_with_deploy_token)
expect(response).to have_gitlab_http_status(:ok)
end
def authorize_upload(params = {}, request_headers = headers)
put api("/projects/#{project.id}/packages/maven/com/example/my-app/#{version}/maven-metadata.xml/authorize"), params: params, headers: request_headers
end
......@@ -531,6 +553,12 @@ describe API::MavenPackages do
expect(project.reload.packages.last.build_info.pipeline).to eq job.pipeline
end
it 'allows upload with deploy token' do
upload_file(params, headers_with_deploy_token)
expect(response).to have_gitlab_http_status(:ok)
end
context 'version is not correct' do
let(:version) { '$%123' }
......
......@@ -65,7 +65,8 @@ module API
end
def find_user_from_sources
find_user_from_access_token ||
deploy_token_from_request ||
find_user_from_access_token ||
find_user_from_job_token ||
find_user_from_warden
end
......@@ -90,12 +91,16 @@ module API
end
def api_access_allowed?(user)
Gitlab::UserAccess.new(user).allowed? && user.can?(:access_api)
user_allowed_or_deploy_token?(user) && user.can?(:access_api)
end
def api_access_denied_message(user)
Gitlab::Auth::UserAccessDeniedReason.new(user).rejection_message
end
def user_allowed_or_deploy_token?(user)
Gitlab::UserAccess.new(user).allowed? || user.is_a?(DeployToken)
end
end
class_methods do
......
......@@ -25,6 +25,7 @@ module Gitlab
PRIVATE_TOKEN_PARAM = :private_token
JOB_TOKEN_HEADER = 'HTTP_JOB_TOKEN'.freeze
JOB_TOKEN_PARAM = :job_token
DEPLOY_TOKEN_HEADER = 'HTTP_DEPLOY_TOKEN'.freeze
RUNNER_TOKEN_PARAM = :token
RUNNER_JOB_TOKEN_PARAM = :token
......@@ -101,6 +102,16 @@ module Gitlab
access_token.user || raise(UnauthorizedError)
end
# This returns a deploy token, not a user since a deploy token does not
# belong to a user.
def deploy_token_from_request
return unless route_authentication_setting[:deploy_token_allowed]
token = current_request.env[DEPLOY_TOKEN_HEADER].presence
DeployToken.active.find_by_token(token)
end
def find_runner_from_token
return unless api_request?
......
......@@ -17,6 +17,10 @@ describe Gitlab::Auth::AuthFinders do
request.update_param(key, value)
end
def set_header(key, value)
env[key] = value
end
describe '#find_user_from_warden' do
context 'with CSRF token' do
before do
......@@ -31,7 +35,7 @@ describe Gitlab::Auth::AuthFinders do
context 'with valid credentials' do
it 'returns the user' do
env['warden'] = double("warden", authenticate: user)
set_header('warden', double("warden", authenticate: user))
expect(find_user_from_warden).to eq user
end
......@@ -41,7 +45,7 @@ describe Gitlab::Auth::AuthFinders do
context 'without CSRF token' do
it 'returns nil' do
allow(Gitlab::RequestForgeryProtection).to receive(:verified?).and_return(false)
env['warden'] = double("warden", authenticate: user)
set_header('warden', double("warden", authenticate: user))
expect(find_user_from_warden).to be_nil
end
......@@ -51,8 +55,8 @@ describe Gitlab::Auth::AuthFinders do
describe '#find_user_from_feed_token' do
context 'when the request format is atom' do
before do
env['SCRIPT_NAME'] = 'url.atom'
env['HTTP_ACCEPT'] = 'application/atom+xml'
set_header('SCRIPT_NAME', 'url.atom')
set_header('HTTP_ACCEPT', 'application/atom+xml')
end
context 'when feed_token param is provided' do
......@@ -94,7 +98,7 @@ describe Gitlab::Auth::AuthFinders do
context 'when the request format is not atom' do
it 'returns nil' do
env['SCRIPT_NAME'] = 'json'
set_header('SCRIPT_NAME', 'json')
set_param(:feed_token, user.feed_token)
......@@ -104,7 +108,7 @@ describe Gitlab::Auth::AuthFinders do
context 'when the request format is empty' do
it 'the method call does not modify the original value' do
env['SCRIPT_NAME'] = 'url.atom'
set_header('SCRIPT_NAME', 'url.atom')
env.delete('action_dispatch.request.formats')
......@@ -118,7 +122,7 @@ describe Gitlab::Auth::AuthFinders do
describe '#find_user_from_static_object_token' do
shared_examples 'static object request' do
before do
env['SCRIPT_NAME'] = path
set_header('SCRIPT_NAME', path)
end
context 'when token header param is present' do
......@@ -174,7 +178,7 @@ describe Gitlab::Auth::AuthFinders do
context 'when request format is not archive nor blob' do
before do
env['script_name'] = 'url'
set_header('script_name', 'url')
end
it 'returns nil' do
......@@ -183,11 +187,46 @@ describe Gitlab::Auth::AuthFinders do
end
end
describe '#deploy_token_from_request' do
let_it_be(:deploy_token) { create(:deploy_token) }
let_it_be(:route_authentication_setting) { { deploy_token_allowed: true } }
subject { deploy_token_from_request }
it { is_expected.to be_nil }
shared_examples 'an unauthenticated route' do
context 'when route is not allowed to use deploy_tokens' do
let(:route_authentication_setting) { { deploy_token_allowed: false } }
it { is_expected.to be_nil }
end
end
context 'with deploy token headers' do
before do
set_header(described_class::DEPLOY_TOKEN_HEADER, deploy_token.token)
end
it { is_expected.to eq deploy_token }
it_behaves_like 'an unauthenticated route'
context 'with incorrect token' do
before do
set_header(described_class::DEPLOY_TOKEN_HEADER, 'invalid_token')
end
it { is_expected.to be_nil }
end
end
end
describe '#find_user_from_access_token' do
let(:personal_access_token) { create(:personal_access_token, user: user) }
before do
env['SCRIPT_NAME'] = 'url.atom'
set_header('SCRIPT_NAME', 'url.atom')
end
it 'returns nil if no access_token present' do
......@@ -196,13 +235,13 @@ describe Gitlab::Auth::AuthFinders do
context 'when validate_access_token! returns valid' do
it 'returns user' do
env[described_class::PRIVATE_TOKEN_HEADER] = personal_access_token.token
set_header(described_class::PRIVATE_TOKEN_HEADER, personal_access_token.token)
expect(find_user_from_access_token).to eq user
end
it 'returns exception if token has no user' do
env[described_class::PRIVATE_TOKEN_HEADER] = personal_access_token.token
set_header(described_class::PRIVATE_TOKEN_HEADER, personal_access_token.token)
allow_any_instance_of(PersonalAccessToken).to receive(:user).and_return(nil)
expect { find_user_from_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
......@@ -211,7 +250,7 @@ describe Gitlab::Auth::AuthFinders do
context 'with OAuth headers' do
it 'returns user' do
env['HTTP_AUTHORIZATION'] = "Bearer #{personal_access_token.token}"
set_header('HTTP_AUTHORIZATION', "Bearer #{personal_access_token.token}")
expect(find_user_from_access_token).to eq user
end
......@@ -228,7 +267,7 @@ describe Gitlab::Auth::AuthFinders do
let(:personal_access_token) { create(:personal_access_token, user: user) }
before do
env[described_class::PRIVATE_TOKEN_HEADER] = personal_access_token.token
set_header(described_class::PRIVATE_TOKEN_HEADER, personal_access_token.token)
end
it 'returns exception if token has no user' do
......@@ -252,19 +291,19 @@ describe Gitlab::Auth::AuthFinders do
end
it 'returns the user for RSS requests' do
env['SCRIPT_NAME'] = 'url.atom'
set_header('SCRIPT_NAME', 'url.atom')
expect(find_user_from_web_access_token(:rss)).to eq(user)
end
it 'returns the user for ICS requests' do
env['SCRIPT_NAME'] = 'url.ics'
set_header('SCRIPT_NAME', 'url.ics')
expect(find_user_from_web_access_token(:ics)).to eq(user)
end
it 'returns the user for API requests' do
env['SCRIPT_NAME'] = '/api/endpoint'
set_header('SCRIPT_NAME', '/api/endpoint')
expect(find_user_from_web_access_token(:api)).to eq(user)
end
......@@ -274,12 +313,12 @@ describe Gitlab::Auth::AuthFinders do
let(:personal_access_token) { create(:personal_access_token, user: user) }
before do
env['SCRIPT_NAME'] = 'url.atom'
set_header('SCRIPT_NAME', 'url.atom')
end
context 'passed as header' do
it 'returns token if valid personal_access_token' do
env[described_class::PRIVATE_TOKEN_HEADER] = personal_access_token.token
set_header(described_class::PRIVATE_TOKEN_HEADER, personal_access_token.token)
expect(find_personal_access_token).to eq personal_access_token
end
......@@ -298,7 +337,7 @@ describe Gitlab::Auth::AuthFinders do
end
it 'returns exception if invalid personal_access_token' do
env[described_class::PRIVATE_TOKEN_HEADER] = 'invalid_token'
set_header(described_class::PRIVATE_TOKEN_HEADER, 'invalid_token')
expect { find_personal_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
end
......@@ -310,7 +349,7 @@ describe Gitlab::Auth::AuthFinders do
context 'passed as header' do
it 'returns token if valid oauth_access_token' do
env['HTTP_AUTHORIZATION'] = "Bearer #{token.token}"
set_header('HTTP_AUTHORIZATION', "Bearer #{token.token}")
expect(find_oauth_access_token.token).to eq token.token
end
......@@ -329,7 +368,7 @@ describe Gitlab::Auth::AuthFinders do
end
it 'returns exception if invalid oauth_access_token' do
env['HTTP_AUTHORIZATION'] = "Bearer invalid_token"
set_header('HTTP_AUTHORIZATION', "Bearer invalid_token")
expect { find_oauth_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
end
......@@ -337,7 +376,7 @@ describe Gitlab::Auth::AuthFinders do
describe '#find_personal_access_token_from_http_basic_auth' do
def auth_header_with(token)
env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials('username', token)
set_header('HTTP_AUTHORIZATION', ActionController::HttpAuthentication::Basic.encode_credentials('username', token))
end
context 'access token is valid' do
......@@ -389,7 +428,7 @@ describe Gitlab::Auth::AuthFinders do
end
def set_auth(username, password)
env['HTTP_AUTHORIZATION'] = basic_http_auth(username, password)
set_header('HTTP_AUTHORIZATION', basic_http_auth(username, password))
end
subject { find_user_from_basic_auth_job }
......@@ -502,20 +541,20 @@ describe Gitlab::Auth::AuthFinders do
context 'when the job token is in the headers' do
it 'returns the user if valid job token' do
env[described_class::JOB_TOKEN_HEADER] = job.token
set_header(described_class::JOB_TOKEN_HEADER, job.token)
is_expected.to eq(user)
expect(@current_authenticated_job).to eq(job)
end
it 'returns nil without job token' do
env[described_class::JOB_TOKEN_HEADER] = ''
set_header(described_class::JOB_TOKEN_HEADER, '')
is_expected.to be_nil
end
it 'returns exception if invalid job token' do
env[described_class::JOB_TOKEN_HEADER] = 'invalid token'
set_header(described_class::JOB_TOKEN_HEADER, 'invalid token')
expect { subject }.to raise_error(Gitlab::Auth::UnauthorizedError)
end
......@@ -524,7 +563,7 @@ describe Gitlab::Auth::AuthFinders do
let(:route_authentication_setting) { { job_token_allowed: false } }
it 'sets current_user to nil' do
env[described_class::JOB_TOKEN_HEADER] = job.token
set_header(described_class::JOB_TOKEN_HEADER, job.token)
allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(true)
......@@ -586,7 +625,7 @@ describe Gitlab::Auth::AuthFinders do
context 'with API requests' do
before do
env['SCRIPT_NAME'] = '/api/endpoint'
set_header('SCRIPT_NAME', '/api/endpoint')
end
it 'returns the runner if token is valid' do
......@@ -614,7 +653,7 @@ describe Gitlab::Auth::AuthFinders do
context 'without API requests' do
before do
env['SCRIPT_NAME'] = 'url.ics'
set_header('SCRIPT_NAME', 'url.ics')
end
it 'returns nil if token is valid' do
......
......@@ -3625,6 +3625,24 @@ describe Project do
expect(projects).to contain_exactly(public_project)
end
end
context 'with deploy token users' do
let_it_be(:private_project) { create(:project, :private) }
subject { described_class.all.public_or_visible_to_user(user) }
context 'deploy token user without project' do
let_it_be(:user) { create(:deploy_token) }
it { is_expected.to eq [] }
end
context 'deploy token user with project' do
let_it_be(:user) { create(:deploy_token, projects: [private_project]) }
it { is_expected.to include(private_project) }
end
end
end
describe '.ids_with_issuables_available_for' do
......
......@@ -672,4 +672,28 @@ describe ProjectPolicy do
end
end
end
context 'deploy token access' do
let!(:project_deploy_token) do
create(:project_deploy_token, project: project, deploy_token: deploy_token)
end
subject { described_class.new(deploy_token, project) }
context 'a deploy token with read_package_registry scope' do
let(:deploy_token) { create(:deploy_token, read_package_registry: true) }
it { is_expected.to be_allowed(:read_package) }
it { is_expected.to be_allowed(:read_project) }
it { is_expected.to be_disallowed(:create_package) }
end
context 'a deploy token with write_package_registry scope' do
let(:deploy_token) { create(:deploy_token, write_package_registry: true) }
it { is_expected.to be_allowed(:create_package) }
it { is_expected.to be_allowed(:read_project) }
it { is_expected.to be_disallowed(:destroy_package) }
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