Commit 1a2683e1 authored by Steve Azzopardi's avatar Steve Azzopardi

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into ce-to-ee-2018-11-29

parents 21abe22f ed3e9197
......@@ -1140,6 +1140,7 @@ review-deploy:
GITLAB_ADMIN_PASSWORD: "${REVIEW_APPS_ROOT_PASSWORD}"
GITHUB_ACCESS_TOKEN: "${REVIEW_APPS_QA_GITHUB_ACCESS_TOKEN}"
EE_LICENSE: "${REVIEW_APPS_EE_LICENSE}"
QA_DEBUG: "true"
artifacts:
paths:
- ./qa/gitlab-qa-run-*
......@@ -1155,6 +1156,7 @@ review-deploy:
review-qa-smoke:
<<: *review-qa-base
retry: 2
script:
- gitlab-qa Test::Instance::Smoke "${QA_IMAGE}" "${CI_ENVIRONMENT_URL}"
......
......@@ -4,8 +4,9 @@ entry.
## 11.5.1 (2018-11-26)
### Security (16 changes)
### Security (17 changes)
- Escape user fullname while rendering autocomplete template to prevent XSS.
- Fix CRLF vulnerability in Project hooks.
- Fix possible XSS attack in Markdown urls with spaces.
- Redact sensitive information on gitlab-workhorse log.
......@@ -598,12 +599,13 @@ entry.
## 11.3.11 (2018-11-26)
### Security (32 changes)
### Security (33 changes)
- Filter user sensitive data from discussions JSON. !2537
- Escape entity title while autocomplete template rendering to prevent XSS. !2557
- Resolve reflected XSS in Ouath authorize window.
- Restrict Personal Access Tokens to API scope on web requests.
- Fix XSS in merge request source branch name.
- Escape user fullname while rendering autocomplete template to prevent XSS.
- Fix CRLF vulnerability in Project hooks.
- Fix possible XSS attack in Markdown urls with spaces.
- Redact sensitive information on gitlab-workhorse log.
......@@ -615,8 +617,8 @@ entry.
- Markdown API no longer displays confidential title references unless authorized.
- Provide email notification when a user changes their email address.
- Properly filter private references from system notes.
- Restrict Personal Access Tokens to API scope on web requests.
- Redact personal tokens in unsubscribe links.
- Resolve reflected XSS in Ouath authorize window.
- Fix SSRF in project integrations.
- Fix stored XSS in merge requests from imported repository.
- Fixed ability to comment on locked/confidential issues.
......
......@@ -447,7 +447,7 @@ group :ed25519 do
end
# Gitaly GRPC client
gem 'gitaly-proto', '~> 1.1.0', require: 'gitaly'
gem 'gitaly-proto', '~> 1.2.0', require: 'gitaly'
gem 'grpc', '~> 1.15.0'
gem 'google-protobuf', '~> 3.6'
......
......@@ -297,7 +297,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gitaly-proto (1.1.0)
gitaly-proto (1.2.0)
grpc (~> 1.0)
github-markup (1.7.0)
gitlab-default_value_for (3.1.1)
......@@ -1040,7 +1040,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 1.1.0)
gitaly-proto (~> 1.2.0)
github-markup (~> 1.7.0)
gitlab-default_value_for (~> 3.1.1)
gitlab-license (~> 1.0)
......
......@@ -296,7 +296,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gitaly-proto (1.1.0)
gitaly-proto (1.2.0)
grpc (~> 1.0)
github-markup (1.7.0)
gitlab-license (1.0.0)
......@@ -1032,7 +1032,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 1.1.0)
gitaly-proto (~> 1.2.0)
github-markup (~> 1.7.0)
gitlab-license (~> 1.0)
gitlab-markup (~> 1.6.5)
......
import bp from '../../../breakpoints';
import { slugify } from '../../../lib/utils/text_utility';
import { parseQueryStringIntoObject } from '../../../lib/utils/common_utils';
import { mergeUrlParams, redirectTo } from '../../../lib/utils/url_utility';
......@@ -26,7 +25,8 @@ export default class Wikis {
if (!this.newWikiForm) return;
const slugInput = this.newWikiForm.querySelector('#new_wiki_path');
const slug = slugify(slugInput.value);
const slug = slugInput.value;
if (slug.length > 0) {
const wikisPath = slugInput.getAttribute('data-wikis-path');
......
......@@ -5,23 +5,12 @@ class Admin::ImpersonationsController < Admin::ApplicationController
before_action :authenticate_impersonator!
def destroy
original_user = current_user
warden.set_user(impersonator, scope: :user)
Gitlab::AppLogger.info("User #{impersonator.username} has stopped impersonating #{original_user.username}")
session[:impersonator_id] = nil
original_user = stop_impersonation
redirect_to admin_user_path(original_user), status: :found
end
private
def impersonator
@impersonator ||= User.find(session[:impersonator_id]) if session[:impersonator_id]
end
def authenticate_impersonator!
render_404 unless impersonator && impersonator.admin? && !impersonator.blocked?
end
......
......@@ -2,6 +2,7 @@
class Admin::UsersController < Admin::ApplicationController
before_action :user, except: [:index, :new, :create]
before_action :check_impersonation_availability, only: :impersonate
def index
@users = User.order_name_asc.filter(params[:filter])
......@@ -227,6 +228,10 @@ class Admin::UsersController < Admin::ApplicationController
result[:status] == :success
end
def check_impersonation_availability
access_denied! unless Gitlab.config.gitlab.impersonation_enabled
end
end
Admin::UsersController.prepend(EE::Admin::UsersController)
......@@ -28,6 +28,7 @@ class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :require_email, unless: :devise_controller?
before_action :set_usage_stats_consent_flag
before_action :check_impersonation_availability
around_action :set_locale
......@@ -470,4 +471,28 @@ class ApplicationController < ActionController::Base
.new(settings, current_user, application_setting_params)
.execute
end
def check_impersonation_availability
return unless session[:impersonator_id]
unless Gitlab.config.gitlab.impersonation_enabled
stop_impersonation
access_denied! _('Impersonation has been disabled')
end
end
def stop_impersonation
impersonated_user = current_user
Gitlab::AppLogger.info("User #{impersonator.username} has stopped impersonating #{impersonated_user.username}")
warden.set_user(impersonator, scope: :user)
session[:impersonator_id] = nil
impersonated_user
end
def impersonator
@impersonator ||= User.find(session[:impersonator_id]) if session[:impersonator_id]
end
end
......@@ -72,6 +72,10 @@ module UsersHelper
end
end
def impersonation_enabled?
Gitlab.config.gitlab.impersonation_enabled
end
private
def get_profile_tabs
......
......@@ -15,8 +15,6 @@ class RemoteMirror < ActiveRecord::Base
insecure_mode: true,
algorithm: 'aes-256-cbc'
default_value_for :only_protected_branches, true
belongs_to :project, inverse_of: :remote_mirrors
validates :url, presence: true, url: { protocols: %w(ssh git http https), allow_blank: true, enforce_user: true }
......
......@@ -85,6 +85,12 @@ class WikiPage
alias_method :to_param, :slug
def human_title
return 'Home' if title == 'home'
title
end
# The formatted title of this page.
def title
if @attributes[:title]
......
......@@ -6,6 +6,7 @@ class AccessTokenValidationService
EXPIRED = :expired
REVOKED = :revoked
INSUFFICIENT_SCOPE = :insufficient_scope
IMPERSONATION_DISABLED = :impersonation_disabled
attr_reader :token, :request
......@@ -24,6 +25,11 @@ class AccessTokenValidationService
elsif !self.include_any_scope?(scopes)
return INSUFFICIENT_SCOPE
elsif token.respond_to?(:impersonation) &&
token.impersonation &&
!Gitlab.config.gitlab.impersonation_enabled
return IMPERSONATION_DISABLED
else
return VALID
end
......
# frozen_string_literal: true
module Ci
class ArchiveTraceService
def execute(job)
job.trace.archive!
rescue ::Gitlab::Ci::Trace::AlreadyArchivedError
# It's already archived, thus we can safely ignore this exception.
rescue => e
# Tracks this error with application logs, Sentry, and Prometheus.
# If `archive!` keeps failing for over a week, that could incur data loss.
# (See more https://docs.gitlab.com/ee/administration/job_traces.html#new-live-trace-architecture)
# In order to avoid interrupting the system, we do not raise an exception here.
archive_error(e, job)
end
private
def failed_archive_counter
@failed_archive_counter ||=
Gitlab::Metrics.counter(:job_trace_archive_failed_total,
"Counter of failed attempts of trace archiving")
end
def archive_error(error, job)
failed_archive_counter.increment
Rails.logger.error "Failed to archive trace. id: #{job.id} message: #{error.message}"
Gitlab::Sentry
.track_exception(error,
issue_url: 'https://gitlab.com/gitlab-org/gitlab-ce/issues/51502',
extra: { job_id: job.id })
end
end
end
......@@ -8,6 +8,7 @@ module Files
transformer = Lfs::FileTransformer.new(project, @branch_name)
actions = actions_after_lfs_transformation(transformer, params[:actions])
actions = transform_move_actions(actions)
commit_actions!(actions)
end
......@@ -26,6 +27,16 @@ module Files
end
end
# When moving a file, `content: nil` means "use the contents of the previous
# file", while `content: ''` means "move the file and set it to empty"
def transform_move_actions(actions)
actions.map do |action|
action[:infer_content] = true if action[:content].nil?
action
end
end
def commit_actions!(actions)
repository.multi_action(
current_user,
......
......@@ -10,7 +10,7 @@
%span.cred (Auditor)
.float-right
- if @user != current_user && @user.can?(:log_in)
- if impersonation_enabled? && @user != current_user && @user.can?(:log_in)
= link_to 'Impersonate', impersonate_admin_user_path(@user), method: :post, class: "btn btn-nr btn-grouped btn-info"
= link_to edit_admin_user_path(@user), class: "btn btn-nr btn-grouped" do
%i.fa.fa-pencil-square-o
......
%li{ class: active_when(params[:id] == wiki_page.slug) }
= link_to project_wiki_path(@project, wiki_page) do
= wiki_page.title.capitalize
= wiki_page.human_title
- @content_class = "limit-container-width" unless fluid_layout
- add_to_breadcrumbs _("Wiki"), project_wiki_path(@project, @page)
- breadcrumb_title @page.persisted? ? _("Edit") : _("New")
- page_title @page.persisted? ? _("Edit") : _("New"), @page.title.capitalize, _("Wiki")
- page_title @page.persisted? ? _("Edit") : _("New"), @page.human_title, _("Wiki")
= wiki_page_errors(@error)
......@@ -12,9 +12,9 @@
.nav-text
%h2.wiki-page-title
- if @page.persisted?
= link_to @page.title.capitalize, project_wiki_path(@project, @page)
= link_to @page.human_title, project_wiki_path(@project, @page)
- else
= @page.title.capitalize
= @page.human_title
%span.light
&middot;
- if @page.persisted?
......@@ -30,7 +30,7 @@
= link_to project_wiki_history_path(@project, @page), class: "btn" do
= s_("Wiki|Page history")
- if can?(current_user, :admin_wiki, @project)
#delete-wiki-modal-wrapper{ data: { delete_wiki_url: project_wiki_path(@project, @page), page_title: @page.title.capitalize } }
#delete-wiki-modal-wrapper{ data: { delete_wiki_url: project_wiki_path(@project, @page), page_title: @page.human_title } }
= render 'form', uploads_path: wiki_attachment_upload_url
......
- page_title _("History"), @page.title.capitalize, _("Wiki")
- page_title _("History"), @page.human_title, _("Wiki")
.wiki-page-header.has-sidebar-toggle
%button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" }
......@@ -6,7 +6,7 @@
.nav-text
%h2.wiki-page-title
= link_to @page.title.capitalize, project_wiki_path(@project, @page)
= link_to @page.human_title, project_wiki_path(@project, @page)
%span.light
&middot;
= _("History")
......
- @content_class = "limit-container-width" unless fluid_layout
- breadcrumb_title @page.title.capitalize
- breadcrumb_title @page.human_title
- wiki_breadcrumb_dropdown_links(@page.slug)
- page_title @page.title.capitalize, _("Wiki")
- page_title @page.human_title, _("Wiki")
- add_to_breadcrumbs _("Wiki"), get_project_wiki_path(@project)
.wiki-page-header.has-sidebar-toggle
......@@ -9,7 +9,7 @@
= icon('angle-double-left')
.nav-text
%h2.wiki-page-title= @page.title.capitalize
%h2.wiki-page-title= @page.human_title
%span.wiki-last-edit-by
- if @page.last_version
= (_("Last edited by %{name}") % { name: "<strong>#{@page.last_version.author_name}</strong>" }).html_safe
......
......@@ -7,7 +7,7 @@ class ArchiveTraceWorker
# rubocop: disable CodeReuse/ActiveRecord
def perform(job_id)
Ci::Build.without_archived_trace.find_by(id: job_id).try do |job|
job.trace.archive!
Ci::ArchiveTraceService.new.execute(job)
end
end
# rubocop: enable CodeReuse/ActiveRecord
......
......@@ -11,21 +11,9 @@ module Ci
# This could happen when ArchiveTraceWorker sidekiq jobs were lost by receiving SIGKILL
# More details in https://gitlab.com/gitlab-org/gitlab-ce/issues/36791
Ci::Build.finished.with_live_trace.find_each(batch_size: 100) do |build|
begin
build.trace.archive!
rescue ::Gitlab::Ci::Trace::AlreadyArchivedError
rescue => e
failed_archive_counter.increment
Rails.logger.error "Failed to archive stale live trace. id: #{build.id} message: #{e.message}"
end
Ci::ArchiveTraceService.new.execute(build)
end
end
# rubocop: enable CodeReuse/ActiveRecord
private
def failed_archive_counter
@failed_archive_counter ||= Gitlab::Metrics.counter(:job_trace_archive_failed_total, "Counter of failed attempts of traces archiving")
end
end
end
---
title: Add config to prohibit impersonation
merge_request: 23338
author:
type: added
---
title: 'Commits API: Preserve file content in move operations if unspecified'
merge_request: 23387
author:
type: fixed
---
title: Remove needless auto-capitalization on Wiki page titles
merge_request: 23288
author:
type: fixed
......@@ -114,6 +114,9 @@ production: &base
# The default is 'shared/cache/archive/' relative to the root of the Rails app.
# repository_downloads_path: shared/cache/archive/
## Impersonation settings
impersonation_enabled: true
## Reply by email
# Allow users to comment on issues and merge requests by replying to notification emails.
# For documentation on how to set this up, see http://doc.gitlab.com/ce/administration/reply_by_email.html
......
......@@ -166,6 +166,7 @@ Settings.gitlab['domain_whitelist'] ||= []
Settings.gitlab['import_sources'] ||= Gitlab::ImportSources.values
Settings.gitlab['trusted_proxies'] ||= []
Settings.gitlab['no_todos_messages'] ||= YAML.load_file(Rails.root.join('config', 'no_todos_messages.yml'))
Settings.gitlab['impersonation_enabled'] ||= true if Settings.gitlab['impersonation_enabled'].nil?
Settings.gitlab['usage_ping_enabled'] = true if Settings.gitlab['usage_ping_enabled'].nil?
#
......
......@@ -232,6 +232,43 @@ For more information, refer to the
Impersonation tokens are used exactly like regular personal access tokens, and can be passed in either the
`private_token` parameter or the `Private-Token` header.
#### Disable impersonation
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/40385) in GitLab
11.6.
By default, impersonation is enabled. To disable impersonation, GitLab must be
reconfigured:
**For Omnibus installations**
1. Edit `/etc/gitlab/gitlab.rb`:
```ruby
gitlab_rails['impersonation_enabled'] = false
```
1. Save the file and [reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure)
GitLab for the changes to take effect.
To re-enable impersonation, remove this configuration and reconfigure GitLab.
---
**For installations from source**
1. Edit `config/gitlab.yml`:
```yaml
gitlab:
impersonation_enabled: false
```
1. Save the file and [restart](../administration/restart_gitlab.md#installations-from-source)
GitLab for the changes to take effect.
To re-enable impersonation, remove this configuration and restart GitLab.
### Sudo
NOTE: **Note:**
......@@ -547,7 +584,7 @@ When you try to access an API URL that does not exist you will receive 404 Not F
```
HTTP/1.1 404 Not Found
Content-Type: application/json
{
{ f
"error": "404 Not Found"
}
```
......
......@@ -87,7 +87,7 @@ POST /projects/:id/repository/commits
| `action` | string | yes | The action to perform, `create`, `delete`, `move`, `update`, `chmod`|
| `file_path` | string | yes | Full path to the file. Ex. `lib/class.rb` |
| `previous_path` | string | no | Original full path to the file being moved. Ex. `lib/class1.rb`. Only considered for `move` action. |
| `content` | string | no | File content, required for all except `delete` and `chmod`. Optional for `move` |
| `content` | string | no | File content, required for all except `delete`, `chmod`, and `move`. Move actions that do not specify `content` will preserve the existing file content, and any other value of `content` will overwrite the file content. |
| `encoding` | string | no | `text` or `base64`. `text` is default. |
| `last_commit_id` | string | no | Last known file commit id. Will be only considered in update, move and delete actions. |
| `execute_filemode` | boolean | no | When `true/false` enables/disables the execute flag on the file. Only considered for `chmod` action. |
......
......@@ -1286,6 +1286,62 @@ If the rebase operation fails, the response will include the following:
}
```
## Rebase a merge request
Automatically rebase the `source_branch` of the merge request against its
`target_branch`.
If you don't have permissions to push to the merge request's source branch -
you'll get a `403 Forbidden` response.
```
PUT /projects/:id/merge_requests/:merge_request_iid/rebase
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `merge_request_iid` | integer | yes | The internal ID of the merge request |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/76/merge_requests/1/rebase
```
This is an asynchronous request. The API will return an empty `202 Accepted`
response if the request is enqueued successfully.
You can poll the [Get single MR](#get-single-mr) endpoint with the
`include_rebase_in_progress` parameter to check the status of the
asynchronous request.
If the rebase operation is ongoing, the response will include the following:
```json
{
"rebase_in_progress": true
"merge_error": null
}
```
Once the rebase operation has completed successfully, the response will include
the following:
```json
{
"rebase_in_progress": false,
"merge_error": null,
}
```
If the rebase operation fails, the response will include the following:
```json
{
"rebase_in_progress": false,
"merge_error": "Rebase failed. Please rebase locally",
}
```
## Comments on merge requests
Comments are done via the [notes](notes.md) resource.
......
......@@ -53,6 +53,8 @@ from teams other than your own.
#### Security requirements
1. If your merge request is processing, storing, or transferring any kind of [RED or ORANGE data][https://docs.google.com/document/d/15eNKGA3zyZazsJMldqTBFbYMnVUSQSpU14lo22JMZQY/edit] (this is a confidential document), it must be
**approved by a [Security Engineer][team]**.
1. If your merge request involves implementing, utilizing, or is otherwise related to any type of authentication, authorization, or session handling mechanism, it must be
**approved by a [Security Engineer][team]**.
1. If your merge request has a goal which requires a cryptographic function such as: confidentiality, integrity, authentication, or non-repudiation, it must be
......@@ -85,6 +87,23 @@ If an author is unsure if a merge request needs a domain expert's opinion, that'
usually a pretty good sign that it does, since without it the required level of
confidence in their solution will not have been reached.
Before the review, the author is requested to submit comments on the merge
request diff alerting the reviewer to anything important as well as for anything
that demands further explanation or attention. Examples of content that may
warrant a comment could be:
- The addition of a linting rule (Rubocop, JS etc)
- The addition of a library (Ruby gem, JS lib etc)
- Where not obvious, a link to the parent class or method
- Any benchmarking performed to complement the change
- Potentially insecure code
Do not add these comments directly to the source code, unless the
reviewer requires you to do so.
This
[saves reviewers time and helps authors catch mistakes earlier](https://www.ibm.com/developerworks/rational/library/11-proven-practices-for-peer-review/index.html#__RefHeading__97_174136755).
### The responsibility of the maintainer
Maintainers are responsible for the overall health, quality, and consistency of
......
# GraphQL
We use [Apollo] and [Vue Apollo][vue-apollo] for working with GraphQL
on the frontend.
In order to use GraphQL, you need to enable the `graphql` feature flag,
read more about [Feature Flags][feature-flags].
## Apollo Client
To save duplicated clients getting created in different apps, we have a
[default client][defualt-client] that should be used. This setups the
Apollo client with the correct URL and also sets the CSRF headers.
## GraphQL Queries
To save query compilation at runtime, webpack can directly import `.graphql`
files. This allows webpack to preprocess the query at compile time instead
of the client doing compilation of queries.
## Usage in Vue
To use Vue Apollo, import the [Vue Apollo][vue-apollo] plugin as well
as the default client. This should be created at the same point
the Vue application is mounted.
```javascript
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import defaultClient from '~/lib/graphql';
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
defaultClient,
});
new Vue({
...,
apolloProvider,
...
});
```
Read more about [Vue Apollo][vue-apollo] in the [Vue Apollo documentation][vue-apollo-docs].
### Testing
With [Vue test utils][vue-test-utils] it is easy to quickly test components that
fetch GraphQL queries. The simplest way is to use `shallowMount` and then set
the data on the component
```javascript
it('tests apollo component', () => {
const vm = shallowMount(App);
vm.setData({
...mock data
});
});
```
## Usage outside of Vue
It is also possible to use GraphQL outside of Vue by directly importing
and using the default client with queries.
```javascript
import defaultClient from '~/lib/graphql';
import query from './query.graphql';
defaultClient.query(query)
.then(result => console.log(result));
```
Read more about the [Apollo] client in the [Apollo documentation][apollo-client-docs].
[Apollo]: https://www.apollographql.com/
[vue-apollo]: https://github.com/Akryum/vue-apollo/
[vue-apollo-docs]: https://akryum.github.io/vue-apollo/
[feature-flags]: ../feature_flags.md
[default-client]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/lib/graphql.js
[apollo-client-docs]: https://www.apollographql.com/docs/tutorial/client.html
[vue-test-utils]: https://vue-test-utils.vuejs.org/
......@@ -54,6 +54,9 @@ Vuex specific design patterns and practices.
## [Axios](axios.md)
Axios specific practices and gotchas.
## [GraphQL](graphql.md)
How to use GraphQL
## [Icons and Illustrations](icons.md)
How we use SVG for our Icons and Illustrations.
......
......@@ -604,6 +604,8 @@ If you fail to restore this encryption key file along with the application data
backup, users with two-factor authentication enabled and GitLab Runners will
lose access to your GitLab server.
You may also want to restore any TLS keys, certificates, or [SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079).
Depending on your case, you might want to run the restore command with one or
more of the following options:
......
......@@ -44,6 +44,5 @@ This page gathers all the resources for the topic **Authentication** within GitL
- [Kanboard Plugin GitLab Authentication](https://github.com/kanboard/plugin-gitlab-auth)
- [Jenkins GitLab OAuth Plugin](https://wiki.jenkins.io/display/JENKINS/GitLab+OAuth+Plugin)
- [Set up Gitlab CE with Active Directory authentication](https://www.caseylabs.com/setup-gitlab-ce-with-active-directory-authentication/)
- [How to customize GitLab to support OpenID authentication](http://eric.van-der-vlist.com/blog/2013/11/23/how-to-customize-gitlab-to-support-openid-authentication/)
- [Openshift - Configuring Authentication and User Agent](https://docs.openshift.org/latest/install_config/configuring_authentication.html#GitLab)
......@@ -239,13 +239,10 @@ by GitLab before installing any of the above applications.
## Getting the external IP address
NOTE: **Note:**
You need a load balancer installed in your cluster in order to obtain the
external IP address with the following procedure. It can be deployed using the
[**Ingress** application](#installing-applications).
NOTE: **Note:**
Knative will include its own load balancer in the form of [Istio](https://istio.io).
At this time, to determine the external IP address, you will need to follow the manual approach.
With the following procedure, a load balancer must be installed in your cluster
to obtain the external IP address. You can use either
[Ingress](#installing-applications), or Knative's own load balancer
([Istio](https://istio.io)) if using [Knative](#installing-applications).
In order to publish your web application, you first need to find the external IP
address associated to your load balancer.
......@@ -254,7 +251,7 @@ address associated to your load balancer.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17052) in GitLab 10.6.
If you installed the Ingress [via the **Applications**](#installing-applications),
If you [installed Ingress or Knative](#installing-applications),
you should see the Ingress IP address on this same page within a few minutes.
If you don't see this, GitLab might not be able to determine the IP address of
your ingress application in which case you should manually determine it.
......
......@@ -95,6 +95,7 @@ module API
Gitlab::Auth::TokenNotFoundError,
Gitlab::Auth::ExpiredError,
Gitlab::Auth::RevokedError,
Gitlab::Auth::ImpersonationDisabled,
Gitlab::Auth::InsufficientScopeError]
base.__send__(:rescue_from, *error_classes, oauth2_bearer_token_error_handler) # rubocop:disable GitlabSecurity/PublicSend
......@@ -122,6 +123,11 @@ module API
:invalid_token,
"Token was revoked. You have to re-authorize from the user.")
when Gitlab::Auth::ImpersonationDisabled
Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(
:invalid_token,
"Token is an impersonation token but impersonation was disabled.")
when Gitlab::Auth::InsufficientScopeError
# FIXME: ForbiddenError (inherited from Bearer::Forbidden of Rack::Oauth2)
# does not include WWW-Authenticate header, which breaks the standard.
......
......@@ -7,6 +7,7 @@ module Gitlab
TokenNotFoundError = Class.new(AuthenticationError)
ExpiredError = Class.new(AuthenticationError)
RevokedError = Class.new(AuthenticationError)
ImpersonationDisabled = Class.new(AuthenticationError)
UnauthorizedError = Class.new(AuthenticationError)
class InsufficientScopeError < AuthenticationError
......@@ -69,6 +70,8 @@ module Gitlab
raise ExpiredError
when AccessTokenValidationService::REVOKED
raise RevokedError
when AccessTokenValidationService::IMPERSONATION_DISABLED
raise ImpersonationDisabled
end
end
......
......@@ -385,7 +385,8 @@ module Gitlab
file_path: encode_binary(action[:file_path]),
previous_path: encode_binary(action[:previous_path]),
base64_content: action[:encoding] == 'base64',
execute_filemode: !!action[:execute_filemode]
execute_filemode: !!action[:execute_filemode],
infer_content: !!action[:infer_content]
)
rescue RangeError
raise ArgumentError, "Unknown action '#{action[:action]}'"
......
......@@ -4479,6 +4479,9 @@ msgstr ""
msgid "ImageDiffViewer|Swipe"
msgstr ""
msgid "Impersonation has been disabled"
msgstr ""
msgid "Import"
msgstr ""
......
......@@ -264,5 +264,17 @@ describe Admin::UsersController do
expect(flash[:alert]).to eq("You are now impersonating #{user.username}")
end
end
context "when impersonation is disabled" do
before do
stub_config_setting(impersonation_enabled: false)
end
it "shows error page" do
post :impersonate, id: user.username
expect(response).to have_gitlab_http_status(404)
end
end
end
end
......@@ -217,7 +217,10 @@ describe Projects::EnvironmentsController do
end
it 'loads the terminals for the environment' do
expect_any_instance_of(Environment).to receive(:terminals)
# In EE we have to stub EE::Environment since it overwrites the
# "terminals" method.
expect_any_instance_of(defined?(EE) ? EE::Environment : Environment)
.to receive(:terminals)
get :terminal, environment_params
end
......@@ -240,7 +243,9 @@ describe Projects::EnvironmentsController do
context 'and valid id' do
it 'returns the first terminal for the environment' do
expect_any_instance_of(Environment)
# In EE we have to stub EE::Environment since it overwrites the
# "terminals" method.
expect_any_instance_of(defined?(EE) ? EE::Environment : Environment)
.to receive(:terminals)
.and_return([:fake_terminal])
......
......@@ -205,77 +205,120 @@ describe "Admin::Users" do
describe 'Impersonation' do
let(:another_user) { create(:user) }
before do
visit admin_user_path(another_user)
end
context 'before impersonating' do
subject { visit admin_user_path(user_to_visit) }
let(:user_to_visit) { another_user }
context 'for other users' do
it 'shows impersonate button for other users' do
subject
expect(page).to have_content('Impersonate')
end
end
context 'for admin itself' do
let(:user_to_visit) { current_user }
it 'does not show impersonate button for admin itself' do
visit admin_user_path(current_user)
subject
expect(page).not_to have_content('Impersonate')
end
end
it 'does not show impersonate button for blocked user' do
context 'for blocked user' do
before do
another_user.block
end
visit admin_user_path(another_user)
it 'does not show impersonate button for blocked user' do
subject
expect(page).not_to have_content('Impersonate')
end
end
another_user.activate
context 'when impersonation is disabled' do
before do
stub_config_setting(impersonation_enabled: false)
end
it 'does not show impersonate button' do
subject
expect(page).not_to have_content('Impersonate')
end
end
end
context 'when impersonating' do
subject { click_link 'Impersonate' }
before do
click_link 'Impersonate'
visit admin_user_path(another_user)
end
it 'logs in as the user when impersonate is clicked' do
subject
expect(page.find(:css, '.header-user .profile-link')['data-user']).to eql(another_user.username)
end
it 'sees impersonation log out icon' do
icon = first('.fa.fa-user-secret')
subject
icon = first('.fa.fa-user-secret')
expect(icon).not_to be nil
end
context 'a user with an expired password' do
before do
another_user.update(password_expires_at: Time.now - 5.minutes)
end
it 'does not redirect to password change page' do
subject
expect(current_path).to eq('/')
end
end
end
context 'ending impersonation' do
subject { find(:css, 'li.impersonation a').click }
before do
visit admin_user_path(another_user)
click_link 'Impersonate'
end
it 'logs out of impersonated user back to original user' do
find(:css, 'li.impersonation a').click
subject
expect(page.find(:css, '.header-user .profile-link')['data-user']).to eq(current_user.username)
end
it 'is redirected back to the impersonated users page in the admin after stopping' do
find(:css, 'li.impersonation a').click
subject
expect(current_path).to eq("/admin/users/#{another_user.username}")
end
end
context 'when impersonating a user with an expired password' do
context 'a user with an expired password' do
before do
another_user.update(password_expires_at: Time.now - 5.minutes)
click_link 'Impersonate'
end
it 'does not redirect to password change page' do
expect(current_path).to eq('/')
end
it 'is redirected back to the impersonated users page in the admin after stopping' do
find(:css, 'li.impersonation a').click
subject
expect(current_path).to eq("/admin/users/#{another_user.username}")
end
end
end
end
describe 'Two-factor Authentication status' do
it 'shows when enabled' do
......
......@@ -165,8 +165,14 @@ describe 'Environment' do
context 'web terminal', :js do
before do
# Stub #terminals as it causes js-enabled feature specs to render the page incorrectly
allow_any_instance_of(Environment).to receive(:terminals) { nil }
# Stub #terminals as it causes js-enabled feature specs to
# render the page incorrectly
#
# In EE we have to stub EE::Environment since it overwrites
# the "terminals" method.
allow_any_instance_of(defined?(EE) ? EE::Environment : Environment)
.to receive(:terminals) { nil }
visit terminal_project_environment_path(project, environment)
end
......
......@@ -85,7 +85,7 @@ describe "User creates wiki page" do
expect(current_path).to eq(project_wiki_path(project, "test"))
page.within(:css, ".nav-text") do
expect(page).to have_content("Test").and have_content("Create Page")
expect(page).to have_content("test").and have_content("Create Page")
end
click_link("Home")
......@@ -97,7 +97,7 @@ describe "User creates wiki page" do
expect(current_path).to eq(project_wiki_path(project, "api"))
page.within(:css, ".nav-text") do
expect(page).to have_content("Create").and have_content("Api")
expect(page).to have_content("Create").and have_content("api")
end
click_link("Home")
......@@ -109,7 +109,7 @@ describe "User creates wiki page" do
expect(current_path).to eq(project_wiki_path(project, "raketasks"))
page.within(:css, ".nav-text") do
expect(page).to have_content("Create").and have_content("Rake")
expect(page).to have_content("Create").and have_content("rake")
end
end
......@@ -200,7 +200,7 @@ describe "User creates wiki page" do
click_button("Create page")
end
expect(page).to have_content("Foo")
expect(page).to have_content("foo")
.and have_content("Last edited by #{user.name}")
.and have_content("My awesome wiki!")
end
......@@ -215,7 +215,7 @@ describe "User creates wiki page" do
end
# Commit message field should have correct value.
expect(page).to have_field("wiki[message]", with: "Create spaces in the name")
expect(page).to have_field("wiki[message]", with: "Create Spaces in the name")
page.within(".wiki-form") do
fill_in(:wiki_content, with: "My awesome wiki!")
......@@ -246,7 +246,7 @@ describe "User creates wiki page" do
click_button("Create page")
end
expect(page).to have_content("Hyphens in the name")
expect(page).to have_content("hyphens in the name")
.and have_content("Last edited by #{user.name}")
.and have_content("My awesome wiki!")
end
......@@ -293,7 +293,7 @@ describe "User creates wiki page" do
click_button("Create page")
end
expect(page).to have_content("Foo")
expect(page).to have_content("foo")
.and have_content("Last edited by #{user.name}")
.and have_content("My awesome wiki!")
end
......@@ -311,7 +311,7 @@ describe "User creates wiki page" do
it 'renders a default sidebar when there is no customized sidebar' do
visit(project_wikis_path(project))
expect(page).to have_content('Another')
expect(page).to have_content('another')
expect(page).to have_content('More Pages')
end
......
......@@ -39,11 +39,11 @@ describe 'User updates wiki page' do
end
expect(current_path).to include('one/two/three-test')
expect(find('.wiki-pages')).to have_content('Three')
expect(find('.wiki-pages')).to have_content('three')
first(:link, text: 'Three').click
first(:link, text: 'three').click
expect(find('.nav-text')).to have_content('Three')
expect(find('.nav-text')).to have_content('three')
click_on('Edit')
......
......@@ -38,7 +38,7 @@ describe 'User views a wiki page' do
it 'shows the history of a page that has a path', :js do
expect(current_path).to include('one/two/three-test')
first(:link, text: 'Three').click
first(:link, text: 'three').click
click_on('Page history')
expect(current_path).to include('one/two/three-test')
......@@ -50,11 +50,11 @@ describe 'User views a wiki page' do
it 'shows an old version of a page', :js do
expect(current_path).to include('one/two/three-test')
expect(find('.wiki-pages')).to have_content('Three')
expect(find('.wiki-pages')).to have_content('three')
first(:link, text: 'Three').click
first(:link, text: 'three').click
expect(find('.nav-text')).to have_content('Three')
expect(find('.nav-text')).to have_content('three')
click_on('Edit')
......
......@@ -279,5 +279,20 @@ describe Gitlab::Auth::UserAuthFinders do
expect { validate_access_token!(scopes: [:sudo]) }.to raise_error(Gitlab::Auth::InsufficientScopeError)
end
end
context 'with impersonation token' do
let(:personal_access_token) { create(:personal_access_token, :impersonation, user: user) }
context 'when impersonation is disabled' do
before do
stub_config_setting(impersonation_enabled: false)
allow_any_instance_of(described_class).to receive(:access_token).and_return(personal_access_token)
end
it 'returns Gitlab::Auth::ImpersonationDisabled' do
expect { validate_access_token! }.to raise_error(Gitlab::Auth::ImpersonationDisabled)
end
end
end
end
end
......@@ -174,7 +174,15 @@ describe RemoteMirror do
end
context 'with remote mirroring enabled' do
it 'defaults to disabling only protected branches' do
expect(remote_mirror.only_protected_branches?).to be_falsey
end
context 'with only protected branches enabled' do
before do
remote_mirror.only_protected_branches = true
end
context 'when it did not update in the last minute' do
it 'schedules a RepositoryUpdateRemoteMirrorWorker to run now' do
expect(RepositoryUpdateRemoteMirrorWorker).to receive(:perform_async).with(remote_mirror.id, Time.now)
......
......@@ -209,6 +209,19 @@ describe API::Helpers do
expect { current_user }.to raise_error Gitlab::Auth::ExpiredError
end
context 'when impersonation is disabled' do
let(:personal_access_token) { create(:personal_access_token, :impersonation, user: user) }
before do
stub_config_setting(impersonation_enabled: false)
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
end
it 'does not allow impersonation tokens' do
expect { current_user }.to raise_error Gitlab::Auth::ImpersonationDisabled
end
end
end
describe "when authenticating using a job token" do
......
require 'spec_helper'
describe Ci::ArchiveTraceService, '#execute' do
subject { described_class.new.execute(job) }
context 'when job is finished' do
let(:job) { create(:ci_build, :success, :trace_live) }
it 'creates an archived trace' do
expect { subject }.not_to raise_error
expect(job.reload.job_artifacts_trace).to be_exist
end
context 'when trace is already archived' do
let!(:job) { create(:ci_build, :success, :trace_artifact) }
it 'ignores an exception' do
expect { subject }.not_to raise_error
end
it 'does not create an archived trace' do
expect { subject }.not_to change { Ci::JobArtifact.trace.count }
end
end
end
context 'when job is running' do
let(:job) { create(:ci_build, :running, :trace_live) }
it 'increments Prometheus counter, sends crash report to Sentry and ignore an error for continuing to archive' do
expect(Gitlab::Sentry)
.to receive(:track_exception)
.with(::Gitlab::Ci::Trace::ArchiveError,
issue_url: 'https://gitlab.com/gitlab-org/gitlab-ce/issues/51502',
extra: { job_id: job.id } ).once
expect(Rails.logger)
.to receive(:error)
.with("Failed to archive trace. id: #{job.id} message: Job is not finished yet")
.and_call_original
expect(Gitlab::Metrics)
.to receive(:counter)
.with(:job_trace_archive_failed_total, "Counter of failed attempts of trace archiving")
.and_call_original
expect { subject }.not_to raise_error
end
end
end
......@@ -122,26 +122,47 @@ describe Files::MultiService do
let(:action) { 'move' }
let(:new_file_path) { 'files/ruby/new_popen.rb' }
let(:result) { subject.execute }
let(:blob) { repository.blob_at_branch(branch_name, new_file_path) }
context 'when original file has been updated' do
before do
update_file(original_file_path)
end
it 'rejects the commit' do
results = subject.execute
expect(results[:status]).to eq(:error)
expect(results[:message]).to match(original_file_path)
expect(result[:status]).to eq(:error)
expect(result[:message]).to match(original_file_path)
end
end
context 'when original file have not been updated' do
context 'when original file has not been updated' do
it 'moves the file' do
results = subject.execute
blob = project.repository.blob_at_branch(branch_name, new_file_path)
expect(result[:status]).to eq(:success)
expect(blob).to be_present
expect(blob.data).to eq(file_content)
end
expect(results[:status]).to eq(:success)
context 'when content is nil' do
let(:file_content) { nil }
it 'moves the existing content untouched' do
original_content = repository.blob_at_branch(branch_name, original_file_path).data
expect(result[:status]).to eq(:success)
expect(blob).to be_present
expect(blob.data).to eq(original_content)
end
end
context 'when content is an empty string' do
let(:file_content) { '' }
it 'moves the file and empties it' do
expect(result[:status]).to eq(:success)
expect(blob).not_to be_nil
expect(blob.data).to eq('')
end
end
end
end
......
......@@ -5,10 +5,11 @@ describe ArchiveTraceWorker do
subject { described_class.new.perform(job&.id) }
context 'when job is found' do
let(:job) { create(:ci_build) }
let(:job) { create(:ci_build, :trace_live) }
it 'executes service' do
expect_any_instance_of(Gitlab::Ci::Trace).to receive(:archive!)
expect_any_instance_of(Ci::ArchiveTraceService)
.to receive(:execute).with(job)
subject
end
......@@ -18,7 +19,8 @@ describe ArchiveTraceWorker do
let(:job) { nil }
it 'does not execute service' do
expect_any_instance_of(Gitlab::Ci::Trace).not_to receive(:archive!)
expect_any_instance_of(Ci::ArchiveTraceService)
.not_to receive(:execute)
subject
end
......
......@@ -30,6 +30,13 @@ describe Ci::ArchiveTracesCronWorker do
it_behaves_like 'archives trace'
it 'executes service' do
expect_any_instance_of(Ci::ArchiveTraceService)
.to receive(:execute).with(build)
subject
end
context 'when a trace had already been archived' do
let!(:build) { create(:ci_build, :success, :trace_live, :trace_artifact) }
let!(:build2) { create(:ci_build, :success, :trace_live) }
......@@ -46,11 +53,12 @@ describe Ci::ArchiveTracesCronWorker do
let!(:build) { create(:ci_build, :success, :trace_live) }
before do
allow(Gitlab::Sentry).to receive(:track_exception)
allow_any_instance_of(Gitlab::Ci::Trace).to receive(:archive!).and_raise('Unexpected error')
end
it 'puts a log' do
expect(Rails.logger).to receive(:error).with("Failed to archive stale live trace. id: #{build.id} message: Unexpected error")
expect(Rails.logger).to receive(:error).with("Failed to archive trace. id: #{build.id} message: Unexpected error")
subject
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment