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-02-07 03:09:12 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-02-07 03:09:12 +0300
commit6168721025dd8e98caeb2bf6844273e6690eaf69 (patch)
tree8c4fb20d793669e488a739bc9951dab8b363eed4 /app
parenta89cb5cbdd832d4d9e80517973aceda6bc0a3856 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/blob/components/blob_header_filepath.vue47
-rw-r--r--app/assets/javascripts/monitoring/constants.js4
-rw-r--r--app/assets/stylesheets/framework/snippets.scss4
-rw-r--r--app/assets/stylesheets/pages/commits.scss10
-rw-r--r--app/models/commit.rb32
-rw-r--r--app/models/concerns/x509_serial_number_attribute.rb45
-rw-r--r--app/models/user.rb6
-rw-r--r--app/models/x509_certificate.rb32
-rw-r--r--app/models/x509_commit_signature.rb44
-rw-r--r--app/models/x509_issuer.rb18
-rw-r--r--app/services/git/branch_hooks_service.rb16
-rw-r--r--app/views/projects/commit/_signature.html.haml3
-rw-r--r--app/views/projects/commit/_signature_badge.html.haml14
-rw-r--r--app/views/projects/commit/x509/_certificate_details.html.haml17
-rw-r--r--app/views/projects/commit/x509/_signature_badge_user.html.haml19
-rw-r--r--app/views/projects/commit/x509/_unverified_signature_badge.html.haml6
-rw-r--r--app/views/projects/commit/x509/_verified_signature_badge.html.haml6
-rw-r--r--app/workers/all_queues.yml8
-rw-r--r--app/workers/create_commit_signature_worker.rb (renamed from app/workers/create_gpg_signature_worker.rb)9
19 files changed, 313 insertions, 27 deletions
diff --git a/app/assets/javascripts/blob/components/blob_header_filepath.vue b/app/assets/javascripts/blob/components/blob_header_filepath.vue
new file mode 100644
index 00000000000..6c6a22e2b36
--- /dev/null
+++ b/app/assets/javascripts/blob/components/blob_header_filepath.vue
@@ -0,0 +1,47 @@
+<script>
+import FileIcon from '~/vue_shared/components/file_icon.vue';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import { numberToHumanSize } from '~/lib/utils/number_utils';
+
+export default {
+ components: {
+ FileIcon,
+ ClipboardButton,
+ },
+ props: {
+ blob: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ blobSize() {
+ return numberToHumanSize(this.blob.size);
+ },
+ gfmCopyText() {
+ return `\`${this.blob.path}\``;
+ },
+ },
+};
+</script>
+<template>
+ <div class="file-header-content d-flex align-items-center lh-100">
+ <slot name="filepathPrepend"></slot>
+
+ <file-icon :file-name="blob.path" :size="18" aria-hidden="true" css-classes="mr-2" />
+ <strong
+ v-if="blob.name"
+ class="file-title-name qa-file-title-name mr-1 js-blob-header-filepath"
+ >{{ blob.name }}</strong
+ >
+
+ <small class="mr-2">{{ blobSize }}</small>
+
+ <clipboard-button
+ :text="blob.path"
+ :gfm="gfmCopyText"
+ :title="__('Copy file path')"
+ css-class="btn-clipboard btn-transparent lh-100 position-static"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/monitoring/constants.js b/app/assets/javascripts/monitoring/constants.js
index 789b3131d11..b468254b0cf 100644
--- a/app/assets/javascripts/monitoring/constants.js
+++ b/app/assets/javascripts/monitoring/constants.js
@@ -110,8 +110,8 @@ export const timeRanges = [
duration: { seconds: 60 * 60 * 24 * 7 * 1 },
},
{
- label: __('2 weeks'),
- duration: { seconds: 60 * 60 * 24 * 7 * 2 },
+ label: __('1 month'),
+ duration: { seconds: 60 * 60 * 24 * 30 },
},
];
diff --git a/app/assets/stylesheets/framework/snippets.scss b/app/assets/stylesheets/framework/snippets.scss
index fbe241df32f..dbcb5086d70 100644
--- a/app/assets/stylesheets/framework/snippets.scss
+++ b/app/assets/stylesheets/framework/snippets.scss
@@ -32,10 +32,6 @@
.snippet-file-content {
border-radius: 3px;
-
- .file-title-flex-parent .btn-clipboard {
- line-height: 28px;
- }
}
.snippet-header {
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index be0311f584f..781b6c09458 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -321,6 +321,16 @@
}
}
+.gpg-popover-certificate-details {
+ ul {
+ padding-left: $gl-padding;
+ }
+
+ li.unstyled {
+ list-style-type: none;
+ }
+}
+
.gpg-popover-status {
display: flex;
align-items: center;
diff --git a/app/models/commit.rb b/app/models/commit.rb
index f2a6a8b6cbb..46222bbc4cd 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -25,7 +25,7 @@ class Commit
attr_accessor :redacted_description_html
attr_accessor :redacted_title_html
attr_accessor :redacted_full_title_html
- attr_reader :gpg_commit, :container
+ attr_reader :container
delegate :repository, to: :container
delegate :project, to: :repository, allow_nil: true
@@ -123,7 +123,6 @@ class Commit
@raw = raw_commit
@container = container
- @gpg_commit = Gitlab::Gpg::Commit.new(self) if container
end
delegate \
@@ -320,13 +319,34 @@ class Commit
)
end
- def signature
- return @signature if defined?(@signature)
+ def has_signature?
+ signature_type && signature_type != :NONE
+ end
+
+ def raw_signature_type
+ strong_memoize(:raw_signature_type) do
+ next unless @raw.instance_of?(Gitlab::Git::Commit)
+
+ @raw.raw_commit.signature_type if defined? @raw.raw_commit.signature_type
+ end
+ end
- @signature = gpg_commit.signature
+ def signature_type
+ @signature_type ||= raw_signature_type || :NONE
end
- delegate :has_signature?, to: :gpg_commit
+ def signature
+ strong_memoize(:signature) do
+ case signature_type
+ when :PGP
+ Gitlab::Gpg::Commit.new(self).signature
+ when :X509
+ Gitlab::X509::Commit.new(self).signature
+ else
+ nil
+ end
+ end
+ end
def revert_branch_name
"revert-#{short_id}"
diff --git a/app/models/concerns/x509_serial_number_attribute.rb b/app/models/concerns/x509_serial_number_attribute.rb
new file mode 100644
index 00000000000..d2a5c736604
--- /dev/null
+++ b/app/models/concerns/x509_serial_number_attribute.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module X509SerialNumberAttribute
+ extend ActiveSupport::Concern
+
+ class_methods do
+ def x509_serial_number_attribute(name)
+ return if ENV['STATIC_VERIFICATION']
+
+ validate_binary_column_exists!(name) unless Rails.env.production?
+
+ attribute(name, Gitlab::Database::X509SerialNumberAttribute.new)
+ end
+
+ # This only gets executed in non-production environments as an additional check to ensure
+ # the column is the correct type. In production it should behave like any other attribute.
+ # See https://gitlab.com/gitlab-org/gitlab/merge_requests/5502 for more discussion
+ def validate_binary_column_exists!(name)
+ return unless database_exists?
+
+ unless table_exists?
+ warn "WARNING: x509_serial_number_attribute #{name.inspect} is invalid since the table doesn't exist - you may need to run database migrations"
+ return
+ end
+
+ column = columns.find { |c| c.name == name.to_s }
+
+ unless column
+ warn "WARNING: x509_serial_number_attribute #{name.inspect} is invalid since the column doesn't exist - you may need to run database migrations"
+ return
+ end
+
+ unless column.type == :binary
+ raise ArgumentError.new("x509_serial_number_attribute #{name.inspect} is invalid since the column type is not :binary")
+ end
+ rescue => error
+ Gitlab::AppLogger.error "X509SerialNumberAttribute initialization: #{error.message}"
+ raise
+ end
+
+ def database_exists?
+ Gitlab::Database.exists?
+ end
+ end
+end
diff --git a/app/models/user.rb b/app/models/user.rb
index aa7e825d516..11bfa485ae9 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -190,6 +190,12 @@ class User < ApplicationRecord
validate :owns_commit_email, if: :commit_email_changed?
validate :signup_domain_valid?, on: :create, if: ->(user) { !user.created_by_id }
+ validates :theme_id, allow_nil: true, inclusion: { in: Gitlab::Themes.valid_ids,
+ message: _("%{placeholder} is not a valid theme") % { placeholder: '%{value}' } }
+
+ validates :color_scheme_id, allow_nil: true, inclusion: { in: Gitlab::ColorSchemes.valid_ids,
+ message: _("%{placeholder} is not a valid color scheme") % { placeholder: '%{value}' } }
+
before_validation :sanitize_attrs
before_validation :set_notification_email, if: :new_record?
before_validation :set_public_email, if: :public_email_changed?
diff --git a/app/models/x509_certificate.rb b/app/models/x509_certificate.rb
new file mode 100644
index 00000000000..43927e65db1
--- /dev/null
+++ b/app/models/x509_certificate.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+class X509Certificate < ApplicationRecord
+ include X509SerialNumberAttribute
+
+ x509_serial_number_attribute :serial_number
+
+ enum certificate_status: {
+ good: 0,
+ revoked: 1
+ }
+
+ belongs_to :x509_issuer, class_name: 'X509Issuer', foreign_key: 'x509_issuer_id', optional: false
+
+ has_many :x509_commit_signatures, inverse_of: 'x509_certificate'
+
+ # rfc 5280 - 4.2.1.2 Subject Key Identifier
+ validates :subject_key_identifier, presence: true, format: { with: /\A(\h{2}:){19}\h{2}\z/ }
+ # rfc 5280 - 4.1.2.6 Subject
+ validates :subject, presence: true
+ # rfc 5280 - 4.1.2.6 Subject (subjectAltName contains the email address)
+ validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
+ # rfc 5280 - 4.1.2.2 Serial number
+ validates :serial_number, presence: true, numericality: { only_integer: true }
+
+ validates :x509_issuer_id, presence: true
+
+ def self.safe_create!(attributes)
+ create_with(attributes)
+ .safe_find_or_create_by!(subject_key_identifier: attributes[:subject_key_identifier])
+ end
+end
diff --git a/app/models/x509_commit_signature.rb b/app/models/x509_commit_signature.rb
new file mode 100644
index 00000000000..ed7c638cecc
--- /dev/null
+++ b/app/models/x509_commit_signature.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+class X509CommitSignature < ApplicationRecord
+ include ShaAttribute
+
+ sha_attribute :commit_sha
+
+ enum verification_status: {
+ unverified: 0,
+ verified: 1
+ }
+
+ belongs_to :project, class_name: 'Project', foreign_key: 'project_id', optional: false
+ belongs_to :x509_certificate, class_name: 'X509Certificate', foreign_key: 'x509_certificate_id', optional: false
+
+ validates :commit_sha, presence: true
+ validates :project_id, presence: true
+ validates :x509_certificate_id, presence: true
+
+ scope :by_commit_sha, ->(shas) { where(commit_sha: shas) }
+
+ def self.safe_create!(attributes)
+ create_with(attributes)
+ .safe_find_or_create_by!(commit_sha: attributes[:commit_sha])
+ end
+
+ # Find commits that are lacking a signature in the database at present
+ def self.unsigned_commit_shas(commit_shas)
+ return [] if commit_shas.empty?
+
+ signed = by_commit_sha(commit_shas).pluck(:commit_sha)
+ commit_shas - signed
+ end
+
+ def commit
+ project.commit(commit_sha)
+ end
+
+ def x509_commit
+ return unless commit
+
+ Gitlab::X509::Commit.new(commit)
+ end
+end
diff --git a/app/models/x509_issuer.rb b/app/models/x509_issuer.rb
new file mode 100644
index 00000000000..514b38808ef
--- /dev/null
+++ b/app/models/x509_issuer.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class X509Issuer < ApplicationRecord
+ has_many :x509_certificates, inverse_of: 'x509_issuer'
+
+ # rfc 5280 - 4.2.1.1 Authority Key Identifier
+ validates :subject_key_identifier, presence: true, format: { with: /\A(\h{2}:){19}\h{2}\z/ }
+ # rfc 5280 - 4.1.2.4 Issuer
+ validates :subject, presence: true
+ # rfc 5280 - 4.2.1.14 CRL Distribution Points
+ # cRLDistributionPoints extension using URI:http
+ validates :crl_url, presence: true, public_url: true
+
+ def self.safe_create!(attributes)
+ create_with(attributes)
+ .safe_find_or_create_by!(subject_key_identifier: attributes[:subject_key_identifier])
+ end
+end
diff --git a/app/services/git/branch_hooks_service.rb b/app/services/git/branch_hooks_service.rb
index 69f1f9eb31f..e1cc1f8c834 100644
--- a/app/services/git/branch_hooks_service.rb
+++ b/app/services/git/branch_hooks_service.rb
@@ -6,7 +6,7 @@ module Git
execute_branch_hooks
super.tap do
- enqueue_update_gpg_signatures
+ enqueue_update_signatures
end
end
@@ -103,14 +103,22 @@ module Git
end
end
- def enqueue_update_gpg_signatures
- unsigned = GpgSignature.unsigned_commit_shas(limited_commits.map(&:sha))
+ def unsigned_x509_shas(commits)
+ X509CommitSignature.unsigned_commit_shas(commits.map(&:sha))
+ end
+
+ def unsigned_gpg_shas(commits)
+ GpgSignature.unsigned_commit_shas(commits.map(&:sha))
+ end
+
+ def enqueue_update_signatures
+ unsigned = unsigned_x509_shas(commits) & unsigned_gpg_shas(commits)
return if unsigned.empty?
signable = Gitlab::Git::Commit.shas_with_signatures(project.repository, unsigned)
return if signable.empty?
- CreateGpgSignatureWorker.perform_async(signable, project.id)
+ CreateCommitSignatureWorker.perform_async(signable, project.id)
end
# It's not sufficient to just check for a blank SHA as it's possible for the
diff --git a/app/views/projects/commit/_signature.html.haml b/app/views/projects/commit/_signature.html.haml
index 145bc629380..aa7c90bad66 100644
--- a/app/views/projects/commit/_signature.html.haml
+++ b/app/views/projects/commit/_signature.html.haml
@@ -1,2 +1,3 @@
- if signature
- = render partial: "projects/commit/#{signature.verification_status}_signature_badge", locals: { signature: signature }
+ - uri = "projects/commit/#{"x509/" if signature.instance_of?(X509CommitSignature)}"
+ = render partial: "#{uri}#{signature.verification_status}_signature_badge", locals: { signature: signature }
diff --git a/app/views/projects/commit/_signature_badge.html.haml b/app/views/projects/commit/_signature_badge.html.haml
index cbd998c60ef..776ce48d4bc 100644
--- a/app/views/projects/commit/_signature_badge.html.haml
+++ b/app/views/projects/commit/_signature_badge.html.haml
@@ -17,12 +17,18 @@
- content = capture do
- if show_user
.clearfix
- = render partial: 'projects/commit/signature_badge_user', locals: { signature: signature }
+ - uri_signature_badge_user = "projects/commit/#{"x509/" if signature.instance_of?(X509CommitSignature)}signature_badge_user"
+ = render partial: "#{uri_signature_badge_user}", locals: { signature: signature }
- = _('GPG Key ID:')
- %span.monospace= signature.gpg_key_primary_keyid
+ - if signature.instance_of?(X509CommitSignature)
+ = render partial: "projects/commit/x509/certificate_details", locals: { signature: signature }
- = link_to(_('Learn more about signing commits'), help_page_path('user/project/repository/gpg_signed_commits/index.md'), class: 'gpg-popover-help-link')
+ = link_to(_('Learn more about x509 signed commits'), help_page_path('user/project/repository/x509_signed_commits/index.md'), class: 'gpg-popover-help-link')
+ - else
+ = _('GPG Key ID:')
+ %span.monospace= signature.gpg_key_primary_keyid
+
+ = link_to(_('Learn more about signing commits'), help_page_path('user/project/repository/gpg_signed_commits/index.md'), class: 'gpg-popover-help-link')
%button{ tabindex: 0, class: css_classes, data: { toggle: 'popover', html: 'true', placement: 'top', title: title, content: content } }
= label
diff --git a/app/views/projects/commit/x509/_certificate_details.html.haml b/app/views/projects/commit/x509/_certificate_details.html.haml
new file mode 100644
index 00000000000..2357c6d803b
--- /dev/null
+++ b/app/views/projects/commit/x509/_certificate_details.html.haml
@@ -0,0 +1,17 @@
+.gpg-popover-certificate-details
+ %strong= _('Certificate Subject')
+ %ul
+ - signature.x509_certificate.subject.split(",").each do |i|
+ - if i.start_with?("CN", "O")
+ %li= i
+ %li= _('Subject Key Identifier:')
+ %li.unstyled= signature.x509_certificate.subject_key_identifier.gsub(":", " ")
+
+.gpg-popover-certificate-details
+ %strong= _('Certificate Issuer')
+ %ul
+ - signature.x509_certificate.x509_issuer.subject.split(",").each do |i|
+ - if i.start_with?("CN", "OU", "O")
+ %li= i
+ %li= _('Subject Key Identifier:')
+ %li.unstyled= signature.x509_certificate.x509_issuer.subject_key_identifier.gsub(":", " ")
diff --git a/app/views/projects/commit/x509/_signature_badge_user.html.haml b/app/views/projects/commit/x509/_signature_badge_user.html.haml
new file mode 100644
index 00000000000..b64ccba2a18
--- /dev/null
+++ b/app/views/projects/commit/x509/_signature_badge_user.html.haml
@@ -0,0 +1,19 @@
+- user = signature.commit.committer
+- user_email = signature.x509_certificate.email
+
+- if user
+ = link_to user_path(user), class: 'gpg-popover-user-link' do
+ %div
+ = user_avatar_without_link(user: user, size: 32)
+
+ %div
+ %strong= user.name
+ %div= user.to_reference
+
+- else
+ = mail_to user_email do
+ %div
+ = user_avatar_without_link(user_email: user_email, size: 32)
+
+ %div
+ %strong= user_email
diff --git a/app/views/projects/commit/x509/_unverified_signature_badge.html.haml b/app/views/projects/commit/x509/_unverified_signature_badge.html.haml
new file mode 100644
index 00000000000..680cc32c7e6
--- /dev/null
+++ b/app/views/projects/commit/x509/_unverified_signature_badge.html.haml
@@ -0,0 +1,6 @@
+- title = capture do
+ = _('This commit was signed with an <strong>unverified</strong> signature.').html_safe
+
+- locals = { signature: signature, title: title, label: _('Unverified'), css_class: 'invalid', icon: 'status_notfound_borderless', show_user: true }
+
+= render partial: 'projects/commit/signature_badge', locals: locals
diff --git a/app/views/projects/commit/x509/_verified_signature_badge.html.haml b/app/views/projects/commit/x509/_verified_signature_badge.html.haml
new file mode 100644
index 00000000000..4964b1b8ee7
--- /dev/null
+++ b/app/views/projects/commit/x509/_verified_signature_badge.html.haml
@@ -0,0 +1,6 @@
+- title = capture do
+ = _('This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user.').html_safe
+
+- locals = { signature: signature, title: title, label: _('Verified'), css_class: 'valid', icon: 'status_success_borderless', show_user: true }
+
+= render partial: 'projects/commit/signature_badge', locals: locals
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 0426a0b8fbb..35852742b0d 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -699,14 +699,14 @@
:latency_sensitive: true
:resource_boundary: :unknown
:weight: 2
-- :name: create_evidence
- :feature_category: :release_governance
+- :name: create_commit_signature
+ :feature_category: :source_code_management
:has_external_dependencies:
:latency_sensitive:
:resource_boundary: :unknown
:weight: 2
-- :name: create_gpg_signature
- :feature_category: :source_code_management
+- :name: create_evidence
+ :feature_category: :release_governance
:has_external_dependencies:
:latency_sensitive:
:resource_boundary: :unknown
diff --git a/app/workers/create_gpg_signature_worker.rb b/app/workers/create_commit_signature_worker.rb
index 2043c3c8e77..027fea3e402 100644
--- a/app/workers/create_gpg_signature_worker.rb
+++ b/app/workers/create_commit_signature_worker.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class CreateGpgSignatureWorker
+class CreateCommitSignatureWorker
include ApplicationWorker
feature_category :source_code_management
@@ -23,7 +23,12 @@ class CreateGpgSignatureWorker
# This calculates and caches the signature in the database
commits.each do |commit|
- Gitlab::Gpg::Commit.new(commit).signature
+ case commit.signature_type
+ when :PGP
+ Gitlab::Gpg::Commit.new(commit).signature
+ when :X509
+ Gitlab::X509::Commit.new(commit).signature
+ end
rescue => e
Rails.logger.error("Failed to create signature for commit #{commit.id}. Error: #{e.message}") # rubocop:disable Gitlab/RailsLogger
end