Skip to content
Snippets Groups Projects
Commit c4f56a88 authored by Mayra Cabrera's avatar Mayra Cabrera
Browse files

Increase test suite around deploy tokens behavior

Also, fixes broken specs
parent a475411f
No related branches found
No related tags found
No related merge requests found
Showing
with 144 additions and 220 deletions
Loading
Loading
@@ -4,4 +4,9 @@ module DeployTokensHelper
deploy_token.errors.present? ||
Rails.env.test?
end
def container_registry_enabled?
Gitlab.config.registry.enabled &&
can?(current_user, :read_container_image, @project)
end
end
Loading
Loading
@@ -17,10 +17,6 @@ class DeployToken < ActiveRecord::Base
scope :read_repository, -> { where(read_repository: true) }
scope :read_registry, -> { where(read_registry: true) }
 
def self.redis_shared_state_key(user_id)
"gitlab:deploy_token:user_#{user_id}"
end
def revoke!
update!(revoked: true)
end
Loading
Loading
@@ -38,7 +34,7 @@ class DeployToken < ActiveRecord::Base
end
 
def has_access_to?(requested_project)
self.projects.first == requested_project
project == requested_project
end
 
def project
Loading
Loading
Loading
Loading
@@ -5,10 +5,4 @@ class ProjectDeployToken < ActiveRecord::Base
validates :deploy_token, presence: true
validates :project, presence: true
validates :deploy_token_id, uniqueness: { scope: [:project_id] }
accepts_nested_attributes_for :deploy_token
def redis_shared_state_key(user_id)
"gitlab:deploy_token:#{project_id}:#{user_id}"
end
end
Loading
Loading
@@ -143,7 +143,7 @@ module Auth
 
def user_can_pull?(requested_project)
has_authentication_ability?(:read_container_image) &&
can_user?(:read_container_image, requested_project)
can?(current_user, :read_container_image, requested_project)
end
 
def deploy_token_can_pull?(requested_project)
Loading
Loading
Loading
Loading
@@ -18,11 +18,12 @@
= f.check_box :read_repository
= label_tag ("deploy_token_read_repository"), 'read_repository'
%span= s_('DeployTokens|Allows read-only access to the repository')
%fieldset
= f.check_box :read_registry
= label_tag ("deploy_token_read_registry"), 'read_registry'
%span= s_('DeployTokens|Allows read-only access to the registry images')
- if container_registry_enabled?
%fieldset
= f.check_box :read_registry
= label_tag ("deploy_token_read_registry"), 'read_registry'
%span= s_('DeployTokens|Allows read-only access to the registry images')
 
.prepend-top-default
= f.submit s_('DeployTokens|Create deploy token'), class: 'btn btn-success'
Loading
Loading
@@ -46,26 +46,30 @@ the following table.
To download a repository using a Deploy Token, you just need to:
 
1. Create a Deploy Token with `read_repository` as a scope.
2. `git clone` the project using the Deploy Token:
2. Take note of your `username` and `token`
3. `git clone` the project using the Deploy Token:
 
 
```bash
git clone http://oauth2:<deploy_token>@gitlab.example.com/tanuki/awesome_project.git
git clone http://<username>:<deploy_token>@gitlab.example.com/tanuki/awesome_project.git
```
 
Just replace `<deploy_token>` with your real token.
Just replace `<username>` and `<deploy_token>` with the proper values
 
### Read container registry images
 
To read the container registry images, you'll need to:
 
1. Create a Deploy Token with `read_registry` as a scope.
2. Log in to GitLab’s Container Registry using the deploy token:
2. Take note of your `username` and `token`
3. Log in to GitLab’s Container Registry using the deploy token:
 
```
docker login registry.example.com -u <username> -p <deploy_token>
```
Just replace `<username>` and `<deploy_token>` with the proper values.
Just replace `<username>` and `<deploy_token>` with the proper values. Then you can simply
pull images from your Container Registry.
 
[ce-17894]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17894
[ce-11845]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11845
Loading
Loading
Loading
Loading
@@ -173,19 +173,17 @@ module Gitlab
end.uniq
end
 
# Project is always sent when using read_scope,
# but is not sent when using read_registry scope
# (since jwt is not context aware of the project)
def deploy_token_check(login, password)
return unless password.present?
 
token =
DeployToken.active.find_by(token: password)
 
return unless token
return unless login != "gitlab+deploy-token-#{token.id}"
return unless token && login
return if login != token.username
 
scopes = abilities_for_scopes(token.scopes)
if valid_scoped_token?(token, available_scopes)
Gitlab::Auth::Result.new(token, token.project, :deploy_token, scopes)
end
Loading
Loading
require 'spec_helper'
describe Projects::DeployTokensController do
let(:project) { create(:project) }
let(:user) { create(:user) }
let!(:member) { project.add_master(user) }
before do
sign_in(user)
end
describe 'POST #create' do
let(:deploy_token_params) { attributes_for(:deploy_token) }
subject do
post :create,
namespace_id: project.namespace,
project_id: project,
deploy_token: deploy_token_params
end
context 'with valid params' do
it 'should create a new DeployToken' do
expect { subject }.to change(DeployToken, :count).by(1)
end
it 'should include a flash notice' do
subject
expect(flash[:notice]).to eq('Your new project deploy token has been created.')
end
it 'should redirect to project settings repository' do
subject
expect(response).to redirect_to project_settings_repository_path(project)
end
end
context 'with invalid params' do
let(:deploy_token_params) { attributes_for(:deploy_token, scopes: []) }
it 'should not create a new DeployToken' do
expect { subject }.not_to change(DeployToken, :count)
end
it 'should redirect to project settings repository' do
subject
expect(response).to redirect_to project_settings_repository_path(project)
end
end
context 'when user does not have enough permissions' do
let!(:member) { project.add_developer(user) }
it 'responds with status 404' do
subject
expect(response).to have_gitlab_http_status(404)
end
end
end
end
require 'spec_helper'
 
describe Projects::Settings::RepositoryController, :clean_gitlab_redis_shared_state do
describe Projects::Settings::RepositoryController do
let(:project) { create(:project_empty_repo, :public) }
let(:user) { create(:user) }
 
Loading
Loading
@@ -16,43 +16,5 @@ describe Projects::Settings::RepositoryController, :clean_gitlab_redis_shared_st
expect(response).to have_gitlab_http_status(200)
expect(response).to render_template(:show)
end
context 'with no deploy token attributes present' do
it 'should build an empty instance of DeployToken' do
get :show, namespace_id: project.namespace, project_id: project
deploy_token = assigns(:deploy_token)
expect(deploy_token).to be_an_instance_of(DeployToken)
expect(deploy_token.name).to be_nil
expect(deploy_token.expires_at).to be_nil
expect(deploy_token.scopes).to eq([])
end
end
context 'with deploy token attributes present' do
let(:deploy_token_key) { "gitlab:deploy_token:#{project.id}:#{user.id}:attributes" }
let(:deploy_token_attributes) do
{
name: 'test-token',
expires_at: Date.today + 1.month
}
end
before do
Gitlab::Redis::SharedState.with do |redis|
redis.set(deploy_token_key, deploy_token_attributes.to_json)
end
get :show, namespace_id: project.namespace, project_id: project
end
it 'should build an instance of DeployToken' do
deploy_token = assigns(:deploy_token)
expect(deploy_token).to be_an_instance_of(DeployToken)
expect(deploy_token.name).to eq(deploy_token_attributes[:name])
expect(deploy_token.expires_at.to_date).to eq(deploy_token_attributes[:expires_at].to_date)
end
end
end
end
Loading
Loading
@@ -195,7 +195,7 @@ describe Gitlab::Auth do
personal_access_token = create(:personal_access_token, scopes: ['read_registry'])
 
expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: '')
expect(gl_auth.find_for_git_client('', personal_access_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(personal_access_token.user, nil, :personal_access_token, [:read_project, :build_download_code, :build_read_container_image]))
expect(gl_auth.find_for_git_client('', personal_access_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(personal_access_token.user, nil, :personal_access_token, [:build_read_container_image]))
end
end
 
Loading
Loading
@@ -262,25 +262,38 @@ describe Gitlab::Auth do
 
context 'when the deploy token has read_repository as scope' do
let(:deploy_token) { create(:deploy_token, read_registry: false, projects: [project]) }
let(:login) { deploy_token.username }
 
it 'succeeds when project is present, token is valid and has read_repository as scope' do
abilities = %i(download_code)
auth_success = Gitlab::Auth::Result.new(deploy_token, project, :deploy_token, abilities)
it 'succeeds when login and token are valid' do
auth_success = Gitlab::Auth::Result.new(deploy_token, project, :deploy_token, [:download_code])
 
expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: '')
expect(gl_auth.find_for_git_client('', deploy_token.token, project: project, ip: 'ip'))
expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: login)
expect(gl_auth.find_for_git_client(login, deploy_token.token, project: project, ip: 'ip'))
.to eq(auth_success)
end
 
it 'fails when login is not valid' do
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'random_login')
expect(gl_auth.find_for_git_client('random_login', deploy_token.token, project: project, ip: 'ip'))
.to eq(auth_failure)
end
it 'fails when token is not valid' do
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
expect(gl_auth.find_for_git_client(login, '123123', project: project, ip: 'ip'))
.to eq(auth_failure)
end
it 'fails if token is nil' do
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: '')
expect(gl_auth.find_for_git_client('', nil, project: project, ip: 'ip'))
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
expect(gl_auth.find_for_git_client(login, nil, project: project, ip: 'ip'))
.to eq(auth_failure)
end
 
