Merge remote-tracking branch 'origin/master' into droplab-templating-xss-fix

This commit is contained in:
Luke "Jared" Bennett 2017-04-26 11:18:10 +01:00
commit 6a6a672037
No known key found for this signature in database
GPG Key ID: 402ED51FB5D306C2
65 changed files with 336 additions and 202 deletions

2
.gitignore vendored
View File

@ -49,7 +49,7 @@ eslint-report.html
/tags /tags
/tmp/* /tmp/*
/vendor/bundle/* /vendor/bundle/*
/builds/* /builds*
/shared/* /shared/*
/.gitlab_workhorse_secret /.gitlab_workhorse_secret
/webpack-report/ /webpack-report/

View File

@ -59,7 +59,7 @@ stages:
.only-master-and-ee-or-mysql: &only-master-and-ee-or-mysql .only-master-and-ee-or-mysql: &only-master-and-ee-or-mysql
only: only:
- /\-(?i)mysql$/ - /mysql/
- master@gitlab-org/gitlab-ce - master@gitlab-org/gitlab-ce
- master@gitlab/gitlabhq - master@gitlab/gitlabhq
- tags@gitlab-org/gitlab-ce - tags@gitlab-org/gitlab-ce

View File

@ -1 +1 @@
5.0.2 5.0.3

View File

@ -716,7 +716,7 @@ GEM
rack rack
shoulda-matchers (2.8.0) shoulda-matchers (2.8.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
sidekiq (4.2.7) sidekiq (4.2.10)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
connection_pool (~> 2.2, >= 2.2.0) connection_pool (~> 2.2, >= 2.2.0)
rack-protection (>= 1.5.0) rack-protection (>= 1.5.0)

View File

@ -5,6 +5,7 @@
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import './breakpoints'; import './breakpoints';
import './flash'; import './flash';
import BlobForkSuggestion from './blob/blob_fork_suggestion';
/* eslint-disable max-len */ /* eslint-disable max-len */
// MergeRequestTabs // MergeRequestTabs
@ -266,6 +267,17 @@ import './flash';
new gl.Diff(); new gl.Diff();
this.scrollToElement('#diffs'); this.scrollToElement('#diffs');
$('.diff-file').each((i, el) => {
new BlobForkSuggestion({
openButtons: $(el).find('.js-edit-blob-link-fork-toggler'),
forkButtons: $(el).find('.js-fork-suggestion-button'),
cancelButtons: $(el).find('.js-cancel-fork-suggestion-button'),
suggestionSections: $(el).find('.js-file-fork-suggestion-section'),
actionTextPieces: $(el).find('.js-file-fork-suggestion-section-action'),
})
.init();
});
}, },
}); });
} }

View File

@ -22,6 +22,7 @@ class PrometheusGraph {
const hasMetrics = $prometheusContainer.data('has-metrics'); const hasMetrics = $prometheusContainer.data('has-metrics');
this.docLink = $prometheusContainer.data('doc-link'); this.docLink = $prometheusContainer.data('doc-link');
this.integrationLink = $prometheusContainer.data('prometheus-integration'); this.integrationLink = $prometheusContainer.data('prometheus-integration');
this.state = '';
$(document).ajaxError(() => {}); $(document).ajaxError(() => {});
@ -38,8 +39,9 @@ class PrometheusGraph {
this.configureGraph(); this.configureGraph();
this.init(); this.init();
} else { } else {
const prevState = this.state;
this.state = '.js-getting-started'; this.state = '.js-getting-started';
this.updateState(); this.updateState(prevState);
} }
} }
@ -53,26 +55,26 @@ class PrometheusGraph {
} }
init() { init() {
this.getData().then((metricsResponse) => { return this.getData().then((metricsResponse) => {
let enoughData = true; let enoughData = true;
Object.keys(metricsResponse.metrics).forEach((key) => { if (typeof metricsResponse === 'undefined') {
let currentKey; enoughData = false;
if (key === 'cpu_values' || key === 'memory_values') {
currentKey = metricsResponse.metrics[key];
if (Object.keys(currentKey).length === 0) {
enoughData = false;
}
}
});
if (!enoughData) {
this.state = '.js-loading';
this.updateState();
} else { } else {
Object.keys(metricsResponse.metrics).forEach((key) => {
if (key === 'cpu_values' || key === 'memory_values') {
const currentData = (metricsResponse.metrics[key])[0];
if (currentData.values.length <= 2) {
enoughData = false;
}
}
});
}
if (enoughData) {
$(prometheusStatesContainer).hide();
$(prometheusParentGraphContainer).show();
this.transformData(metricsResponse); this.transformData(metricsResponse);
this.createGraph(); this.createGraph();
} }
}).catch(() => {
new Flash('An error occurred when trying to load metrics. Please try again.');
}); });
} }
@ -342,6 +344,8 @@ class PrometheusGraph {
getData() { getData() {
const maxNumberOfRequests = 3; const maxNumberOfRequests = 3;
this.state = '.js-loading';
this.updateState();
return gl.utils.backOff((next, stop) => { return gl.utils.backOff((next, stop) => {
$.ajax({ $.ajax({
url: metricsEndpoint, url: metricsEndpoint,
@ -352,12 +356,11 @@ class PrometheusGraph {
this.backOffRequestCounter = this.backOffRequestCounter += 1; this.backOffRequestCounter = this.backOffRequestCounter += 1;
if (this.backOffRequestCounter < maxNumberOfRequests) { if (this.backOffRequestCounter < maxNumberOfRequests) {
next(); next();
} else { } else if (this.backOffRequestCounter >= maxNumberOfRequests) {
stop({ stop(new Error('loading'));
status: resp.status,
metrics: data,
});
} }
} else if (!data.success) {
stop(new Error('loading'));
} else { } else {
stop({ stop({
status: resp.status, status: resp.status,
@ -373,8 +376,9 @@ class PrometheusGraph {
return resp.metrics; return resp.metrics;
}) })
.catch(() => { .catch(() => {
const prevState = this.state;
this.state = '.js-unable-to-connect'; this.state = '.js-unable-to-connect';
this.updateState(); this.updateState(prevState);
}); });
} }
@ -382,19 +386,20 @@ class PrometheusGraph {
Object.keys(metricsResponse.metrics).forEach((key) => { Object.keys(metricsResponse.metrics).forEach((key) => {
if (key === 'cpu_values' || key === 'memory_values') { if (key === 'cpu_values' || key === 'memory_values') {
const metricValues = (metricsResponse.metrics[key])[0]; const metricValues = (metricsResponse.metrics[key])[0];
if (metricValues !== undefined) { this.graphSpecificProperties[key].data = metricValues.values.map(metric => ({
this.graphSpecificProperties[key].data = metricValues.values.map(metric => ({ time: new Date(metric[0] * 1000),
time: new Date(metric[0] * 1000), value: metric[1],
value: metric[1], }));
}));
}
} }
}); });
} }
updateState() { updateState(prevState) {
const $statesContainer = $(prometheusStatesContainer); const $statesContainer = $(prometheusStatesContainer);
$(prometheusParentGraphContainer).hide(); $(prometheusParentGraphContainer).hide();
if (prevState) {
$(`${prevState}`, $statesContainer).addClass('hidden');
}
$(`${this.state}`, $statesContainer).removeClass('hidden'); $(`${this.state}`, $statesContainer).removeClass('hidden');
$(prometheusStatesContainer).show(); $(prometheusStatesContainer).show();
} }

View File

@ -108,8 +108,7 @@
} }
.award-control { .award-control {
margin: 3px 5px 3px 0; margin-right: 5px;
padding: .35em .4em;
outline: 0; outline: 0;
&.disabled { &.disabled {

View File

@ -70,7 +70,7 @@ pre {
} }
hr { hr {
margin: $gl-padding 0; margin: 24px 0;
border-top: 1px solid darken($gray-normal, 8%); border-top: 1px solid darken($gray-normal, 8%);
} }

View File

@ -73,14 +73,6 @@
&.wiki { &.wiki {
padding: 30px $gl-padding; padding: 30px $gl-padding;
.highlight {
margin-bottom: 9px;
> pre {
margin: 0;
}
}
} }
&.blob-no-preview { &.blob-no-preview {

View File

@ -4,7 +4,7 @@
padding: 0; padding: 0;
.timeline-entry { .timeline-entry {
padding: $gl-padding $gl-btn-padding 14px; padding: $gl-padding $gl-btn-padding 0;
border-color: $white-normal; border-color: $white-normal;
color: $gl-text-color; color: $gl-text-color;
border-bottom: 1px solid $border-white-light; border-bottom: 1px solid $border-white-light;

View File

@ -8,6 +8,13 @@
img { img {
max-width: 100%; max-width: 100%;
margin: 0 0 8px;
}
p a:not(.no-attachment-icon) img {
// Remove bottom padding because
// <p> already has $gl-padding bottom
margin-bottom: 0;
} }
*:first-child:not(.katex-display) { *:first-child:not(.katex-display) {
@ -47,44 +54,50 @@
h1 { h1 {
font-size: 1.75em; font-size: 1.75em;
font-weight: 600; font-weight: 600;
margin: 16px 0 10px; margin: 24px 0 16px;
padding: 0 0 0.3em; padding-bottom: 0.3em;
border-bottom: 1px solid $white-dark; border-bottom: 1px solid $white-dark;
color: $gl-text-color; color: $gl-text-color;
&:first-child {
margin-top: 0;
}
} }
h2 { h2 {
font-size: 1.5em; font-size: 1.5em;
font-weight: 600; font-weight: 600;
margin: 16px 0 10px; margin: 24px 0 16px;
padding-bottom: 0.3em;
border-bottom: 1px solid $white-dark;
color: $gl-text-color; color: $gl-text-color;
} }
h3 { h3 {
margin: 16px 0 10px; margin: 24px 0 16px;
font-size: 1.3em; font-size: 1.3em;
} }
h4 { h4 {
margin: 16px 0 10px; margin: 24px 0 16px;
font-size: 1.2em; font-size: 1.2em;
} }
h5 { h5 {
margin: 16px 0 10px; margin: 24px 0 16px;
font-size: 1em; font-size: 1em;
} }
h6 { h6 {
margin: 16px 0 10px; margin: 24px 0 16px;
font-size: 0.95em; font-size: 0.95em;
} }
blockquote { blockquote {
color: $gl-grayish-blue; color: $gl-grayish-blue;
font-size: inherit; font-size: inherit;
padding: 8px 21px; padding: 8px 24px;
margin: 12px 0; margin: 16px 0;
border-left: 3px solid $white-dark; border-left: 3px solid $white-dark;
} }
@ -95,19 +108,20 @@
blockquote p { blockquote p {
color: $gl-grayish-blue !important; color: $gl-grayish-blue !important;
margin: 0;
font-size: inherit; font-size: inherit;
line-height: 1.5; line-height: 1.5;
} }
p { p {
color: $gl-text-color; color: $gl-text-color;
margin: 6px 0 0; margin: 0 0 16px;
} }
table { table {
@extend .table; @extend .table;
@extend .table-bordered; @extend .table-bordered;
margin: 12px 0; margin: 16px 0;
color: $gl-text-color; color: $gl-text-color;
th { th {
@ -120,7 +134,7 @@
} }
pre { pre {
margin: 12px 0; margin-bottom: 16px;
font-size: 13px; font-size: 13px;
line-height: 1.6em; line-height: 1.6em;
overflow-x: auto; overflow-x: auto;
@ -134,7 +148,7 @@
ul, ul,
ol { ol {
padding: 0; padding: 0;
margin: 3px 0 !important; margin: 0 0 16px !important;
} }
ul:dir(rtl), ul:dir(rtl),

View File

@ -29,11 +29,5 @@
.description { .description {
margin-top: 6px; margin-top: 6px;
p {
&:last-child {
margin-bottom: 0;
}
}
} }
} }

View File

@ -52,7 +52,7 @@
.title { .title {
padding: 0; padding: 0;
margin: 0; margin-bottom: 16px;
border-bottom: none; border-bottom: none;
} }
@ -357,6 +357,8 @@
} }
.detail-page-description { .detail-page-description {
padding: 16px 0 0;
small { small {
color: $gray-darkest; color: $gray-darkest;
} }
@ -364,6 +366,8 @@
.edited-text { .edited-text {
color: $gray-darkest; color: $gray-darkest;
display: block;
margin: 0 0 16px;
.author_link { .author_link {
color: $gray-darkest; color: $gray-darkest;

View File

@ -28,7 +28,7 @@
.note-edit-form { .note-edit-form {
.note-form-actions { .note-form-actions {
position: relative; position: relative;
margin-top: $gl-padding; margin: $gl-padding 0;
} }
.note-preview-holder { .note-preview-holder {

View File

@ -102,13 +102,12 @@ ul.notes {
.note-awards { .note-awards {
.js-awards-block { .js-awards-block {
padding: 2px; margin-bottom: 16px;
margin-top: 10px;
} }
} }
.note-header { .note-header {
padding-bottom: 3px; padding-bottom: 8px;
padding-right: 20px; padding-right: 20px;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
@ -151,6 +150,10 @@ ul.notes {
margin-left: 65px; margin-left: 65px;
} }
.note-header {
padding-bottom: 0;
}
&.timeline-entry::after { &.timeline-entry::after {
clear: none; clear: none;
} }

View File

@ -28,7 +28,7 @@ class Admin::GroupsController < Admin::ApplicationController
if @group.save if @group.save
@group.add_owner(current_user) @group.add_owner(current_user)
redirect_to [:admin, @group], notice: 'Group was successfully created.' redirect_to [:admin, @group], notice: "Group '#{@group.name}' was successfully created."
else else
render "new" render "new"
end end

View File

@ -29,7 +29,7 @@ module BlobHelper
link_to 'Edit', edit_path(project, ref, path, options), class: "#{common_classes} btn-sm" link_to 'Edit', edit_path(project, ref, path, options), class: "#{common_classes} btn-sm"
elsif current_user && can?(current_user, :fork_project, project) elsif current_user && can?(current_user, :fork_project, project)
continue_params = { continue_params = {
to: edit_path, to: edit_path(project, ref, path, options),
notice: edit_in_new_fork_notice, notice: edit_in_new_fork_notice,
notice_now: edit_in_new_fork_notice_now notice_now: edit_in_new_fork_notice_now
} }

View File

@ -191,22 +191,23 @@ class MergeRequest < ActiveRecord::Base
merge_request_diff ? merge_request_diff.raw_diffs(*args) : compare.raw_diffs(*args) merge_request_diff ? merge_request_diff.raw_diffs(*args) : compare.raw_diffs(*args)
end end
def diffs(diff_options = nil) def diffs(diff_options = {})
if compare if compare
compare.diffs(diff_options) # When saving MR diffs, `no_collapse` is implicitly added (because we need
# to save the entire contents to the DB), so add that here for
# consistency.
compare.diffs(diff_options.merge(no_collapse: true))
else else
merge_request_diff.diffs(diff_options) merge_request_diff.diffs(diff_options)
end end
end end
def diff_size def diff_size
# The `#diffs` method ends up at an instance of a class inheriting from # Calling `merge_request_diff.diffs.real_size` will also perform
# `Gitlab::Diff::FileCollection::Base`, so use those options as defaults # highlighting, which we don't need here.
# here too, to get the same diff size without performing highlighting. return real_size if merge_request_diff
#
opts = Gitlab::Diff::FileCollection::Base.default_options.merge(diff_options || {})
raw_diffs(opts).size diffs.real_size
end end
def diff_base_commit def diff_base_commit

View File

@ -260,7 +260,7 @@ class MergeRequestDiff < ActiveRecord::Base
new_attributes[:state] = :empty new_attributes[:state] = :empty
else else
diff_collection = compare.diffs(Commit.max_diff_options) diff_collection = compare.diffs(Commit.max_diff_options)
new_attributes[:real_size] = compare.diffs.real_size new_attributes[:real_size] = diff_collection.real_size
if diff_collection.any? if diff_collection.any?
new_diffs = dump_diffs(diff_collection) new_diffs = dump_diffs(diff_collection)

View File

@ -6,8 +6,8 @@ module Users
@params = params.dup @params = params.dup
end end
def execute def execute(skip_authorization: false)
raise Gitlab::Access::AccessDeniedError unless can_create_user? raise Gitlab::Access::AccessDeniedError unless skip_authorization || can_create_user?
user = User.new(build_user_params) user = User.new(build_user_params)

View File

@ -6,8 +6,8 @@ module Users
@params = params.dup @params = params.dup
end end
def execute def execute(skip_authorization: false)
user = Users::BuildService.new(current_user, params).execute user = Users::BuildService.new(current_user, params).execute(skip_authorization: skip_authorization)
@reset_token = user.generate_reset_token if user.recently_sent_password_reset? @reset_token = user.generate_reset_token if user.recently_sent_password_reset?

View File

@ -9,7 +9,7 @@
.bs-callout.bs-callout-warning.clearfix .bs-callout.bs-callout-warning.clearfix
%p %p
User cohorts are only shown when the User cohorts are only shown when the
= link_to 'usage ping', help_page_path('user/admin_area/usage_statistics'), target: '_blank' = link_to 'usage ping', help_page_path('user/admin_area/settings/usage_statistics', anchor: 'usage-ping'), target: '_blank'
is enabled. To enable it and see user cohorts, is enabled. To enable it and see user cohorts,
visit visit
= succeed '.' do = succeed '.' do

View File

@ -56,7 +56,7 @@
Snippets Snippets
- if project_nav_tab? :settings - if project_nav_tab? :settings
= nav_link(path: %w[projects#edit members#show integrations#show repository#show ci_cd#show pages#show]) do = nav_link(path: %w[projects#edit members#show integrations#show services#edit repository#show ci_cd#show pages#show]) do
= link_to edit_project_path(@project), title: 'Settings', class: 'shortcuts-tree' do = link_to edit_project_path(@project), title: 'Settings', class: 'shortcuts-tree' do
%span %span
Settings Settings

View File

@ -0,0 +1,11 @@
- if current_user
.js-file-fork-suggestion-section.file-fork-suggestion.hidden
%span.file-fork-suggestion-note
You're not allowed to
%span.js-file-fork-suggestion-section-action
edit
files in this project directly. Please fork this project,
make your changes there, and submit a merge request.
= link_to 'Fork', nil, method: :post, class: 'js-fork-suggestion-button btn btn-grouped btn-inverted btn-new'
%button.js-cancel-fork-suggestion-button.btn.btn-grouped{ type: 'button' }
Cancel

View File

@ -39,14 +39,4 @@
= replace_blob_link = replace_blob_link
= delete_blob_link = delete_blob_link
- if current_user = render 'projects/fork_suggestion'
.js-file-fork-suggestion-section.file-fork-suggestion.hidden
%span.file-fork-suggestion-note
You're not allowed to
%span.js-file-fork-suggestion-section-action
edit
files in this project directly. Please fork this project,
make your changes there, and submit a merge request.
= link_to 'Fork', nil, method: :post, class: 'js-fork-suggestion-button btn btn-grouped btn-inverted btn-new'
%button.js-cancel-fork-suggestion-button.btn.btn-grouped{ type: 'button' }
Cancel

View File

@ -18,4 +18,6 @@
= view_file_button(diff_commit.id, diff_file.new_path, project) = view_file_button(diff_commit.id, diff_file.new_path, project)
= view_on_environment_button(diff_commit.id, diff_file.new_path, environment) if environment = view_on_environment_button(diff_commit.id, diff_file.new_path, environment) if environment
= render 'projects/fork_suggestion'
= render 'projects/diffs/content', diff_file: diff_file, diff_commit: diff_commit, blob: blob, project: project = render 'projects/diffs/content', diff_file: diff_file, diff_commit: diff_commit, blob: blob, project: project

View File

@ -50,7 +50,7 @@
= link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit' = link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit'
.issue-details.issuable-details .issue-details.issuable-details
.detail-page-description.content-block{ class: ('hide-bottom-border' unless @issue.description.present? ) } .detail-page-description.content-block
.issue-title-data.hidden{ "data" => { "initial-title" => markdown_field(@issue, :title), .issue-title-data.hidden{ "data" => { "initial-title" => markdown_field(@issue, :title),
"endpoint" => rendered_title_namespace_project_issue_path(@project.namespace, @project, @issue), "endpoint" => rendered_title_namespace_project_issue_path(@project.namespace, @project, @issue),
} } } }

View File

@ -36,7 +36,7 @@
%a.btn.btn-default.btn-grouped.pull-right.visible-xs-block.js-sidebar-toggle{ href: "#" } %a.btn.btn-default.btn-grouped.pull-right.visible-xs-block.js-sidebar-toggle{ href: "#" }
= icon('angle-double-left') = icon('angle-double-left')
.detail-page-description.milestone-detail{ class: ('hide-bottom-border' unless @milestone.description.present? ) } .detail-page-description.milestone-detail
%h2.title %h2.title
= markdown_field(@milestone, :title) = markdown_field(@milestone, :title)
%div %div

View File

@ -1,2 +1,3 @@
- page_title @service.title, "Services" - page_title @service.title, "Services"
= render "projects/settings/head"
= render 'form' = render 'form'

View File

@ -14,7 +14,7 @@
%span %span
Members Members
- if can_edit - if can_edit
= nav_link(controller: :integrations) do = nav_link(controller: [:integrations, :services]) do
= link_to project_settings_integrations_path(@project), title: 'Integrations' do = link_to project_settings_integrations_path(@project), title: 'Integrations' do
%span %span
Integrations Integrations

View File

@ -0,0 +1,4 @@
---
title: Show group name on flash container when group is created from Admin area.
merge_request: 10905
author:

View File

@ -0,0 +1,4 @@
---
title: Cleanup markdown spacing
merge_request:
author:

View File

@ -0,0 +1,4 @@
---
title: Don't display the is_admin flag in most API responses
merge_request: 10846
author:

View File

@ -0,0 +1,4 @@
---
title: Add sub-nav for Project Integration Services edit page
merge_request: 10813
author:

View File

@ -0,0 +1,4 @@
---
title: Fix OAuth, LDAP and SAML SSO when regular sign-ups are disabled
merge_request:
author:

View File

@ -0,0 +1,4 @@
---
title: Fix usage ping docs link from empty cohorts page
merge_request:
author:

View File

@ -0,0 +1,4 @@
---
title: Add index to webhooks type column
merge_request:
author:

View File

@ -0,0 +1,4 @@
---
title: Fixed Prometheus monitoring graphs not showing empty states in certain scenarios
merge_request:
author:

View File

@ -0,0 +1,4 @@
---
title: Show sizes correctly in merge requests when diffs overflow
merge_request:
author:

View File

@ -0,0 +1,4 @@
---
title: Upgrade Sidekiq to 4.2.10
merge_request:
author:

View File

@ -0,0 +1,15 @@
class AddIndexToWebHooksType < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :web_hooks, :type
end
def down
remove_concurrent_index :web_hooks, :type
end
end

View File

@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170423064036) do ActiveRecord::Schema.define(version: 20170424142900) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -1371,6 +1371,7 @@ ActiveRecord::Schema.define(version: 20170423064036) do
end end
add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree
add_index "web_hooks", ["type"], name: "index_web_hooks_on_type", using: :btree
add_foreign_key "boards", "projects" add_foreign_key "boards", "projects"
add_foreign_key "chat_teams", "namespaces", on_delete: :cascade add_foreign_key "chat_teams", "namespaces", on_delete: :cascade

View File

@ -48,7 +48,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2016-08-11T07:09:20.351Z", "created_at": "2016-08-11T07:09:20.351Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"location": null, "location": null,
"name": "Administrator", "name": "Administrator",
@ -106,7 +105,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2016-08-11T07:09:20.351Z", "created_at": "2016-08-11T07:09:20.351Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"location": null, "location": null,
"name": "Administrator", "name": "Administrator",
@ -195,7 +193,6 @@ Example of response
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://localhost:3000/root", "web_url": "http://localhost:3000/root",
"created_at": "2016-08-11T07:09:20.351Z", "created_at": "2016-08-11T07:09:20.351Z",
"is_admin": true,
"bio": null, "bio": null,
"location": null, "location": null,
"skype": "", "skype": "",

View File

@ -57,7 +57,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2015-12-21T13:14:24.077Z", "created_at": "2015-12-21T13:14:24.077Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"name": "Administrator", "name": "Administrator",
"skype": "", "skype": "",
@ -101,7 +100,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2015-12-21T13:14:24.077Z", "created_at": "2015-12-21T13:14:24.077Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"name": "Administrator", "name": "Administrator",
"skype": "", "skype": "",
@ -173,7 +171,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2015-12-21T13:14:24.077Z", "created_at": "2015-12-21T13:14:24.077Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"name": "Administrator", "name": "Administrator",
"skype": "", "skype": "",
@ -217,7 +214,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2015-12-21T13:14:24.077Z", "created_at": "2015-12-21T13:14:24.077Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"name": "Administrator", "name": "Administrator",
"skype": "", "skype": "",
@ -284,7 +280,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2015-12-21T13:14:24.077Z", "created_at": "2015-12-21T13:14:24.077Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"name": "Administrator", "name": "Administrator",
"skype": "", "skype": "",

View File

@ -26,7 +26,6 @@ Parameters:
"avatar_url": "http://www.gravatar.com/avatar/cfa35b8cd2ec278026357769582fa563?s=40\u0026d=identicon", "avatar_url": "http://www.gravatar.com/avatar/cfa35b8cd2ec278026357769582fa563?s=40\u0026d=identicon",
"web_url": "http://localhost:3000/john_smith", "web_url": "http://localhost:3000/john_smith",
"created_at": "2015-09-03T07:24:01.670Z", "created_at": "2015-09-03T07:24:01.670Z",
"is_admin": false,
"bio": null, "bio": null,
"skype": "", "skype": "",
"linkedin": "", "linkedin": "",

View File

@ -62,7 +62,6 @@ GET /users
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg", "avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg",
"web_url": "http://localhost:3000/john_smith", "web_url": "http://localhost:3000/john_smith",
"created_at": "2012-05-23T08:00:58Z", "created_at": "2012-05-23T08:00:58Z",
"is_admin": false,
"bio": null, "bio": null,
"location": null, "location": null,
"skype": "", "skype": "",
@ -95,7 +94,6 @@ GET /users
"avatar_url": "http://localhost:3000/uploads/user/avatar/2/index.jpg", "avatar_url": "http://localhost:3000/uploads/user/avatar/2/index.jpg",
"web_url": "http://localhost:3000/jack_smith", "web_url": "http://localhost:3000/jack_smith",
"created_at": "2012-05-23T08:01:01Z", "created_at": "2012-05-23T08:01:01Z",
"is_admin": false,
"bio": null, "bio": null,
"location": null, "location": null,
"skype": "", "skype": "",
@ -169,7 +167,6 @@ Parameters:
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg", "avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg",
"web_url": "http://localhost:3000/john_smith", "web_url": "http://localhost:3000/john_smith",
"created_at": "2012-05-23T08:00:58Z", "created_at": "2012-05-23T08:00:58Z",
"is_admin": false,
"bio": null, "bio": null,
"location": null, "location": null,
"skype": "", "skype": "",
@ -200,7 +197,6 @@ Parameters:
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg", "avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg",
"web_url": "http://localhost:3000/john_smith", "web_url": "http://localhost:3000/john_smith",
"created_at": "2012-05-23T08:00:58Z", "created_at": "2012-05-23T08:00:58Z",
"is_admin": false,
"bio": null, "bio": null,
"location": null, "location": null,
"skype": "", "skype": "",
@ -325,7 +321,6 @@ GET /user
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg", "avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg",
"web_url": "http://localhost:3000/john_smith", "web_url": "http://localhost:3000/john_smith",
"created_at": "2012-05-23T08:00:58Z", "created_at": "2012-05-23T08:00:58Z",
"is_admin": false,
"bio": null, "bio": null,
"location": null, "location": null,
"skype": "", "skype": "",

View File

@ -33,7 +33,6 @@
## Backend howtos ## Backend howtos
- [Architecture](architecture.md) of GitLab - [Architecture](architecture.md) of GitLab
- [CI setup](ci_setup.md) for testing GitLab
- [Gotchas](gotchas.md) to avoid - [Gotchas](gotchas.md) to avoid
- [How to dump production data to staging](db_dump.md) - [How to dump production data to staging](db_dump.md)
- [Instrumentation](instrumentation.md) - [Instrumentation](instrumentation.md)

View File

@ -1,47 +0,0 @@
# CI setup
This document describes what services we use for testing GitLab and GitLab CI.
We currently use four CI services to test GitLab:
1. GitLab CI on [GitHost.io](https://gitlab-ce.githost.io/projects/4/) for the [GitLab.com repo](https://gitlab.com/gitlab-org/gitlab-ce)
2. GitLab CI at ci.gitlab.org to test the private GitLab B.V. repo at dev.gitlab.org
3. [Semephore](https://semaphoreapp.com/gitlabhq/gitlabhq/) for [GitHub.com repo](https://github.com/gitlabhq/gitlabhq)
4. [Mock CI Service](../user/project/integrations/mock_ci.md) for local development
| Software @ configuration being tested | GitLab CI (ci.gitlab.org) | GitLab CI (GitHost.io) | Semaphore |
|---------------------------------------|---------------------------|---------------------------------------------------------------------------|-----------|
| GitLab CE @ MySQL | ✓ | ✓ [Core team can trigger builds](https://gitlab-ce.githost.io/projects/4) | |
| GitLab CE @ PostgreSQL | | | ✓ [Core team can trigger builds](https://semaphoreapp.com/gitlabhq/gitlabhq/branches/master) |
| GitLab EE @ MySQL | ✓ | | |
| GitLab CI @ MySQL | ✓ | | |
| GitLab CI @ PostgreSQL | | | ✓ |
| GitLab CI Runner | ✓ | | ✓ |
| GitLab Shell | ✓ | | ✓ |
| GitLab Shell | ✓ | | ✓ |
Core team has access to trigger builds if needed for GitLab CE.
We use [these build scripts](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml) for testing with GitLab CI.
# Build configuration on [Semaphore](https://semaphoreapp.com/gitlabhq/gitlabhq/) for testing the [GitHub.com repo](https://github.com/gitlabhq/gitlabhq)
- Language: Ruby
- Ruby version: 2.1.8
- database.yml: pg
Build commands
```bash
sudo apt-get install cmake libicu-dev -y (Setup)
bundle install --deployment --path vendor/bundle (Setup)
cp config/gitlab.yml.example config/gitlab.yml (Setup)
bundle exec rake db:create (Setup)
bundle exec rake spinach (Thread #1)
bundle exec rake spec (thread #2)
bundle exec rake rubocop (thread #3)
bundle exec rake brakeman (thread #4)
bundle exec rake jasmine:ci (thread #5)
```
Use rubygems mirror.

View File

@ -448,13 +448,22 @@ is used for Spinach tests as well.
### Monitoring ### Monitoring
The GitLab test suite is [monitored] and a [public dashboard] is available for The GitLab test suite is [monitored] for the `master` branch, and any branch
everyone to see. Feel free to look at the slowest test files and try to improve that includes `rspec-profile` in their name.
them.
A [public dashboard] is available for everyone to see. Feel free to look at the
slowest test files and try to improve them.
[monitored]: ./performance.md#rspec-profiling [monitored]: ./performance.md#rspec-profiling
[public dashboard]: https://redash.gitlab.com/public/dashboards/l1WhHXaxrCWM5Ai9D7YDqHKehq6OU3bx5gssaiWe?org_slug=default [public dashboard]: https://redash.gitlab.com/public/dashboards/l1WhHXaxrCWM5Ai9D7YDqHKehq6OU3bx5gssaiWe?org_slug=default
## CI setup
- On CE, the test suite only runs against PostgreSQL by default. We additionally
run the suite against MySQL for tags, `master`, and any branch that includes
`mysql` in the name.
- On EE, the test suite always runs both PostgreSQL and MySQL.
## Spinach (feature) tests ## Spinach (feature) tests
GitLab [moved from Cucumber to Spinach](https://github.com/gitlabhq/gitlabhq/pull/1426) GitLab [moved from Cucumber to Spinach](https://github.com/gitlabhq/gitlabhq/pull/1426)

View File

@ -12,7 +12,7 @@ You can leave a comment in the following places:
The comment area supports [Markdown] and [slash commands]. One can edit their The comment area supports [Markdown] and [slash commands]. One can edit their
own comment at any time, and anyone with [Master access level][permissions] or own comment at any time, and anyone with [Master access level][permissions] or
higher can also a comment made by someone else. higher can also edit a comment made by someone else.
Apart from the standard comments, you also have the option to create a comment Apart from the standard comments, you also have the option to create a comment
in the form of a resolvable or threaded discussion. in the form of a resolvable or threaded discussion.

View File

@ -431,7 +431,7 @@ Emphasis, aka italics, with *asterisks* or _underscores_.
Strong emphasis, aka bold, with **asterisks** or __underscores__. Strong emphasis, aka bold, with **asterisks** or __underscores__.
Combined emphasis with **_asterisks and underscores_**. Combined emphasis with **asterisks and _underscores_**.
Strikethrough uses two tildes. ~~Scratch this.~~ Strikethrough uses two tildes. ~~Scratch this.~~
``` ```
@ -640,10 +640,11 @@ Here's a line for us to start with.
This line is separated from the one above by two newlines, so it will be a *separate paragraph*. This line is separated from the one above by two newlines, so it will be a *separate paragraph*.
This line is also a separate paragraph, but... This line is also a separate paragraph, but...
This line is only separated by a single newline, so it's a separate line in the *same paragraph*. This line is only separated by a single newline, so it *does not break* and just follows the previous line in the *same paragraph*.
This line is also a separate paragraph, and...
This line is *on its own line*, because the previous line ends with two spaces. (but still in the *same paragraph*)
This line is also a separate paragraph, and...
This line is on its own line, because the previous line ends with two
spaces. spaces.
``` ```
@ -651,11 +652,12 @@ Here's a line for us to start with.
This line is separated from the one above by two newlines, so it will be a *separate paragraph*. This line is separated from the one above by two newlines, so it will be a *separate paragraph*.
This line is also begins a separate paragraph, but... This line is also a separate paragraph, but...
This line is only separated by a single newline, so it's a separate line in the *same paragraph*. This line is only separated by a single newline, so it *does not break* and just follows the previous line in the *same paragraph*.
This line is also a separate paragraph, and...
This line is *on its own line*, because the previous line ends with two spaces. (but still in the *same paragraph*)
This line is also a separate paragraph, and...
This line is on its own line, because the previous line ends with two
spaces. spaces.
### Tables ### Tables

View File

@ -14,7 +14,6 @@ module API
class User < UserBasic class User < UserBasic
expose :created_at expose :created_at
expose :admin?, as: :is_admin
expose :bio, :location, :skype, :linkedin, :twitter, :website_url, :organization expose :bio, :location, :skype, :linkedin, :twitter, :website_url, :organization
end end
@ -41,8 +40,9 @@ module API
expose :external expose :external
end end
class UserWithPrivateToken < UserPublic class UserWithPrivateDetails < UserPublic
expose :private_token expose :private_token
expose :admin?, as: :is_admin
end end
class Email < Grape::Entity class Email < Grape::Entity

View File

@ -1,7 +1,7 @@
module API module API
class Session < Grape::API class Session < Grape::API
desc 'Login to get token' do desc 'Login to get token' do
success Entities::UserWithPrivateToken success Entities::UserWithPrivateDetails
end end
params do params do
optional :login, type: String, desc: 'The username' optional :login, type: String, desc: 'The username'
@ -14,7 +14,7 @@ module API
return unauthorized! unless user return unauthorized! unless user
return render_api_error!('401 Unauthorized. You have 2FA enabled. Please use a personal access token to access the API', 401) if user.two_factor_enabled? return render_api_error!('401 Unauthorized. You have 2FA enabled. Please use a personal access token to access the API', 401) if user.two_factor_enabled?
present user, with: Entities::UserWithPrivateToken present user, with: Entities::UserWithPrivateDetails
end end
end end
end end

View File

@ -433,7 +433,7 @@ module API
success Entities::UserPublic success Entities::UserPublic
end end
get do get do
present current_user, with: sudo? ? Entities::UserWithPrivateToken : Entities::UserPublic present current_user, with: sudo? ? Entities::UserWithPrivateDetails : Entities::UserPublic
end end
desc "Get the currently authenticated user's SSH keys" do desc "Get the currently authenticated user's SSH keys" do

View File

@ -15,6 +15,10 @@ module Gitlab
super.tap { |_| store_highlight_cache } super.tap { |_| store_highlight_cache }
end end
def real_size
@merge_request_diff.real_size
end
private private
# Extracted method to highlight in the same iteration to the diff_collection. # Extracted method to highlight in the same iteration to the diff_collection.

View File

@ -148,7 +148,7 @@ module Gitlab
def build_new_user def build_new_user
user_params = user_attributes.merge(extern_uid: auth_hash.uid, provider: auth_hash.provider, skip_confirmation: true) user_params = user_attributes.merge(extern_uid: auth_hash.uid, provider: auth_hash.provider, skip_confirmation: true)
Users::BuildService.new(nil, user_params).execute Users::BuildService.new(nil, user_params).execute(skip_authorization: true)
end end
def user_attributes def user_attributes

View File

@ -1,11 +1,8 @@
require 'spec_helper' require 'spec_helper'
feature 'Diffs URL', js: true, feature: true do feature 'Diffs URL', js: true, feature: true do
before do let(:project) { create(:project, :public) }
login_as :admin let(:merge_request) { create(:merge_request, source_project: project) }
@merge_request = create(:merge_request)
@project = @merge_request.source_project
end
context 'when visit with */* as accept header' do context 'when visit with */* as accept header' do
before(:each) do before(:each) do
@ -13,9 +10,9 @@ feature 'Diffs URL', js: true, feature: true do
end end
it 'renders the notes' do it 'renders the notes' do
create :note_on_merge_request, project: @project, noteable: @merge_request, note: 'Rebasing with master' create :note_on_merge_request, project: project, noteable: merge_request, note: 'Rebasing with master'
visit diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request) visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
# Load notes and diff through AJAX # Load notes and diff through AJAX
expect(page).to have_css('.note-text', visible: false, text: 'Rebasing with master') expect(page).to have_css('.note-text', visible: false, text: 'Rebasing with master')
@ -25,10 +22,9 @@ feature 'Diffs URL', js: true, feature: true do
context 'when merge request has overflow' do context 'when merge request has overflow' do
it 'displays warning' do it 'displays warning' do
allow_any_instance_of(MergeRequestDiff).to receive(:overflow?).and_return(true) allow(Commit).to receive(:max_diff_options).and_return(max_files: 3)
allow(Commit).to receive(:max_diff_options).and_return(max_files: 20, max_lines: 20)
visit diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request) visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
page.within('.alert') do page.within('.alert') do
expect(page).to have_text("Too many changes to show. Plain diff Email patch To preserve expect(page).to have_text("Too many changes to show. Plain diff Email patch To preserve
@ -36,4 +32,35 @@ feature 'Diffs URL', js: true, feature: true do
end end
end end
end end
context 'when editing file' do
let(:author_user) { create(:user) }
let(:user) { create(:user) }
let(:forked_project) { Projects::ForkService.new(project, author_user).execute }
let(:merge_request) { create(:merge_request_with_diffs, source_project: forked_project, target_project: project, author: author_user) }
let(:changelog_id) { Digest::SHA1.hexdigest("CHANGELOG") }
context 'as author' do
it 'shows direct edit link' do
login_as(author_user)
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
# Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
expect(page).to have_selector("[id=\"#{changelog_id}\"] a.js-edit-blob")
end
end
context 'as user who needs to fork' do
it 'shows fork/cancel confirmation' do
login_as(user)
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
# Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
find("[id=\"#{changelog_id}\"] .js-edit-blob").click
expect(page).to have_selector('.js-fork-suggestion-button', count: 1)
expect(page).to have_selector('.js-cancel-fork-suggestion-button', count: 1)
end
end
end
end end

View File

@ -9,7 +9,6 @@
"avatar_url", "avatar_url",
"web_url", "web_url",
"created_at", "created_at",
"is_admin",
"bio", "bio",
"location", "location",
"skype", "skype",
@ -43,7 +42,6 @@
"avatar_url": { "type": "string" }, "avatar_url": { "type": "string" },
"web_url": { "type": "string" }, "web_url": { "type": "string" },
"created_at": { "type": "date" }, "created_at": { "type": "date" },
"is_admin": { "type": "boolean" },
"bio": { "type": ["string", "null"] }, "bio": { "type": ["string", "null"] },
"location": { "type": ["string", "null"] }, "location": { "type": ["string", "null"] },
"skype": { "type": "string" }, "skype": { "type": "string" },

View File

@ -108,6 +108,18 @@ describe Gitlab::LDAP::User, lib: true do
it "creates a new user if not found" do it "creates a new user if not found" do
expect{ ldap_user.save }.to change{ User.count }.by(1) expect{ ldap_user.save }.to change{ User.count }.by(1)
end end
context 'when signup is disabled' do
before do
stub_application_setting signup_enabled: false
end
it 'creates the user' do
ldap_user.save
expect(gl_user).to be_persisted
end
end
end end
describe 'updating email' do describe 'updating email' do

View File

@ -40,6 +40,20 @@ describe Gitlab::OAuth::User, lib: true do
let(:provider) { 'twitter' } let(:provider) { 'twitter' }
describe 'signup' do describe 'signup' do
context 'when signup is disabled' do
before do
stub_application_setting signup_enabled: false
end
it 'creates the user' do
stub_omniauth_config(allow_single_sign_on: ['twitter'])
oauth_user.save
expect(gl_user).to be_persisted
end
end
it 'marks user as having password_automatically_set' do it 'marks user as having password_automatically_set' do
stub_omniauth_config(allow_single_sign_on: ['twitter'], external_providers: ['twitter']) stub_omniauth_config(allow_single_sign_on: ['twitter'], external_providers: ['twitter'])

View File

@ -211,6 +211,18 @@ describe Gitlab::Saml::User, lib: true do
end end
end end
end end
context 'when signup is disabled' do
before do
stub_application_setting signup_enabled: false
end
it 'creates the user' do
saml_user.save
expect(gl_user).to be_persisted
end
end
end end
describe 'blocking' do describe 'blocking' do

View File

@ -199,10 +199,10 @@ describe MergeRequest, models: true do
end end
context 'when there are no MR diffs' do context 'when there are no MR diffs' do
it 'delegates to the compare object' do it 'delegates to the compare object, setting no_collapse: true' do
merge_request.compare = double(:compare) merge_request.compare = double(:compare)
expect(merge_request.compare).to receive(:diffs).with(options) expect(merge_request.compare).to receive(:diffs).with(options.merge(no_collapse: true))
merge_request.diffs(options) merge_request.diffs(options)
end end
@ -215,15 +215,22 @@ describe MergeRequest, models: true do
end end
context 'when there are MR diffs' do context 'when there are MR diffs' do
before do it 'returns the correct count' do
merge_request.save merge_request.save
expect(merge_request.diff_size).to eq('105')
end end
it 'returns the correct count' do it 'returns the correct overflow count' do
expect(merge_request.diff_size).to eq(105) allow(Commit).to receive(:max_diff_options).and_return(max_files: 2)
merge_request.save
expect(merge_request.diff_size).to eq('2+')
end end
it 'does not perform highlighting' do it 'does not perform highlighting' do
merge_request.save
expect(Gitlab::Diff::Highlight).not_to receive(:new) expect(Gitlab::Diff::Highlight).not_to receive(:new)
merge_request.diff_size merge_request.diff_size
@ -231,7 +238,7 @@ describe MergeRequest, models: true do
end end
context 'when there are no MR diffs' do context 'when there are no MR diffs' do
before do def set_compare(merge_request)
merge_request.compare = CompareService.new( merge_request.compare = CompareService.new(
merge_request.source_project, merge_request.source_project,
merge_request.source_branch merge_request.source_branch
@ -242,10 +249,21 @@ describe MergeRequest, models: true do
end end
it 'returns the correct count' do it 'returns the correct count' do
expect(merge_request.diff_size).to eq(105) set_compare(merge_request)
expect(merge_request.diff_size).to eq('105')
end
it 'returns the correct overflow count' do
allow(Commit).to receive(:max_diff_options).and_return(max_files: 2)
set_compare(merge_request)
expect(merge_request.diff_size).to eq('2+')
end end
it 'does not perform highlighting' do it 'does not perform highlighting' do
set_compare(merge_request)
expect(Gitlab::Diff::Highlight).not_to receive(:new) expect(Gitlab::Diff::Highlight).not_to receive(:new)
merge_request.diff_size merge_request.diff_size

View File

@ -32,6 +32,12 @@ describe API::Keys do
expect(json_response['user']['id']).to eq(user.id) expect(json_response['user']['id']).to eq(user.id)
expect(json_response['user']['username']).to eq(user.username) expect(json_response['user']['username']).to eq(user.username)
end end
it "does not include the user's `is_admin` flag" do
get api("/keys/#{key.id}", admin)
expect(json_response['user']['is_admin']).to be_nil
end
end end
end end
end end

View File

@ -135,6 +135,12 @@ describe API::Users do
expect(json_response['username']).to eq(user.username) expect(json_response['username']).to eq(user.username)
end end
it "does not return the user's `is_admin` flag" do
get api("/users/#{user.id}", user)
expect(json_response['is_admin']).to be_nil
end
it "returns a 401 if unauthenticated" do it "returns a 401 if unauthenticated" do
get api("/users/9998") get api("/users/9998")
expect(response).to have_http_status(401) expect(response).to have_http_status(401)
@ -397,7 +403,6 @@ describe API::Users do
it "updates admin status" do it "updates admin status" do
put api("/users/#{user.id}", admin), { admin: true } put api("/users/#{user.id}", admin), { admin: true }
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['is_admin']).to eq(true)
expect(user.reload.admin).to eq(true) expect(user.reload.admin).to eq(true)
end end
@ -411,7 +416,6 @@ describe API::Users do
it "does not update admin status" do it "does not update admin status" do
put api("/users/#{admin_user.id}", admin), { can_create_group: false } put api("/users/#{admin_user.id}", admin), { can_create_group: false }
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['is_admin']).to eq(true)
expect(admin_user.reload.admin).to eq(true) expect(admin_user.reload.admin).to eq(true)
expect(admin_user.can_create_group).to eq(false) expect(admin_user.can_create_group).to eq(false)
end end

View File

@ -274,5 +274,11 @@ describe API::V3::Users do
expect(new_user).to be_confirmed expect(new_user).to be_confirmed
end end
it 'does not reveal the `is_admin` flag of the user' do
post v3_api('/users', admin), attributes_for(:user)
expect(json_response['is_admin']).to be_nil
end
end end
end end