Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-12-03 21:10:10 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-12-03 21:10:10 +0300
commitc2a6cc86754adb3c5e064cebc58d206a52cb412e (patch)
tree3960c9ae2590e89e25193a0006e84d06f900e378 /app
parentbbd9e2c915c46920ceb51376db19599cbf9ba836 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue2
-rw-r--r--app/assets/javascripts/graphql_shared/queries/users_search.query.graphql9
-rw-r--r--app/assets/javascripts/layout_nav.js29
-rw-r--r--app/assets/javascripts/merge_request.js12
-rw-r--r--app/assets/javascripts/notes/components/multiline_comment_utils.js10
-rw-r--r--app/assets/javascripts/whats_new/index.js20
-rw-r--r--app/assets/javascripts/whats_new/utils/notification.js17
-rw-r--r--app/assets/stylesheets/framework/animations.scss3
-rw-r--r--app/assets/stylesheets/framework/header.scss11
-rw-r--r--app/assets/stylesheets/pages/detail_page.scss6
-rw-r--r--app/assets/stylesheets/pages/issuable.scss18
-rw-r--r--app/assets/stylesheets/themes/theme_helper.scss29
-rw-r--r--app/controllers/concerns/dependency_proxy/auth.rb43
-rw-r--r--app/controllers/concerns/dependency_proxy/group_access.rb26
-rw-r--r--app/controllers/concerns/dependency_proxy_access.rb24
-rw-r--r--app/controllers/groups/dependency_proxies_controller.rb2
-rw-r--r--app/controllers/groups/dependency_proxy_auth_controller.rb11
-rw-r--r--app/controllers/groups/dependency_proxy_for_containers_controller.rb5
-rw-r--r--app/controllers/jwt_controller.rb3
-rw-r--r--app/controllers/profiles/gpg_keys_controller.rb19
-rw-r--r--app/controllers/projects/jobs_controller.rb2
-rw-r--r--app/controllers/repositories/git_http_controller.rb2
-rw-r--r--app/helpers/issuables_helper.rb6
-rw-r--r--app/helpers/merge_requests_helper.rb19
-rw-r--r--app/models/dependency_proxy/registry.rb9
-rw-r--r--app/models/namespace_onboarding_action.rb8
-rw-r--r--app/models/user_detail.rb2
-rw-r--r--app/services/auth/dependency_proxy_authentication_service.rb43
-rw-r--r--app/services/dependency_proxy/auth_token_service.rb21
-rw-r--r--app/services/onboarding_progress_service.rb11
-rw-r--r--app/services/post_receive_service.rb6
-rw-r--r--app/views/groups/dependency_proxies/show.html.haml2
-rw-r--r--app/views/layouts/header/_default.html.haml1
-rw-r--r--app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml37
-rw-r--r--app/views/projects/merge_requests/_mr_title.html.haml10
-rw-r--r--app/views/shared/issuable/_close_reopen_draft_report_toggle.html.haml37
36 files changed, 347 insertions, 168 deletions
diff --git a/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue b/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue
index 06f436adb8e..6fee40fb061 100644
--- a/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue
+++ b/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue
@@ -107,7 +107,7 @@ export default {
v-if="!popoverDismissed"
show
:target="target"
- placement="rightbottom"
+ placement="right"
trigger="manual"
container="viewport"
:css-classes="['suggest-gitlab-ci-yml', 'ml-4']"
diff --git a/app/assets/javascripts/graphql_shared/queries/users_search.query.graphql b/app/assets/javascripts/graphql_shared/queries/users_search.query.graphql
new file mode 100644
index 00000000000..b64ceb8e2c9
--- /dev/null
+++ b/app/assets/javascripts/graphql_shared/queries/users_search.query.graphql
@@ -0,0 +1,9 @@
+#import "../fragments/user.fragment.graphql"
+
+query usersSearch($search: String!) {
+ users(search: $search) {
+ nodes {
+ ...User
+ }
+ }
+}
diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js
index 4d2955a8d3d..ab83f1ecc14 100644
--- a/app/assets/javascripts/layout_nav.js
+++ b/app/assets/javascripts/layout_nav.js
@@ -1,6 +1,7 @@
import $ from 'jquery';
import ContextualSidebar from './contextual_sidebar';
import initFlyOutNav from './fly_out_nav';
+import { setNotification } from './whats_new/utils/notification';
function hideEndFade($scrollingTabs) {
$scrollingTabs.each(function scrollTabsLoop() {
@@ -14,25 +15,17 @@ function hideEndFade($scrollingTabs) {
function initDeferred() {
$(document).trigger('init.scrolling-tabs');
- const whatsNewTriggerEl = document.querySelector('.js-whats-new-trigger');
- if (whatsNewTriggerEl) {
- const storageKey = whatsNewTriggerEl.getAttribute('data-storage-key');
+ const appEl = document.getElementById('whats-new-app');
+ if (!appEl) return;
- $('.header-help').on('show.bs.dropdown', () => {
- const displayNotification = JSON.parse(localStorage.getItem(storageKey));
- if (displayNotification === false) {
- $('.js-whats-new-notification-count').remove();
- }
- });
-
- whatsNewTriggerEl.addEventListener('click', () => {
- import(/* webpackChunkName: 'whatsNewApp' */ '~/whats_new')
- .then(({ default: initWhatsNew }) => {
- initWhatsNew();
- })
- .catch(() => {});
- });
- }
+ setNotification(appEl);
+ document.querySelector('.js-whats-new-trigger').addEventListener('click', () => {
+ import(/* webpackChunkName: 'whatsNewApp' */ '~/whats_new')
+ .then(({ default: initWhatsNew }) => {
+ initWhatsNew(appEl);
+ })
+ .catch(() => {});
+ });
}
export default function initLayoutNav() {
diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js
index fe4e2cee69f..344f8dee5ea 100644
--- a/app/assets/javascripts/merge_request.js
+++ b/app/assets/javascripts/merge_request.js
@@ -102,14 +102,6 @@ MergeRequest.prototype.initMRBtnListeners = function() {
return $('.btn-close, .btn-reopen').on('click', function(e) {
const $this = $(this);
const shouldSubmit = $this.hasClass('btn-comment');
- if ($this.hasClass('js-btn-issue-action')) {
- const url = $this.data('endpoint');
- return axios
- .put(url)
- .then(() => window.location.reload())
- .catch(() => createFlash(__('Something went wrong.')));
- }
-
if (shouldSubmit && $this.data('submitted')) {
return;
}
@@ -171,10 +163,6 @@ MergeRequest.decreaseCounter = function(by = 1) {
MergeRequest.hideCloseButton = function() {
const el = document.querySelector('.merge-request .js-issuable-actions');
- const closeDropdownItem = el.querySelector('li.close-item');
- if (closeDropdownItem) {
- closeDropdownItem.classList.add('hidden');
- }
// Dropdown for mobile screen
el.querySelector('li.js-close-item').classList.add('hidden');
};
diff --git a/app/assets/javascripts/notes/components/multiline_comment_utils.js b/app/assets/javascripts/notes/components/multiline_comment_utils.js
index dbae10c8f6c..2451400e980 100644
--- a/app/assets/javascripts/notes/components/multiline_comment_utils.js
+++ b/app/assets/javascripts/notes/components/multiline_comment_utils.js
@@ -103,9 +103,15 @@ export function getCommentedLines(selectedCommentPosition, diffLines) {
};
}
+ const findLineCodeIndex = line => position => {
+ return [position.line_code, position.left?.line_code, position.right?.line_code].includes(
+ line.line_code,
+ );
+ };
+
const { start, end } = selectedCommentPosition;
- const startLine = diffLines.findIndex(l => l.line_code === start.line_code);
- const endLine = diffLines.findIndex(l => l.line_code === end.line_code);
+ const startLine = diffLines.findIndex(findLineCodeIndex(start));
+ const endLine = diffLines.findIndex(findLineCodeIndex(end));
return { startLine, endLine };
}
diff --git a/app/assets/javascripts/whats_new/index.js b/app/assets/javascripts/whats_new/index.js
index a57c9718156..2b9e7a2815e 100644
--- a/app/assets/javascripts/whats_new/index.js
+++ b/app/assets/javascripts/whats_new/index.js
@@ -1,26 +1,34 @@
import Vue from 'vue';
+import { mapState } from 'vuex';
import App from './components/app.vue';
import store from './store';
+import { getStorageKey, setNotification } from './utils/notification';
let whatsNewApp;
-export default () => {
+export default el => {
if (whatsNewApp) {
store.dispatch('openDrawer');
} else {
- const whatsNewElm = document.getElementById('whats-new-app');
+ const storageKey = getStorageKey(el);
whatsNewApp = new Vue({
- el: whatsNewElm,
+ el,
store,
components: {
App,
},
+ computed: {
+ ...mapState(['open']),
+ },
+ watch: {
+ open() {
+ setNotification(el);
+ },
+ },
render(createElement) {
return createElement('app', {
- props: {
- storageKey: whatsNewElm.getAttribute('data-storage-key'),
- },
+ props: { storageKey },
});
},
});
diff --git a/app/assets/javascripts/whats_new/utils/notification.js b/app/assets/javascripts/whats_new/utils/notification.js
new file mode 100644
index 00000000000..f261a089554
--- /dev/null
+++ b/app/assets/javascripts/whats_new/utils/notification.js
@@ -0,0 +1,17 @@
+export const getStorageKey = appEl => appEl.getAttribute('data-storage-key');
+
+export const setNotification = appEl => {
+ const storageKey = getStorageKey(appEl);
+ const notificationEl = document.querySelector('.header-help');
+ let notificationCountEl = notificationEl.querySelector('.js-whats-new-notification-count');
+
+ if (JSON.parse(localStorage.getItem(storageKey)) === false) {
+ notificationEl.classList.remove('with-notifications');
+ if (notificationCountEl) {
+ notificationCountEl.parentElement.removeChild(notificationCountEl);
+ notificationCountEl = null;
+ }
+ } else {
+ notificationEl.classList.add('with-notifications');
+ }
+};
diff --git a/app/assets/stylesheets/framework/animations.scss b/app/assets/stylesheets/framework/animations.scss
index 196fb3a7088..a93c70c75d3 100644
--- a/app/assets/stylesheets/framework/animations.scss
+++ b/app/assets/stylesheets/framework/animations.scss
@@ -103,7 +103,8 @@
@include transition(color);
}
-a {
+a,
+.notification-dot {
@include transition(background-color, color, border);
}
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 52319d9658b..0286c2f517b 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -556,12 +556,17 @@
border: 1px solid $gray-normal;
}
-.header-user-notification-dot {
+.notification-dot {
background-color: $orange-300;
height: 12px;
width: 12px;
- right: 8px;
- top: -8px;
+ margin-top: -15px;
+ pointer-events: none;
+ visibility: hidden;
+}
+
+.with-notifications .notification-dot {
+ visibility: visible;
}
.with-performance-bar .navbar-gitlab {
diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss
index f357d508d5d..f237d57aa88 100644
--- a/app/assets/stylesheets/pages/detail_page.scss
+++ b/app/assets/stylesheets/pages/detail_page.scss
@@ -41,12 +41,6 @@
@include media-breakpoint-down(xs) {
width: 100%;
margin-top: 10px;
-
- > .issue-btn-group {
- > .btn {
- width: 100%;
- }
- }
}
}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 57725aa3002..e5528c25e82 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -890,24 +890,6 @@
}
}
-.issuable-close-dropdown {
- .dropdown-menu {
- min-width: 270px;
- left: auto;
- right: 0;
- }
-
- .description {
- .text {
- margin: 0;
- }
- }
-
- .dropdown-toggle > .icon {
- margin: 0 3px;
- }
-}
-
/*
* Following overrides are done to prevent
* legacy dropdown styles from influencing
diff --git a/app/assets/stylesheets/themes/theme_helper.scss b/app/assets/stylesheets/themes/theme_helper.scss
index 85115cfd5d9..417377b514e 100644
--- a/app/assets/stylesheets/themes/theme_helper.scss
+++ b/app/assets/stylesheets/themes/theme_helper.scss
@@ -64,14 +64,20 @@
color: $search-and-nav-links;
> a {
+ .notification-dot {
+ border: 2px solid $nav-svg-color;
+ }
+
+ &.header-help-dropdown-toggle {
+ .notification-dot {
+ background-color: $search-and-nav-links;
+ }
+ }
+
&.header-user-dropdown-toggle {
.header-user-avatar {
border-color: $search-and-nav-links;
}
-
- .header-user-notification-dot {
- border: 2px solid $nav-svg-color;
- }
}
&:hover,
@@ -84,9 +90,14 @@
fill: currentColor;
}
- &.header-user-dropdown-toggle .header-user-notification-dot {
+ .notification-dot {
+ will-change: border-color, background-color;
border-color: $nav-svg-color + 33;
}
+
+ &.header-help-dropdown-toggle .notification-dot {
+ background-color: $white;
+ }
}
}
@@ -101,9 +112,15 @@
}
}
- &.header-user-dropdown-toggle .header-user-notification-dot {
+ .notification-dot {
border-color: $white;
}
+
+ &.header-help-dropdown-toggle {
+ .notification-dot {
+ background-color: $nav-svg-color;
+ }
+ }
}
.impersonated-user,
diff --git a/app/controllers/concerns/dependency_proxy/auth.rb b/app/controllers/concerns/dependency_proxy/auth.rb
new file mode 100644
index 00000000000..22618ca6366
--- /dev/null
+++ b/app/controllers/concerns/dependency_proxy/auth.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module DependencyProxy
+ module Auth
+ extend ActiveSupport::Concern
+
+ included do
+ # We disable `authenticate_user!` since the `DependencyProxy::Auth` performs auth using JWT token
+ skip_before_action :authenticate_user!, raise: false
+ prepend_before_action :authenticate_user_from_jwt_token!
+ end
+
+ def authenticate_user_from_jwt_token!
+ return unless dependency_proxy_for_private_groups?
+
+ authenticate_with_http_token do |token, _|
+ user = user_from_token(token)
+ sign_in(user) if user
+ end
+
+ request_bearer_token! unless current_user
+ end
+
+ private
+
+ def dependency_proxy_for_private_groups?
+ Feature.enabled?(:dependency_proxy_for_private_groups, default_enabled: false)
+ end
+
+ def request_bearer_token!
+ # unfortunately, we cannot use https://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token.html#method-i-authentication_request
+ response.headers['WWW-Authenticate'] = ::DependencyProxy::Registry.authenticate_header
+ render plain: '', status: :unauthorized
+ end
+
+ def user_from_token(token)
+ token_payload = DependencyProxy::AuthTokenService.decoded_token_payload(token)
+ User.find(token_payload['user_id'])
+ rescue JWT::DecodeError, JWT::ExpiredSignature, JWT::ImmatureSignature
+ nil
+ end
+ end
+end
diff --git a/app/controllers/concerns/dependency_proxy/group_access.rb b/app/controllers/concerns/dependency_proxy/group_access.rb
new file mode 100644
index 00000000000..2a923d02752
--- /dev/null
+++ b/app/controllers/concerns/dependency_proxy/group_access.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module DependencyProxy
+ module GroupAccess
+ extend ActiveSupport::Concern
+
+ included do
+ before_action :verify_dependency_proxy_enabled!
+ before_action :authorize_read_dependency_proxy!
+ end
+
+ private
+
+ def verify_dependency_proxy_enabled!
+ render_404 unless group.dependency_proxy_feature_available?
+ end
+
+ def authorize_read_dependency_proxy!
+ access_denied! unless can?(current_user, :read_dependency_proxy, group)
+ end
+
+ def authorize_admin_dependency_proxy!
+ access_denied! unless can?(current_user, :admin_dependency_proxy, group)
+ end
+ end
+end
diff --git a/app/controllers/concerns/dependency_proxy_access.rb b/app/controllers/concerns/dependency_proxy_access.rb
deleted file mode 100644
index 5036d0cfce4..00000000000
--- a/app/controllers/concerns/dependency_proxy_access.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# frozen_string_literal: true
-
-module DependencyProxyAccess
- extend ActiveSupport::Concern
-
- included do
- before_action :verify_dependency_proxy_enabled!
- before_action :authorize_read_dependency_proxy!
- end
-
- private
-
- def verify_dependency_proxy_enabled!
- render_404 unless group.dependency_proxy_feature_available?
- end
-
- def authorize_read_dependency_proxy!
- access_denied! unless can?(current_user, :read_dependency_proxy, group)
- end
-
- def authorize_admin_dependency_proxy!
- access_denied! unless can?(current_user, :admin_dependency_proxy, group)
- end
-end
diff --git a/app/controllers/groups/dependency_proxies_controller.rb b/app/controllers/groups/dependency_proxies_controller.rb
index 367dbafdd59..b896b240daf 100644
--- a/app/controllers/groups/dependency_proxies_controller.rb
+++ b/app/controllers/groups/dependency_proxies_controller.rb
@@ -2,7 +2,7 @@
module Groups
class DependencyProxiesController < Groups::ApplicationController
- include DependencyProxyAccess
+ include DependencyProxy::GroupAccess
before_action :authorize_admin_dependency_proxy!, only: :update
before_action :dependency_proxy
diff --git a/app/controllers/groups/dependency_proxy_auth_controller.rb b/app/controllers/groups/dependency_proxy_auth_controller.rb
new file mode 100644
index 00000000000..e3e9bd88e24
--- /dev/null
+++ b/app/controllers/groups/dependency_proxy_auth_controller.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class Groups::DependencyProxyAuthController < ApplicationController
+ include DependencyProxy::Auth
+
+ feature_category :dependency_proxy
+
+ def authenticate
+ render plain: '', status: :ok
+ end
+end
diff --git a/app/controllers/groups/dependency_proxy_for_containers_controller.rb b/app/controllers/groups/dependency_proxy_for_containers_controller.rb
index f46902ef90f..22aea424998 100644
--- a/app/controllers/groups/dependency_proxy_for_containers_controller.rb
+++ b/app/controllers/groups/dependency_proxy_for_containers_controller.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
class Groups::DependencyProxyForContainersController < Groups::ApplicationController
- include DependencyProxyAccess
+ include DependencyProxy::Auth
+ include DependencyProxy::GroupAccess
include SendFileUpload
before_action :ensure_token_granted!
@@ -9,7 +10,7 @@ class Groups::DependencyProxyForContainersController < Groups::ApplicationContro
attr_reader :token
- feature_category :package_registry
+ feature_category :dependency_proxy
def manifest
result = DependencyProxy::PullManifestService.new(image, tag, token).execute
diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb
index 5199bb25c8c..85ee2204324 100644
--- a/app/controllers/jwt_controller.rb
+++ b/app/controllers/jwt_controller.rb
@@ -11,7 +11,8 @@ class JwtController < ApplicationController
feature_category :authentication_and_authorization
SERVICES = {
- Auth::ContainerRegistryAuthenticationService::AUDIENCE => Auth::ContainerRegistryAuthenticationService
+ ::Auth::ContainerRegistryAuthenticationService::AUDIENCE => ::Auth::ContainerRegistryAuthenticationService,
+ ::Auth::DependencyProxyAuthenticationService::AUDIENCE => ::Auth::DependencyProxyAuthenticationService
}.freeze
def auth
diff --git a/app/controllers/profiles/gpg_keys_controller.rb b/app/controllers/profiles/gpg_keys_controller.rb
index 7f04927f517..f1d4f9b2cc0 100644
--- a/app/controllers/profiles/gpg_keys_controller.rb
+++ b/app/controllers/profiles/gpg_keys_controller.rb
@@ -2,6 +2,7 @@
class Profiles::GpgKeysController < Profiles::ApplicationController
before_action :set_gpg_key, only: [:destroy, :revoke]
+ skip_before_action :authenticate_user!, only: [:get_keys]
feature_category :users
@@ -39,6 +40,24 @@ class Profiles::GpgKeysController < Profiles::ApplicationController
end
end
+ # Get all gpg keys of a user(params[:username]) in a text format
+ def get_keys
+ if params[:username].present?
+ begin
+ user = UserFinder.new(params[:username]).find_by_username
+ if user.present?
+ render plain: user.gpg_keys.select(&:verified?).map(&:key).join("\n")
+ else
+ render_404
+ end
+ rescue => e
+ render html: e.message
+ end
+ else
+ render_404
+ end
+ end
+
private
def gpg_key_params
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index 1f998e0083a..4610756192a 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -15,7 +15,7 @@ class Projects::JobsController < Projects::ApplicationController
before_action :authorize_create_proxy_build!, only: :proxy_websocket_authorize
before_action :verify_proxy_request!, only: :proxy_websocket_authorize
before_action do
- push_frontend_feature_flag(:ci_job_line_links, @project)
+ push_frontend_feature_flag(:ci_job_line_links, @project, default_enabled: true)
end
before_action only: :index do
frontend_experimentation_tracking_data(:jobs_empty_state, 'click_button')
diff --git a/app/controllers/repositories/git_http_controller.rb b/app/controllers/repositories/git_http_controller.rb
index 639e403d797..3cf0a23b7f6 100644
--- a/app/controllers/repositories/git_http_controller.rb
+++ b/app/controllers/repositories/git_http_controller.rb
@@ -80,6 +80,8 @@ module Repositories
return if Gitlab::Database.read_only?
return unless repo_type.project?
+ OnboardingProgressService.new(project.namespace).execute(action: :git_read)
+
if Feature.enabled?(:project_statistics_sync, project, default_enabled: true)
Projects::FetchStatisticsIncrementService.new(project).execute
else
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 1f289265916..547977c01a9 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -319,12 +319,6 @@ module IssuablesHelper
issuable_path(issuable, close_reopen_params(issuable, :reopen))
end
- def toggle_draft_issuable_path(issuable)
- wip_event = issuable.work_in_progress? ? 'unwip' : 'wip'
-
- issuable_path(issuable, { merge_request: { wip_event: wip_event } })
- end
-
def issuable_path(issuable, *options)
polymorphic_path(issuable, *options)
end
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index 9cb7edbaeb6..35ceddada5f 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -39,19 +39,6 @@ module MergeRequestsHelper
end
end
- def ci_build_details_path(merge_request)
- build_url = merge_request.source_project.ci_service.build_page(merge_request.diff_head_sha, merge_request.source_branch)
- return unless build_url
-
- parsed_url = URI.parse(build_url)
-
- unless parsed_url.userinfo.blank?
- parsed_url.userinfo = ''
- end
-
- parsed_url.to_s
- end
-
def merge_path_description(merge_request, separator)
if merge_request.for_fork?
"Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.full_path}:#{@merge_request.target_branch}"
@@ -166,6 +153,12 @@ module MergeRequestsHelper
current_user.fork_of(project)
end
end
+
+ def toggle_draft_merge_request_path(issuable)
+ wip_event = issuable.work_in_progress? ? 'unwip' : 'wip'
+
+ issuable_path(issuable, { merge_request: { wip_event: wip_event } })
+ end
end
MergeRequestsHelper.prepend_if_ee('EE::MergeRequestsHelper')
diff --git a/app/models/dependency_proxy/registry.rb b/app/models/dependency_proxy/registry.rb
index 471d5be2600..6492acf325a 100644
--- a/app/models/dependency_proxy/registry.rb
+++ b/app/models/dependency_proxy/registry.rb
@@ -1,8 +1,9 @@
# frozen_string_literal: true
class DependencyProxy::Registry
- AUTH_URL = 'https://auth.docker.io'.freeze
- LIBRARY_URL = 'https://registry-1.docker.io/v2'.freeze
+ AUTH_URL = 'https://auth.docker.io'
+ LIBRARY_URL = 'https://registry-1.docker.io/v2'
+ PROXY_AUTH_URL = Gitlab::Utils.append_path(Gitlab.config.gitlab.url, "jwt/auth")
class << self
def auth_url(image)
@@ -17,6 +18,10 @@ class DependencyProxy::Registry
"#{LIBRARY_URL}/#{image_path(image)}/blobs/#{blob_sha}"
end
+ def authenticate_header
+ "Bearer realm=\"#{PROXY_AUTH_URL}\",service=\"#{::Auth::DependencyProxyAuthenticationService::AUDIENCE}\""
+ end
+
private
def image_path(image)
diff --git a/app/models/namespace_onboarding_action.rb b/app/models/namespace_onboarding_action.rb
index 7c3ab051d93..e1121279e2e 100644
--- a/app/models/namespace_onboarding_action.rb
+++ b/app/models/namespace_onboarding_action.rb
@@ -1,10 +1,14 @@
# frozen_string_literal: true
class NamespaceOnboardingAction < ApplicationRecord
- belongs_to :namespace
+ belongs_to :namespace, optional: false
+
+ validates :action, presence: true
ACTIONS = {
- subscription_created: 1
+ subscription_created: 1,
+ git_write: 2,
+ git_read: 4
}.freeze
enum action: ACTIONS
diff --git a/app/models/user_detail.rb b/app/models/user_detail.rb
index 9674f9a41da..ef799b01452 100644
--- a/app/models/user_detail.rb
+++ b/app/models/user_detail.rb
@@ -31,3 +31,5 @@ class UserDetail < ApplicationRecord
self.bio = '' if bio_changed? && bio.nil?
end
end
+
+UserDetail.prepend_if_ee('EE::UserDetail')
diff --git a/app/services/auth/dependency_proxy_authentication_service.rb b/app/services/auth/dependency_proxy_authentication_service.rb
new file mode 100644
index 00000000000..1b8c16b7c79
--- /dev/null
+++ b/app/services/auth/dependency_proxy_authentication_service.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module Auth
+ class DependencyProxyAuthenticationService < BaseService
+ AUDIENCE = 'dependency_proxy'
+ HMAC_KEY = 'gitlab-dependency-proxy'
+ DEFAULT_EXPIRE_TIME = 1.minute
+
+ def execute(authentication_abilities:)
+ return error('dependency proxy not enabled', 404) unless ::Gitlab.config.dependency_proxy.enabled
+ return error('access forbidden', 403) unless current_user
+
+ { token: authorized_token.encoded }
+ end
+
+ class << self
+ include ::Gitlab::Utils::StrongMemoize
+
+ def secret
+ strong_memoize(:secret) do
+ OpenSSL::HMAC.hexdigest(
+ 'sha256',
+ ::Settings.attr_encrypted_db_key_base,
+ HMAC_KEY
+ )
+ end
+ end
+
+ def token_expire_at
+ Time.current + Gitlab::CurrentSettings.container_registry_token_expire_delay.minutes
+ end
+ end
+
+ private
+
+ def authorized_token
+ JSONWebToken::HMACToken.new(self.class.secret).tap do |token|
+ token['user_id'] = current_user.id
+ token.expire_time = self.class.token_expire_at
+ end
+ end
+ end
+end
diff --git a/app/services/dependency_proxy/auth_token_service.rb b/app/services/dependency_proxy/auth_token_service.rb
new file mode 100644
index 00000000000..16279ed12b0
--- /dev/null
+++ b/app/services/dependency_proxy/auth_token_service.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module DependencyProxy
+ class AuthTokenService < DependencyProxy::BaseService
+ attr_reader :token
+
+ def initialize(token)
+ @token = token
+ end
+
+ def execute
+ JSONWebToken::HMACToken.decode(token, ::Auth::DependencyProxyAuthenticationService.secret).first
+ end
+
+ class << self
+ def decoded_token_payload(token)
+ self.new(token).execute
+ end
+ end
+ end
+end
diff --git a/app/services/onboarding_progress_service.rb b/app/services/onboarding_progress_service.rb
new file mode 100644
index 00000000000..c45edcaaf33
--- /dev/null
+++ b/app/services/onboarding_progress_service.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class OnboardingProgressService
+ def initialize(namespace)
+ @namespace = namespace
+ end
+
+ def execute(action:)
+ NamespaceOnboardingAction.create_action(@namespace, action)
+ end
+end
diff --git a/app/services/post_receive_service.rb b/app/services/post_receive_service.rb
index 79b613f6a88..bd9588844ad 100644
--- a/app/services/post_receive_service.rb
+++ b/app/services/post_receive_service.rb
@@ -40,6 +40,8 @@ class PostReceiveService
response.add_basic_message(redirect_message)
response.add_basic_message(project_created_message)
+
+ record_onboarding_progress
end
response
@@ -90,6 +92,10 @@ class PostReceiveService
banner&.message
end
+
+ def record_onboarding_progress
+ NamespaceOnboardingAction.create_action(project.namespace, :git_write)
+ end
end
PostReceiveService.prepend_if_ee('EE::PostReceiveService')
diff --git a/app/views/groups/dependency_proxies/show.html.haml b/app/views/groups/dependency_proxies/show.html.haml
index ff1312eb763..abbfb2d3b91 100644
--- a/app/views/groups/dependency_proxies/show.html.haml
+++ b/app/views/groups/dependency_proxies/show.html.haml
@@ -7,7 +7,7 @@
- link_start = '<a href="%{url}">'.html_safe % { url: help_page_path('user/packages/dependency_proxy/index') }
= _('Create a local proxy for storing frequently used upstream images. %{link_start}Learn more%{link_end} about dependency proxies.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
-- if @group.public?
+- if Feature.enabled?(:dependency_proxy_for_private_groups, default_enabled: false) || @group.public?
- if can?(current_user, :admin_dependency_proxy, @group)
= form_for(@dependency_proxy, method: :put, url: group_dependency_proxy_path(@group)) do |f|
.form-group
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 794d1589172..8aba9426ec0 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -74,6 +74,7 @@
%span.gl-sr-only
= s_('Nav|Help')
= sprite_icon('question')
+ %span.notification-dot.rounded-circle.gl-absolute
= sprite_icon('chevron-down', css_class: 'caret-down')
.dropdown-menu.dropdown-menu-right
= render 'layouts/header/help_dropdown'
diff --git a/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml b/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml
new file mode 100644
index 00000000000..3a8629b3b6e
--- /dev/null
+++ b/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml
@@ -0,0 +1,37 @@
+- display_issuable_type = issuable_display_type(@merge_request)
+- button_action_class = @merge_request.closed? ? 'btn-default' : 'btn-warning btn-warning-secondary'
+- button_class = "btn gl-button #{!@merge_request.closed? && 'js-draft-toggle-button'}"
+- toggle_class = "btn gl-button dropdown-toggle"
+
+.float-left.btn-group.gl-ml-3.gl-display-none.gl-display-md-flex
+ = link_to @merge_request.closed? ? reopen_issuable_path(@merge_request) : toggle_draft_merge_request_path(@merge_request), method: :put, class: "#{button_class} #{button_action_class}" do
+ - if @merge_request.closed?
+ = _('Reopen')
+ = display_issuable_type
+ - else
+ = @merge_request.work_in_progress? ? _('Mark as ready') : _('Mark as draft')
+
+ - if !@merge_request.closed? || !issuable_author_is_current_user(@merge_request)
+ = button_tag type: 'button', class: "#{toggle_class} #{button_action_class}", data: { 'toggle' => 'dropdown' } do
+ %span.gl-sr-only= _('Toggle dropdown')
+ = sprite_icon "angle-down", size: 12
+
+ %ul.dropdown-menu.dropdown-menu-right
+ - if @merge_request.open?
+ %li
+ = link_to close_issuable_path(@merge_request), method: :put do
+ .description
+ %strong.title
+ = _('Close')
+ = display_issuable_type
+
+ - unless issuable_author_is_current_user(@merge_request)
+ - unless @merge_request.closed?
+ %li.divider.droplab-item-ignore
+
+ %li
+ %a{ href: new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request)) }
+ .description
+ %strong.title= _('Report abuse')
+ %p.text.gl-mb-0
+ = _('Report %{display_issuable_type} that are abusive, inappropriate or spam.') % { display_issuable_type: display_issuable_type.pluralize }
diff --git a/app/views/projects/merge_requests/_mr_title.html.haml b/app/views/projects/merge_requests/_mr_title.html.haml
index 5b30b6e3379..f7cc15cec5a 100644
--- a/app/views/projects/merge_requests/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/_mr_title.html.haml
@@ -25,8 +25,8 @@
= sprite_icon('chevron-double-lg-left')
.detail-page-header-actions.js-issuable-actions
- .clearfix.issue-btn-group.dropdown
- %button.gl-button.btn.btn-default.float-left.gl-display-md-none{ type: "button", data: { toggle: "dropdown" } }
+ .clearfix.dropdown
+ %button.gl-button.btn.btn-default.float-left.gl-display-md-none.gl-w-full{ type: "button", data: { toggle: "dropdown" } }
Options
= sprite_icon('chevron-down', css_class: 'gl-text-gray-500')
.dropdown-menu.dropdown-menu-right
@@ -35,12 +35,12 @@
%li= link_to 'Edit', edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
- if @merge_request.opened?
%li
- = link_to @merge_request.work_in_progress? ? _('Mark as ready') : _('Mark as draft'), toggle_draft_issuable_path(@merge_request), method: :put, class: "js-draft-toggle-button"
+ = link_to @merge_request.work_in_progress? ? _('Mark as ready') : _('Mark as draft'), toggle_draft_merge_request_path(@merge_request), method: :put, class: "js-draft-toggle-button"
%li{ class: [merge_request_button_visibility(@merge_request, true), 'js-close-item'] }
= link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, title: 'Close merge request'
- if can_reopen_merge_request
%li{ class: merge_request_button_visibility(@merge_request, false) }
- = link_to 'Reopen', merge_request_path(@merge_request, merge_request: { state_event: :reopen }), method: :put, class: 'reopen-mr-link', title: 'Reopen merge request'
+ = link_to 'Reopen', merge_request_path(@merge_request, merge_request: { state_event: :reopen }), method: :put, title: 'Reopen merge request'
- unless @merge_request.merged? || current_user == @merge_request.author
%li= link_to 'Report abuse', new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request))
@@ -48,6 +48,6 @@
= link_to 'Edit', edit_project_merge_request_path(@project, @merge_request), class: "d-none d-md-block btn gl-button btn-grouped js-issuable-edit qa-edit-button"
- if can_update_merge_request && !are_close_and_open_buttons_hidden
- = render 'shared/issuable/close_reopen_draft_report_toggle', issuable: @merge_request
+ = render 'projects/merge_requests/close_reopen_draft_report_toggle'
- elsif !@merge_request.merged?
= link_to _('Report abuse'), new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request)), class: 'gl-display-none gl-display-md-block gl-button btn btn-warning-secondary float-right gl-ml-3', title: _('Report abuse')
diff --git a/app/views/shared/issuable/_close_reopen_draft_report_toggle.html.haml b/app/views/shared/issuable/_close_reopen_draft_report_toggle.html.haml
deleted file mode 100644
index 250e1516318..00000000000
--- a/app/views/shared/issuable/_close_reopen_draft_report_toggle.html.haml
+++ /dev/null
@@ -1,37 +0,0 @@
-- display_issuable_type = issuable_display_type(issuable)
-- button_action_class = issuable.closed? ? 'btn-default' : 'btn-warning btn-warning-secondary'
-- button_class = "btn gl-button #{!issuable.closed? && 'js-draft-toggle-button'}"
-- toggle_class = "btn gl-button dropdown-toggle"
-
-.float-left.btn-group.gl-ml-3.issuable-close-dropdown.d-none.d-md-inline-flex.js-issuable-close-dropdown
- = link_to issuable.closed? ? reopen_issuable_path(issuable) : toggle_draft_issuable_path(issuable), method: :put, class: "#{button_class} #{button_action_class}" do
- - if issuable.closed?
- = _('Reopen')
- = display_issuable_type
- - else
- = issuable.work_in_progress? ? _('Mark as ready') : _('Mark as draft')
-
- - if !issuable.closed? || !issuable_author_is_current_user(issuable)
- = button_tag type: 'button', class: "#{toggle_class} #{button_action_class}", data: { 'toggle' => 'dropdown' } do
- %span.sr-only= _('Toggle dropdown')
- = sprite_icon "angle-down", size: 12
-
- %ul.js-issuable-close-menu.dropdown-menu.dropdown-menu-right
- - if issuable.open?
- %li
- = link_to close_issuable_path(issuable), method: :put do
- .description
- %strong.title
- = _('Close')
- = display_issuable_type
-
- - unless issuable_author_is_current_user(issuable)
- - unless issuable.closed?
- %li.divider.droplab-item-ignore
-
- %li.report-item
- %a.report-abuse-link{ href: new_abuse_report_path(user_id: issuable.author.id, ref_url: merge_request_url(issuable)) }
- .description
- %strong.title= _('Report abuse')
- %p.text
- = _('Report %{display_issuable_type} that are abusive, inappropriate or spam.') % { display_issuable_type: display_issuable_type.pluralize }