it 'fails if token is not related to project' do
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: '')
expect(gl_auth.find_for_git_client('', 'abcdef', project: project, ip: 'ip'))
another_deploy_token = create(:deploy_token)
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
expect(gl_auth.find_for_git_client(login, another_deploy_token.token, project: project, ip: 'ip'))
.to eq(auth_failure)
end
 
Loading
Loading
@@ -296,30 +309,42 @@ describe Gitlab::Auth do
 
context 'when the deploy token has read_registry as a scope' do
let(:deploy_token) { create(:deploy_token, read_repository: false, projects: [project]) }
let(:login) { deploy_token.username }
 
context 'when registry enabled' do
before do
stub_container_registry_config(enabled: true)
end
 
it 'succeeds if deploy token does have read_registry as scope' do
abilities = %i(read_project build_download_code build_read_container_image)
auth_success = Gitlab::Auth::Result.new(deploy_token, project, :deploy_token, abilities)
it 'succeeds when login and token are valid' do
auth_success = Gitlab::Auth::Result.new(deploy_token, project, :deploy_token, [:build_read_container_image])
 
expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: '')
expect(gl_auth.find_for_git_client('', deploy_token.token, project: nil, ip: 'ip'))
expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: login)
expect(gl_auth.find_for_git_client(login, deploy_token.token, project: nil, ip: 'ip'))
.to eq(auth_success)
end
 
it 'fails when login is not valid' do
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'random_login')
expect(gl_auth.find_for_git_client('random_login', deploy_token.token, project: project, ip: 'ip'))
.to eq(auth_failure)
end
it 'fails when token is not valid' do
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
expect(gl_auth.find_for_git_client(login, '123123', project: project, ip: 'ip'))
.to eq(auth_failure)
end
it 'fails if token is nil' do
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: '')
expect(gl_auth.find_for_git_client('', nil, project: nil, ip: 'ip'))
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
expect(gl_auth.find_for_git_client(login, nil, project: nil, ip: 'ip'))
.to eq(auth_failure)
end
 
it 'fails if token is not related to project' do
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: '')
expect(gl_auth.find_for_git_client('', 'abcdef', project: nil, ip: 'ip'))
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
expect(gl_auth.find_for_git_client(login, 'abcdef', project: nil, ip: 'ip'))
.to eq(auth_failure)
end
 
Loading
Loading
@@ -338,30 +363,9 @@ describe Gitlab::Auth do
stub_container_registry_config(enabled: false)
end
 
it 'fails if deploy token have read_registry as scope' do
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: '')
expect(gl_auth.find_for_git_client('', deploy_token.token, project: nil, ip: 'ip'))
.to eq(auth_failure)
end
it 'fails if token is nil' do
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: '')
expect(gl_auth.find_for_git_client('', nil, project: nil, ip: 'ip'))
.to eq(auth_failure)
end
it 'fails if token is not related to project' do
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: '')
expect(gl_auth.find_for_git_client('', 'abcdef', project: nil, ip: 'ip'))
.to eq(auth_failure)
end
it 'fails if token has been revoked' do
deploy_token.revoke!
expect(deploy_token.revoked?).to be_truthy
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'deploy-token')
expect(gl_auth.find_for_git_client('deploy-token', deploy_token.token, project: nil, ip: 'ip'))
it 'fails when login and token are valid' do
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login)
expect(gl_auth.find_for_git_client(login, deploy_token.token, project: nil, ip: 'ip'))
.to eq(auth_failure)
end
end
Loading
Loading
Loading
Loading
@@ -147,21 +147,29 @@ describe Gitlab::GitAccess do
end
 
context 'when actor is DeployToken' do
let(:project_deploy_token) { create(:project_deploy_token, project: project) }
let(:actor) { project_deploy_token.deploy_token }
let(:actor) { create(:deploy_token, projects: [project]) }
 
context 'when DeployToken is active and belongs to project' do
it 'allows pull access' do
expect { pull_access_check }.not_to raise_error
end
it 'blocks the push' do
expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:upload])
end
end
 
context 'when DeployToken does not belong to project' do
let(:actor) { create(:deploy_token) }
let(:another_project) { create(:project) }
let(:actor) { create(:deploy_token, projects: [another_project]) }
 
it 'blocks pull access' do
expect { pull_access_check }.to raise_not_found
end
it 'blocks the push' do
expect { push_access_check }.to raise_not_found
end
end
end
end
Loading
Loading
@@ -613,6 +621,41 @@ describe Gitlab::GitAccess do
end
end
 
