diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss
index dc127cd2554..c6c9f3b7365 100644
--- a/app/assets/stylesheets/performance_bar.scss
+++ b/app/assets/stylesheets/performance_bar.scss
@@ -6,7 +6,7 @@
left: 0;
top: 0;
width: 100%;
- z-index: #{$zindex-modal-backdrop + 1};
+ z-index: #{$zindex-modal-backdrop - 1};
height: $performance-bar-height;
background: $black;
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index 7a6f9a5564f..a793a8ddbdb 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -28,6 +28,11 @@ class RegistrationsController < Devise::RegistrationsController
super do |new_user|
persist_accepted_terms_if_required(new_user)
set_role_required(new_user)
+
+ if pending_approval?
+ NotificationService.new.new_instance_access_request(new_user)
+ end
+
yield new_user if block_given?
end
@@ -131,6 +136,12 @@ class RegistrationsController < Devise::RegistrationsController
render action: 'new'
end
+ def pending_approval?
+ return false unless Gitlab::CurrentSettings.require_admin_approval_after_user_signup
+
+ resource.persisted? && resource.blocked_pending_approval?
+ end
+
def sign_up_params
params.require(:user).permit(:username, :email, :name, :first_name, :last_name, :password)
end
diff --git a/app/graphql/mutations/concerns/mutations/package_eventable.rb b/app/graphql/mutations/concerns/mutations/package_eventable.rb
new file mode 100644
index 00000000000..86fd7b9a88a
--- /dev/null
+++ b/app/graphql/mutations/concerns/mutations/package_eventable.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Mutations
+ module PackageEventable
+ extend ActiveSupport::Concern
+
+ private
+
+ def track_event(event, scope)
+ ::Packages::CreateEventService.new(nil, current_user, event_name: event, scope: scope).execute
+ ::Gitlab::Tracking.event(event.to_s, scope.to_s)
+ end
+ end
+end
diff --git a/app/graphql/mutations/container_repositories/destroy.rb b/app/graphql/mutations/container_repositories/destroy.rb
new file mode 100644
index 00000000000..8312193147f
--- /dev/null
+++ b/app/graphql/mutations/container_repositories/destroy.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module Mutations
+ module ContainerRepositories
+ class Destroy < Mutations::BaseMutation
+ include ::Mutations::PackageEventable
+
+ graphql_name 'DestroyContainerRepository'
+
+ authorize :destroy_container_image
+
+ argument :id,
+ ::Types::GlobalIDType[::ContainerRepository],
+ required: true,
+ description: 'ID of the container repository.'
+
+ field :container_repository,
+ Types::ContainerRepositoryType,
+ null: false,
+ description: 'The container repository policy after scheduling the deletion.'
+
+ def resolve(id:)
+ container_repository = authorized_find!(id: id)
+
+ container_repository.delete_scheduled!
+ DeleteContainerRepositoryWorker.perform_async(current_user.id, container_repository.id)
+ track_event(:delete_repository, :container)
+
+ {
+ container_repository: container_repository,
+ errors: []
+ }
+ end
+
+ private
+
+ def find_object(id:)
+ # TODO: remove this line when the compatibility layer is removed
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
+ id = ::Types::GlobalIDType[::ContainerRepository].coerce_isolated_input(id)
+ GitlabSchema.find_by_gid(id)
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/container_repositories_resolver.rb b/app/graphql/resolvers/container_repositories_resolver.rb
index 8b04055e920..b4b2893a3b8 100644
--- a/app/graphql/resolvers/container_repositories_resolver.rb
+++ b/app/graphql/resolvers/container_repositories_resolver.rb
@@ -2,6 +2,8 @@
module Resolvers
class ContainerRepositoriesResolver < BaseResolver
+ include ::Mutations::PackageEventable
+
type Types::ContainerRepositoryType, null: true
argument :name, GraphQL::STRING_TYPE,
@@ -13,12 +15,5 @@ module Resolvers
.execute
.tap { track_event(:list_repositories, :container) }
end
-
- private
-
- def track_event(event, scope)
- ::Packages::CreateEventService.new(nil, current_user, event_name: event, scope: scope).execute
- ::Gitlab::Tracking.event(event.to_s, scope.to_s)
- end
end
end
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index c2262fdd0e3..802e98f7d62 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -82,6 +82,7 @@ module Types
mount_mutation Mutations::DesignManagement::Delete, calls_gitaly: true
mount_mutation Mutations::DesignManagement::Move
mount_mutation Mutations::ContainerExpirationPolicies::Update
+ mount_mutation Mutations::ContainerRepositories::Destroy
mount_mutation Mutations::Ci::PipelineCancel
mount_mutation Mutations::Ci::PipelineDestroy
mount_mutation Mutations::Ci::PipelineRetry
diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb
index 0a0dc77e5e2..017981c8c8e 100644
--- a/app/helpers/emails_helper.rb
+++ b/app/helpers/emails_helper.rb
@@ -214,6 +214,24 @@ module EmailsHelper
end
end
+ def instance_access_request_text(user, format: nil)
+ gitlab_host = Gitlab.config.gitlab.host
+
+ _('%{username} has asked for a GitLab account on your instance %{host}:') % { username: sanitize_name(user.name), host: gitlab_host }
+ end
+
+ def instance_access_request_link(user, format: nil)
+ url = admin_user_url(user)
+
+ case format
+ when :html
+ user_page = ''.html_safe % { url: url }
+ _("Click %{link_start}here%{link_end} to view the request.").html_safe % { link_start: user_page, link_end: ''.html_safe }
+ else
+ _('Click %{link_to} to view the request.') % { link_to: url }
+ end
+ end
+
def contact_your_administrator_text
_('Please contact your administrator with any questions.')
end
diff --git a/app/mailers/emails/profile.rb b/app/mailers/emails/profile.rb
index 96cf3571968..6f44b63f8d0 100644
--- a/app/mailers/emails/profile.rb
+++ b/app/mailers/emails/profile.rb
@@ -9,6 +9,15 @@ module Emails
mail(to: @user.notification_email, subject: subject("Account was created for you"))
end
+ def instance_access_request_email(user, recipient)
+ @user = user
+ @recipient = recipient
+
+ profile_email_with_layout(
+ to: recipient.notification_email,
+ subject: subject(_("GitLab Account Request")))
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def new_ssh_key_email(key_id)
@key = Key.find_by(id: key_id)
@@ -63,13 +72,9 @@ module Emails
@target_url = edit_profile_password_url
Gitlab::I18n.with_locale(@user.preferred_language) do
- mail(
+ profile_email_with_layout(
to: @user.notification_email,
- subject: subject(_("%{host} sign-in from new location") % { host: Gitlab.config.gitlab.host })
- ) do |format|
- format.html { render layout: 'mailer' }
- format.text { render layout: 'mailer' }
- end
+ subject: subject(_("%{host} sign-in from new location") % { host: Gitlab.config.gitlab.host }))
end
end
@@ -82,6 +87,15 @@ module Emails
mail(to: @user.notification_email, subject: subject(_("Two-factor authentication disabled")))
end
end
+
+ private
+
+ def profile_email_with_layout(to:, subject:, layout: 'mailer')
+ mail(to: to, subject: subject) do |format|
+ format.html { render layout: layout }
+ format.text { render layout: layout }
+ end
+ end
end
end
diff --git a/app/models/concerns/has_repository.rb b/app/models/concerns/has_repository.rb
index 978a54bdee7..3dea4a9f5fb 100644
--- a/app/models/concerns/has_repository.rb
+++ b/app/models/concerns/has_repository.rb
@@ -109,6 +109,11 @@ module HasRepository
Gitlab::RepositoryUrlBuilder.build(repository.full_path, protocol: :http)
end
+ # Is overridden in EE::Project for Geo support
+ def lfs_http_url_to_repo(_operation = nil)
+ http_url_to_repo
+ end
+
def web_url(only_path: nil)
Gitlab::UrlBuilder.build(self, only_path: only_path)
end
diff --git a/app/models/project.rb b/app/models/project.rb
index d3a389056fb..68d498c2bd5 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1469,11 +1469,6 @@ class Project < ApplicationRecord
services.public_send(hooks_scope).any? # rubocop:disable GitlabSecurity/PublicSend
end
- # Is overridden in EE
- def lfs_http_url_to_repo(_)
- http_url_to_repo
- end
-
def feature_usage
super.presence || build_feature_usage
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 2cb0edaf2ce..082c98c0104 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -28,6 +28,8 @@ class User < ApplicationRecord
DEFAULT_NOTIFICATION_LEVEL = :participating
+ INSTANCE_ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT = 10
+
add_authentication_token_field :incoming_email_token, token_generator: -> { SecureRandom.hex.to_i(16).to_s(36) }
add_authentication_token_field :feed_token
add_authentication_token_field :static_object_token
@@ -341,6 +343,7 @@ class User < ApplicationRecord
# Scopes
scope :admins, -> { where(admin: true) }
+ scope :instance_access_request_approvers_to_be_notified, -> { admins.active.order_recent_sign_in.limit(INSTANCE_ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT) }
scope :blocked, -> { with_states(:blocked, :ldap_blocked) }
scope :blocked_pending_approval, -> { with_states(:blocked_pending_approval) }
scope :external, -> { where(external: true) }
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 1f3f637b8ed..85113d3ca22 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -370,6 +370,16 @@ class NotificationService
end
end
+ def new_instance_access_request(user)
+ recipients = User.instance_access_request_approvers_to_be_notified # https://gitlab.com/gitlab-org/gitlab/-/issues/277016 will change this
+
+ return true if recipients.empty?
+
+ recipients.each do |recipient|
+ mailer.instance_access_request_email(user, recipient).deliver_later
+ end
+ end
+
# Members
def new_access_request(member)
return true unless member.notifiable?(:subscription)
diff --git a/app/views/notify/instance_access_request_email.html.haml b/app/views/notify/instance_access_request_email.html.haml
new file mode 100644
index 00000000000..87bc655164b
--- /dev/null
+++ b/app/views/notify/instance_access_request_email.html.haml
@@ -0,0 +1,10 @@
+#content
+ = email_default_heading(say_hello(@recipient))
+ %p
+ = instance_access_request_text(@user, format: :html)
+ %p
+ = _("Username: %{username}") % { username: @user.username }
+ %p
+ = _("Email: %{email}") % { email: @user.email }
+ %p
+ = instance_access_request_link(@user, format: :html)
diff --git a/app/views/notify/instance_access_request_email.text.erb b/app/views/notify/instance_access_request_email.text.erb
new file mode 100644
index 00000000000..317f962a29c
--- /dev/null
+++ b/app/views/notify/instance_access_request_email.text.erb
@@ -0,0 +1,8 @@
+<%= say_hello(@recipient) %>
+
+<%= instance_access_request_text(@user) %>
+
+<%= _("Username: %{username}") % { username: @user.username } %>
+<%= _("Email: %{email}") % { email: @user.email } %>
+
+<%= instance_access_request_link(@user) %>
diff --git a/changelogs/unreleased/10io-graphql-destroy-mutation-for-container-repositories.yml b/changelogs/unreleased/10io-graphql-destroy-mutation-for-container-repositories.yml
new file mode 100644
index 00000000000..2d9bda79eec
--- /dev/null
+++ b/changelogs/unreleased/10io-graphql-destroy-mutation-for-container-repositories.yml
@@ -0,0 +1,5 @@
+---
+title: Add container repository destroy GraphQL mutation
+merge_request: 47175
+author:
+type: added
diff --git a/changelogs/unreleased/229704-aqualls-metrics-modal.yml b/changelogs/unreleased/229704-aqualls-metrics-modal.yml
new file mode 100644
index 00000000000..04377daf53b
--- /dev/null
+++ b/changelogs/unreleased/229704-aqualls-metrics-modal.yml
@@ -0,0 +1,5 @@
+---
+title: Update detailed_metric.vue modal to match Pajamas guidelines
+merge_request: 46183
+author:
+type: changed
diff --git a/changelogs/unreleased/257879-user-admin-approval-admin-email-notification.yml b/changelogs/unreleased/257879-user-admin-approval-admin-email-notification.yml
new file mode 100644
index 00000000000..0b60c90ed8a
--- /dev/null
+++ b/changelogs/unreleased/257879-user-admin-approval-admin-email-notification.yml
@@ -0,0 +1,5 @@
+---
+title: Send email notifications to admins about users pending approval
+merge_request: 46895
+author:
+type: added
diff --git a/changelogs/unreleased/ci-jwt-signing-key-enable-by-default.yml b/changelogs/unreleased/ci-jwt-signing-key-enable-by-default.yml
new file mode 100644
index 00000000000..4f063762bfa
--- /dev/null
+++ b/changelogs/unreleased/ci-jwt-signing-key-enable-by-default.yml
@@ -0,0 +1,5 @@
+---
+title: Use dedicated signing key for CI_JOB_JWT by default
+merge_request: 47336
+author:
+type: changed
diff --git a/changelogs/unreleased/lfs-ssh-authentication.yml b/changelogs/unreleased/lfs-ssh-authentication.yml
new file mode 100644
index 00000000000..14309c4927b
--- /dev/null
+++ b/changelogs/unreleased/lfs-ssh-authentication.yml
@@ -0,0 +1,5 @@
+---
+title: Fix internal lfs_authenticate API for non-project repositories
+merge_request: 47404
+author:
+type: fixed
diff --git a/config/feature_flags/development/ci_jwt_signing_key.yml b/config/feature_flags/development/ci_jwt_signing_key.yml
index 5fae64b71d2..4f37b49d797 100644
--- a/config/feature_flags/development/ci_jwt_signing_key.yml
+++ b/config/feature_flags/development/ci_jwt_signing_key.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/258546
milestone: '13.6'
type: development
group: group::release management
-default_enabled: false
+default_enabled: true
diff --git a/doc/administration/auth/ldap/ldap-troubleshooting.md b/doc/administration/auth/ldap/ldap-troubleshooting.md
index 089ff07af52..8920479949d 100644
--- a/doc/administration/auth/ldap/ldap-troubleshooting.md
+++ b/doc/administration/auth/ldap/ldap-troubleshooting.md
@@ -69,10 +69,10 @@ options = {
# :filter is optional
# 'cn' looks for all "cn"s under :base
# '*' is the search string - here, it's a wildcard
- filter: Net::Ldap::Filter.eq('cn', '*'),
+ filter: Net::LDAP::Filter.eq('cn', '*'),
# :attributes is optional
- # the attributes we want to get returned
+ # the attributes we want to get returnedk
attributes: %w(dn cn memberuid member submember uniquemember memberof)
}
adapter.ldap_search(options)
diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md
index 49ef9385167..7ee177f87db 100644
--- a/doc/administration/packages/container_registry.md
+++ b/doc/administration/packages/container_registry.md
@@ -397,6 +397,20 @@ To configure the `s3` storage driver in Omnibus:
}
```
+ To avoid using static credentials, use an
+ [IAM role](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html)
+ and omit `accesskey` and `secretkey`. Make sure that your IAM profile follows
+ [the permissions documented by Docker](https://docs.docker.com/registry/storage-drivers/s3/#s3-permission-scopes).
+
+ ```ruby
+ registry['storage'] = {
+ 's3' => {
+ 'bucket' => 'your-s3-bucket',
+ 'region' => 'your-s3-region'
+ }
+ }
+ ```
+
- `regionendpoint` is only required when configuring an S3 compatible service such as MinIO. It takes a URL such as `http://127.0.0.1:9000`.
- `your-s3-bucket` should be the name of a bucket that exists, and can't include subdirectories.
@@ -412,8 +426,8 @@ when you [deployed your Docker registry](https://docs.docker.com/registry/deploy
```yaml
storage:
s3:
- accesskey: 's3-access-key'
- secretkey: 's3-secret-key-for-access-key'
+ accesskey: 's3-access-key' # Not needed if IAM role used
+ secretkey: 's3-secret-key-for-access-key' # Not needed if IAM role used
bucket: 'your-s3-bucket'
region: 'your-s3-region'
regionendpoint: 'your-s3-regionendpoint'
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index 8162505270c..013265a180a 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -6093,6 +6093,41 @@ type DestroyBoardPayload {
errors: [String!]!
}
+"""
+Autogenerated input type of DestroyContainerRepository
+"""
+input DestroyContainerRepositoryInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ ID of the container repository.
+ """
+ id: ContainerRepositoryID!
+}
+
+"""
+Autogenerated return type of DestroyContainerRepository
+"""
+type DestroyContainerRepositoryPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ The container repository policy after scheduling the deletion.
+ """
+ containerRepository: ContainerRepository!
+
+ """
+ Errors encountered during execution of the mutation.
+ """
+ errors: [String!]!
+}
+
"""
Autogenerated input type of DestroyNote
"""
@@ -13470,6 +13505,7 @@ type Mutation {
designManagementUpload(input: DesignManagementUploadInput!): DesignManagementUploadPayload
destroyBoard(input: DestroyBoardInput!): DestroyBoardPayload
destroyBoardList(input: DestroyBoardListInput!): DestroyBoardListPayload
+ destroyContainerRepository(input: DestroyContainerRepositoryInput!): DestroyContainerRepositoryPayload
destroyNote(input: DestroyNoteInput!): DestroyNotePayload
destroySnippet(input: DestroySnippetInput!): DestroySnippetPayload
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index 523d29202f1..314f45740fc 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -16765,6 +16765,112 @@
"enumValues": null,
"possibleTypes": null
},
+ {
+ "kind": "INPUT_OBJECT",
+ "name": "DestroyContainerRepositoryInput",
+ "description": "Autogenerated input type of DestroyContainerRepository",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "id",
+ "description": "ID of the container repository.",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ContainerRepositoryID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "DestroyContainerRepositoryPayload",
+ "description": "Autogenerated return type of DestroyContainerRepository",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "containerRepository",
+ "description": "The container repository policy after scheduling the deletion.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "OBJECT",
+ "name": "ContainerRepository",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Errors encountered during execution of the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
{
"kind": "INPUT_OBJECT",
"name": "DestroyNoteInput",
@@ -38177,6 +38283,33 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "destroyContainerRepository",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "DestroyContainerRepositoryInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "DestroyContainerRepositoryPayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "destroyNote",
"description": null,
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index b50da3a15b0..dcd89f173c8 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -1002,6 +1002,16 @@ Autogenerated return type of DestroyBoard.
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+### DestroyContainerRepositoryPayload
+
+Autogenerated return type of DestroyContainerRepository.
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `containerRepository` | ContainerRepository! | The container repository policy after scheduling the deletion. |
+| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+
### DestroyNotePayload
Autogenerated return type of DestroyNote.
diff --git a/doc/ci/examples/authenticating-with-hashicorp-vault/index.md b/doc/ci/examples/authenticating-with-hashicorp-vault/index.md
index 79229d9ea0b..c0fb94acdf2 100644
--- a/doc/ci/examples/authenticating-with-hashicorp-vault/index.md
+++ b/doc/ci/examples/authenticating-with-hashicorp-vault/index.md
@@ -56,7 +56,7 @@ The JWT's payload looks like this:
}
```
-The JWT is encoded by using RS256 and signed with your GitLab instance's OpenID Connect private key. The expire time for the token will be set to job's timeout, if specified, or 5 minutes if it is not. The key used to sign this token may change without any notice. In such case retrying the job will generate new JWT using the current signing key.
+The JWT is encoded by using RS256 and signed with a dedicated private key. The expire time for the token will be set to job's timeout, if specified, or 5 minutes if it is not. The key used to sign this token may change without any notice. In such case retrying the job will generate new JWT using the current signing key.
You can use this JWT and your instance's JWKS endpoint (`https://gitlab.example.com/-/jwks`) to authenticate with a Vault server that is configured to allow the JWT Authentication method for authentication.
diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md
index 110be6498f7..6fca482975c 100644
--- a/doc/ci/triggers/README.md
+++ b/doc/ci/triggers/README.md
@@ -50,7 +50,7 @@ with the [GitLab Container Registry](../../user/packages/container_registry/inde
This way of triggering can only be used when invoked inside `.gitlab-ci.yml`,
and it creates a dependent pipeline relation visible on the
-[pipeline graph](../multi_project_pipelines.md#overview). For example:
+[pipeline graph](../multi_project_pipelines.md). For example:
```yaml
build_docs:
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 50ae85572b1..c40f92bb01a 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -3837,8 +3837,8 @@ For more information, see [Deployments Safety](../environments/deployment_safety
These methods are supported:
- [`tag_name`](#releasetag_name)
+- [`description`](#releasedescription)
- [`name`](#releasename) (optional)
-- [`description`](#releasedescription) (optional)
- [`ref`](#releaseref) (optional)
- [`milestones`](#releasemilestones) (optional)
- [`released_at`](#releasereleased_at) (optional)
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index 9f1be9fd03e..fee59c60f79 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -899,6 +899,32 @@ it.each([
);
```
+**Note**: only use template literal block if pretty print is **not** needed for spec output. For example, empty strings, nested objects etc.
+
+For example, when testing the difference between an empty search string and a non-empty search string, the use of the array block syntax with the pretty print option would be preferred. That way the differences between an empty string e.g. `''` and a non-empty string e.g. `'search string'` would be visible in the spec output. Whereas with a template literal block, the empty string would be shown as a space, which could lead to a confusing developer experience
+
+```javascript
+// bad
+it.each`
+ searchTerm | expected
+ ${''} | ${{ issue: { users: { nodes: [] } } }}
+ ${'search term'} | ${{ issue: { other: { nested: [] } } }}
+`('when search term is $searchTerm, it returns $expected', ({ searchTerm, expected }) => {
+ expect(search(searchTerm)).toEqual(expected)
+});
+
+// good
+it.each([
+ ['', { issue: { users: { nodes: [] } } }],
+ ['search term', { issue: { other: { nested: [] } } }],
+])('when search term is %p, expect to return %p',
+ (searchTerm, expected) => {
+ expect(search(searchTerm)).toEqual(expected)
+ }
+);
+
+```
+
```javascript
// test suite with tagged template literal block
describe.each`
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index bf394fef7b5..a3450713dad 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -166,7 +166,7 @@ a base domain of `example.com`, you'd need a DNS entry like:
```
In this case, the deployed applications are served from `example.com`, and `1.2.3.4`
-is the IP address of your load balancer; generally NGINX ([see requirements](#requirements)).
+is the IP address of your load balancer; generally NGINX ([see requirements](requirements.md)).
Setting up the DNS record is beyond the scope of this document; check with your
DNS provider for information.
@@ -183,7 +183,7 @@ See [Auto DevOps requirements for Amazon ECS](requirements.md#auto-devops-requir
## Enabling/Disabling Auto DevOps
-When first using Auto DevOps, review the [requirements](#requirements) to ensure
+When first using Auto DevOps, review the [requirements](requirements.md) to ensure
all the necessary components to make full use of Auto DevOps are available. First-time
users should follow the [quick start guide](quick_start_guide.md).
diff --git a/doc/user/admin_area/broadcast_messages.md b/doc/user/admin_area/broadcast_messages.md
index 985008061ac..f594f6d736f 100644
--- a/doc/user/admin_area/broadcast_messages.md
+++ b/doc/user/admin_area/broadcast_messages.md
@@ -65,11 +65,16 @@ To add a broadcast message:
1. Navigate to the **Admin Area > Messages** page.
1. Add the text for the message to the **Message** field. Markdown and emoji are supported.
-1. If required, click the **Customize colors** link to edit the background color and font color of the message.
+1. Select one of the suggested background colors, or add the hex code of a different color. The default color is orange.
1. If required, add a **Target Path** to only show the broadcast message on URLs matching that path. You can use the wildcard character `*` to match multiple URLs, for example `/users/*/issues`.
1. Select a date for the message to start and end.
1. Click the **Add broadcast message** button.
+NOTE: **Note:**
+The **Background color** field expects the value to be a hexadecimal code because
+the form uses the [color_field](https://api.rubyonrails.org/v6.0.3.4/classes/ActionView/Helpers/FormHelper.html#method-i-color_field)
+helper method, which generates the proper HTML to render.
+
NOTE: **Note:**
Once a broadcast message has expired, it is no longer displayed in the UI but is still listed in the
list of broadcast messages. User can also dismiss a broadcast message if the option **Dismissable** is set.
diff --git a/doc/user/admin_area/settings/sign_up_restrictions.md b/doc/user/admin_area/settings/sign_up_restrictions.md
index 335ddce4e2d..39056ed804a 100644
--- a/doc/user/admin_area/settings/sign_up_restrictions.md
+++ b/doc/user/admin_area/settings/sign_up_restrictions.md
@@ -46,6 +46,22 @@ To enforce confirmation of the email address used for new sign ups:
1. Go to **Admin Area > Settings > General** and expand **Sign-up restrictions**.
1. Select the **Enable email restrictions for sign ups** checkbox, then select **Save changes**.
+## Soft email confirmation
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/47003) in GitLab 12.2.
+> - It's [deployed behind a feature flag](../../..//user/feature_flags.md), disabled by default.
+> - It's enabled on GitLab.com.
+> - It's recommended for production use.
+> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-soft-email-confirmation).
+
+CAUTION: **Warning:**
+This feature might not be available to you. Check the **version history** note above for details.
+
+The soft email confirmation improves the signup experience for new users by allowing
+them to sign in without an immediate confirmation when an email confirmation is required.
+GitLab shows the user a reminder to confirm their email address, and the user can't
+create or update pipelines until their email address is confirmed.
+
## Minimum password length limit
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20661) in GitLab 12.6
@@ -90,6 +106,25 @@ semicolon, comma, or a new line.

+### Enable or disable soft email confirmation
+
+Soft email confirmation is under development but ready for production use.
+It is deployed behind a feature flag that is **disabled by default**.
+[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
+can opt to disable it.
+
+To enable it:
+
+```ruby
+Feature.enable(:soft_email_confirmation)
+```
+
+To disable it:
+
+```ruby
+Feature.disable(:soft_email_confirmation)
+```
+