summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/jwt_controller.rb26
-rw-r--r--app/controllers/projects/git_http_client_controller.rb8
-rw-r--r--app/controllers/projects/git_http_controller.rb2
-rw-r--r--app/helpers/lfs_helper.rb16
-rw-r--r--app/models/ci/build.rb13
-rw-r--r--app/models/project.rb6
-rw-r--r--app/policies/project_policy.rb17
-rw-r--r--app/services/auth/container_registry_authentication_service.rb34
-rw-r--r--db/migrate/20160808085531_add_token_to_build.rb10
-rw-r--r--db/migrate/20160808085602_add_index_for_build_token.rb12
-rw-r--r--db/schema.rb3
-rw-r--r--lib/api/internal.rb14
-rw-r--r--lib/ci/api/helpers.rb14
-rw-r--r--lib/gitlab/auth.rb108
-rw-r--r--lib/gitlab/git_access.rb19
-rw-r--r--spec/lib/gitlab/auth_spec.rb76
-rw-r--r--spec/lib/gitlab/git_access_spec.rb101
17 files changed, 363 insertions, 116 deletions
diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb
index 66ebdcc37a7..ed5d28e0d2c 100644
--- a/app/controllers/jwt_controller.rb
+++ b/app/controllers/jwt_controller.rb
@@ -11,7 +11,10 @@ class JwtController < ApplicationController
service = SERVICES[params[:service]]
return head :not_found unless service
- result = service.new(@project, @user, auth_params).execute
+ @@authentication_result ||= Gitlab::Auth.Result.new
+
+ result = service.new(@authentication_result.project, @authentication_result.user, auth_params).
+ execute(capabilities: @authentication_result.capabilities || [])
render json: result, status: result[:http_status]
end
@@ -20,30 +23,13 @@ class JwtController < ApplicationController
def authenticate_project_or_user
authenticate_with_http_basic do |login, password|
- # if it's possible we first try to authenticate project with login and password
- @project = authenticate_project(login, password)
- return if @project
-
- @user = authenticate_user(login, password)
- return if @user
+ @authentication_result = Gitlab::Auth.find_for_git_client(login, password, ip: request.ip)
- render_403
+ render_403 unless @authentication_result.succeeded?
end
end
def auth_params
params.permit(:service, :scope, :account, :client_id)
end
-
- def authenticate_project(login, password)
- if login == 'gitlab-ci-token'
- Project.with_builds_enabled.find_by(runners_token: password)
- end
- end
-
- def authenticate_user(login, password)
- user = Gitlab::Auth.find_with_user_password(login, password)
- Gitlab::Auth.rate_limit!(request.ip, success: user.present?, login: login)
- user
- end
end
diff --git a/app/controllers/projects/git_http_client_controller.rb b/app/controllers/projects/git_http_client_controller.rb
index f5ce63fdfed..6870102c296 100644
--- a/app/controllers/projects/git_http_client_controller.rb
+++ b/app/controllers/projects/git_http_client_controller.rb
@@ -4,7 +4,7 @@ class Projects::GitHttpClientController < Projects::ApplicationController
include ActionController::HttpAuthentication::Basic
include KerberosSpnegoHelper
- attr_reader :user
+ attr_reader :user, :capabilities
# Git clients will not know what authenticity token to send along
skip_before_action :verify_authenticity_token
@@ -34,6 +34,8 @@ class Projects::GitHttpClientController < Projects::ApplicationController
@user = auth_result.user
end
+ @capabilities = auth_result.capabilities || []
+
if ci? || user
return # Allow access
end
@@ -118,6 +120,10 @@ class Projects::GitHttpClientController < Projects::ApplicationController
@ci.present?
end
+ def has_capability?(capability)
+ @capabilities.include?(capability)
+ end
+
def verify_workhorse_api!
Gitlab::Workhorse.verify_api_request!(request.headers)
end
diff --git a/app/controllers/projects/git_http_controller.rb b/app/controllers/projects/git_http_controller.rb
index 9805705c4e3..89afaaed510 100644
--- a/app/controllers/projects/git_http_controller.rb
+++ b/app/controllers/projects/git_http_controller.rb
@@ -86,7 +86,7 @@ class Projects::GitHttpController < Projects::GitHttpClientController
end
def access
- @access ||= Gitlab::GitAccess.new(user, project, 'http')
+ @access ||= Gitlab::GitAccess.new(user, project, 'http', capabilities: capabilities)
end
def access_check
diff --git a/app/helpers/lfs_helper.rb b/app/helpers/lfs_helper.rb
index 5d82abfca79..a2359d94443 100644
--- a/app/helpers/lfs_helper.rb
+++ b/app/helpers/lfs_helper.rb
@@ -25,13 +25,25 @@ module LfsHelper
def lfs_download_access?
return false unless project.lfs_enabled?
- project.public? || ci? || (user && user.can?(:download_code, project))
+ project.public? || ci? || user_can_download_code? || build_can_download_code?
+ end
+
+ def user_can_download_code?
+ has_capability?(:download_code) && user && user.can?(:download_code, project)
+ end
+
+ def build_can_download_code?
+ has_capability?(:build_download_code) && user && user.can?(:build_download_code, project)
end
def lfs_upload_access?
return false unless project.lfs_enabled?
- user && user.can?(:push_code, project)
+ privileged_user_can_push_code?
+ end
+
+ def privileged_user_can_push_code?
+ has_capability?(:push_code) && user && user.can?(:push_code, project)
end
def render_lfs_forbidden
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index fb16bc06d71..29788b8285d 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -1,5 +1,7 @@
module Ci
class Build < CommitStatus
+ include TokenAuthenticatable
+
belongs_to :runner, class_name: 'Ci::Runner'
belongs_to :trigger_request, class_name: 'Ci::TriggerRequest'
belongs_to :erased_by, class_name: 'User'
@@ -23,7 +25,10 @@ module Ci
acts_as_taggable
+ add_authentication_token_field :token
+
before_save :update_artifacts_size, if: :artifacts_file_changed?
+ before_save :ensure_token
before_destroy { project }
after_create :execute_hooks
@@ -173,7 +178,7 @@ module Ci
end
def repo_url
- auth = "gitlab-ci-token:#{token}@"
+ auth = "gitlab-ci-token:#{ensure_token}@"
project.http_url_to_repo.sub(/^https?:\/\//) do |prefix|
prefix + auth
end
@@ -341,12 +346,8 @@ module Ci
)
end
- def token
- project.runners_token
- end
-
def valid_token?(token)
- project.valid_runners_token?(token)
+ self.token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token)
end
def has_tags?
diff --git a/app/models/project.rb b/app/models/project.rb
index f3f3ffbbd28..16ca2b688c8 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1138,12 +1138,6 @@ class Project < ActiveRecord::Base
self.runners_token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.runners_token)
end
- # TODO (ayufan): For now we use runners_token (backward compatibility)
- # In 8.4 every build will have its own individual token valid for time of build
- def valid_build_token?(token)
- self.builds_enabled? && self.runners_token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.runners_token)
- end
-
def build_coverage_enabled?
build_coverage_regex.present?
end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index acf36d422d1..ce686af2ade 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -64,6 +64,12 @@ class ProjectPolicy < BasePolicy
can! :read_deployment
end
+ # Permissions given when an user is direct member of a group
+ def team_member_reporter_access!
+ can! :build_download_code
+ can! :build_read_container_image
+ end
+
def developer_access!
can! :admin_merge_request
can! :update_merge_request
@@ -109,6 +115,8 @@ class ProjectPolicy < BasePolicy
can! :read_commit_status
can! :read_pipeline
can! :read_container_image
+ can! :build_download_code
+ can! :build_read_container_image
end
def owner_access!
@@ -130,10 +138,11 @@ class ProjectPolicy < BasePolicy
def team_access!(user)
access = project.team.max_member_access(user.id)
- guest_access! if access >= Gitlab::Access::GUEST
- reporter_access! if access >= Gitlab::Access::REPORTER
- developer_access! if access >= Gitlab::Access::DEVELOPER
- master_access! if access >= Gitlab::Access::MASTER
+ guest_access! if access >= Gitlab::Access::GUEST
+ reporter_access! if access >= Gitlab::Access::REPORTER
+ team_member_reporter_access! if access >= Gitlab::Access::REPORTER
+ developer_access! if access >= Gitlab::Access::DEVELOPER
+ master_access! if access >= Gitlab::Access::MASTER
end
def archived_access!
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index 6072123b851..ba0b60abfe4 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -4,7 +4,9 @@ module Auth
AUDIENCE = 'container_registry'
- def execute
+ def execute(capabilities: capabilities)
+ @capabilities = capabilities
+
return error('not found', 404) unless registry.enabled
unless current_user || project
@@ -74,9 +76,9 @@ module Auth
case requested_action
when 'pull'
- requested_project == project || can?(current_user, :read_container_image, requested_project)
+ build_can_pull?(requested_project) || user_can_pull?(requested_project)
when 'push'
- requested_project == project || can?(current_user, :create_container_image, requested_project)
+ build_can_push?(requested_project) || user_can_push?(requested_project)
else
false
end
@@ -85,5 +87,31 @@ module Auth
def registry
Gitlab.config.registry
end
+
+ private
+
+ def build_can_pull?(requested_project)
+ # Build can:
+ # 1. pull from it's own project (for ex. a build)
+ # 2. read images from dependent projects if creator of build is a team member
+ @capabilities.include?(:build_read_container_image) &&
+ (requested_project == project || can?(current_user, :build_read_container_image, requested_project))
+ end
+
+ def user_can_pull?(requested_project)
+ @capabilities.include?(:read_container_image) &&
+ can?(current_user, :read_container_image, requested_project)
+ end
+
+ def build_can_push?(requested_project)
+ # Build can push only to project to from which he originates
+ @capabilities.include?(:build_create_container_image) &&
+ requested_project == project
+ end
+
+ def user_can_push?(requested_project)
+ @capabilities.include?(:create_container_image) &&
+ can?(current_user, :create_container_image, requested_project)
+ end
end
end
diff --git a/db/migrate/20160808085531_add_token_to_build.rb b/db/migrate/20160808085531_add_token_to_build.rb
new file mode 100644
index 00000000000..3ed2a103ae3
--- /dev/null
+++ b/db/migrate/20160808085531_add_token_to_build.rb
@@ -0,0 +1,10 @@
+class AddTokenToBuild < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ def change
+ add_column :ci_builds, :token, :string
+ end
+end
diff --git a/db/migrate/20160808085602_add_index_for_build_token.rb b/db/migrate/20160808085602_add_index_for_build_token.rb
new file mode 100644
index 00000000000..10ef42afce1
--- /dev/null
+++ b/db/migrate/20160808085602_add_index_for_build_token.rb
@@ -0,0 +1,12 @@
+class AddIndexForBuildToken < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def change
+ add_concurrent_index :ci_builds, :token, unique: true
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 61873e38113..af00094c6b6 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,6 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
+
ActiveRecord::Schema.define(version: 20160913162434) do
# These are extensions that must be enabled in order to support this database
@@ -181,6 +182,7 @@ ActiveRecord::Schema.define(version: 20160913162434) do
t.string "when"
t.text "yaml_variables"
t.datetime "queued_at"
+ t.string "token"
end
add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree
@@ -192,6 +194,7 @@ ActiveRecord::Schema.define(version: 20160913162434) do
add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree
add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree
add_index "ci_builds", ["status"], name: "index_ci_builds_on_status", using: :btree
+ add_index "ci_builds", ["token"], name: "index_ci_builds_on_token", unique: true, using: :btree
create_table "ci_commits", force: :cascade do |t|
t.integer "project_id"
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index 6e6efece7c4..2ec94570506 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -51,9 +51,9 @@ module API
access =
if wiki?
- Gitlab::GitAccessWiki.new(actor, project, protocol)
+ Gitlab::GitAccessWiki.new(actor, project, protocol, capabilities: ssh_capabilities)
else
- Gitlab::GitAccess.new(actor, project, protocol)
+ Gitlab::GitAccess.new(actor, project, protocol, capabilities: ssh_capabilities)
end
access_status = access.check(params[:action], params[:changes])
@@ -130,6 +130,16 @@ module API
{ success: true, recovery_codes: codes }
end
+
+ private
+
+ def ssh_capabilities
+ [
+ :read_project,
+ :download_code,
+ :push_code
+ ]
+ end
end
end
end
diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb
index ba80c89df78..3dfebc0cac1 100644
--- a/lib/ci/api/helpers.rb
+++ b/lib/ci/api/helpers.rb
@@ -14,12 +14,20 @@ module Ci
end
def authenticate_build_token!(build)
- token = (params[BUILD_TOKEN_PARAM] || env[BUILD_TOKEN_HEADER]).to_s
- forbidden! unless token && build.valid_token?(token)
+ forbidden! unless build_token_valid?
end
def runner_registration_token_valid?
- params[:token] == current_application_settings.runners_registration_token
+ ActiveSupport::SecurityUtils.variable_size_secure_compare(
+ params[:token],
+ current_application_settings.runners_registration_token)
+ end
+
+ def build_token_valid?
+ token = (params[BUILD_TOKEN_PARAM] || env[BUILD_TOKEN_HEADER]).to_s
+
+ # We require to also check `runners_token` to maintain compatibility with old version of runners
+ token && (build.valid_token?(token) || build.project.valid_runners_token?(token))
end
def update_runner_last_contact(save: true)
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index 91f0270818a..7af9bb9a326 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -1,21 +1,23 @@
module Gitlab
module Auth
- Result = Struct.new(:user, :type)
+ Result = Struct.new(:user, :project, :type, :capabilities) do
+ def succeeded?
+ user.present? || [:ci].include?(type)
+ end
+ end
class << self
def find_for_git_client(login, password, project:, ip:)
raise "Must provide an IP for rate limiting" if ip.nil?
- result = Result.new
-
- if valid_ci_request?(login, password, project)
- result.type = :ci
- else
- result = populate_result(login, password)
- end
+ result = service_access_token_check(login, password, project) ||
+ build_access_token_check(login, password) ||
+ user_with_password_for_git(login, password) ||
+ oauth_access_token_check(login, password) ||
+ personal_access_token_check(login, password) ||
+ Result.new
- success = result.user.present? || [:ci, :missing_personal_token].include?(result.type)
- rate_limit!(ip, success: success, login: login)
+ rate_limit!(ip, success: result.succeeded?, login: login)
result
end
@@ -57,44 +59,36 @@ module Gitlab
private
- def valid_ci_request?(login, password, project)
+ def service_access_token_check(login, password, project)
matched_login = /(?<service>^[a-zA-Z]*-ci)-token$/.match(login)
- return false unless project && matched_login.present?
+ return unless project && matched_login.present?
underscored_service = matched_login['service'].underscore
- if underscored_service == 'gitlab_ci'
- project && project.valid_build_token?(password)
- elsif Service.available_services_names.include?(underscored_service)
+ if Service.available_services_names.include?(underscored_service)
# We treat underscored_service as a trusted input because it is included
# in the Service.available_services_names whitelist.
service = project.public_send("#{underscored_service}_service")
- service && service.activated? && service.valid_token?(password)
- end
- end
-
- def populate_result(login, password)
- result =
- user_with_password_for_git(login, password) ||
- oauth_access_token_check(login, password) ||
- personal_access_token_check(login, password)
-
- if result
- result.type = nil unless result.user
-
- if result.user && result.user.two_factor_enabled? && result.type == :gitlab_or_ldap
- result.type = :missing_personal_token
+ if service && service.activated? && service.valid_token?(password)
+ Result.new(nil, project, :ci, build_capabilities)
end
end
-
- result || Result.new
end
def user_with_password_for_git(login, password)
user = find_with_user_password(login, password)
- Result.new(user, :gitlab_or_ldap) if user
+ return unless user
+
+ type =
+ if user.two_factor_enabled?
+ :missing_personal_token
+ else
+ :gitlab_or_ldap
+ end
+
+ Result.new(user, nil, type, full_capabilities)
end
def oauth_access_token_check(login, password)
@@ -102,7 +96,7 @@ module Gitlab
token = Doorkeeper::AccessToken.by_token(password)
if token && token.accessible?
user = User.find_by(id: token.resource_owner_id)
- Result.new(user, :oauth)
+ Result.new(user, nil, :oauth, read_capabilities)
end
end
end
@@ -111,9 +105,53 @@ module Gitlab
if login && password
user = User.find_by_personal_access_token(password)
validation = User.by_login(login)
- Result.new(user, :personal_token) if user == validation
+ if user && user == validation
+ Result.new(user, nil, :personal_token, full_capabilities)
+ end
+ end
+ end
+
+ def build_access_token_check(login, password)
+ return unless login == 'gitlab-ci-token'
+ return unless password
+
+ build = Ci::Build.running.find_by_token(password)
+ return unless build
+
+ if build.user
+ # If user is assigned to build, use restricted credentials of user
+ Result.new(build.user, build.project, :build, build_capabilities)
+ else
+ # Otherwise use generic CI credentials (backward compatibility)
+ Result.new(nil, build.project, :ci, build_capabilities)
end
end
+
+ private
+
+ def build_capabilities
+ [
+ :read_project,
+ :build_download_code,
+ :build_read_container_image,
+ :build_create_container_image
+ ]
+ end
+
+ def read_capabilities
+ [
+ :read_project,
+ :download_code,
+ :read_container_image
+ ]
+ end
+
+ def full_capabilities
+ read_capabilities + [
+ :push_code,
+ :update_container_image
+ ]
+ end
end
end
end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index 1882eb8d050..63b707db814 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -5,12 +5,13 @@ module Gitlab
DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive }
PUSH_COMMANDS = %w{ git-receive-pack }
- attr_reader :actor, :project, :protocol, :user_access
+ attr_reader :actor, :project, :protocol, :user_access, :capabilities
- def initialize(actor, project, protocol)
+ def initialize(actor, project, protocol, capabilities: capabilities)
@actor = actor
@project = project
@protocol = protocol
+ @capabilities = capabilities
@user_access = UserAccess.new(user, project: project)
end
@@ -60,14 +61,26 @@ module Gitlab
end
def user_download_access_check
- unless user_access.can_do_action?(:download_code)
+ unless user_can_download_code? || build_can_download_code?
return build_status_object(false, "You are not allowed to download code from this project.")
end
build_status_object(true)
end
+ def user_can_download_code?
+ capabilities.include?(:download_code) && user_access.can_do_action?(:download_code)
+ end
+
+ def build_can_download_code?
+ capabilities.include?(:build_download_code) && user_access.can_do_action?(:build_download_code)
+ end
+
def user_push_access_check(changes)
+ unless capabilities.include?(:push_code)
+ return build_status_object(false, "You are not allowed to upload code for this project.")
+ end
+
if changes.blank?
return build_status_object(true)
end
diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb
index 7c23e02d05a..b665517bbb0 100644
--- a/spec/lib/gitlab/auth_spec.rb
+++ b/spec/lib/gitlab/auth_spec.rb
@@ -4,15 +4,51 @@ describe Gitlab::Auth, lib: true do
let(:gl_auth) { described_class }
describe 'find_for_git_client' do
- it 'recognizes CI' do
- token = '123'
+ context 'build token' do
+ subject { gl_auth.find_for_git_client('gitlab-ci-token', build.token, project: project, ip: 'ip') }
+
+ context 'for running build' do
+ let!(:build) { create(:ci_build, :running) }
+ let(:project) { build.project }
+
+ before do
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: 'gitlab-ci-token')
+ end
+
+ it 'recognises user-less build' do
+ expect(subject).to eq(Gitlab::Auth::Result.new(nil, build.project, :ci, build_capabilities))
+ end
+
+ it 'recognises user token' do
+ build.update(user: create(:user))
+
+ expect(subject).to eq(Gitlab::Auth::Result.new(build.user, build.project, :build, build_capabilities))
+ end
+ end
+
+ context 'for non-running build' do
+ let!(:build) { create(:ci_build, :pending) }
+ let(:project) { build.project }
+
+ before do
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'gitlab-ci-token')
+ end
+
+ it 'denies authentication' do
+ expect(subject).to eq(Gitlab::Auth::Result.new)
+ end
+ end
+ end
+
+ it 'recognizes other ci services' do
project = create(:empty_project)
- project.update_attributes(runners_token: token)
+ project.create_drone_ci_service(active: true)
+ project.drone_ci_service.update(token: 'token')
ip = 'ip'
- expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'gitlab-ci-token')
- expect(gl_auth.find_for_git_client('gitlab-ci-token', token, project: project, ip: ip)).to eq(Gitlab::Auth::Result.new(nil, :ci))
+ expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'drone-ci-token')
+ expect(gl_auth.find_for_git_client('drone-ci-token', 'token', project: project, ip: ip)).to eq(Gitlab::Auth::Result.new(nil, project, :ci, build_capabilities))
end
it 'recognizes master passwords' do
@@ -20,7 +56,7 @@ describe Gitlab::Auth, lib: true do
ip = 'ip'
expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: user.username)
- expect(gl_auth.find_for_git_client(user.username, 'password', project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, :gitlab_or_ldap))
+ expect(gl_auth.find_for_git_client(user.username, 'password', project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, full_capabilities))
end
it 'recognizes OAuth tokens' do
@@ -30,7 +66,7 @@ describe Gitlab::Auth, lib: true do
ip = 'ip'
expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'oauth2')
- expect(gl_auth.find_for_git_client("oauth2", token.token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, :oauth))
+ expect(gl_auth.find_for_git_client("oauth2", token.token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, nil, :oauth, read_capabilities))
end
it 'returns double nil for invalid credentials' do
@@ -92,4 +128,30 @@ describe Gitlab::Auth, lib: true do
end
end
end
+
+ private
+
+ def build_capabilities
+ [
+ :read_project,
+ :build_download_code,
+ :build_read_container_image,
+ :build_create_container_image
+ ]
+ end
+
+ def read_capabilities
+ [
+ :read_project,
+ :download_code,
+ :read_container_image
+ ]
+ end
+
+ def full_capabilities
+ read_capabilities + [
+ :push_code,
+ :update_container_image
+ ]
+ end
end
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index f12c9a370f7..77dce676cdb 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -1,10 +1,17 @@
require 'spec_helper'
describe Gitlab::GitAccess, lib: true do
- let(:access) { Gitlab::GitAccess.new(actor, project, 'web') }
+ let(:access) { Gitlab::GitAccess.new(actor, project, 'web', capabilities: capabilities) }
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:actor) { user }
+ let(:capabilities) do
+ [
+ :read_project,
+ :download_code,
+ :push_code
+ ]
+ end
describe '#check with single protocols allowed' do
def disable_protocol(protocol)
@@ -111,6 +118,36 @@ describe Gitlab::GitAccess, lib: true do
end
end
end
+
+ describe 'build capabilities permissions' do
+ let(:capabilities) { build_capabilities }
+
+ describe 'reporter user' do
+ before { project.team << [user, :reporter] }
+
+ context 'pull code' do
+ it { expect(subject).to be_allowed }
+ end
+ end
+
+ describe 'admin user' do
+ let(:user) { create(:admin) }
+
+ context 'when member of the project' do
+ before { project.team << [user, :reporter] }
+
+ context 'pull code' do
+ it { expect(subject).to be_allowed }
+ end
+ end
+
+ context 'when is not member of the project' do
+ context 'pull code' do
+ it { expect(subject).not_to be_allowed }
+ end
+ end
+ end
+ end
end
describe 'push_access_check' do
@@ -281,40 +318,58 @@ describe Gitlab::GitAccess, lib: true do
admin: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false }))
end
end
+
end
- describe 'deploy key permissions' do
- let(:key) { create(:deploy_key) }
- let(:actor) { key }
+ shared_examples 'can not push code' do
+ subject { access.check('git-receive-pack', '_any') }
- context 'push code' do
- subject { access.check('git-receive-pack', '_any') }
+ context 'when project is authorized' do
+ before { key.projects << project }
- context 'when project is authorized' do
- before { key.projects << project }
+ it { expect(subject).not_to be_allowed }
+ end
+
+ context 'when unauthorized' do
+ context 'to public project' do
+ let(:project) { create(:project, :public) }
it { expect(subject).not_to be_allowed }
end
- context 'when unauthorized' do
- context 'to public project' do
- let(:project) { create(:project, :public) }
-
- it { expect(subject).not_to be_allowed }
- end
-
- context 'to internal project' do
- let(:project) { create(:project, :internal) }
+ context 'to internal project' do
+ let(:project) { create(:project, :internal) }
- it { expect(subject).not_to be_allowed }
- end
+ it { expect(subject).not_to be_allowed }
+ end
- context 'to private project' do
- let(:project) { create(:project, :internal) }
+ context 'to private project' do
+ let(:project) { create(:project, :internal) }
- it { expect(subject).not_to be_allowed }
- end
+ it { expect(subject).not_to be_allowed }
end
end
end
+
+ describe 'build capabilities permissions' do
+ let(:capabilities) { build_capabilities }
+
+ it_behaves_like 'cannot push code'
+ end
+
+ describe 'deploy key permissions' do
+ let(:key) { create(:deploy_key) }
+ let(:actor) { key }
+
+ it_behaves_like 'cannot push code'
+ end
+
+ private
+
+ def build_capabilities
+ [
+ :read_project,
+ :build_download_code
+ ]
+ end
end