describe 'deploy token permissions' do
let(:deploy_token) { create(:deploy_token) }
let(:actor) { deploy_token }
context 'pull code' do
context 'when project is authorized' do
before do
deploy_token.projects << project
end
it { expect { pull_access_check }.not_to raise_error }
end
context 'when unauthorized' do
context 'from public project' do
let(:project) { create(:project, :public, :repository) }
it { expect { pull_access_check }.not_to raise_error }
end
context 'from internal project' do
let(:project) { create(:project, :internal, :repository) }
it { expect { pull_access_check }.to raise_not_found }
end
context 'from private project' do
let(:project) { create(:project, :private, :repository) }
it { expect { pull_access_check }.to raise_not_found }
end
end
end
end
describe 'build authentication_abilities permissions' do
let(:authentication_abilities) { build_authentication_abilities }
 
Loading
Loading
Loading
Loading
@@ -145,6 +145,9 @@ pipeline_schedule:
- pipelines
pipeline_schedule_variables:
- pipeline_schedule
deploy_tokens:
- project_deploy_tokens
- projects
deploy_keys:
- user
- deploy_keys_projects
Loading
Loading
@@ -281,6 +284,7 @@ project:
- project_badges
- source_of_merge_requests
- internal_ids
- project_deploy_tokens
- deploy_tokens
award_emoji:
- awardable
Loading
Loading
Loading
Loading
@@ -70,8 +70,27 @@ describe DeployToken do
end
 
describe '#username' do
it 'returns Ghost username' do
it 'returns a harcoded username' do
expect(deploy_token.username).to eq("gitlab+deploy-token-#{deploy_token.id}")
end
end
describe '#has_access_to?' do
let(:project) { create(:project) }
subject(:deploy_token) { create(:deploy_token, projects: [project]) }
context 'when the deploy token has access to the project' do
it 'should return true' do
expect(deploy_token.has_access_to?(project)).to be_truthy
end
end
context 'when the deploy token does not have access to the project' do
it 'should return false' do
another_project = create(:project)
expect(deploy_token.has_access_to?(another_project)).to be_falsy
end
end
end
end
Loading
Loading
@@ -3,7 +3,7 @@ require 'spec_helper'
describe DeployTokenPolicy do
let(:current_user) { create(:user) }
let(:project) { create(:project) }
let(:deploy_token) { create(:deploy_token, project: project) }
let(:deploy_token) { create(:deploy_token, projects: [project]) }
 
subject { described_class.new(current_user, deploy_token) }
 
Loading
Loading
Loading
Loading
@@ -13,20 +13,4 @@ describe Projects::Settings::DeployTokensPresenter do
expect(presenter.length).to eq(3)
end
end
describe '#temporal_token' do
context 'when a deploy token has been created recently' do
it 'returns the token of the deploy' do
deploy_token = ::DeployTokens::CreateService.new(project, user, attributes_for(:deploy_token)).execute
expect(presenter.temporal_token).to eq(deploy_token.token)
end
end
context 'when a deploy token has not been created recently' do
it 'does returns nil' do
expect(presenter.temporal_token).to be_nil
end
end
end
end
Loading
Loading
@@ -558,7 +558,6 @@ describe Auth::ContainerRegistryAuthenticationService do
let(:project) { create(:project, :public) }
 
context 'when pulling and pushing' do
let(:current_user) { create(:deploy_token, projects: [project]) }
let(:current_params) do
{ scope: "repository:#{project.full_path}:pull,push" }
end
Loading
Loading
Loading
Loading
@@ -20,20 +20,6 @@ describe DeployTokens::CreateService, :clean_gitlab_redis_shared_state do
it 'returns a DeployToken' do
expect(subject).to be_an_instance_of DeployToken
end
it 'should store the token on redis' do
redis_key = DeployToken.redis_shared_state_key(user.id)
subject
expect(Gitlab::Redis::SharedState.with { |redis| redis.get(redis_key) }).not_to be_nil
end
it 'should not store deploy token attributes on redis' do
redis_key = DeployToken.redis_shared_state_key(user.id) + ":attributes"
subject
expect(Gitlab::Redis::SharedState.with { |redis| redis.get(redis_key) }).to be_nil
end
end
 
context 'when the deploy token is invalid' do
Loading
Loading
@@ -46,20 +32,6 @@ describe DeployTokens::CreateService, :clean_gitlab_redis_shared_state do
it 'should not create a new ProjectDeployToken' do
expect { subject }.not_to change { ProjectDeployToken.count }
end
it 'should not store the token on redis' do
redis_key = DeployToken.redis_shared_state_key(user.id)
subject
expect(Gitlab::Redis::SharedState.with { |redis| redis.get(redis_key) }).to be_nil
end
it 'should store deploy token attributes on redis' do
redis_key = DeployToken.redis_shared_state_key(user.id) + ":attributes"
subject
expect(Gitlab::Redis::SharedState.with { |redis| redis.get(redis_key) }).not_to be_nil
end
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment