summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouwe Maan <douwe@gitlab.com>2016-11-10 10:23:44 +0000
committerAlejandro Rodríguez <alejorro70@gmail.com>2016-11-24 14:08:26 -0300
commit30b6d4595807f72f09f092e25a6f554a238ea796 (patch)
treeb9edb55fcee2d5b76cbf6b1edd75523c48bbeaa5
parentca2830f533e07adfedb19acca9b66be8bbe97b48 (diff)
downloadgitlab-ce-30b6d4595807f72f09f092e25a6f554a238ea796.tar.gz
Merge branch 'zj-fix-label-creation-non-members' into 'security'
Fix label creation non members Fixes https://gitlab.com/gitlab-org/gitlab-ce/issues/23416 See merge request !2006
-rw-r--r--app/services/issuable_base_service.rb8
-rw-r--r--app/services/labels/find_or_create_service.rb7
-rw-r--r--changelogs/unreleased/zj-fix-label-creation-non-members.yml4
-rw-r--r--lib/api/helpers.rb14
-rw-r--r--lib/api/issues.rb74
-rw-r--r--lib/api/merge_requests.rb10
-rw-r--r--spec/requests/api/issues_spec.rb14
-rw-r--r--spec/requests/api/merge_requests_spec.rb27
-rw-r--r--spec/services/labels/transfer_service_spec.rb2
9 files changed, 78 insertions, 82 deletions
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 575795788de..617defd8fe8 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -85,14 +85,15 @@ class IssuableBaseService < BaseService
def find_or_create_label_ids
labels = params.delete(:labels)
+
return unless labels
- params[:label_ids] = labels.split(',').map do |label_name|
+ params[:label_ids] = labels.split(",").map do |label_name|
service = Labels::FindOrCreateService.new(current_user, project, title: label_name.strip)
label = service.execute
- label.id
- end
+ label.try(:id)
+ end.compact
end
def process_label_ids(attributes, existing_label_ids: nil)
@@ -140,6 +141,7 @@ class IssuableBaseService < BaseService
params.delete(:state_event)
params[:author] ||= current_user
+
label_ids = process_label_ids(params)
issuable.assign_attributes(params)
diff --git a/app/services/labels/find_or_create_service.rb b/app/services/labels/find_or_create_service.rb
index d622f9edd33..cf4f7606c94 100644
--- a/app/services/labels/find_or_create_service.rb
+++ b/app/services/labels/find_or_create_service.rb
@@ -22,9 +22,14 @@ module Labels
).execute(skip_authorization: skip_authorization)
end
+ # Only creates the label if current_user can do so, if the label does not exist
+ # and the user can not create the label, nil is returned
def find_or_create_label
new_label = available_labels.find_by(title: title)
- new_label ||= project.labels.create(params)
+
+ if new_label.nil? && (skip_authorization || Ability.allowed?(current_user, :admin_label, project))
+ new_label = project.labels.create(params)
+ end
new_label
end
diff --git a/changelogs/unreleased/zj-fix-label-creation-non-members.yml b/changelogs/unreleased/zj-fix-label-creation-non-members.yml
new file mode 100644
index 00000000000..ae4824f82fa
--- /dev/null
+++ b/changelogs/unreleased/zj-fix-label-creation-non-members.yml
@@ -0,0 +1,4 @@
+---
+title: Non members cannot create labels through the API
+merge_request:
+author:
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 2c593dbb4ea..32fbd2750e1 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -182,20 +182,6 @@ module API
ActionController::Parameters.new(attrs).permit!
end
- # Helper method for validating all labels against its names
- def validate_label_params(params)
- errors = {}
-
- params[:labels].to_s.split(',').each do |label_name|
- label = available_labels.find_or_initialize_by(title: label_name.strip)
- next if label.valid?
-
- errors[label.title] = label.errors
- end
-
- errors
- end
-
# Checks the occurrences of datetime attributes, each attribute if present in the params hash must be in ISO 8601
# format (YYYY-MM-DDTHH:MM:SSZ) or a Bad Request error is invoked.
#
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index eea5b91d4f9..78847c537ca 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -19,6 +19,15 @@ module API
def filter_issues_milestone(issues, milestone)
issues.includes(:milestone).where('milestones.title' => milestone)
end
+
+ def issue_params
+ new_params = declared(params, include_parent_namespace: false, include_missing: false).to_h
+ new_params = new_params.with_indifferent_access
+ new_params.delete(:id)
+ new_params.delete(:issue_id)
+
+ new_params
+ end
end
resource :issues do
@@ -86,6 +95,10 @@ module API
end
end
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+
resource :projects do
# Get a list of project issues
#
@@ -152,17 +165,10 @@ module API
post ':id/issues' do
required_attributes! [:title]
- keys = [:title, :description, :assignee_id, :milestone_id, :due_date, :confidential]
+ keys = [:title, :description, :assignee_id, :milestone_id, :due_date, :confidential, :labels]
keys << :created_at if current_user.admin? || user_project.owner == current_user
attrs = attributes_for_keys(keys)
- # Validate label names in advance
- if (errors = validate_label_params(params)).any?
- render_api_error!({ labels: errors }, 400)
- end
-
- attrs[:labels] = params[:labels] if params[:labels]
-
# Convert and filter out invalid confidential flags
attrs['confidential'] = to_boolean(attrs['confidential'])
attrs.delete('confidential') if attrs['confidential'].nil?
@@ -180,41 +186,35 @@ module API
end
end
- # Update an existing issue
- #
- # Parameters:
- # id (required) - The ID of a project
- # issue_id (required) - The ID of a project issue
- # title (optional) - The title of an issue
- # description (optional) - The description of an issue
- # assignee_id (optional) - The ID of a user to assign issue
- # milestone_id (optional) - The ID of a milestone to assign issue
- # labels (optional) - The labels of an issue
- # state_event (optional) - The state event of an issue (close|reopen)
- # updated_at (optional) - Date time string, ISO 8601 formatted
- # due_date (optional) - Date time string in the format YEAR-MONTH-DAY
- # confidential (optional) - Boolean parameter if the issue should be confidential
- # Example Request:
- # PUT /projects/:id/issues/:issue_id
+ desc 'Update an existing issue' do
+ success Entities::Issue
+ end
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ requires :issue_id, type: Integer, desc: "The ID of a project issue"
+ optional :title, type: String, desc: 'The new title of the issue'
+ optional :description, type: String, desc: 'The description of an issue'
+ optional :assignee_id, type: Integer, desc: 'The ID of a user to assign issue'
+ optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign issue'
+ optional :labels, type: String, desc: 'The labels of an issue'
+ optional :state_event, type: String, values: ['close', 'reopen'], desc: 'The state event of an issue'
+ # TODO 9.0, use the Grape DateTime type here
+ optional :updated_at, type: String, desc: 'Date time string, ISO 8601 formatted'
+ optional :due_date, type: String, desc: 'Date time string in the format YEAR-MONTH-DAY'
+ # TODO 9.0, use the Grape boolean type here
+ optional :confidential, type: String, desc: 'Boolean parameter if the issue should be confidential'
+ end
put ':id/issues/:issue_id' do
issue = user_project.issues.find(params[:issue_id])
authorize! :update_issue, issue
- keys = [:title, :description, :assignee_id, :milestone_id, :state_event, :due_date, :confidential]
- keys << :updated_at if current_user.admin? || user_project.owner == current_user
- attrs = attributes_for_keys(keys)
-
- # Validate label names in advance
- if (errors = validate_label_params(params)).any?
- render_api_error!({ labels: errors }, 400)
- end
-
- attrs[:labels] = params[:labels] if params[:labels]
# Convert and filter out invalid confidential flags
- attrs['confidential'] = to_boolean(attrs['confidential'])
- attrs.delete('confidential') if attrs['confidential'].nil?
+ params[:confidential] = to_boolean(params[:confidential])
+ params.delete(:confidential) if params[:confidential].nil?
+
+ params.delete(:updated_at) unless current_user.admin? || user_project.owner == current_user
- issue = ::Issues::UpdateService.new(user_project, current_user, attrs).execute(issue)
+ issue = ::Issues::UpdateService.new(user_project, current_user, issue_params).execute(issue)
if issue.valid?
present issue, with: Entities::Issue, current_user: current_user, project: user_project
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 009913c6242..2f1b274689a 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -77,11 +77,6 @@ module API
mr_params = declared_params
- # Validate label names in advance
- if (errors = validate_label_params(mr_params)).any?
- render_api_error!({ labels: errors }, 400)
- end
-
merge_request = ::MergeRequests::CreateService.new(user_project, current_user, mr_params).execute
if merge_request.valid?
@@ -157,11 +152,6 @@ module API
mr_params = declared_params(include_missing: false)
- # Validate label names in advance
- if (errors = validate_label_params(mr_params)).any?
- render_api_error!({ labels: errors }, 400)
- end
-
merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, mr_params).execute(merge_request)
if merge_request.valid?
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 7bae055b241..b17553211d2 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -697,6 +697,14 @@ describe API::API, api: true do
expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time)
end
end
+
+ context 'the user can only read the issue' do
+ it 'cannot create new labels' do
+ expect do
+ post api("/projects/#{project.id}/issues", non_member), title: 'new issue', labels: 'label, label2'
+ end.not_to change { project.labels.count }
+ end
+ end
end
describe 'POST /projects/:id/issues with spam filtering' do
@@ -839,8 +847,8 @@ describe API::API, api: true do
end
it 'removes all labels' do
- put api("/projects/#{project.id}/issues/#{issue.id}", user),
- labels: ''
+ put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: ''
+
expect(response).to have_http_status(200)
expect(json_response['labels']).to eq([])
end
@@ -892,8 +900,8 @@ describe API::API, api: true do
update_time = 2.weeks.ago
put api("/projects/#{project.id}/issues/#{issue.id}", user),
labels: 'label3', state_event: 'close', updated_at: update_time
- expect(response).to have_http_status(200)
+ expect(response).to have_http_status(200)
expect(json_response['labels']).to include 'label3'
expect(Time.parse(json_response['updated_at'])).to be_like_time(update_time)
end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 37fcb2bc3a9..3ecf3eea5f5 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -402,14 +402,6 @@ describe API::API, api: true do
end
end
- describe "PUT /projects/:id/merge_requests/:merge_request_id to close MR" do
- it "returns merge_request" do
- put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), state_event: "close"
- expect(response).to have_http_status(200)
- expect(json_response['state']).to eq('closed')
- end
- end
-
describe "PUT /projects/:id/merge_requests/:merge_request_id/merge" do
let(:pipeline) { create(:ci_pipeline_without_jobs) }
@@ -486,6 +478,15 @@ describe API::API, api: true do
end
describe "PUT /projects/:id/merge_requests/:merge_request_id" do
+ context "to close a MR" do
+ it "returns merge_request" do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), state_event: "close"
+
+ expect(response).to have_http_status(200)
+ expect(json_response['state']).to eq('closed')
+ end
+ end
+
it "updates title and returns merge_request" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), title: "New title"
expect(response).to have_http_status(200)
@@ -511,10 +512,10 @@ describe API::API, api: true do
end
it 'allows special label names' do
- put api("/projects/#{project.id}/merge_requests/#{merge_request.id}",
- user),
- title: 'new issue',
- labels: 'label, label?, label&foo, ?, &'
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user),
+ title: 'new issue',
+ labels: 'label, label?, label&foo, ?, &'
+
expect(response.status).to eq(200)
expect(json_response['labels']).to include 'label'
expect(json_response['labels']).to include 'label?'
@@ -543,7 +544,7 @@ describe API::API, api: true do
it "returns 404 if note is attached to non existent merge request" do
post api("/projects/#{project.id}/merge_requests/404/comments", user),
- note: 'My comment'
+ note: 'My comment'
expect(response).to have_http_status(404)
end
end
diff --git a/spec/services/labels/transfer_service_spec.rb b/spec/services/labels/transfer_service_spec.rb
index ddf3527dc0f..13654a0881c 100644
--- a/spec/services/labels/transfer_service_spec.rb
+++ b/spec/services/labels/transfer_service_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Labels::TransferService, services: true do
describe '#execute' do
- let(:user) { create(:user) }
+ let(:user) { create(:admin) }
let(:group_1) { create(:group) }
let(:group_2) { create(:group) }
let(:group_3) { create(:group) }