Commit a7fec177 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge commit '68526805' into fix/gb/encrypt-runners-tokens

* commit '68526805': (57 commits)
parents 439d22b9 68526805
...@@ -2,6 +2,29 @@ ...@@ -2,6 +2,29 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 11.5.1 (2018-11-26)
### 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.
- Do not follow redirects in Prometheus service when making http requests to the configured api url.
- Don't expose confidential information in commit message list.
- Provide email notification when a user changes their email address.
- Restrict Personal Access Tokens to API scope on web requests.
- Resolve reflected XSS in Ouath authorize window.
- Fix SSRF in project integrations.
- Fixed ability to comment on locked/confidential issues.
- Fixed ability of guest users to edit/delete comments on locked or confidential issues.
- Fix milestone promotion authorization check.
- Configure mermaid to not render HTML content in diagrams.
- Fix a possible symlink time of check to time of use race condition in GitLab Pages.
- Removed ability to see private group names when the group id is entered in the url.
- Fix stored XSS for Environments.
## 11.5.0 (2018-11-22) ## 11.5.0 (2018-11-22)
### Security (10 changes, 1 of them is from the community) ### Security (10 changes, 1 of them is from the community)
...@@ -264,6 +287,36 @@ entry. ...@@ -264,6 +287,36 @@ entry.
- Disables stop environment button while the deploy is in progress. - Disables stop environment button while the deploy is in progress.
## 11.4.8 (2018-11-27)
### Security (24 changes)
- Escape entity title while autocomplete template rendering to prevent XSS. !2571
- Resolve reflected XSS in Ouath authorize window.
- 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.
- Do not follow redirects in Prometheus service when making http requests to the configured api url.
- Persist only SHA digest of PersonalAccessToken#token.
- Don't expose confidential information in commit message list.
- Provide email notification when a user changes their email address.
- Restrict Personal Access Tokens to API scope on web requests.
- Redact personal tokens in unsubscribe links.
- Fix SSRF in project integrations.
- Fixed ability to comment on locked/confidential issues.
- Fixed ability of guest users to edit/delete comments on locked or confidential issues.
- Fix milestone promotion authorization check.
- Monkey kubeclient to not follow any redirects.
- Configure mermaid to not render HTML content in diagrams.
- Fix a possible symlink time of check to time of use race condition in GitLab Pages.
- Removed ability to see private group names when the group id is entered in the url.
- Fix stored XSS for Environments.
- Prevent SSRF attacks in HipChat integration.
- Validate Wiki attachments are valid temporary files.
## 11.4.7 (2018-11-20) ## 11.4.7 (2018-11-20)
- No changes. - No changes.
...@@ -544,6 +597,45 @@ entry. ...@@ -544,6 +597,45 @@ entry.
- Check frozen string in style builds. (gfyoung) - Check frozen string in style builds. (gfyoung)
## 11.3.11 (2018-11-26)
### Security (33 changes)
- Filter user sensitive data from discussions JSON. !2537
- Escape entity title while autocomplete template rendering to prevent XSS. !2557
- 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.
- Set timeout for syntax highlighting.
- Do not follow redirects in Prometheus service when making http requests to the configured api url.
- Persist only SHA digest of PersonalAccessToken#token.
- Sanitize JSON data properly to fix XSS on Issue details page.
- Don't expose confidential information in commit message list.
- 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.
- 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.
- Fixed ability of guest users to edit/delete comments on locked or confidential issues.
- Fix milestone promotion authorization check.
- Monkey kubeclient to not follow any redirects.
- Configure mermaid to not render HTML content in diagrams.
- Redact confidential events in the API.
- Fix xss vulnerability sourced from package.json.
- Fix a possible symlink time of check to time of use race condition in GitLab Pages.
- Removed ability to see private group names when the group id is entered in the url.
- Fix stored XSS for Environments.
- Block loopback addresses in UrlBlocker.
- Prevent SSRF attacks in HipChat integration.
- Validate Wiki attachments are valid temporary files.
## 11.3.10 (2018-11-18) ## 11.3.10 (2018-11-18)
### Security (1 change) ### Security (1 change)
......
...@@ -7,6 +7,11 @@ gem_versions = {} ...@@ -7,6 +7,11 @@ gem_versions = {}
gem_versions['activerecord_sane_schema_dumper'] = rails5? ? '1.0' : '0.2' gem_versions['activerecord_sane_schema_dumper'] = rails5? ? '1.0' : '0.2'
gem_versions['rails'] = rails5? ? '5.0.7' : '4.2.10' gem_versions['rails'] = rails5? ? '5.0.7' : '4.2.10'
gem_versions['rails-i18n'] = rails5? ? '~> 5.1' : '~> 4.0.9' gem_versions['rails-i18n'] = rails5? ? '~> 5.1' : '~> 4.0.9'
# The 2.0.6 version of rack requires monkeypatch to be present in
# `config.ru`. This can be removed once a new update for Rack
# is available that contains https://github.com/rack/rack/pull/1201.
gem_versions['rack'] = rails5? ? '2.0.6' : '1.6.11'
# --- The end of special code for migrating to Rails 5.0 --- # --- The end of special code for migrating to Rails 5.0 ---
source 'https://rubygems.org' source 'https://rubygems.org'
...@@ -154,6 +159,8 @@ gem 'icalendar' ...@@ -154,6 +159,8 @@ gem 'icalendar'
gem 'diffy', '~> 3.1.0' gem 'diffy', '~> 3.1.0'
# Application server # Application server
gem 'rack', gem_versions['rack']
group :unicorn do group :unicorn do
gem 'unicorn', '~> 5.1.0' gem 'unicorn', '~> 5.1.0'
gem 'unicorn-worker-killer', '~> 0.4.4' gem 'unicorn-worker-killer', '~> 0.4.4'
......
...@@ -1088,6 +1088,7 @@ DEPENDENCIES ...@@ -1088,6 +1088,7 @@ DEPENDENCIES
pry-rails (~> 0.3.4) pry-rails (~> 0.3.4)
puma (~> 3.12) puma (~> 3.12)
puma_worker_killer puma_worker_killer
rack (= 2.0.6)
rack-attack (~> 4.4.1) rack-attack (~> 4.4.1)
rack-cors (~> 1.0.0) rack-cors (~> 1.0.0)
rack-oauth2 (~> 1.2.1) rack-oauth2 (~> 1.2.1)
......
...@@ -1079,6 +1079,7 @@ DEPENDENCIES ...@@ -1079,6 +1079,7 @@ DEPENDENCIES
pry-rails (~> 0.3.4) pry-rails (~> 0.3.4)
puma (~> 3.12) puma (~> 3.12)
puma_worker_killer puma_worker_killer
rack (= 1.6.11)
rack-attack (~> 4.4.1) rack-attack (~> 4.4.1)
rack-cors (~> 1.0.0) rack-cors (~> 1.0.0)
rack-oauth2 (~> 1.2.1) rack-oauth2 (~> 1.2.1)
......
...@@ -10,10 +10,10 @@ const Api = { ...@@ -10,10 +10,10 @@ const Api = {
projectsPath: '/api/:version/projects.json', projectsPath: '/api/:version/projects.json',
projectPath: '/api/:version/projects/:id', projectPath: '/api/:version/projects/:id',
projectLabelsPath: '/:namespace_path/:project_path/labels', projectLabelsPath: '/:namespace_path/:project_path/labels',
mergeRequestPath: '/api/:version/projects/:id/merge_requests/:mrid', projectMergeRequestPath: '/api/:version/projects/:id/merge_requests/:mrid',
projectMergeRequestChangesPath: '/api/:version/projects/:id/merge_requests/:mrid/changes',
projectMergeRequestVersionsPath: '/api/:version/projects/:id/merge_requests/:mrid/versions',
mergeRequestsPath: '/api/:version/merge_requests', mergeRequestsPath: '/api/:version/merge_requests',
mergeRequestChangesPath: '/api/:version/projects/:id/merge_requests/:mrid/changes',
mergeRequestVersionsPath: '/api/:version/projects/:id/merge_requests/:mrid/versions',
groupLabelsPath: '/groups/:namespace_path/-/labels', groupLabelsPath: '/groups/:namespace_path/-/labels',
issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key', issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key',
projectTemplatePath: '/api/:version/projects/:id/templates/:type/:key', projectTemplatePath: '/api/:version/projects/:id/templates/:type/:key',
...@@ -99,36 +99,36 @@ const Api = { ...@@ -99,36 +99,36 @@ const Api = {
}, },
// Return Merge Request for project // Return Merge Request for project
mergeRequest(projectPath, mergeRequestId, params = {}) { projectMergeRequest(projectPath, mergeRequestId, params = {}) {
const url = Api.buildUrl(Api.mergeRequestPath) const url = Api.buildUrl(Api.projectMergeRequestPath)
.replace(':id', encodeURIComponent(projectPath)) .replace(':id', encodeURIComponent(projectPath))
.replace(':mrid', mergeRequestId); .replace(':mrid', mergeRequestId);
return axios.get(url, { params }); return axios.get(url, { params });
}, },
mergeRequests(params = {}) { projectMergeRequestChanges(projectPath, mergeRequestId) {
const url = Api.buildUrl(Api.mergeRequestsPath); const url = Api.buildUrl(Api.projectMergeRequestChangesPath)
return axios.get(url, { params });
},
mergeRequestChanges(projectPath, mergeRequestId) {
const url = Api.buildUrl(Api.mergeRequestChangesPath)
.replace(':id', encodeURIComponent(projectPath)) .replace(':id', encodeURIComponent(projectPath))
.replace(':mrid', mergeRequestId); .replace(':mrid', mergeRequestId);
return axios.get(url); return axios.get(url);
}, },
mergeRequestVersions(projectPath, mergeRequestId) { projectMergeRequestVersions(projectPath, mergeRequestId) {
const url = Api.buildUrl(Api.mergeRequestVersionsPath) const url = Api.buildUrl(Api.projectMergeRequestVersionsPath)
.replace(':id', encodeURIComponent(projectPath)) .replace(':id', encodeURIComponent(projectPath))
.replace(':mrid', mergeRequestId); .replace(':mrid', mergeRequestId);
return axios.get(url); return axios.get(url);
}, },
mergeRequests(params = {}) {
const url = Api.buildUrl(Api.mergeRequestsPath);
return axios.get(url, { params });
},
newLabel(namespacePath, projectPath, data, callback) { newLabel(namespacePath, projectPath, data, callback) {
let url; let url;
......
...@@ -26,6 +26,9 @@ export default function renderMermaid($els) { ...@@ -26,6 +26,9 @@ export default function renderMermaid($els) {
}, },
// mermaidAPI options // mermaidAPI options
theme: 'neutral', theme: 'neutral',
flowchart: {
htmlLabels: false,
},
}); });
$els.each((i, el) => { $els.each((i, el) => {
......
...@@ -41,13 +41,13 @@ export default { ...@@ -41,13 +41,13 @@ export default {
return Api.project(`${namespace}/${project}`); return Api.project(`${namespace}/${project}`);
}, },
getProjectMergeRequestData(projectId, mergeRequestId, params = {}) { getProjectMergeRequestData(projectId, mergeRequestId, params = {}) {
return Api.mergeRequest(projectId, mergeRequestId, params); return Api.projectMergeRequest(projectId, mergeRequestId, params);
}, },
getProjectMergeRequestChanges(projectId, mergeRequestId) { getProjectMergeRequestChanges(projectId, mergeRequestId) {
return Api.mergeRequestChanges(projectId, mergeRequestId); return Api.projectMergeRequestChanges(projectId, mergeRequestId);
}, },
getProjectMergeRequestVersions(projectId, mergeRequestId) { getProjectMergeRequestVersions(projectId, mergeRequestId) {
return Api.mergeRequestVersions(projectId, mergeRequestId); return Api.projectMergeRequestVersions(projectId, mergeRequestId);
}, },
getBranchData(projectId, currentBranchId) { getBranchData(projectId, currentBranchId) {
return Api.branchSingle(projectId, currentBranchId); return Api.branchSingle(projectId, currentBranchId);
......
...@@ -23,13 +23,19 @@ export const receiveMergeRequestsError = ({ commit, dispatch }, { type, search } ...@@ -23,13 +23,19 @@ export const receiveMergeRequestsError = ({ commit, dispatch }, { type, search }
export const receiveMergeRequestsSuccess = ({ commit }, data) => export const receiveMergeRequestsSuccess = ({ commit }, data) =>
commit(types.RECEIVE_MERGE_REQUESTS_SUCCESS, data); commit(types.RECEIVE_MERGE_REQUESTS_SUCCESS, data);
export const fetchMergeRequests = ({ dispatch, state: { state } }, { type, search = '' }) => { export const fetchMergeRequests = (
{ dispatch, state: { state }, rootState: { currentProjectId } },
{ type, search = '' },
) => {
dispatch('requestMergeRequests'); dispatch('requestMergeRequests');
dispatch('resetMergeRequests'); dispatch('resetMergeRequests');
const scope = type ? scopes[type] : 'all'; const scope = type && scopes[type];
const request = scope
? Api.mergeRequests({ scope, state, search })
: Api.projectMergeRequest(currentProjectId, '', { state, search });
return Api.mergeRequests({ scope, state, search }) return request
.then(({ data }) => dispatch('receiveMergeRequestsSuccess', data)) .then(({ data }) => dispatch('receiveMergeRequestsSuccess', data))
.catch(() => dispatch('receiveMergeRequestsError', { type, search })); .catch(() => dispatch('receiveMergeRequestsError', { type, search }));
}; };
......
...@@ -128,7 +128,7 @@ export default { ...@@ -128,7 +128,7 @@ export default {
}; };
</script> </script>
<template> <template>
<div class="prepend-top-default js-environment-container"> <div class="prepend-top-default append-bottom-default js-environment-container">
<div class="environment-information"> <div class="environment-information">
<ci-icon :status="iconStatus" /> <ci-icon :status="iconStatus" />
<p class="inline append-bottom-0" v-html="environment"></p> <p class="inline append-bottom-0" v-html="environment"></p>
......
...@@ -28,20 +28,22 @@ export default { ...@@ -28,20 +28,22 @@ export default {
<div class="bs-callout bs-callout-warning"> <div class="bs-callout bs-callout-warning">
<p v-if="tags.length" class="js-stuck-with-tags append-bottom-0"> <p v-if="tags.length" class="js-stuck-with-tags append-bottom-0">
{{ {{
s__(`This job is stuck, because you don't have s__(`This job is stuck because you don't have
any active runners online with any of these tags assigned to them:`) any active runners online with any of these tags assigned to them:`)
}} }}
<span v-for="(tag, index) in tags" :key="index" class="badge badge-primary"> {{ tag }} </span> <span v-for="(tag, index) in tags" :key="index" class="badge badge-primary append-right-4">
{{ tag }}
</span>
</p> </p>
<p v-else-if="hasNoRunnersForProject" class="js-stuck-no-runners append-bottom-0"> <p v-else-if="hasNoRunnersForProject" class="js-stuck-no-runners append-bottom-0">
{{ {{
s__(`Job|This job is stuck, because the project s__(`Job|This job is stuck because the project
doesn't have any runners online assigned to it.`) doesn't have any runners online assigned to it.`)
}} }}
</p> </p>
<p v-else class="js-stuck-no-active-runner append-bottom-0"> <p v-else class="js-stuck-no-active-runner append-bottom-0">
{{ {{
s__(`This job is stuck, because you don't s__(`This job is stuck because you don't
have any active runners that can run this job.`) have any active runners that can run this job.`)
}} }}
</p> </p>
......
...@@ -12,11 +12,11 @@ class ApplicationController < ActionController::Base ...@@ -12,11 +12,11 @@ class ApplicationController < ActionController::Base
include WorkhorseHelper include WorkhorseHelper
include EnforcesTwoFactorAuthentication include EnforcesTwoFactorAuthentication
include WithPerformanceBar include WithPerformanceBar
include SessionlessAuthentication
# this can be removed after switching to rails 5 # this can be removed after switching to rails 5
# https://gitlab.com/gitlab-org/gitlab-ce/issues/51908 # https://gitlab.com/gitlab-org/gitlab-ce/issues/51908
include InvalidUTF8ErrorHandler unless Gitlab.rails5? include InvalidUTF8ErrorHandler unless Gitlab.rails5?
before_action :authenticate_sessionless_user!
before_action :authenticate_user! before_action :authenticate_user!
before_action :enforce_terms!, if: :should_enforce_terms? before_action :enforce_terms!, if: :should_enforce_terms?
before_action :validate_user_service_ticket! before_action :validate_user_service_ticket!
...@@ -153,13 +153,6 @@ class ApplicationController < ActionController::Base ...@@ -153,13 +153,6 @@ class ApplicationController < ActionController::Base
end end
end end
# This filter handles personal access tokens, and atom requests with rss tokens
def authenticate_sessionless_user!
user = Gitlab::Auth::RequestAuthenticator.new(request).find_sessionless_user
sessionless_sign_in(user) if user
end
def log_exception(exception) def log_exception(exception)
Raven.capture_exception(exception) if sentry_enabled? Raven.capture_exception(exception) if sentry_enabled?
...@@ -426,25 +419,11 @@ class ApplicationController < ActionController::Base ...@@ -426,25 +419,11 @@ class ApplicationController < ActionController::Base
Gitlab::I18n.with_user_locale(current_user, &block) Gitlab::I18n.with_user_locale(current_user, &block)
end end
def sessionless_sign_in(user)
if user && can?(user, :log_in)
# Notice we are passing store false, so the user is not
# actually stored in the session and a token is needed
# for every request. If you want the token to work as a
# sign in token, you can simply remove store: false.
sign_in(user, store: false, message: :sessionless_sign_in)
end
end
def set_page_title_header def set_page_title_header
# Per https://tools.ietf.org/html/rfc5987, headers need to be ISO-8859-1, not UTF-8 # Per https://tools.ietf.org/html/rfc5987, headers need to be ISO-8859-1, not UTF-8
response.headers['Page-Title'] = URI.escape(page_title('GitLab')) response.headers['Page-Title'] = URI.escape(page_title('GitLab'))
end end
def sessionless_user?
current_user && !session.keys.include?('warden.user.user.key')
end
def peek_request? def peek_request?
request.path.start_with?('/-/peek') request.path.start_with?('/-/peek')
end end
......
...@@ -6,6 +6,7 @@ module NotesActions ...@@ -6,6 +6,7 @@ module NotesActions
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
prepend_before_action :normalize_create_params, only: [:create]
before_action :set_polling_interval_header, only: [:index] before_action :set_polling_interval_header, only: [:index]
before_action :require_noteable!, only: [:index, :create] before_action :require_noteable!, only: [:index, :create]
before_action :authorize_admin_note!, only: [:update, :destroy] before_action :authorize_admin_note!, only: [:update, :destroy]
...@@ -247,6 +248,15 @@ module NotesActions ...@@ -247,6 +248,15 @@ module NotesActions
DiscussionSerializer.new(project: project, noteable: noteable, current_user: current_user, note_entity: ProjectNoteEntity) DiscussionSerializer.new(project: project, noteable: noteable, current_user: current_user, note_entity: ProjectNoteEntity)
end end
# Avoids checking permissions in the wrong object - this ensures that the object we checked permissions for
# is the object we're actually creating a note in.
def normalize_create_params
params[:note].try do |note|
note[:noteable_id] = params[:target_id]
note[:noteable_type] = params[:target_type].classify
end
end
def note_project def note_project
strong_memoize(:note_project) do strong_memoize(:note_project) do
next nil unless project next nil unless project
......
# frozen_string_literal: true
# == SessionlessAuthentication
#
# Controller concern to handle PAT and RSS token authentication methods
#
module SessionlessAuthentication
# This filter handles personal access tokens, and atom requests with rss tokens
def authenticate_sessionless_user!(request_format)
user = Gitlab::Auth::RequestAuthenticator.new(request).find_sessionless_user(request_format)
sessionless_sign_in(user) if user
end
def sessionless_user?
current_user && !session.keys.include?('warden.user.user.key')
end
def sessionless_sign_in(user)
if user && can?(user, :log_in)
# Notice we are passing store false, so the user is not
# actually stored in the session and a token is needed
# for every request. If you want the token to work as a
# sign in token, you can simply remove store: false.
sign_in(user, store: false, message: :sessionless_sign_in)
end
end
end
...@@ -4,6 +4,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController ...@@ -4,6 +4,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
include ParamsBackwardCompatibility include ParamsBackwardCompatibility
include RendersMemberAccess include RendersMemberAccess
prepend_before_action(only: [:index]) { authenticate_sessionless_user!(:rss) }
before_action :set_non_archived_param before_action :set_non_archived_param
before_action :default_sorting before_action :default_sorting
skip_cross_project_access_check :index, :starred skip_cross_project_access_check :index, :starred
......
...@@ -4,6 +4,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -4,6 +4,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
include ActionView::Helpers::NumberHelper include ActionView::Helpers::NumberHelper
before_action :authorize_read_project!, only: :index before_action :authorize_read_project!, only: :index
before_action :authorize_read_group!, only: :index
before_action :find_todos, only: [:index, :destroy_all] before_action :find_todos, only: [:index, :destroy_all]
def index def index
...@@ -60,6 +61,15 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -60,6 +61,15 @@ class Dashboard::TodosController < Dashboard::ApplicationController
end end
end end
def authorize_read_group!
group_id = params[:group_id]
if group_id.present?
group = Group.find(group_id)
render_404 unless can?(current_user, :read_group, group)
end
end
def find_todos def find_todos
@todos ||= TodosFinder.new(current_user, todo_params).execute @todos ||= TodosFinder.new(current_user, todo_params).execute
end end
......
...@@ -4,6 +4,9 @@ class DashboardController < Dashboard::ApplicationController ...@@ -4,6 +4,9 @@ class DashboardController < Dashboard::ApplicationController
include IssuesAction include IssuesAction
include MergeRequestsAction include MergeRequestsAction
prepend_before_action(only: [:issues]) { authenticate_sessionless_user!(:rss) }
prepend_before_action(only: [:issues_calendar]) { authenticate_sessionless_user!(:ics) }
before_action :event_filter, only: :activity before_action :event_filter, only: :activity
before_action :projects, only: [:issues, :merge_requests] before_action :projects, only: [:issues, :merge_requests]
before_action :set_show_full_reference, only: [:issues, :merge_requests] before_action :set_show_full_reference, only: [:issues, :merge_requests]
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
class GraphqlController < ApplicationController class GraphqlController < ApplicationController
# Unauthenticated users have access to the API for public data # Unauthenticated users have access to the API for public data
skip_before_action :authenticate_user! skip_before_action :authenticate_user!
prepend_before_action(only: [:execute]) { authenticate_sessionless_user!(:api) }
before_action :check_graphql_feature_flag! before_action :check_graphql_feature_flag!
......
...@@ -9,6 +9,9 @@ class GroupsController < Groups::ApplicationController ...@@ -9,6 +9,9 @@ class GroupsController < Groups::ApplicationController
respond_to :html respond_to :html
prepend_before_action(only: [:show, :issues]) { authenticate_sessionless_user!(:rss) }
prepend_before_action(only: [:issues_calendar]) { authenticate_sessionless_user!(:ics) }
before_action :authenticate_user!, only: [:new, :create] before_action :authenticate_user!, only: [:new, :create]
before_action :group, except: [:index, :new, :create] before_action :group, except: [:index, :new, :create]
......
...@@ -9,7 +9,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController ...@@ -9,7 +9,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
before_action :verify_user_oauth_applications_enabled, except: :index before_action :verify_user_oauth_applications_enabled, except: :index
before_action :authenticate_user! before_action :authenticate_user!
before_action :add_gon_variables before_action :add_gon_variables
before_action :load_scopes, only: [:index, :create, :edit] before_action :load_scopes, only: [:index, :create, :edit, :update]
helper_method :can? helper_method :can?
......
...@@ -6,6 +6,7 @@ class Projects::CommitsController < Projects::ApplicationController ...@@ -6,6 +6,7 @@ class Projects::CommitsController < Projects::ApplicationController
include ExtractsPath include ExtractsPath
include RendersCommits include RendersCommits
prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) }
before_action :whitelist_query_limiting, except: :commits_root before_action :whitelist_query_limiting, except: :commits_root
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :assign_ref_vars, except: :commits_root before_action :assign_ref_vars, except: :commits_root
......
...@@ -9,10 +9,6 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -9,10 +9,6 @@ class Projects::IssuesController < Projects::ApplicationController
include IssuesCalendar include IssuesCalendar
include SpammableActions include SpammableActions
def self.authenticate_user_only_actions
%i[new]
end
def self.issue_except_actions def self.issue_except_actions
%i[index calendar new create bulk_update] %i[index calendar new create bulk_update]
end end
...@@ -21,7 +17,10 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -21,7 +17,10 @@ class Projects::IssuesController < Projects::ApplicationController
%i[index calendar] %i[index calendar]
end end
prepend_before_action :authenticate_user!, only: authenticate_user_only_actions prepend_before_action(only: [:index]) { authenticate_sessionless_user!(:rss) }
prepend_before_action(only: [:calendar]) { authenticate_sessionless_user!(:ics) }
prepend_before_action :authenticate_new_issue!, only: [:new]
prepend_before_action :store_uri, only: [:new, :show]
before_action :whitelist_query_limiting, only: [:create, :create_merge_request, :move, :bulk_update] before_action :whitelist_query_limiting, only: [:create, :create_merge_request, :move, :bulk_update]
before_action :check_issues_available! before_action :check_issues_available!
...@@ -232,16 +231,18 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -232,16 +231,18 @@ class Projects::IssuesController < Projects::ApplicationController
] + [{ label_ids: [], assignee_ids: [] }] ] + [{ label_ids: [], assignee_ids: [] }]
end end
def authenticate_user! def authenticate_new_issue!
return if current_user return if current_user
notice = "Please sign in to create the new issue." notice = "Please sign in to create the new issue."
redirect_to new_user_session_path, notice: notice
end
def store_uri
if request.get? && !request.xhr? if request.get? && !request.xhr?
store_location_for :user, request.fullpath store_location_for :user, request.fullpath
end end
redirect_to new_user_session_path, notice: notice
end end
def serializer def serializer
......
...@@ -11,7 +11,10 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -11,7 +11,10 @@ class Projects::MilestonesController < Projects::ApplicationController
before_action :authorize_read_milestone! before_action :authorize_read_milestone!
# Allow admin milestone # Allow admin milestone
before_action :authorize_admin_milestone!, except: [:index, :show, :merge_requests, :participants, :labels, :promote] before_action :authorize_admin_milestone!, except: [:index, :show, :merge_requests, :participants, :labels]
# Allow to promote milestone
before_action :authorize_promote_milestone!, only: :promote
respond_to :html respond_to :html
...@@ -78,7 +81,7 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -78,7 +81,7 @@ class Projects::MilestonesController < Projects::ApplicationController
def promote def promote
promoted_milestone = Milestones::PromoteService.new(project, current_user).execute(milestone) promoted_milestone = Milestones::PromoteService.new(project, current_user).execute(milestone)
flash[:notice] = flash_notice_for(promoted_milestone, project.group) flash[:notice] = flash_notice_for(promoted_milestone, project_group)
respond_to do |format| respond_to do |format|
format.html do format.html do
...@@ -109,6 +112,12 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -109,6 +112,12 @@ class Projects::MilestonesController < Projects::ApplicationController
protected protected
def project_group
strong_memoize(:project_group) do
project.group
end
end
def milestones def milestones
strong_memoize(:milestones) do strong_memoize(:milestones) do
MilestonesFinder.new(search_params).execute MilestonesFinder.new(search_params).execute
...@@ -125,13 +134,17 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -125,13 +134,17 @@ class Projects::MilestonesController < Projects::ApplicationController
return render_404 unless can?(current_user, :admin_milestone, @project) return render_404 unless can?(current_user, :admin_milestone, @project)
end end
def authorize_promote_milestone!
return render_404 unless can?(current_user, :admin_milestone, project_group)
end
def milestone_params def milestone_params
params.require(:milestone).permit(:title, :description, :start_date, :due_date, :state_event) params.require(:milestone).permit(:title, :description, :start_date, :due_date, :state_event)
end end
def search_params def search_params
if request.format.json? && @project.group && can?(current_user, :read_group, @project.group) if request.format.json? && project_group && can?(current_user, :read_group, project_group)
groups = @project.group.self_and_ancestors_ids groups = project_group.self_and_ancestors_ids
end end
params.permit(:state).merge(project_ids: @project.id, group_ids: groups) params.permit(:state).merge(project_ids: @project.id, group_ids: groups)
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
class Projects::TagsController < Projects::ApplicationController class Projects::TagsController < Projects::ApplicationController
include SortingHelper include SortingHelper
prepend_before_action(only: [:index]) { authenticate_sessionless_user!(:rss) }
# Authorize # Authorize
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :authorize_download_code! before_action :authorize_download_code!
......
...@@ -7,6 +7,8 @@ class ProjectsController < Projects::ApplicationController ...@@ -7,6 +7,8 @@ class ProjectsController < Projects::ApplicationController
include PreviewMarkdown include PreviewMarkdown
include SendFileUpload include SendFileUpload
prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) }
before_action :whitelist_query_limiting, only: [:create] before_action :whitelist_query_limiting, only: [:create]
before_action :authenticate_user!, except: [:index, :show, :activity, :refs] before_action :authenticate_user!, except: [:index, :show, :activity, :refs]
before_action :redirect_git_extension, only: [:show] before_action :redirect_git_extension, only: [:show]
......
...@@ -14,6 +14,7 @@ class UsersController < ApplicationController ...@@ -14,6 +14,7 @@ class UsersController < ApplicationController
calendar_activities: true calendar_activities: true
skip_before_action :authenticate_user! skip_before_action :authenticate_user!
prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) }
before_action :user, except: [:exists] before_action :user, except: [:exists]
before_action :authorize_read_user_profile!, before_action :authorize_read_user_profile!,
only: [:calendar, :calendar_activities, :groups, :projects, :contributed_projects, :snippets] only: [:calendar, :calendar_activities, :groups, :projects, :contributed_projects, :snippets]
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
module MilestonesHelper module MilestonesHelper
include EntityDateHelper include EntityDateHelper
include Gitlab::Utils::StrongMemoize
def milestones_filter_path(opts = {}) def milestones_filter_path(opts = {})
if @project if @project
...@@ -243,4 +244,16 @@ module MilestonesHelper ...@@ -243,4 +244,16 @@ module MilestonesHelper
dashboard_milestone_path(milestone.safe_title, title: milestone.title) dashboard_milestone_path(milestone.safe_title, title: milestone.title)
end end
end end
def can_admin_project_milestones?
strong_memoize(:can_admin_project_milestones) do
can?(current_user, :admin_milestone, @project)
end
end
def can_admin_group_milestones?
strong_memoize(:can_admin_group_milestones) do
can?(current_user, :admin_milestone, @project.group)
end
end
end end
...@@ -26,7 +26,7 @@ module Emails ...@@ -26,7 +26,7 @@ module Emails
mail_answer_note_thread(@merge_request, @note, note_thread_options(recipient_id)) mail_answer_note_thread(@merge_request, @note, note_thread_options(recipient_id))
end end
def note_snippet_email(recipient_id, note_id) def note_project_snippet_email(recipient_id, note_id)
setup_note_mail(note_id, recipient_id) setup_note_mail(note_id, recipient_id)
@snippet = @note.noteable @snippet = @note.noteable
......
...@@ -15,6 +15,8 @@ module Ci ...@@ -15,6 +15,8 @@ module Ci
WRITE_LOCK_SLEEP = 0.01.seconds WRITE_LOCK_SLEEP = 0.01.seconds
WRITE_LOCK_TTL = 1.minute WRITE_LOCK_TTL = 1.minute
FailedToPersistDataError = Class.new(StandardError)
# Note: The ordering of this enum is related to the precedence of persist store. # Note: The ordering of this enum is related to the precedence of persist store.
# The bottom item takes the higest precedence, and the top item takes the lowest precedence. # The bottom item takes the higest precedence, and the top item takes the lowest precedence.
enum data_store: { enum data_store: {
...@@ -109,16 +111,19 @@ module Ci ...@@ -109,16 +111,19 @@ module Ci
def unsafe_persist_to!(new_store) def unsafe_persist_to!(new_store)
return if data_store == new_store.to_s return if data_store == new_store.to_s
raise ArgumentError, 'Can not persist empty data' unless size > 0
old_store_class = self.class.get_store_class(data_store) current_data = get_data
get_data.tap do |the_data| unless current_data&.bytesize.to_i == CHUNK_SIZE
self.raw_data = nil raise FailedToPersistDataError, 'Data is not fullfilled in a bucket'
self.data_store = new_store
unsafe_set_data!(the_data)
end end
old_store_class = self.class.get_store_class(data_store)
self.raw_data = nil
self.data_store = new_store
unsafe_set_data!(current_data)
old_store_class.delete_data(self) old_store_class.delete_data(self)
end end
......
...@@ -15,7 +15,7 @@ module CacheMarkdownField ...@@ -15,7 +15,7 @@ module CacheMarkdownField
# Increment this number every time the renderer changes its output # Increment this number every time the renderer changes its output
CACHE_REDCARPET_VERSION = 3 CACHE_REDCARPET_VERSION = 3
CACHE_COMMONMARK_VERSION_START = 10 CACHE_COMMONMARK_VERSION_START = 10
CACHE_COMMONMARK_VERSION = 11 CACHE_COMMONMARK_VERSION = 12
# changes to these attributes cause the cache to be invalidates # changes to these attributes cause the cache to be invalidates
INVALIDATED_BY = %w[author project].freeze INVALIDATED_BY = %w[author project].freeze
......
...@@ -8,6 +8,7 @@ class EnvironmentStatus ...@@ -8,6 +8,7 @@ class EnvironmentStatus
delegate :id, to: :environment delegate :id, to: :environment
delegate :name, to: :environment delegate :name, to: :environment
delegate :project, to: :environment delegate :project, to: :environment
delegate :status, to: :deployment, allow_nil: true
delegate :deployed_at, to: :deployment, allow_nil: true delegate :deployed_at, to: :deployment, allow_nil: true
def self.for_merge_request(mr, user) def self.for_merge_request(mr, user)
...@@ -43,22 +44,6 @@ class EnvironmentStatus ...@@ -43,22 +44,6 @@ class EnvironmentStatus
.merge_request_diff_files.where(deleted_file: false) .merge_request_diff_files.where(deleted_file: false)
end end
##
# Since frontend has not supported all statuses yet, BE has to
# proxy some status to a supported status.
def status
return unless deployment
case deployment.status
when 'created'
'running'
when 'canceled'
'failed'
else
deployment.status
end
end
private private
PAGE_EXTENSIONS = /\A\.(s?html?|php|asp|cgi|pl)\z/i.freeze PAGE_EXTENSIONS = /\A\.(s?html?|php|asp|cgi|pl)\z/i.freeze
......
...@@ -324,7 +324,7 @@ class Note < ActiveRecord::Base ...@@ -324,7 +324,7 @@ class Note < ActiveRecord::Base
end end
def to_ability_name def to_ability_name
for_personal_snippet? ? 'personal_snippet' : noteable_type.underscore for_snippet? ? noteable.class.name.underscore : noteable_type.underscore
end end
def can_be_discussion_note? def can_be_discussion_note?
......
# frozen_string_literal: true # frozen_string_literal: true
class PoolRepository < ActiveRecord::Base class PoolRepository < ActiveRecord::Base
POOL_PREFIX = '@pools'
belongs_to :shard belongs_to :shard
validates :shard, presence: true validates :shard, presence: true
# For now, only pool repositories are tracked in the database. However, we may has_many :member_projects, class_name: 'Project'
# want to add other repository types in the future
self.table_name = 'repositories'
has_many :pool_member_projects, class_name: 'Project', foreign_key: :pool_repository_id after_create :correct_disk_path
def shard_name def shard_name
shard&.name shard&.name
...@@ -19,4 +15,15 @@ class PoolRepository < ActiveRecord::Base ...@@ -19,4 +15,15 @@ class PoolRepository < ActiveRecord::Base
def shard_name=(name) def shard_name=(name)
self.shard = Shard.by_name(name) self.shard = Shard.by_name(name)
end end
private
def correct_disk_path
update!(disk_path: storage.disk_path)
end
def storage
Storage::HashedProject
.new(self, prefix: Storage::HashedProject::POOL_PATH_PREFIX)
end
end end
...@@ -878,9 +878,9 @@ class Project < ActiveRecord::Base ...@@ -878,9 +878,9 @@ class Project < ActiveRecord::Base
end end
def readme_url def readme_url
readme = repository.readme readme_path = repository.readme_path
if readme if readme_path
Gitlab::Routing.url_helpers.project_blob_url(self, File.join(default_branch, readme.path)) Gitlab::Routing.url_helpers.project_blob_url(self, File.join(default_branch, readme_path))
end end
end end
......
...@@ -71,7 +71,7 @@ class PrometheusService < MonitoringService ...@@ -71,7 +71,7 @@ class PrometheusService < MonitoringService
end end
def prometheus_client def prometheus_client
RestClient::Resource.new(api_url) if api_url && manual_configuration? && active? RestClient::Resource.new(api_url, max_redirects: 0) if api_url && manual_configuration? && active?
end end
def prometheus_available? def prometheus_available?
......
...@@ -35,7 +35,7 @@ class Repository ...@@ -35,7 +35,7 @@ class Repository
# #
# For example, for entry `:commit_count` there's a method called `commit_count` which # For example, for entry `:commit_count` there's a method called `commit_count` which
# stores its data in the `commit_count` cache key. # stores its data in the `commit_count` cache key.
CACHED_METHODS = %i(size commit_count rendered_readme contribution_guide CACHED_METHODS = %i(size commit_count rendered_readme readme_path contribution_guide
changelog license_blob license_key gitignore changelog license_blob license_key gitignore
gitlab_ci_yml branch_names tag_names branch_count gitlab_ci_yml branch_names tag_names branch_count
tag_count avatar exists? root_ref has_visible_content? tag_count avatar exists? root_ref has_visible_content?
...@@ -48,7 +48,7 @@ class Repository ...@@ -48,7 +48,7 @@ class Repository
# changed. This Hash maps file types (as returned by Gitlab::FileDetector) to # changed. This Hash maps file types (as returned by Gitlab::FileDetector) to
# the corresponding methods to call for refreshing caches. # the corresponding methods to call for refreshing caches.
METHOD_CACHES_FOR_FILE_TYPES = { METHOD_CACHES_FOR_FILE_TYPES = {
readme: :rendered_readme, readme: %i(rendered_readme readme_path),
changelog: :changelog, changelog: :changelog,
license: %i(license_blob license_key license), license: %i(license_blob license_key license),
contributing: :contribution_guide, contributing: :contribution_guide,
...@@ -591,6 +591,11 @@ class Repository ...@@ -591,6 +591,11 @@ class Repository
head_tree&.readme head_tree&.readme
end end
def readme_path
readme&.path
end
cache_method :readme_path
def rendered_readme def rendered_readme
return unless readme return unless readme
......
...@@ -5,17 +5,19 @@ module Storage ...@@ -5,17 +5,19 @@ module Storage
attr_accessor :project attr_accessor :project
delegate :gitlab_shell, :repository_storage, to: :project delegate :gitlab_shell, :repository_storage, to: :project
ROOT_PATH_PREFIX = '@hashed'.freeze REPOSITORY_PATH_PREFIX = '@hashed'
POOL_PATH_PREFIX = '@pools'
def initialize(project) def initialize(project, prefix: REPOSITORY_PATH_PREFIX)
@project = project @project = project
@prefix = prefix
end end
# Base directory # Base directory
# #
# @return [String] directory where repository is stored # @return [String] directory where repository is stored
def base_dir def base_dir
"#{ROOT_PATH_PREFIX}/#{disk_hash[0..1]}/#{disk_hash[2..3]}" if disk_hash "#{@prefix}/#{disk_hash[0..1]}/#{disk_hash[2..3]}" if disk_hash
end end
# Disk path is used to build repository and project's wiki path on disk # Disk path is used to build repository and project's wiki path on disk
......
...@@ -2,4 +2,6 @@ ...@@ -2,4 +2,6 @@
class CommitPolicy < BasePolicy class CommitPolicy < BasePolicy
delegate { @subject.project } delegate { @subject.project }
rule { can?(:download_code) }.enable :read_commit
end end
...@@ -9,8 +9,17 @@ class NotePolicy < BasePolicy ...@@ -9,8 +9,17 @@ class NotePolicy < BasePolicy
condition(:editable, scope: :subject) { @subject.editable? } condition(:editable, scope: :subject) { @subject.editable? }
condition(:can_read_noteable) { can?(:"read_#{@subject.to_ability_name}") }
rule { ~editable }.prevent :admin_note rule { ~editable }.prevent :admin_note
# If user can't read the issue/MR/etc then they should not be allowed to do anything to their own notes
rule { ~can_read_noteable }.policy do
prevent :read_note
prevent :admin_note
prevent :resolve_note
end
rule { is_author }.policy do rule { is_author }.policy do
enable :read_note enable :read_note
enable :admin_note enable :admin_note
......
= email_default_heading("Hello, #{@resource.name}!")
- if @resource.try(:unconfirmed_email?)
%p
We're contacting you to notify you that your email is being changed to #{@resource.reload.unconfirmed_email}.
- else
%p
We're contacting you to notify you that your email has been changed to #{@resource.email}.
%p
If you did not initiate this change, please contact your administrator
immediately.
Hello, <%= @resource.name %>!
<% if @resource.try(:unconfirmed_email?) %>
We're contacting you to notify you that your email is being changed to <%= @resource.reload.unconfirmed_email %>.
<% else %>
We're contacting you to notify you that your email has been changed to <%= @resource.email %>.
<% end %>
If you did not initiate this change, please contact your administrator
immediately.
- page_title 'Edit', @label.name, 'Labels' - add_to_breadcrumbs _("Labels"), group_labels_path(@group)
- breadcrumb_title _("Edit")
- page_title "Edit", @label.name, _("Labels")
%h3.page-title %h3.page-title
Edit Label Edit Label
......
- breadcrumb_title "Labels" - add_to_breadcrumbs _("Labels"), group_labels_path(@group)
- page_title 'New Label' - breadcrumb_title _("New")
- page_title _("New Label")
%h3.page-title %h3.page-title
New Label New Label
......
- page_title "Milestones" - breadcrumb_title _("Edit")
- page_title _("Milestones")
- render "header_title" - render "header_title"
%h3.page-title %h3.page-title
Edit Milestone Edit Milestone
%hr
= render "form" = render "form"
- breadcrumb_title "Milestones" - @no_container = true
- page_title "Milestones" - add_to_breadcrumbs _("Milestones"), group_milestones_path(@group)
- breadcrumb_title _("New")
- page_title _("Milestones"), @milestone.name, _("Milestones")
%h3.page-title %div{ class: container_class }
New Milestone %h3.page-title
New Milestone
= render "form" %hr
= render "form"
...@@ -8,62 +8,50 @@ ...@@ -8,62 +8,50 @@
- ref = local_assigns.fetch(:ref) { merge_request&.source_branch } - ref = local_assigns.fetch(:ref) { merge_request&.source_branch }
- link = commit_path(project, commit, merge_request: merge_request) - link = commit_path(project, commit, merge_request: merge_request)
- cache_key = [project.full_path, %li.commit.flex-row.js-toggle-container{ id: "commit-#{commit.short_id}" }
ref,
commit.id, .avatar-cell.d-none.d-sm-block
Gitlab::CurrentSettings.current_application_settings, = author_avatar(commit, size: 36, has_tooltip: false)
@path.presence,
current_controller?(:commits), .commit-detail.flex-list
merge_request&.iid, .commit-content.qa-commit-content
view_details, - if view_details && merge_request
commit.status(ref), = link_to commit.title, project_commit_path(project, commit.id, merge_request_iid: merge_request.iid), class: "commit-row-message item-title"
I18n.locale].compact - else
= link_to_markdown_field(commit, :title, link, class: "commit-row-message item-title")
= cache(cache_key, expires_in: 1.day) do %span.commit-row-message.d-block.d-sm-none
%li.commit.flex-row.js-toggle-container{ id: "commit-#{commit.short_id}" } &middot;
= commit.short_id
.avatar-cell.d-none.d-sm-block - if commit.status(ref)
= author_avatar(commit, size: 36, has_tooltip: false) .d-block.d-sm-none
= render_commit_status(commit, ref: ref)
.commit-detail.flex-list - if commit.description?
.commit-content.qa-commit-content %button.text-expander.js-toggle-button
- if view_details && merge_request = sprite_icon('ellipsis_h', size: 12)
= link_to commit.title, project_commit_path(project, commit.id, merge_request_iid: merge_request.iid), class: "commit-row-message item-title"
- else
= link_to_markdown_field(commit, :title, link, class: "commit-row-message item-title")
%span.commit-row-message.d-block.d-sm-none
&middot;
= commit.short_id
- if commit.status(ref)
.d-block.d-sm-none
= render_commit_status(commit, ref: ref)
- if commit.description?
%button.text-expander.js-toggle-button
= sprite_icon('ellipsis_h', size: 12)
.committer .committer
- commit_author_link = commit_author_link(commit, avatar: false, size: 24) - commit_author_link = commit_author_link(commit, avatar: false, size: 24)
- commit_timeago = time_ago_with_tooltip(commit.authored_date, placement: 'bottom') - commit_timeago = time_ago_with_tooltip(commit.authored_date, placement: 'bottom')
- commit_text = _('%{commit_author_link} authored %{commit_timeago}') % { commit_author_link: commit_author_link, commit_timeago: commit_timeago } - commit_text = _('%{commit_author_link} authored %{commit_timeago}') % { commit_author_link: commit_author_link, commit_timeago: commit_timeago }
#{ commit_text.html_safe } #{ commit_text.html_safe }
- if commit.description? - if commit.description?
%pre.commit-row-description.js-toggle-content.append-bottom-8 %pre.commit-row-description.js-toggle-content.append-bottom-8
= preserve(markdown_field(commit, :description)) = preserve(markdown_field(commit, :description))
.commit-actions.flex-row.d-none.d-sm-flex .commit-actions.flex-row.d-none.d-sm-flex
- if request.xhr? - if request.xhr?
= render partial: 'projects/commit/signature', object: commit.signature = render partial: 'projects/commit/signature', object: commit.signature
- else - else
= render partial: 'projects/commit/ajax_signature', locals: { commit: commit } = render partial: 'projects/commit/ajax_signature', locals: { commit: commit }
- if commit.status(ref) - if commit.status(ref)
= render_commit_status(commit, ref: ref) = render_commit_status(commit, ref: ref)
.js-commit-pipeline-status{ data: { endpoint: pipelines_project_commit_path(project, commit.id, ref: ref) } } .js-commit-pipeline-status{ data: { endpoint: pipelines_project_commit_path(project, commit.id, ref: ref) } }
.commit-sha-group .commit-sha-group
.label.label-monospace .label.label-monospace
= commit.short_id = commit.short_id
= clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard"), class: "btn btn-default", container: "body") = clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard"), class: "btn btn-default", container: "body")
= link_to_browse_code(project, commit) = link_to_browse_code(project, commit)
- @no_container = true - @no_container = true
- add_to_breadcrumbs "Labels", project_labels_path(@project)
- breadcrumb_title "Edit"
- page_title "Edit", @label.name, "Labels" - page_title "Edit", @label.name, "Labels"
%div{ class: container_class } %div{ class: container_class }
......
- @no_container = true - @no_container = true
- breadcrumb_title "Labels" - add_to_breadcrumbs "Labels", project_labels_path(@project)
- breadcrumb_title "New"
- page_title "New Label" - page_title "New Label"
%div{ class: container_class } %div{ class: container_class }
......
- @no_container = true - @no_container = true
- breadcrumb_title "Edit"
- add_to_breadcrumbs "Milestones", project_milestones_path(@project)
- page_title "Edit", @milestone.title, "Milestones" - page_title "Edit", @milestone.title, "Milestones"
%div{ class: container_class } %div{ class: container_class }
%h3.page-title %h3.page-title
......
- @no_container = true - @no_container = true
- breadcrumb_title "Milestones" - add_to_breadcrumbs "Milestones", project_milestones_path(@project)
- breadcrumb_title "New"
- page_title "New Milestone" - page_title "New Milestone"
%div{ class: container_class } %div{ class: container_class }
......
- @content_class = "limit-container-width" unless fluid_layout - @content_class = "limit-container-width" unless fluid_layout
- page_title _("Edit"), @page.title.capitalize, _("Wiki") - 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")
= wiki_page_errors(@error) = wiki_page_errors(@error)
......
...@@ -35,8 +35,8 @@ ...@@ -35,8 +35,8 @@
.col-sm-2 .col-sm-2
.milestone-actions.d-flex.justify-content-sm-start.justify-content-md-end .milestone-actions.d-flex.justify-content-sm-start.justify-content-md-end
- if @project - if @project
- if can?(current_user, :admin_milestone, milestone.project) and milestone.active? - if can_admin_project_milestones? and milestone.active?
- if @project.group - if can_admin_group_milestones?
%button.js-promote-project-milestone-button.btn.btn-blank.btn-sm.btn-grouped.has-tooltip{ title: _('Promote to Group Milestone'), %button.js-promote-project-milestone-button.btn.btn-blank.btn-sm.btn-grouped.has-tooltip{ title: _('Promote to Group Milestone'),
disabled: true, disabled: true,
type: 'button', type: 'button',
......
...@@ -3,31 +3,31 @@ ...@@ -3,31 +3,31 @@
.d-none.d-sm-block .d-none.d-sm-block
- if can?(current_user, :update_personal_snippet, @snippet) - if can?(current_user, :update_personal_snippet, @snippet)
= link_to edit_snippet_path(@snippet), class: "btn btn-grouped" do = link_to edit_snippet_path(@snippet), class: "btn btn-grouped" do
Edit = _("Edit")
- if can?(current_user, :admin_personal_snippet, @snippet) - if can?(current_user, :admin_personal_snippet, @snippet)
= link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-inverted btn-remove", title: 'Delete Snippet' do = link_to snippet_path(@snippet), method: :delete, data: { confirm: _("Are you sure?") }, class: "btn btn-grouped btn-inverted btn-remove", title: _('Delete Snippet') do
Delete = _("Delete")
= link_to new_snippet_path, class: "btn btn-grouped btn-inverted btn-success", title: "New snippet" do = link_to new_snippet_path, class: "btn btn-grouped btn-inverted btn-create", title: _("New snippet") do
New snippet = _("New snippet")
- if @snippet.submittable_as_spam_by?(current_user) - if @snippet.submittable_as_spam_by?(current_user)
= link_to 'Submit as spam', mark_as_spam_snippet_path(@snippet), method: :post, class: 'btn btn-grouped btn-spam', title: 'Submit as spam' = link_to _('Submit as spam'), mark_as_spam_snippet_path(@snippet), method: :post, class: 'btn btn-grouped btn-spam', title: _('Submit as spam')
.d-block.d-sm-none.dropdown .d-block.d-sm-none.dropdown
%button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } } %button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } }
Options = _("Options")
= icon('caret-down') = icon('caret-down')
.dropdown-menu.dropdown-menu-full-width .dropdown-menu.dropdown-menu-full-width
%ul %ul
%li %li
= link_to new_snippet_path, title: "New snippet" do = link_to new_snippet_path, title: _("New snippet") do
New snippet = _("New snippet")
- if can?(current_user, :admin_personal_snippet, @snippet) - if can?(current_user, :admin_personal_snippet, @snippet)
%li %li
= link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do = link_to snippet_path(@snippet), method: :delete, data: { confirm: _("Are you sure?") }, title: _('Delete Snippet') do
Delete = _("Delete")
- if can?(current_user, :update_personal_snippet, @snippet) - if can?(current_user, :update_personal_snippet, @snippet)
%li %li
= link_to edit_snippet_path(@snippet) do = link_to edit_snippet_path(@snippet) do
Edit = _("Edit")
- if @snippet.submittable_as_spam_by?(current_user) - if @snippet.submittable_as_spam_by?(current_user)
%li %li
= link_to 'Submit as spam', mark_as_spam_snippet_path(@snippet), method: :post = link_to _('Submit as spam'), mark_as_spam_snippet_path(@snippet), method: :post
...@@ -5,6 +5,6 @@ ...@@ -5,6 +5,6 @@
= render partial: 'shared/snippets/snippet', collection: @snippets, locals: { link_project: link_project } = render partial: 'shared/snippets/snippet', collection: @snippets, locals: { link_project: link_project }
- if @snippets.empty? - if @snippets.empty?
%li %li
.nothing-here-block Nothing here. .nothing-here-block= _("Nothing here.")
= paginate @snippets, theme: 'gitlab' = paginate @snippets, theme: 'gitlab'
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.nav-links.snippet-scope-menu.mobile-separator.nav.nav-tabs .nav-links.snippet-scope-menu.mobile-separator.nav.nav-tabs
%li{ class: active_when(params[:scope].nil?) } %li{ class: active_when(params[:scope].nil?) }
= link_to subject_snippets_path(subject) do = link_to subject_snippets_path(subject) do
All = _("All")
%span.badge.badge-pill %span.badge.badge-pill
- if include_private - if include_private
= subject.snippets.count = subject.snippets.count
...@@ -14,18 +14,18 @@ ...@@ -14,18 +14,18 @@
- if include_private - if include_private
%li{ class: active_when(params[:scope] == "are_private") } %li{ class: active_when(params[:scope] == "are_private") }
= link_to subject_snippets_path(subject, scope: 'are_private') do = link_to subject_snippets_path(subject, scope: 'are_private') do
Private = _("Private")
%span.badge.badge-pill %span.badge.badge-pill
= subject.snippets.are_private.count = subject.snippets.are_private.count
%li{ class: active_when(params[:scope] == "are_internal") } %li{ class: active_when(params[:scope] == "are_internal") }
= link_to subject_snippets_path(subject, scope: 'are_internal') do = link_to subject_snippets_path(subject, scope: 'are_internal') do
Internal = _("Internal")
%span.badge.badge-pill %span.badge.badge-pill
= subject.snippets.are_internal.count = subject.snippets.are_internal.count
%li{ class: active_when(params[:scope] == "are_public") } %li{ class: active_when(params[:scope] == "are_public") }
= link_to subject_snippets_path(subject, scope: 'are_public') do = link_to subject_snippets_path(subject, scope: 'are_public') do
Public = _("Public")
%span.badge.badge-pill %span.badge.badge-pill
= subject.snippets.are_public.count = subject.snippets.are_public.count
- page_title "Edit", "#{@snippet.title} (#{@snippet.to_reference})", "Snippets" - page_title _("Edit"), "#{@snippet.title} (#{@snippet.to_reference})", _("Snippets")
%h3.page-title %h3.page-title
Edit Snippet = _("Edit Snippet")
%hr %hr
= render 'shared/snippets/form', url: snippet_path(@snippet) = render 'shared/snippets/form', url: snippet_path(@snippet)
- page_title "By #{@user.name}", "Snippets" - page_title _("By %{user_name}") % { user_name: @user.name }, _("Snippets")
%ol.breadcrumb %ol.breadcrumb
%li.breadcrumb-item %li.breadcrumb-item
= link_to snippets_path do = link_to snippets_path do
Snippets = _("Snippets")
%li.breadcrumb-item %li.breadcrumb-item
= @user.name = @user.name
.float-right.d-none.d-sm-block .float-right.d-none.d-sm-block
= link_to user_path(@user) do = link_to user_path(@user) do
#{@user.name} profile page = _("%{user_name} profile page") % { user_name: @user.name }
= render 'snippets' = render 'snippets'
- @hide_top_links = true - @hide_top_links = true
- @hide_breadcrumbs = true - @hide_breadcrumbs = true
- page_title "New Snippet" - page_title _("New Snippet")
.page-title-holder .page-title-holder
%h1.page-title= _('New Snippet') %h1.page-title= _('New Snippet')
......
- if current_user - if current_user
- if note.emoji_awardable? - if note.emoji_awardable?
.note-actions-item .note-actions-item
= link_to '#', title: 'Add reaction', class: "note-action-button note-emoji-button js-add-award js-note-emoji has-tooltip", data: { position: 'right' } do = link_to '#', title: _('Add reaction'), class: "note-action-button note-emoji-button js-add-award js-note-emoji has-tooltip", data: { position: 'right' } do
= icon('spinner spin') = icon('spinner spin')
%span{ class: 'link-highlight award-control-icon-neutral' }= custom_icon('emoji_slightly_smiling_face') %span{ class: 'link-highlight award-control-icon-neutral' }= custom_icon('emoji_slightly_smiling_face')
%span{ class: 'link-highlight award-control-icon-positive' }= custom_icon('emoji_smiley') %span{ class: 'link-highlight award-control-icon-positive' }= custom_icon('emoji_smiley')
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
- if note_editable - if note_editable
.note-actions-item .note-actions-item
= button_tag title: 'Edit comment', class: 'note-action-button js-note-edit has-tooltip btn btn-transparent', data: { container: 'body' } do = button_tag title: _('Edit comment'), class: 'note-action-button js-note-edit has-tooltip btn btn-transparent', data: { container: 'body' } do
%span.link-highlight %span.link-highlight
= custom_icon('icon_pencil') = custom_icon('icon_pencil')
......
- @hide_top_links = true - @hide_top_links = true
- @content_class = "limit-container-width limited-inner-width-container" unless fluid_layout - @content_class = "limit-container-width limited-inner-width-container" unless fluid_layout
- add_to_breadcrumbs "Snippets", dashboard_snippets_path - add_to_breadcrumbs _("Snippets"), dashboard_snippets_path
- breadcrumb_title @snippet.to_reference - breadcrumb_title @snippet.to_reference
- page_title "#{@snippet.title} (#{@snippet.to_reference})", "Snippets" - page_title "#{@snippet.title} (#{@snippet.to_reference})", _("Snippets")
= render 'shared/snippets/header' = render 'shared/snippets/header'
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
%li %li
%span.light %span.light
%i.fa.fa-clock-o %i.fa.fa-clock-o
= event.created_at.strftime('%-I:%M%P') = event.created_at.to_time.in_time_zone.strftime('%-I:%M%P')
- if event.visible_to_user?(current_user) - if event.visible_to_user?(current_user)
- if event.push? - if event.push?
#{event.action_name} #{event.ref_type} #{event.action_name} #{event.ref_type}
......
---
title: Add a rebase API endpoint for merge requests
merge_request: 23296
author:
type: added
---
title: Show user contributions in correct timezone within user profile
merge_request: 23419
author:
type: changed
---
title: Scope default MR search in WebIDE dropdown to current project
merge_request: 23400
author:
type: changed
---
title: Improves performance of Project#readme_url by caching the README path
merge_request: 23357
author:
type: performance
---
title: Adds margins between tags when a job is stuck
merge_request:
author:
type: fixed
---
title: Validate chunk size when persist
merge_request: 23341
author:
type: fixed
---
title: Externalize strings from `/app/views/snippets`
merge_request: 23351
author: Tao Wang
type: other
---
title: Include new link in breadcrumb for issues, merge requests, milestones, and labels
merge_request: 18515
author: George Tsiolis
type: changed
---
title: Return real deployment status to frontend
merge_request: 23270
author:
type: fixed
---
title: Redact sensitive information on gitlab-workhorse log
merge_request:
author:
type: security
---
title: Do not follow redirects in Prometheus service when making http requests to the configured api url
merge_request:
author:
type: security
---
title: Don't expose confidential information in commit message list
merge_request:
author:
type: security
---
title: Provide email notification when a user changes their email address
merge_request:
author:
type: security
---
title: Restrict Personal Access Tokens to API scope on web requests
merge_request:
author:
type: security
---
title: Resolve reflected XSS in Ouath authorize window
merge_request:
author:
type: security
---
title: Fix SSRF in project integrations
merge_request:
author:
type: security
---
title: Fix CRLF vulnerability in Project hooks
merge_request:
author:
type: security
---
title: Fixed ability to comment on locked/confidential issues.
merge_request:
author:
type: security
---
title: Fixed ability of guest users to edit/delete comments on locked or confidential issues.
merge_request:
author:
type: security
---
title: Fix milestone promotion authorization check
merge_request:
author:
type: security
---
title: Configure mermaid to not render HTML content in diagrams
merge_request:
author:
type: security
---
title: Fix a possible symlink time of check to time of use race condition in GitLab
Pages
merge_request:
author:
type: security
---
title: Removed ability to see private group names when the group id is entered in
the url.
merge_request:
author:
type: security
---
title: Fix stored XSS for Environments
merge_request:
author:
type: security
---
title: Fix possible XSS attack in Markdown urls with spaces
merge_request: 2599
author:
type: security
---
title: Add monkey patch to unicorn to fix eof? problem
merge_request: 23385
author:
type: fixed
...@@ -13,6 +13,10 @@ if defined?(Unicorn) ...@@ -13,6 +13,10 @@ if defined?(Unicorn)
# Max memory size (RSS) per worker # Max memory size (RSS) per worker
use Unicorn::WorkerKiller::Oom, min, max use Unicorn::WorkerKiller::Oom, min, max
end end
# Monkey patch for fixing Rack 2.0.6 bug:
# https://gitlab.com/gitlab-org/gitlab-ee/issues/8539
Unicorn::StreamInput.send(:public, :eof?) # rubocop:disable GitlabSecurity/PublicSend
end end
require ::File.expand_path('../config/environment', __FILE__) require ::File.expand_path('../config/environment', __FILE__)
......
...@@ -103,6 +103,9 @@ module Gitlab ...@@ -103,6 +103,9 @@ module Gitlab
# - Webhook URLs (:hook) # - Webhook URLs (:hook)
# - Sentry DSN (:sentry_dsn) # - Sentry DSN (:sentry_dsn)
# - File content from Web Editor (:content) # - File content from Web Editor (:content)
#
# NOTE: It is **IMPORTANT** to also update gitlab-workhorse's filter when adding parameters here to not
# introduce another security vulnerability: https://gitlab.com/gitlab-org/gitlab-workhorse/issues/182
config.filter_parameters += [/token$/, /password/, /secret/, /key$/] config.filter_parameters += [/token$/, /password/, /secret/, /key$/]
config.filter_parameters += %i( config.filter_parameters += %i(
certificate certificate
......
...@@ -103,6 +103,9 @@ Devise.setup do |config| ...@@ -103,6 +103,9 @@ Devise.setup do |config|
# Send a notification email when the user's password is changed # Send a notification email when the user's password is changed
config.send_password_change_notification = true config.send_password_change_notification = true
# Send a notification email when the user's email is changed
config.send_email_changed_notification = true
# ==> Configuration for :validatable # ==> Configuration for :validatable
# Range for password length. Default is 6..128. # Range for password length. Default is 6..128.
config.password_length = 8..128 config.password_length = 8..128
......
...@@ -48,6 +48,13 @@ Doorkeeper.configure do ...@@ -48,6 +48,13 @@ Doorkeeper.configure do
# #
force_ssl_in_redirect_uri false force_ssl_in_redirect_uri false
# Specify what redirect URI's you want to block during Application creation.
# Any redirect URI is whitelisted by default.
#
# You can use this option in order to forbid URI's with 'javascript' scheme
# for example.
forbid_redirect_uri { |uri| %w[data vbscript javascript].include?(uri.scheme.to_s.downcase) }
# Provide support for an owner to be assigned to each registered application (disabled by default) # Provide support for an owner to be assigned to each registered application (disabled by default)
# Optional parameter confirmation: true (default false) if you want to enforce ownership of # Optional parameter confirmation: true (default false) if you want to enforce ownership of
# a registered application # a registered application
......
...@@ -33,22 +33,22 @@ class Rack::Attack ...@@ -33,22 +33,22 @@ class Rack::Attack
throttle('throttle_authenticated_api', Gitlab::Throttle.authenticated_api_options) do |req| throttle('throttle_authenticated_api', Gitlab::Throttle.authenticated_api_options) do |req|
Gitlab::Throttle.settings.throttle_authenticated_api_enabled && Gitlab::Throttle.settings.throttle_authenticated_api_enabled &&
req.api_request? && req.api_request? &&
req.authenticated_user_id req.authenticated_user_id([:api])
end end
throttle('throttle_authenticated_web', Gitlab::Throttle.authenticated_web_options) do |req| throttle('throttle_authenticated_web', Gitlab::Throttle.authenticated_web_options) do |req|
Gitlab::Throttle.settings.throttle_authenticated_web_enabled && Gitlab::Throttle.settings.throttle_authenticated_web_enabled &&
req.web_request? && req.web_request? &&
req.authenticated_user_id req.authenticated_user_id([:api, :rss, :ics])
end end
class Request class Request
def unauthenticated? def unauthenticated?
!authenticated_user_id !authenticated_user_id([:api, :rss, :ics])
end end
def authenticated_user_id def authenticated_user_id(request_formats)
Gitlab::Auth::RequestAuthenticator.new(self).user&.id Gitlab::Auth::RequestAuthenticator.new(self).user(request_formats)&.id
end end
def api_request? def api_request?
......
# frozen_string_literal: true
class CleanupEnvironmentsExternalUrl < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
update_column_in_batches(:environments, :external_url, nil) do |table, query|
query.where(table[:external_url].matches('javascript://%'))
end
end
def down
end
end
class RenameRepositoriesPoolRepositories < ActiveRecord::Migration[5.0]
include Gitlab::Database::MigrationHelpers
# This change doesn't require downtime as the table is not in use, so we're
# free to change an empty table
DOWNTIME = false
def change
rename_table :repositories, :pool_repositories
end
end
# frozen_string_literal: true
class DropNotNullConstraintPoolRepositoryDiskPath < ActiveRecord::Migration[5.0]
DOWNTIME = false
def change
change_column_null :pool_repositories, :disk_path, true
end
end
# frozen_string_literal: true
class MigrateForbiddenRedirectUris < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
FORBIDDEN_SCHEMES = %w[data:// vbscript:// javascript://]
NEW_URI = 'http://forbidden-scheme-has-been-overwritten'
disable_ddl_transaction!
def up
update_forbidden_uris(:oauth_applications)
update_forbidden_uris(:oauth_access_grants)
end
def down
# noop
end
private
def update_forbidden_uris(table_name)
update_column_in_batches(table_name, :redirect_uri, NEW_URI) do |table, query|
where_clause = FORBIDDEN_SCHEMES.map do |scheme|
table[:redirect_uri].matches("#{scheme}%")
end.inject(&:or)
query.where(where_clause)
end
end
end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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