Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
bf360857d9
commit
ff3955ef8e
6
Gemfile
6
Gemfile
|
|
@ -20,11 +20,7 @@ gem 'bootsnap', '~> 1.16.0', require: false
|
|||
# Pin openssl to match the version bundled with our supported Rubies.
|
||||
# See https://stdgems.org/openssl/#gem-version.
|
||||
gem 'openssl', '2.2.2'
|
||||
# This gem was originally bundled with Ruby 2.7, but is unbundled as of Ruby 3.
|
||||
# Since the latest version caused problems with GitLab, we pin this to an older
|
||||
# version for now.
|
||||
# See https://gitlab.com/gitlab-org/gitlab/-/issues/376417
|
||||
gem 'ipaddr', '1.2.2'
|
||||
gem 'ipaddr', '~> 1.2.5'
|
||||
|
||||
# Responders respond_to and respond_with
|
||||
gem 'responders', '~> 3.0'
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@
|
|||
{"name":"ice_nine","version":"0.11.2","platform":"ruby","checksum":"5d506a7d2723d5592dc121b9928e4931742730131f22a1a37649df1c1e2e63db"},
|
||||
{"name":"imagen","version":"0.1.8","platform":"ruby","checksum":"fde7b727d4fe79c6bb5ac46c1f7184bf87a6d54df54d712ad2be039d2f93a162"},
|
||||
{"name":"invisible_captcha","version":"2.0.0","platform":"ruby","checksum":"a381edcb1d1b8744e9dc398ecad142c3e2ab077604645f85eeb02f9ea535c042"},
|
||||
{"name":"ipaddr","version":"1.2.2","platform":"ruby","checksum":"27916ee6367d549850d3675bc020f1f1ddafbbe1cfc58635f17dfa56c42f9f79"},
|
||||
{"name":"ipaddr","version":"1.2.5","platform":"ruby","checksum":"4e679c71d6d8ed99f925487082f70f9a958de155591caa0e7f6cef9aa160f17a"},
|
||||
{"name":"ipaddress","version":"0.8.3","platform":"ruby","checksum":"85640c4f9194c26937afc8c78e3074a8e7c97d5d1210358d1440f01034d006f5"},
|
||||
{"name":"jaeger-client","version":"1.1.0","platform":"ruby","checksum":"cb5e9b9bbee6ee8d6a82d03d947a5b04543d8c0a949c22e484254f18d8a458a8"},
|
||||
{"name":"jaro_winkler","version":"1.5.4","platform":"java","checksum":"0454333a50b44a09745878bfe57859893631ff7dfe48c029827894944514fe7c"},
|
||||
|
|
|
|||
|
|
@ -819,7 +819,7 @@ GEM
|
|||
parser (>= 2.5, != 2.5.1.1)
|
||||
invisible_captcha (2.0.0)
|
||||
rails (>= 5.0)
|
||||
ipaddr (1.2.2)
|
||||
ipaddr (1.2.5)
|
||||
ipaddress (0.8.3)
|
||||
jaeger-client (1.1.0)
|
||||
opentracing (~> 0.3)
|
||||
|
|
@ -1767,7 +1767,7 @@ DEPENDENCIES
|
|||
httparty (~> 0.20.0)
|
||||
icalendar
|
||||
invisible_captcha (~> 2.0.0)
|
||||
ipaddr (= 1.2.2)
|
||||
ipaddr (~> 1.2.5)
|
||||
ipaddress (~> 0.8.3)
|
||||
ipynbdiff!
|
||||
jira-ruby (~> 2.1.4)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
@import './pages/hierarchy';
|
||||
@import './pages/issues';
|
||||
@import './pages/labels';
|
||||
@import './pages/login';
|
||||
@import './pages/merge_requests';
|
||||
@import './pages/note_form';
|
||||
@import './pages/notes';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
@import 'framework/variables';
|
||||
@import 'mixins_and_variables_and_functions';
|
||||
|
||||
/* Login Page */
|
||||
.login-page {
|
||||
.container {
|
||||
|
|
@ -756,222 +756,6 @@ input:-ms-input-placeholder {
|
|||
svg {
|
||||
fill: currentColor;
|
||||
}
|
||||
.login-page .container {
|
||||
max-width: 960px;
|
||||
}
|
||||
.login-page .navbar-gitlab .container {
|
||||
max-width: none;
|
||||
}
|
||||
.login-page .flash-container {
|
||||
margin-bottom: 16px;
|
||||
position: relative;
|
||||
top: 8px;
|
||||
}
|
||||
.login-page .brand-holder {
|
||||
font-size: 18px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.login-page .brand-holder p {
|
||||
font-size: 16px;
|
||||
color: #888;
|
||||
}
|
||||
.login-page .brand-holder h3 {
|
||||
font-size: 22px;
|
||||
}
|
||||
.login-page .brand-holder img {
|
||||
max-width: 100%;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.login-page .brand-holder a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.login-page p {
|
||||
font-size: 13px;
|
||||
}
|
||||
.login-page .signin-text p {
|
||||
margin-bottom: 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.login-page .borderless .login-box,
|
||||
.login-page .borderless .omniauth-container {
|
||||
box-shadow: none;
|
||||
}
|
||||
.login-page .borderless .g-recaptcha > div {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.login-page .login-box,
|
||||
.login-page .omniauth-container {
|
||||
box-shadow: 0 0 0 1px #dcdcde;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
.login-page .login-box .login-heading h3,
|
||||
.login-page .omniauth-container .login-heading h3 {
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
.login-page .login-box .login-footer,
|
||||
.login-page .omniauth-container .login-footer {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.login-page .login-box .login-footer p:last-child,
|
||||
.login-page .omniauth-container .login-footer p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.login-page .login-box a.forgot,
|
||||
.login-page .omniauth-container a.forgot {
|
||||
float: right;
|
||||
padding-top: 6px;
|
||||
}
|
||||
.login-page .login-box .nav .active a,
|
||||
.login-page .omniauth-container .nav .active a {
|
||||
background: transparent;
|
||||
}
|
||||
.login-page .login-box .login-body,
|
||||
.login-page .omniauth-container .login-body {
|
||||
font-size: 13px;
|
||||
}
|
||||
.login-page .login-box .login-body input + p,
|
||||
.login-page .login-box .login-body input ~ p.field-validation,
|
||||
.login-page .omniauth-container .login-body input + p,
|
||||
.login-page .omniauth-container .login-body input ~ p.field-validation {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.login-page .login-box .login-body .username .validation-success,
|
||||
.login-page .omniauth-container .login-body .username .validation-success {
|
||||
color: #217645;
|
||||
}
|
||||
.login-page .login-box .login-body .username .validation-error,
|
||||
.login-page .omniauth-container .login-body .username .validation-error {
|
||||
color: #dd2b0e;
|
||||
}
|
||||
.login-page .omniauth-container {
|
||||
border-radius: 0.25rem;
|
||||
font-size: 13px;
|
||||
}
|
||||
.login-page .omniauth-container p {
|
||||
margin: 0;
|
||||
}
|
||||
.login-page .omniauth-container form {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: none;
|
||||
}
|
||||
.login-page .new-session-tabs {
|
||||
display: flex;
|
||||
box-shadow: 0 0 0 1px #dcdcde;
|
||||
border-top-right-radius: 4px;
|
||||
border-top-left-radius: 4px;
|
||||
}
|
||||
.login-page .new-session-tabs.nav-links-unboxed {
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
.login-page .new-session-tabs.nav-links-unboxed .nav-item {
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
border-bottom: 1px solid #dcdcde;
|
||||
background-color: transparent;
|
||||
}
|
||||
.login-page .new-session-tabs.custom-provider-tabs {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.login-page .new-session-tabs.custom-provider-tabs li {
|
||||
min-width: 85px;
|
||||
flex-basis: auto;
|
||||
}
|
||||
.login-page .new-session-tabs.custom-provider-tabs li:nth-child(n + 5) {
|
||||
border-top: 1px solid #dcdcde;
|
||||
}
|
||||
.login-page .new-session-tabs.custom-provider-tabs a {
|
||||
font-size: 16px;
|
||||
}
|
||||
.login-page .new-session-tabs li {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
border-left: 1px solid #dcdcde;
|
||||
}
|
||||
.login-page .new-session-tabs li:first-of-type {
|
||||
border-left: 0;
|
||||
border-top-left-radius: 4px;
|
||||
}
|
||||
.login-page .new-session-tabs li:last-of-type {
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
.login-page .new-session-tabs li:not(.active) {
|
||||
background-color: #fbfafd;
|
||||
}
|
||||
.login-page .new-session-tabs li a {
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
}
|
||||
.login-page .new-session-tabs li.active > a {
|
||||
cursor: default;
|
||||
}
|
||||
.login-page .form-control:active,
|
||||
.login-page .form-control:focus {
|
||||
background-color: #fff;
|
||||
}
|
||||
.login-page .submit-container {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.login-page input[type="submit"] {
|
||||
margin-bottom: 0;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
.login-page .devise-errors h2 {
|
||||
margin-top: 0;
|
||||
font-size: 14px;
|
||||
color: #ae1800;
|
||||
}
|
||||
@media (max-width: 575.98px) {
|
||||
.login-page .col-md-5.float-right {
|
||||
float: none !important;
|
||||
margin-bottom: 45px;
|
||||
}
|
||||
}
|
||||
.devise-layout-html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
.devise-layout-html body {
|
||||
height: calc(100% - 51px);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.devise-layout-html body.navless {
|
||||
height: calc(100% - 11px);
|
||||
}
|
||||
.devise-layout-html body .page-wrap {
|
||||
min-height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.devise-layout-html body .footer-container,
|
||||
.devise-layout-html body hr.footer-fixed {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 40px;
|
||||
background: #fff;
|
||||
}
|
||||
.devise-layout-html body .login-page-broadcast {
|
||||
margin-top: 40px;
|
||||
}
|
||||
.devise-layout-html body .navless-container {
|
||||
padding: 0 15px 65px;
|
||||
}
|
||||
.devise-layout-html body .flash-container {
|
||||
padding-bottom: 65px;
|
||||
}
|
||||
@media (max-width: 575.98px) {
|
||||
.devise-layout-html body .flash-container {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.gl-display-flex {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ module Types
|
|||
class UserAchievementType < BaseObject
|
||||
graphql_name 'UserAchievement'
|
||||
|
||||
authorize :read_achievement
|
||||
authorize :read_user_achievement
|
||||
|
||||
field :id,
|
||||
::Types::GlobalIDType[::Achievements::UserAchievement],
|
||||
|
|
|
|||
|
|
@ -3,5 +3,10 @@
|
|||
module Achievements
|
||||
class UserAchievementPolicy < ::BasePolicy
|
||||
delegate { @subject.achievement.namespace }
|
||||
delegate { @subject.user }
|
||||
|
||||
rule { can?(:read_user_profile) | can?(:admin_achievement) }.enable :read_user_achievement
|
||||
|
||||
rule { ~can?(:read_achievement) }.prevent :read_user_achievement
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
- page_title _('Enter Admin Mode')
|
||||
- add_page_specific_style 'page_bundles/login'
|
||||
|
||||
.row.justify-content-center
|
||||
.col-md-5.new-session-forms-container
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
- page_title _('Enter 2FA for Admin Mode')
|
||||
- add_page_specific_style 'page_bundles/login'
|
||||
|
||||
.row.justify-content-center
|
||||
.col-md-5.new-session-forms-container
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
- add_page_specific_style 'page_bundles/login'
|
||||
!!! 5
|
||||
%html.devise-layout-html{ class: system_message_class }
|
||||
= render "layouts/head", { startup_filename: 'signin' }
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
- add_page_specific_style 'page_bundles/login'
|
||||
!!! 5
|
||||
%html.devise-layout-html{ lang: "en", class: system_message_class }
|
||||
= render "layouts/head"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
!!! 5
|
||||
%html.devise-layout-html.navless{ class: system_message_class }
|
||||
- add_page_specific_style 'page_bundles/signup'
|
||||
- add_page_specific_style 'page_bundles/login'
|
||||
= render "layouts/head"
|
||||
%body.signup-page{ class: "#{user_application_theme} #{client_class_list}", data: { page: body_data_page, qa_selector: 'signup_page' } }
|
||||
= render "layouts/header/logo_with_title"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
!!! 5
|
||||
%html{ lang: "en" }
|
||||
= render "layouts/head"
|
||||
- add_page_specific_style 'page_bundles/login'
|
||||
%body.login-page.application.navless{ class: user_application_theme, data: { page: body_data_page } }
|
||||
= render "layouts/header/logo_with_title"
|
||||
= render "layouts/broadcast"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
- @html_class = "subscriptions-layout-html"
|
||||
- page_title _('Your profile')
|
||||
- add_page_specific_style 'page_bundles/signup'
|
||||
- add_page_specific_style 'page_bundles/login'
|
||||
- gitlab_experience_text = _('To personalize your GitLab experience, we\'d like to know a bit more about you')
|
||||
- content_for :page_specific_javascripts do
|
||||
= render "layouts/google_tag_manager_head"
|
||||
|
|
|
|||
|
|
@ -51,7 +51,8 @@
|
|||
.avatar-holder
|
||||
= link_to avatar_icon_for_user(@user, 400, current_user: current_user), target: '_blank', rel: 'noopener noreferrer' do
|
||||
= render Pajamas::AvatarComponent.new(@user, alt: "", size: 96, avatar_options: { itemprop: "image" })
|
||||
#js-user-achievements{ data: { root_url: root_url, user_id: @user.id } }
|
||||
- if Ability.allowed?(current_user, :read_user_profile, @user)
|
||||
#js-user-achievements{ data: { root_url: root_url, user_id: @user.id } }
|
||||
.gl-display-inline-block.gl-vertical-align-top.gl-text-left.gl-max-w-80
|
||||
- if @user.blocked? || !@user.confirmed?
|
||||
.user-info
|
||||
|
|
|
|||
|
|
@ -294,6 +294,7 @@ module Gitlab
|
|||
config.assets.precompile << "page_bundles/jira_connect.css"
|
||||
config.assets.precompile << "page_bundles/jira_connect_users.css"
|
||||
config.assets.precompile << "page_bundles/learn_gitlab.css"
|
||||
config.assets.precompile << "page_bundles/login.css"
|
||||
config.assets.precompile << "page_bundles/marketing_popover.css"
|
||||
config.assets.precompile << "page_bundles/members.css"
|
||||
config.assets.precompile << "page_bundles/merge_conflicts.css"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class FinalizeIssuesIidScopingToNamespace < Gitlab::Database::Migration[2.1]
|
||||
MIGRATION = 'IssuesInternalIdScopeUpdater'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
def up
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: MIGRATION,
|
||||
table_name: :internal_ids,
|
||||
column_name: :id,
|
||||
job_arguments: [],
|
||||
finalize: true)
|
||||
end
|
||||
|
||||
def down; end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
b0091fc76ead45dab7a0cd4d2b0a65858703cb18a98cca7715b88bceac8c2ed0
|
||||
|
|
@ -327,6 +327,7 @@ ETag
|
|||
ETags
|
||||
Etsy
|
||||
Excon
|
||||
exfiltrate
|
||||
exfiltration
|
||||
ExifTool
|
||||
expirable
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ If you are coming to GitLab from another platform, the following information is
|
|||
| Topic | Description |
|
||||
|:----------------------------------------------------|:------------|
|
||||
| [Importing to GitLab](user/project/import/index.md) | Import your projects from GitHub, Bitbucket, GitLab.com, FogBugz, and SVN into GitLab. |
|
||||
| [Migrating from SVN](user/project/import/svn.md) | Convert a SVN repository to Git and GitLab. |
|
||||
| [Migrating from SVN](user/project/import/index.md#import-from-subversion) | Convert a SVN repository to Git and GitLab. |
|
||||
|
||||
## Build an integration with GitLab
|
||||
|
||||
|
|
|
|||
|
|
@ -617,6 +617,12 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
|
|||
migration might take multiple hours or days to complete on larger GitLab instances. Make sure the migration
|
||||
has completed successfully before upgrading to 15.7.0 or later.
|
||||
- Due to [a bug introduced in GitLab 15.4](https://gitlab.com/gitlab-org/gitlab/-/issues/390155), if one or more Git repositories in Gitaly Cluster is [unavailable](../administration/gitaly/recovery.md#unavailable-repositories), then [Repository checks](../administration/repository_checks.md#repository-checks) and [Geo replication and verification](../administration/geo/index.md) stop running for all project or project wiki repositories in the affected Gitaly Cluster. The bug was fixed by [reverting the change in GitLab 15.9.0](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110823). Before upgrading to this version, check if you have any "unavailable" repositories. See [the bug issue](https://gitlab.com/gitlab-org/gitlab/-/issues/390155) for more information.
|
||||
- A redesigned sign-in page is enabled by default in GitLab 15.4 and later, with improvements shipping in later releases. For more information, see [epic 8557](https://gitlab.com/groups/gitlab-org/-/epics/8557).
|
||||
It can be disabled with a feature flag. Start [a Rails console](../administration/operations/rails_console.md) and run:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:restyle_login_page)
|
||||
```
|
||||
|
||||
### 15.3.4
|
||||
|
||||
|
|
|
|||
|
|
@ -425,7 +425,8 @@ For more information, see
|
|||
|
||||
## Users with minimal access **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40942) in GitLab 13.4.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40942) in GitLab 13.4.
|
||||
> - Support for inviting users with minimal access role [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106438) in GitLab 15.9.
|
||||
|
||||
Owners can add members with a "minimal access" role to a root group. Such users do not:
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,20 @@ Keep in mind the limitations of [migrating using file exports](../settings/impor
|
|||
When migrating from self-managed to GitLab.com, user associations (such as comment author)
|
||||
are changed to the user who is importing the projects.
|
||||
|
||||
## Security
|
||||
|
||||
Only import projects from sources you trust. If you import a project from an untrusted source,
|
||||
an attacker could steal your sensitive data. For example, an imported project
|
||||
with a malicious `.gitlab-ci.yml` file could allow an attacker to exfiltrate group CI/CD variables.
|
||||
|
||||
GitLab self-managed administrators can reduce their attack surface by disabling import sources they don't need:
|
||||
|
||||
1. On the top bar, select **Main menu > Admin**.
|
||||
1. On the left sidebar, select **Settings > General**.
|
||||
1. Expand **Visibility and access controls**.
|
||||
1. Scroll to **Import sources**.
|
||||
1. Clear checkboxes for importers that are not required.
|
||||
|
||||
## Available project importers
|
||||
|
||||
You can import projects from:
|
||||
|
|
@ -65,7 +79,7 @@ You can then [connect your external repository to get CI/CD benefits](../../../c
|
|||
|
||||
GitLab can not automatically migrate Subversion repositories to Git. Converting Subversion repositories to Git can be difficult, but several tools exist including:
|
||||
|
||||
- [`git svn`](https://git-scm.com/book/en/v2/Git-and-Other-Systems-Migrating-to-Git), for very small and simple repositories.
|
||||
- [`git svn`](https://git-scm.com/book/en/v2/Git-and-Other-Systems-Migrating-to-Git), for very small and basic repositories.
|
||||
- [`reposurgeon`](http://www.catb.org/~esr/reposurgeon/repository-editing.html), for larger and more complex repositories.
|
||||
|
||||
## Migrate using the API
|
||||
|
|
@ -82,10 +96,9 @@ over a series of Docker pulls and pushes. Re-run any CI pipelines to retrieve an
|
|||
|
||||
## Migrate between two self-managed GitLab instances
|
||||
|
||||
To migrate from an existing self-managed GitLab instance to a new self-managed GitLab instance, it's
|
||||
best to [back up](../../../raketasks/backup_restore.md)
|
||||
the existing instance and restore it on the new instance. For example, this is useful when migrating
|
||||
a self-managed instance from an old server to a new server.
|
||||
To migrate from an existing self-managed GitLab instance to a new self-managed GitLab instance,
|
||||
you should [back up](../../../raketasks/backup_restore.md)
|
||||
the existing instance and restore it on the new instance. For example, you could use this method to migrate a self-managed instance from an old server to a new server.
|
||||
|
||||
The backups produced don't depend on the operating system running GitLab. You can therefore use
|
||||
the restore method to switch between different operating system distributions or versions, as long
|
||||
|
|
@ -159,17 +172,3 @@ For more information, see:
|
|||
including settings that need checking afterwards and other limitations.
|
||||
|
||||
For support, customers must enter into a paid engagement with GitLab Professional Services.
|
||||
|
||||
## Security
|
||||
|
||||
Only import projects from sources you trust. If you import a project from an untrusted source, it
|
||||
may be possible for an attacker to steal your sensitive data. For example, an imported project
|
||||
with a malicious `.gitlab-ci.yml` file could allow an attacker to exfiltrate group CI/CD variables.
|
||||
|
||||
GitLab self-managed administrators can reduce their attack surface by disabling import sources they don't need:
|
||||
|
||||
1. On the top bar, select **Main menu > Admin**.
|
||||
1. On the left sidebar, select **Settings > General**.
|
||||
1. Expand **Visibility and access controls**.
|
||||
1. Scroll to **Import sources**.
|
||||
1. Clear checkboxes for importers that are not required.
|
||||
|
|
|
|||
|
|
@ -12,11 +12,32 @@ module Gitlab
|
|||
end
|
||||
|
||||
def match?(requested_ip, requested_port = nil)
|
||||
return false unless ip.include?(requested_ip)
|
||||
requested_ip = IPAddr.new(requested_ip) if requested_ip.is_a?(String)
|
||||
|
||||
return false unless ip_include?(requested_ip)
|
||||
return true if port.nil?
|
||||
|
||||
port == requested_port
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Prior to ipaddr v1.2.3, if the allow list were the IPv4 to IPv6
|
||||
# mapped address ::ffff:169.254.168.100 and the requested IP were
|
||||
# 169.254.168.100 or ::ffff:169.254.168.100, the IP would be
|
||||
# considered in the allow list. However, with
|
||||
# https://github.com/ruby/ipaddr/pull/31, IPAddr#include? will
|
||||
# only match if the IP versions are the same. This method
|
||||
# preserves backwards compatibility if the versions differ by
|
||||
# checking inclusion by coercing an IPv4 address to its IPv6
|
||||
# mapped address.
|
||||
def ip_include?(requested_ip)
|
||||
return true if ip.include?(requested_ip)
|
||||
return ip.include?(requested_ip.ipv4_mapped) if requested_ip.ipv4? && ip.ipv6?
|
||||
return ip.ipv4_mapped.include?(requested_ip) if requested_ip.ipv6? && ip.ipv4?
|
||||
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { GlEmptyState, GlTabs, GlTab, GlSprintf } from '@gitlab/ui';
|
||||
import { GlEmptyState, GlModal, GlTabs, GlTab, GlSprintf } from '@gitlab/ui';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
|
||||
import VueApollo from 'vue-apollo';
|
||||
|
|
@ -7,7 +7,7 @@ import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
|
|||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { createAlert } from '~/alert';
|
||||
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import AdditionalMetadata from '~/packages_and_registries/package_registry/components/details/additional_metadata.vue';
|
||||
import PackagesApp from '~/packages_and_registries/package_registry/pages/details.vue';
|
||||
import DependencyRow from '~/packages_and_registries/package_registry/components/details/dependency_row.vue';
|
||||
|
|
@ -66,6 +66,7 @@ describe('PackagesApp', () => {
|
|||
};
|
||||
|
||||
const { __typename, ...packageWithoutTypename } = packageData();
|
||||
const showMock = jest.fn();
|
||||
|
||||
function createComponent({
|
||||
resolver = jest.fn().mockResolvedValue(packageDetailsQuery()),
|
||||
|
|
@ -86,17 +87,11 @@ describe('PackagesApp', () => {
|
|||
stubs: {
|
||||
PackageTitle,
|
||||
DeletePackages,
|
||||
GlModal: {
|
||||
template: `
|
||||
<div>
|
||||
<slot name="modal-title"></slot>
|
||||
<p><slot></slot></p>
|
||||
</div>
|
||||
`,
|
||||
GlModal: stubComponent(GlModal, {
|
||||
methods: {
|
||||
show: jest.fn(),
|
||||
show: showMock,
|
||||
},
|
||||
},
|
||||
}),
|
||||
GlSprintf,
|
||||
GlTabs,
|
||||
GlTab,
|
||||
|
|
@ -251,7 +246,7 @@ describe('PackagesApp', () => {
|
|||
|
||||
await findDeleteButton().trigger('click');
|
||||
|
||||
expect(findDeleteModal().find('p').text()).toBe(
|
||||
expect(findDeleteModal().text()).toBe(
|
||||
'You are about to delete version 1.0.0 of @gitlab-org/package-15. Are you sure?',
|
||||
);
|
||||
});
|
||||
|
|
@ -331,13 +326,15 @@ describe('PackagesApp', () => {
|
|||
|
||||
await waitForPromises();
|
||||
|
||||
const showDeleteFileSpy = jest.spyOn(wrapper.vm.$refs.deleteFileModal, 'show');
|
||||
const showDeletePackageSpy = jest.spyOn(wrapper.vm.$refs.deleteModal, 'show');
|
||||
|
||||
findPackageFiles().vm.$emit('delete-files', [fileToDelete]);
|
||||
|
||||
expect(showDeletePackageSpy).not.toHaveBeenCalled();
|
||||
expect(showDeleteFileSpy).toHaveBeenCalled();
|
||||
expect(showMock).toHaveBeenCalledTimes(1);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(findDeleteFileModal().text()).toBe(
|
||||
'You are about to delete foo-1.0.1.tgz. This is a destructive action that may render your package unusable. Are you sure?',
|
||||
);
|
||||
});
|
||||
|
||||
it('when its the only file opens delete package confirmation modal', async () => {
|
||||
|
|
@ -360,17 +357,13 @@ describe('PackagesApp', () => {
|
|||
|
||||
await waitForPromises();
|
||||
|
||||
const showDeleteFileSpy = jest.spyOn(wrapper.vm.$refs.deleteFileModal, 'show');
|
||||
const showDeletePackageSpy = jest.spyOn(wrapper.vm.$refs.deleteModal, 'show');
|
||||
|
||||
findPackageFiles().vm.$emit('delete-files', [fileToDelete]);
|
||||
|
||||
expect(showDeletePackageSpy).toHaveBeenCalled();
|
||||
expect(showDeleteFileSpy).not.toHaveBeenCalled();
|
||||
expect(showMock).toHaveBeenCalledTimes(1);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(findDeleteModal().find('p').text()).toBe(
|
||||
expect(findDeleteModal().text()).toBe(
|
||||
'Deleting the last package asset will remove version 1.0.0 of @gitlab-org/package-15. Are you sure?',
|
||||
);
|
||||
});
|
||||
|
|
@ -542,15 +535,13 @@ describe('PackagesApp', () => {
|
|||
|
||||
await waitForPromises();
|
||||
|
||||
const showDeletePackageSpy = jest.spyOn(wrapper.vm.$refs.deleteModal, 'show');
|
||||
|
||||
findPackageFiles().vm.$emit('delete-files', packageFiles());
|
||||
|
||||
expect(showDeletePackageSpy).toHaveBeenCalled();
|
||||
expect(showMock).toHaveBeenCalledTimes(1);
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(findDeleteModal().find('p').text()).toBe(
|
||||
expect(findDeleteModal().text()).toBe(
|
||||
'Deleting all package assets will remove version 1.0.0 of @gitlab-org/package-15. Are you sure?',
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,5 +20,5 @@ RSpec.describe GitlabSchema.types['UserAchievement'], feature_category: :user_pr
|
|||
|
||||
it { expect(described_class.graphql_name).to eq('UserAchievement') }
|
||||
it { expect(described_class).to have_graphql_fields(fields) }
|
||||
it { expect(described_class).to require_graphql_authorizations(:read_achievement) }
|
||||
it { expect(described_class).to require_graphql_authorizations(:read_user_achievement) }
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'fast_spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::UrlBlockers::IpAllowlistEntry do
|
||||
RSpec.describe Gitlab::UrlBlockers::IpAllowlistEntry, feature_category: :integrations do
|
||||
let(:ipv4) { IPAddr.new('192.168.1.1') }
|
||||
|
||||
describe '#initialize' do
|
||||
|
|
@ -65,11 +65,31 @@ RSpec.describe Gitlab::UrlBlockers::IpAllowlistEntry do
|
|||
end
|
||||
|
||||
it 'matches IPv6 within IPv6 range' do
|
||||
ipv6_range = IPAddr.new('fd84:6d02:f6d8:c89e::/124')
|
||||
ipv6_range = IPAddr.new('::ffff:192.168.1.0/8')
|
||||
ip_allowlist_entry = described_class.new(ipv6_range)
|
||||
|
||||
expect(ip_allowlist_entry).to be_match(ipv6_range.to_range.last.to_s, 8080)
|
||||
expect(ip_allowlist_entry).not_to be_match('fd84:6d02:f6d8:f::f', 8080)
|
||||
end
|
||||
|
||||
it 'matches IPv4 to IPv6 mapped addresses in allow list' do
|
||||
ipv6_range = IPAddr.new('::ffff:192.168.1.1')
|
||||
ip_allowlist_entry = described_class.new(ipv6_range)
|
||||
|
||||
expect(ip_allowlist_entry).to be_match(ipv4, 8080)
|
||||
expect(ip_allowlist_entry).to be_match(ipv6_range.to_range.last.to_s, 8080)
|
||||
expect(ip_allowlist_entry).not_to be_match('::ffff:192.168.1.0', 8080)
|
||||
expect(ip_allowlist_entry).not_to be_match('::ffff:169.254.168.101', 8080)
|
||||
end
|
||||
|
||||
it 'matches IPv4 to IPv6 mapped addresses in requested IP' do
|
||||
ipv4_range = IPAddr.new('192.168.1.1/24')
|
||||
ip_allowlist_entry = described_class.new(ipv4_range)
|
||||
|
||||
expect(ip_allowlist_entry).to be_match(ipv4, 8080)
|
||||
expect(ip_allowlist_entry).to be_match('::ffff:192.168.1.0', 8080)
|
||||
expect(ip_allowlist_entry).to be_match('::ffff:192.168.1.1', 8080)
|
||||
expect(ip_allowlist_entry).not_to be_match('::ffff:169.254.170.100/8', 8080)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe FinalizeIssuesIidScopingToNamespace, :migration, feature_category: :team_planning do
|
||||
let(:batched_migrations) { table(:batched_background_migrations) }
|
||||
|
||||
let!(:migration) { described_class::MIGRATION }
|
||||
|
||||
describe '#up' do
|
||||
shared_examples 'finalizes the migration' do
|
||||
it 'finalizes the migration' do
|
||||
allow_next_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigrationRunner) do |runner|
|
||||
expect(runner).to receive(:finalize).with('"IssuesInternalIdScopeUpdater"', :internal_ids, :id, [nil, "up"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when migration is missing' do
|
||||
it 'warns migration not found' do
|
||||
expect(Gitlab::AppLogger)
|
||||
.to receive(:warn).with(/Could not find batched background migration for the given configuration:/)
|
||||
|
||||
migrate!
|
||||
end
|
||||
end
|
||||
|
||||
context 'with migration present' do
|
||||
let!(:migration) do
|
||||
batched_migrations.create!(
|
||||
job_class_name: 'IssuesInternalIdScopeUpdater',
|
||||
table_name: :internal_ids,
|
||||
column_name: :id,
|
||||
job_arguments: [nil, 'up'],
|
||||
interval: 2.minutes,
|
||||
min_value: 1,
|
||||
max_value: 2,
|
||||
batch_size: 1000,
|
||||
sub_batch_size: 200,
|
||||
gitlab_schema: :gitlab_main,
|
||||
status: 3 # finished
|
||||
)
|
||||
end
|
||||
|
||||
context 'when migration finished successfully' do
|
||||
it 'does not raise exception' do
|
||||
expect { migrate! }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context 'with different migration statuses' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:status, :description) do
|
||||
0 | 'paused'
|
||||
1 | 'active'
|
||||
4 | 'failed'
|
||||
5 | 'finalizing'
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
migration.update!(status: status)
|
||||
end
|
||||
|
||||
it_behaves_like 'finalizes the migration'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Achievements::UserAchievementPolicy, feature_category: :user_profile do
|
||||
let(:maintainer) { create(:user) }
|
||||
|
||||
let(:group) { create(:group, :public) }
|
||||
|
||||
let(:current_user) { create(:user) }
|
||||
let(:achievement) { create(:achievement, namespace: group) }
|
||||
let(:achievement_owner) { create(:user) }
|
||||
let(:user_achievement) { create(:user_achievement, achievement: achievement, user: achievement_owner) }
|
||||
|
||||
before do
|
||||
group.add_maintainer(maintainer)
|
||||
end
|
||||
|
||||
subject { described_class.new(current_user, user_achievement) }
|
||||
|
||||
it 'is readable to everyone when user has public profile' do
|
||||
is_expected.to be_allowed(:read_user_achievement)
|
||||
end
|
||||
|
||||
context 'when user has private profile' do
|
||||
before do
|
||||
achievement_owner.update!(private_profile: true)
|
||||
end
|
||||
|
||||
context 'for achievement owner' do
|
||||
let(:current_user) { achievement_owner }
|
||||
|
||||
it 'is visible' do
|
||||
is_expected.to be_allowed(:read_user_achievement)
|
||||
end
|
||||
end
|
||||
|
||||
context 'for group maintainer' do
|
||||
let(:current_user) { maintainer }
|
||||
|
||||
it 'is visible' do
|
||||
is_expected.to be_allowed(:read_user_achievement)
|
||||
end
|
||||
end
|
||||
|
||||
context 'for others' do
|
||||
it 'is hidden' do
|
||||
is_expected.not_to be_allowed(:read_user_achievement)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when group is private' do
|
||||
let(:group) { create(:group, :private) }
|
||||
|
||||
context 'for achievement owner' do
|
||||
let(:current_user) { achievement_owner }
|
||||
|
||||
it 'is hidden' do
|
||||
is_expected.not_to be_allowed(:read_user_achievement)
|
||||
end
|
||||
end
|
||||
|
||||
context 'for group maintainer' do
|
||||
let(:current_user) { maintainer }
|
||||
|
||||
it 'is visible' do
|
||||
is_expected.to be_allowed(:read_user_achievement)
|
||||
end
|
||||
end
|
||||
|
||||
context 'for others' do
|
||||
it 'is hidden' do
|
||||
is_expected.not_to be_allowed(:read_user_achievement)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue