Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-05-06 21:10:00 +00:00
parent 73886079f3
commit 5f0e3773e9
57 changed files with 1351 additions and 199 deletions

View File

@ -0,0 +1,42 @@
<script>
import ActiveToggle from './active_toggle.vue';
import JiraTriggerFields from './jira_trigger_fields.vue';
export default {
name: 'IntegrationForm',
components: {
ActiveToggle,
JiraTriggerFields,
},
props: {
activeToggleProps: {
type: Object,
required: true,
},
showActive: {
type: Boolean,
required: true,
},
triggerFieldsProps: {
type: Object,
required: true,
},
type: {
type: String,
required: true,
},
},
computed: {
isJira() {
return this.type === 'jira';
},
},
};
</script>
<template>
<div>
<active-toggle v-if="showActive" v-bind="activeToggleProps" />
<jira-trigger-fields v-if="isJira" v-bind="triggerFieldsProps" />
</div>
</template>

View File

@ -0,0 +1,99 @@
<script>
import { GlFormCheckbox, GlFormRadio } from '@gitlab/ui';
export default {
name: 'JiraTriggerFields',
components: {
GlFormCheckbox,
GlFormRadio,
},
props: {
initialTriggerCommit: {
type: Boolean,
required: true,
},
initialTriggerMergeRequest: {
type: Boolean,
required: true,
},
initialEnableComments: {
type: Boolean,
required: true,
},
initialCommentDetail: {
type: String,
required: false,
default: 'standard',
},
},
data() {
return {
triggerCommit: this.initialTriggerCommit,
triggerMergeRequest: this.initialTriggerMergeRequest,
enableComments: this.initialEnableComments,
commentDetail: this.initialCommentDetail,
};
},
};
</script>
<template>
<div class="form-group row pt-2" role="group">
<label for="service[trigger]" class="col-form-label col-sm-2 pt-0">{{ __('Trigger') }}</label>
<div class="col-sm-10">
<label class="weight-normal mb-2">
{{
s__(
'Integrations|When a Jira issue is mentioned in a commit or merge request a remote link and comment (if enabled) will be created.',
)
}}
</label>
<input name="service[commit_events]" type="hidden" value="false" />
<gl-form-checkbox v-model="triggerCommit" name="service[commit_events]">
{{ __('Commit') }}
</gl-form-checkbox>
<input name="service[merge_requests_events]" type="hidden" value="false" />
<gl-form-checkbox v-model="triggerMergeRequest" name="service[merge_requests_events]">
{{ __('Merge request') }}
</gl-form-checkbox>
<div
v-show="triggerCommit || triggerMergeRequest"
class="mt-4"
data-testid="comment-settings"
>
<label>
{{ s__('Integrations|Comment settings:') }}
</label>
<input name="service[comment_on_event_enabled]" type="hidden" value="false" />
<gl-form-checkbox v-model="enableComments" name="service[comment_on_event_enabled]">
{{ s__('Integrations|Enable comments') }}
</gl-form-checkbox>
<div v-show="enableComments" class="mt-4" data-testid="comment-detail">
<label>
{{ s__('Integrations|Comment detail:') }}
</label>
<gl-form-radio v-model="commentDetail" value="standard" name="service[comment_detail]">
{{ s__('Integrations|Standard') }}
<template #help>
{{ s__('Integrations|Includes commit title and branch') }}
</template>
</gl-form-radio>
<gl-form-radio v-model="commentDetail" value="all_details" name="service[comment_detail]">
{{ s__('Integrations|All details') }}
<template #help>
{{
s__(
'Integrations|Includes Standard plus entire commit message, commit hash, and issue IDs',
)
}}
</template>
</gl-form-radio>
</div>
</div>
</div>
</div>
</template>

View File

@ -1,26 +1,45 @@
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import ActiveToggle from './components/active_toggle.vue';
import IntegrationForm from './components/integration_form.vue';
export default el => {
if (!el) {
return null;
}
const { showActive: showActiveStr, activated: activatedStr } = el.dataset;
const showActive = parseBoolean(showActiveStr);
const activated = parseBoolean(activatedStr);
if (!showActive) {
return null;
function parseBooleanInData(data) {
const result = {};
Object.entries(data).forEach(([key, value]) => {
result[key] = parseBoolean(value);
});
return result;
}
const { type, commentDetail, ...booleanAttributes } = el.dataset;
const {
showActive,
activated,
commitEvents,
mergeRequestEvents,
enableComments,
} = parseBooleanInData(booleanAttributes);
return new Vue({
el,
render(createElement) {
return createElement(ActiveToggle, {
return createElement(IntegrationForm, {
props: {
initialActivated: activated,
activeToggleProps: {
initialActivated: activated,
},
showActive,
type,
triggerFieldsProps: {
initialTriggerCommit: commitEvents,
initialTriggerMergeRequest: mergeRequestEvents,
initialEnableComments: enableComments,
initialCommentDetail: commentDetail,
},
},
});
},

View File

@ -5,7 +5,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
# NOTE: Use @application_setting in this controller when you need to access
# application_settings after it has been modified. This is because the
# ApplicationSetting model uses Gitlab::ThreadMemoryCache for caching and the
# ApplicationSetting model uses Gitlab::ProcessMemoryCache for caching and the
# cache might be stale immediately after an update.
# https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/30233
before_action :set_application_setting, except: :integrations

View File

@ -16,4 +16,8 @@ module X509Helper
rescue
{}
end
def x509_signature?(sig)
sig.is_a?(X509CommitSignature) || sig.is_a?(Gitlab::X509::Signature)
end
end

View File

@ -421,7 +421,7 @@ class ApplicationSetting < ApplicationRecord
# can cause a significant amount of load on Redis, let's cache it in
# memory.
def self.cache_backend
Gitlab::ThreadMemoryCache.cache_backend
Gitlab::ProcessMemoryCache.cache_backend
end
def recaptcha_or_login_protection_enabled

View File

@ -141,7 +141,7 @@ class MergeRequestDiff < ApplicationRecord
after_create :save_git_content, unless: :importing?
after_create_commit :set_as_latest_diff, unless: :importing?
after_save :update_external_diff_store, if: -> { !importing? && saved_change_to_external_diff? }
after_save :update_external_diff_store
def self.find_by_diff_refs(diff_refs)
find_by(start_commit_sha: diff_refs.start_sha, head_commit_sha: diff_refs.head_sha, base_commit_sha: diff_refs.base_sha)
@ -401,8 +401,10 @@ class MergeRequestDiff < ApplicationRecord
end
def update_external_diff_store
update_column(:external_diff_store, external_diff.object_store) if
has_attribute?(:external_diff_store)
return unless has_attribute?(:external_diff_store)
return unless saved_change_to_external_diff? || saved_change_to_stored_externally?
update_column(:external_diff_store, external_diff.object_store)
end
def saved_change_to_external_diff?

View File

@ -177,6 +177,7 @@ class JiraService < IssueTrackerService
noteable_id = noteable.respond_to?(:iid) ? noteable.iid : noteable.id
noteable_type = noteable_name(noteable)
entity_url = build_entity_url(noteable_type, noteable_id)
entity_meta = build_entity_meta(noteable)
data = {
user: {
@ -185,12 +186,15 @@ class JiraService < IssueTrackerService
},
project: {
name: project.full_path,
url: resource_url(namespace_project_path(project.namespace, project)) # rubocop:disable Cop/ProjectPathHelper
url: resource_url(project_path(project))
},
entity: {
id: entity_meta[:id],
name: noteable_type.humanize.downcase,
url: entity_url,
title: noteable.title
title: noteable.title,
description: entity_meta[:description],
branch: entity_meta[:branch]
}
}
@ -264,14 +268,11 @@ class JiraService < IssueTrackerService
end
def add_comment(data, issue)
user_name = data[:user][:name]
user_url = data[:user][:url]
entity_name = data[:entity][:name]
entity_url = data[:entity][:url]
entity_title = data[:entity][:title]
project_name = data[:project][:name]
message = "[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]:\n'#{entity_title.chomp}'"
message = comment_message(data)
link_title = "#{entity_name.capitalize} - #{entity_title}"
link_props = build_remote_link_props(url: entity_url, title: link_title)
@ -280,6 +281,37 @@ class JiraService < IssueTrackerService
end
end
def comment_message(data)
user_link = build_jira_link(data[:user][:name], data[:user][:url])
entity = data[:entity]
entity_ref = all_details? ? "#{entity[:name]} #{entity[:id]}" : "a #{entity[:name]}"
entity_link = build_jira_link(entity_ref, entity[:url])
project_link = build_jira_link(project.full_name, Gitlab::Routing.url_helpers.project_url(project))
branch =
if entity[:branch].present?
s_('JiraService| on branch %{branch_link}') % {
branch_link: build_jira_link(entity[:branch], project_tree_url(project, entity[:branch]))
}
end
entity_message = entity[:description].presence if all_details?
entity_message ||= entity[:title].chomp
s_('JiraService|%{user_link} mentioned this issue in %{entity_link} of %{project_link}%{branch}:{quote}%{entity_message}{quote}') % {
user_link: user_link,
entity_link: entity_link,
project_link: project_link,
branch: branch,
entity_message: entity_message
}
end
def build_jira_link(title, url)
"[#{title}|#{url}]"
end
def has_resolution?(issue)
issue.respond_to?(:resolution) && issue.resolution.present?
end
@ -353,6 +385,23 @@ class JiraService < IssueTrackerService
)
end
def build_entity_meta(noteable)
if noteable.is_a?(Commit)
{
id: noteable.short_id,
description: noteable.safe_message,
branch: noteable.ref_names(project.repository).first
}
elsif noteable.is_a?(MergeRequest)
{
id: noteable.to_reference,
branch: noteable.source_branch
}
else
{}
end
end
def noteable_name(noteable)
name = noteable.model_name.singular

View File

@ -7,6 +7,7 @@ class SnippetRepository < ApplicationRecord
EMPTY_FILE_PATTERN = /^#{DEFAULT_EMPTY_FILE_NAME}(\d+)\.txt$/.freeze
CommitError = Class.new(StandardError)
InvalidPathError = Class.new(CommitError)
belongs_to :snippet, inverse_of: :snippet_repository
@ -40,8 +41,9 @@ class SnippetRepository < ApplicationRecord
rescue Gitlab::Git::Index::IndexError,
Gitlab::Git::CommitError,
Gitlab::Git::PreReceiveError,
Gitlab::Git::CommandError => e
raise CommitError, e.message
Gitlab::Git::CommandError => error
raise commit_error_exception(error)
end
def transform_file_entries(files)
@ -85,4 +87,16 @@ class SnippetRepository < ApplicationRecord
def build_empty_file_name(index)
"#{DEFAULT_EMPTY_FILE_NAME}#{index}.txt"
end
def commit_error_exception(error)
if error.is_a?(Gitlab::Git::Index::IndexError) && invalid_path_error?(error.message)
InvalidPathError.new('Invalid Path') # To avoid returning the message with the path included
else
CommitError.new(error.message)
end
end
def invalid_path_error?(message)
message.downcase.start_with?('invalid path', 'path cannot include directory traversal')
end
end

View File

@ -41,4 +41,8 @@ class X509CommitSignature < ApplicationRecord
Gitlab::X509::Commit.new(commit)
end
def user
commit.committer
end
end

View File

@ -1,3 +1,3 @@
- if signature
- uri = "projects/commit/#{"x509/" if signature.instance_of?(X509CommitSignature)}"
- uri = "projects/commit/#{"x509/" if x509_signature?(signature)}"
= render partial: "#{uri}#{signature.verification_status}_signature_badge", locals: { signature: signature }

View File

@ -17,13 +17,13 @@
- content = capture do
- if show_user
.clearfix
- uri_signature_badge_user = "projects/commit/#{"x509/" if signature.instance_of?(X509CommitSignature)}signature_badge_user"
- uri_signature_badge_user = "projects/commit/#{"x509/" if x509_signature?(signature)}signature_badge_user"
= render partial: "#{uri_signature_badge_user}", locals: { signature: signature }
- if signature.instance_of?(X509CommitSignature)
- if x509_signature?(signature)
= render partial: "projects/commit/x509/certificate_details", locals: { signature: signature }
= link_to(_('Learn more about x509 signed commits'), help_page_path('user/project/repository/x509_signed_commits/index.md'), class: 'gpg-popover-help-link')
= link_to(_('Learn more about X.509 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

View File

@ -1,5 +1,5 @@
- user = signature.commit.committer
- user_email = signature.x509_certificate.email
- user = signature.user
- if user
= link_to user_path(user), class: 'gpg-popover-user-link' do

View File

@ -30,6 +30,9 @@
= markdown_field(release, :description)
.row-fixed-content.controls.flex-row
- if tag.has_signature?
= render partial: 'projects/commit/signature', object: tag.signature
= render 'projects/buttons/download', project: @project, ref: tag.name, pipeline: @tags_pipelines[tag.name]
- if can?(current_user, :admin_tag, @project)

View File

@ -39,6 +39,8 @@
= s_("TagsPage|Can't find HEAD commit for this tag")
.nav-controls
- if @tag.has_signature?
= render partial: 'projects/commit/signature', object: @tag.signature
- if can?(current_user, :admin_tag, @project)
= link_to edit_project_tag_release_path(@project, @tag.name), class: 'btn btn-edit controls-item has-tooltip', title: s_('TagsPage|Edit release notes') do
= icon("pencil")

View File

@ -8,9 +8,10 @@
= markdown @service.help
.service-settings
.js-vue-integration-settings{ data: { show_active: @service.show_active_box?.to_s, activated: (@service.active || @service.new_record?).to_s } }
.js-vue-integration-settings{ data: { show_active: @service.show_active_box?.to_s, activated: (@service.active || @service.new_record?).to_s, type: @service.to_param, merge_request_events: @service.merge_requests_events.to_s,
commit_events: @service.commit_events.to_s, enable_comments: @service.comment_on_event_enabled.to_s, comment_detail: @service.comment_detail } }
- if @service.configurable_events.present?
- if @service.configurable_events.present? && !@service.is_a?(JiraService)
.form-group.row
%label.col-form-label.col-sm-2= _('Trigger')
@ -31,22 +32,6 @@
%p.text-muted
= @service.class.event_description(event)
- if @service.configurable_event_actions.present?
.form-group.row
%label.col-form-label.col-sm-2= _('Event Actions')
.col-sm-10
- @service.configurable_event_actions.each do |action|
.form-group
.form-check
= form.check_box service_event_action_field_name(action), class: 'form-check-input'
= form.label service_event_action_field_name(action), class: 'form-check-label' do
%strong
= event_action_description(action)
%p.text-muted
= event_action_description(action)
- @service.global_fields.each do |field|
- type = field[:type]

View File

@ -0,0 +1,5 @@
---
title: Update Jira comment to include more information
merge_request: 30258
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Correctly track the store that external MR diffs are placed on
merge_request: 31005
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Display x509 signed tags
merge_request: 27211
author: Roger Meier
type: added

View File

@ -0,0 +1,5 @@
---
title: Fix 500 error loading environments index
merge_request: 31184
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Use process-wide cache for application settings and performance bar
merge_request: 31135
author:
type: performance

View File

@ -1,12 +1,12 @@
require 'gitlab/testing/request_blocker_middleware'
require 'gitlab/testing/request_inspector_middleware'
require 'gitlab/testing/clear_thread_memory_cache_middleware'
require 'gitlab/testing/clear_process_memory_cache_middleware'
Rails.application.configure do
# Make sure the middleware is inserted first in middleware chain
config.middleware.insert_before(ActionDispatch::Static, Gitlab::Testing::RequestBlockerMiddleware)
config.middleware.insert_before(ActionDispatch::Static, Gitlab::Testing::RequestInspectorMiddleware)
config.middleware.insert_before(ActionDispatch::Static, Gitlab::Testing::ClearThreadMemoryCacheMiddleware)
config.middleware.insert_before(ActionDispatch::Static, Gitlab::Testing::ClearProcessMemoryCacheMiddleware)
# Settings specified here will take precedence over those in config/application.rb

View File

@ -1,3 +0,0 @@
# frozen_string_literal: true
Gitlab::ThreadMemoryCache.cache_backend

View File

@ -151,18 +151,18 @@ The response will be `404 Not Found` if the vulnerability export is not finished
Example response:
```csv
Scanner Type,Scanner Name,Status,Vulnerability,Details,Additional Info,Severity,CVE
container_scanning,Clair,confirmed,CVE-2017-16997 in glibc,,CVE-2017-16997 in glibc,critical,CVE-2017-16997
container_scanning,Clair,detected,CVE-2017-18269 in glibc,,CVE-2017-18269 in glibc,critical,CVE-2017-18269
container_scanning,Clair,detected,CVE-2018-1000001 in glibc,,CVE-2018-1000001 in glibc,high,CVE-2018-1000001
container_scanning,Clair,detected,CVE-2016-10228 in glibc,,CVE-2016-10228 in glibc,medium,CVE-2016-10228
container_scanning,Clair,confirmed,CVE-2010-4052 in glibc,,CVE-2010-4052 in glibc,low,CVE-2010-4052
container_scanning,Clair,detected,CVE-2018-18520 in elfutils,,CVE-2018-18520 in elfutils,low,CVE-2018-18520
container_scanning,Clair,detected,CVE-2018-16869 in nettle,,CVE-2018-16869 in nettle,unknown,CVE-2018-16869
dependency_scanning,Gemnasium,detected,Regular Expression Denial of Service in debug,,Regular Expression Denial of Service in debug,unknown,yarn.lock:debug:gemnasium:37283ed4-0380-40d7-ada7-2d994afcc62a
dependency_scanning,Gemnasium,detected,Authentication bypass via incorrect DOM traversal and canonicalization in saml2-js,,Authentication bypass via incorrect DOM traversal and canonicalization in saml2-js,unknown,yarn.lock:saml2-js:gemnasium:9952e574-7b5b-46fa-a270-aeb694198a98
sast,Find Security Bugs,detected,Predictable pseudorandom number generator,,Predictable pseudorandom number generator,medium,818bf5dacb291e15d9e6dc3c5ac32178:PREDICTABLE_RANDOM:src/main/java/com/gitlab/security_products/tests/App.java:47
sast,Find Security Bugs,detected,Cipher with no integrity,,Cipher with no integrity,medium,e6449b89335daf53c0db4c0219bc1634:CIPHER_INTEGRITY:src/main/java/com/gitlab/security_products/tests/App.java:29
sast,Find Security Bugs,detected,Predictable pseudorandom number generator,,Predictable pseudorandom number generator,medium,e8ff1d01f74cd372f78da8f5247d3e73:PREDICTABLE_RANDOM:src/main/java/com/gitlab/security_products/tests/App.java:41
sast,Find Security Bugs,confirmed,ECB mode is insecure 2,,ECB mode is insecure,medium,ea0f905fc76f2739d5f10a1fd1e37a10:ECB_MODE:src/main/java/com/gitlab/security_products/tests/App.java:29
```
Group Name,Project Name,Scanner Type,Scanner Name,Status,Vulnerability,Details,Additional Info,Severity,CVE
Gitlab.org,Defend,container_scanning,Clair,confirmed,CVE-2017-16997 in glibc,,CVE-2017-16997 in glibc,critical,CVE-2017-16997
Gitlab.org,Defend,container_scanning,Clair,detected,CVE-2017-18269 in glibc,,CVE-2017-18269 in glibc,critical,CVE-2017-18269
Gitlab.org,Defend,container_scanning,Clair,detected,CVE-2018-1000001 in glibc,,CVE-2018-1000001 in glibc,high,CVE-2018-1000001
Gitlab.org,Defend,container_scanning,Clair,detected,CVE-2016-10228 in glibc,,CVE-2016-10228 in glibc,medium,CVE-2016-10228
Gitlab.org,Defend,container_scanning,Clair,confirmed,CVE-2010-4052 in glibc,,CVE-2010-4052 in glibc,low,CVE-2010-4052
Gitlab.org,Defend,container_scanning,Clair,detected,CVE-2018-18520 in elfutils,,CVE-2018-18520 in elfutils,low,CVE-2018-18520
Gitlab.org,Defend,container_scanning,Clair,detected,CVE-2018-16869 in nettle,,CVE-2018-16869 in nettle,unknown,CVE-2018-16869
Gitlab.org,Defend,dependency_scanning,Gemnasium,detected,Regular Expression Denial of Service in debug,,Regular Expression Denial of Service in debug,unknown,yarn.lock:debug:gemnasium:37283ed4-0380-40d7-ada7-2d994afcc62a
Gitlab.org,Defend,dependency_scanning,Gemnasium,detected,Authentication bypass via incorrect DOM traversal and canonicalization in saml2-js,,Authentication bypass via incorrect DOM traversal and canonicalization in saml2-js,unknown,yarn.lock:saml2-js:gemnasium:9952e574-7b5b-46fa-a270-aeb694198a98
Gitlab.org,Defend,sast,Find Security Bugs,detected,Predictable pseudorandom number generator,,Predictable pseudorandom number generator,medium,818bf5dacb291e15d9e6dc3c5ac32178:PREDICTABLE_RANDOM:src/main/java/com/gitlab/security_products/tests/App.java:47
Gitlab.org,Defend,sast,Find Security Bugs,detected,Cipher with no integrity,,Cipher with no integrity,medium,e6449b89335daf53c0db4c0219bc1634:CIPHER_INTEGRITY:src/main/java/com/gitlab/security_products/tests/App.java:29
Gitlab.org,Defend,sast,Find Security Bugs,detected,Predictable pseudorandom number generator,,Predictable pseudorandom number generator,medium,e8ff1d01f74cd372f78da8f5247d3e73:PREDICTABLE_RANDOM:src/main/java/com/gitlab/security_products/tests/App.java:41
Gitlab.org,Defend,sast,Find Security Bugs,confirmed,ECB mode is insecure 2,,ECB mode is insecure,medium,ea0f905fc76f2739d5f10a1fd1e37a10:ECB_MODE:src/main/java/com/gitlab/security_products/tests/App.java:29
Gitlab.org,Defend,```

View File

@ -468,6 +468,21 @@ When a merge request author has been blocked for longer than
the `Review-response` SLO, they are free to remind the reviewer through Slack or assign
another reviewer.
#### Customer critical merge requests
A merge request may benefit from being considered a customer critical priority because there is a significant benefit to the business in doing so.
Properties of customer critical merge requests:
- The [Senior Director of Development](https://about.gitlab.com/job-families/engineering/engineering-management/#senior-director-engineering) [@clefelhocz1](https://gitlab.com/clefelhocz1) is the DRI for deciding if a merge request will be customer critical.
- The DRI will assign the `customer-critical-merge-request` label to the merge request.
- It is required that the reviewer(s) and maintainer(s) involved with a customer critical merge request are engaged as soon as this decision is made.
- It is required to prioritize work for those involved on a customer critical merge request so that they have the time available necessary to focus on it.
- It is required to adhere to GitLab [values](https://about.gitlab.com/handbook/values.md) and processes when working on customer critical merge requests, taking particular note of family and friends first/work second, definition of done, iteration, and release when it's ready.
- Customer critical merge requests are required to not reduce security, introduce data-loss risk, reduce availability, nor break existing functionality per the process for [prioritizing technical decisions](https://about.gitlab.com/handbook/engineering/#prioritizing-technical-decisions.md).
- On customer critical requests, it is _recommended_ that those involved _consider_ coordinating synchronously (Zoom, Slack) in addition to asynchronously (merge requests comments) if they believe this will reduce elapsed time to merge even though this _may_ sacrifice [efficiency](https://about.gitlab.com/company/culture/all-remote/asynchronous/#evaluating-efficiency.md).
- After a customer critical merge request is merged, a retrospective must be completed with the intention of reducing the frequency of future customer critical merge requests.
## Examples
How code reviews are conducted can surprise new contributors. Here are some examples of code reviews that should help to orient you as to what to expect.

View File

@ -552,10 +552,12 @@ being cleaned up will be minimal.
## Limitations
Moving or renaming existing Container Registry repositories is not supported
- Moving or renaming existing Container Registry repositories is not supported
once you have pushed images, because the images are signed, and the
signature includes the repository name. To move or rename a repository with a
Container Registry, you will have to delete all existing images.
- Prior to GitLab 12.10, any tags that use the same image ID as the `latest` tag
will not be deleted by the expiration policy.
## Troubleshooting the GitLab Container Registry

View File

@ -2,7 +2,7 @@
type: concepts, howto
---
# Signing commits with X.509
# Signing commits and tags with X.509
[X.509](https://en.wikipedia.org/wiki/X.509) is a standard format for public key
certificates issued by a public or private Public Key Infrastructure (PKI).
@ -16,7 +16,7 @@ instead of a web of trust with GPG.
GitLab uses its own certificate store and therefore defines the trust chain.
For a commit to be *verified* by GitLab:
For a commit or tag to be *verified* by GitLab:
- The signing certificate email must match a verified email address used by the committer in GitLab.
- The Certificate Authority has to be trusted by the GitLab instance, see also
@ -27,6 +27,11 @@ For a commit to be *verified* by GitLab:
NOTE: **Note:** Certificate revocation lists are checked on a daily basis via background worker.
NOTE: **Note:** Self signed certificates without `authorityKeyIdentifier`,
`subjectKeyIdentifier`, and `crlDistributionPoints` are not supported. We
recommend using certificates from a PKI that are in line with
[RFC 5280](https://tools.ietf.org/html/rfc5280).
## Obtaining an X.509 key pair
If your organization has Public Key Infrastructure (PKI), that PKI will provide
@ -98,3 +103,31 @@ To verify that a commit is signed, you can use the `--show-signature` flag:
```sh
git log --show-signature
```
## Signing tags
After you have [associated your X.509 certificate with Git](#associating-your-x509-certificate-with-git) you
can start signing your tags:
1. Tag like you used to, the only difference is the addition of the `-s` flag:
```sh
git tag -s v1.1.1 -m "My signed tag"
```
1. Push to GitLab and check that your tags [are verified](#verifying-tags).
If you don't want to type the `-s` flag every time you tag, you can tell Git
to sign your tags automatically:
```sh
git config --global tag.gpgsign true
```
## Verifying tags
To verify that a tag is signed, you can use the `--verify` flag:
```sh
git tag --verify v1.1.1
```

View File

@ -134,11 +134,7 @@ class Feature
end
def l1_cache_backend
if Gitlab::Utils.to_boolean(ENV['USE_THREAD_MEMORY_CACHE'])
Gitlab::ThreadMemoryCache.cache_backend
else
Gitlab::ProcessMemoryCache.cache_backend
end
Gitlab::ProcessMemoryCache.cache_backend
end
def l2_cache_backend

View File

@ -117,7 +117,7 @@ module Gitlab
# the migration can succeed, to achieve that, we'll identify in migration retries
# that the path is invalid
def set_file_path_error(error)
@invalid_path_error = error.message.downcase.start_with?('invalid path', 'path cannot include directory traversal')
@invalid_path_error = error.is_a?(SnippetRepository::InvalidPathError)
end
end
end

View File

@ -66,6 +66,27 @@ module Gitlab
@raw_tag.tagger
end
def has_signature?
signature_type != :NONE
end
def signature_type
@raw_tag.signature_type || :NONE
end
def signature
return unless has_signature?
case signature_type
when :PGP
nil # not implemented, see https://gitlab.com/gitlab-org/gitlab/issues/19260
when :X509
X509::Tag.new(@raw_tag).signature
else
nil
end
end
private
def message_from_gitaly_tag

View File

@ -2,13 +2,25 @@
module Gitlab
module Json
INVALID_LEGACY_TYPES = [String, TrueClass, FalseClass].freeze
class << self
def parse(*args)
adapter.parse(*args)
def parse(string, *args, **named_args)
legacy_mode = legacy_mode_enabled?(named_args.delete(:legacy_mode))
data = adapter.parse(string, *args, **named_args)
handle_legacy_mode!(data) if legacy_mode
data
end
def parse!(*args)
adapter.parse!(*args)
def parse!(string, *args, **named_args)
legacy_mode = legacy_mode_enabled?(named_args.delete(:legacy_mode))
data = adapter.parse!(string, *args, **named_args)
handle_legacy_mode!(data) if legacy_mode
data
end
def dump(*args)
@ -28,6 +40,20 @@ module Gitlab
def adapter
::JSON
end
def parser_error
::JSON::ParserError
end
def legacy_mode_enabled?(arg_value)
arg_value.nil? ? false : arg_value
end
def handle_legacy_mode!(data)
return data unless Feature.enabled?(:json_wrapper_legacy_mode, default_enabled: true)
raise parser_error if INVALID_LEGACY_TYPES.any? { |type| data.is_a?(type) }
end
end
end
end

View File

@ -44,7 +44,7 @@ module Gitlab
end
def self.l1_cache_backend
Gitlab::ThreadMemoryCache.cache_backend
Gitlab::ProcessMemoryCache.cache_backend
end
def self.l2_cache_backend

View File

@ -2,13 +2,13 @@
module Gitlab
module Testing
class ClearThreadMemoryCacheMiddleware
class ClearProcessMemoryCacheMiddleware
def initialize(app)
@app = app
end
def call(env)
Gitlab::ThreadMemoryCache.cache_backend.clear
Gitlab::ProcessMemoryCache.cache_backend.clear
@app.call(env)
end

View File

@ -1,15 +0,0 @@
# frozen_string_literal: true
module Gitlab
class ThreadMemoryCache
THREAD_KEY = :thread_memory_cache
def self.cache_backend
# Note ActiveSupport::Cache::MemoryStore is thread-safe. Since
# each backend is local per thread we probably don't need to worry
# about synchronizing access, but this is a drop-in replacement
# for ActiveSupport::Cache::RedisStore.
Thread.current[THREAD_KEY] ||= ActiveSupport::Cache::MemoryStore.new
end
end
end

View File

@ -22,6 +22,10 @@ module Gitlab
X509Certificate.safe_create!(certificate_attributes) unless verified_signature.nil?
end
def user
User.find_by_any_email(@email)
end
def verified_signature
strong_memoize(:verified_signature) { verified_signature? }
end

41
lib/gitlab/x509/tag.rb Normal file
View File

@ -0,0 +1,41 @@
# frozen_string_literal: true
require 'openssl'
require 'digest'
module Gitlab
module X509
class Tag
include Gitlab::Utils::StrongMemoize
def initialize(raw_tag)
@raw_tag = raw_tag
end
def signature
signature = X509::Signature.new(signature_text, signed_text, @raw_tag.tagger.email, Time.at(@raw_tag.tagger.date.seconds))
return if signature.verified_signature.nil?
signature
end
private
def signature_text
@raw_tag.message.slice(@raw_tag.message.index("-----BEGIN SIGNED MESSAGE-----")..-1)
rescue
nil
end
def signed_text
# signed text is reconstructed as long as there is no specific gitaly function
%{object #{@raw_tag.target_commit.id}
type commit
tag #{@raw_tag.name}
tagger #{@raw_tag.tagger.name} <#{@raw_tag.tagger.email}> #{@raw_tag.tagger.date.seconds} #{@raw_tag.tagger.timezone}
#{@raw_tag.message.gsub(/-----BEGIN SIGNED MESSAGE-----(.*)-----END SIGNED MESSAGE-----/m, "")}}
end
end
end
end

View File

@ -3410,12 +3410,6 @@ msgstr ""
msgid "Business metrics (Custom)"
msgstr ""
msgid "Buy EE"
msgstr ""
msgid "Buy GitLab Enterprise Edition"
msgstr ""
msgid "Buy more Pipeline minutes"
msgstr ""
@ -5593,18 +5587,9 @@ msgstr ""
msgid "Connection timed out"
msgstr ""
msgid "Contact an owner of group %{namespace_name} to upgrade the plan."
msgstr ""
msgid "Contact owner %{link_start}%{owner_name}%{link_end} to upgrade the plan."
msgstr ""
msgid "Contact sales to upgrade"
msgstr ""
msgid "Contact your Administrator to upgrade your license."
msgstr ""
msgid "Container Registry"
msgstr ""
@ -8572,9 +8557,6 @@ msgstr ""
msgid "Estimated"
msgstr ""
msgid "Event Actions"
msgstr ""
msgid "EventFilterBy|Filter by all"
msgstr ""
@ -9793,10 +9775,7 @@ msgstr ""
msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
msgstr ""
msgid "Geo|All"
msgstr ""
msgid "Geo|All %{replicable_type}"
msgid "Geo|All %{replicable_name}"
msgstr ""
msgid "Geo|All projects"
@ -11306,9 +11285,6 @@ msgstr ""
msgid "Improve Merge Requests and customer support with GitLab Enterprise Edition."
msgstr ""
msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
msgstr ""
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
msgstr ""
@ -11411,6 +11387,12 @@ msgstr ""
msgid "Insights"
msgstr ""
msgid "Insights|Some items are not visible beacuse the project was filtered out in the insights.yml file (see the projects.only config for more information)."
msgstr ""
msgid "Insights|This project is filtered out in the insights.yml file (see the projects.only config for more information)."
msgstr ""
msgid "Install"
msgstr ""
@ -11461,6 +11443,30 @@ msgstr ""
msgid "Integrations allow you to integrate GitLab with other applications"
msgstr ""
msgid "Integrations|All details"
msgstr ""
msgid "Integrations|Comment detail:"
msgstr ""
msgid "Integrations|Comment settings:"
msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
msgid "Integrations|Includes Standard plus entire commit message, commit hash, and issue IDs"
msgstr ""
msgid "Integrations|Includes commit title and branch"
msgstr ""
msgid "Integrations|Standard"
msgstr ""
msgid "Integrations|When a Jira issue is mentioned in a commit or merge request a remote link and comment (if enabled) will be created."
msgstr ""
msgid "Interested parties can even contribute by pushing commits if they want to."
msgstr ""
@ -11764,6 +11770,12 @@ msgstr ""
msgid "Jira project: %{importProject}"
msgstr ""
msgid "JiraService| on branch %{branch_link}"
msgstr ""
msgid "JiraService|%{user_link} mentioned this issue in %{entity_link} of %{project_link}%{branch}:{quote}%{entity_message}{quote}"
msgstr ""
msgid "JiraService|Events for %{noteable_model_name} are disabled."
msgstr ""
@ -12189,6 +12201,9 @@ msgstr ""
msgid "Learn more about Web Terminal"
msgstr ""
msgid "Learn more about X.509 signed commits"
msgstr ""
msgid "Learn more about adding certificates to your project by following the %{docs_link_start}documentation on GitLab Pages%{docs_link_end}."
msgstr ""
@ -12210,9 +12225,6 @@ msgstr ""
msgid "Learn more about the dependency list"
msgstr ""
msgid "Learn more about x509 signed commits"
msgstr ""
msgid "Learn more in the"
msgstr ""
@ -16671,24 +16683,51 @@ msgstr ""
msgid "Promoted issue to an epic."
msgstr ""
msgid "Promotions|Buy EE"
msgstr ""
msgid "Promotions|Buy GitLab Enterprise Edition"
msgstr ""
msgid "Promotions|Contact an owner of group %{namespace_name} to upgrade the plan."
msgstr ""
msgid "Promotions|Contact owner %{link_start}%{owner_name}%{link_end} to upgrade the plan."
msgstr ""
msgid "Promotions|Contact your Administrator to upgrade your license."
msgstr ""
msgid "Promotions|Don't show me this again"
msgstr ""
msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
msgstr ""
msgid "Promotions|Improve issues management with Issue weight and GitLab Enterprise Edition."
msgstr ""
msgid "Promotions|Learn more"
msgstr ""
msgid "Promotions|See the other features in the %{subscription_link_start}bronze plan%{subscription_link_end}"
msgstr ""
msgid "Promotions|Start GitLab Ultimate trial"
msgstr ""
msgid "Promotions|This feature is locked."
msgstr ""
msgid "Promotions|Upgrade plan"
msgstr ""
msgid "Promotions|Upgrade your plan"
msgstr ""
msgid "Promotions|Weight"
msgstr ""
msgid "Promotions|Weighting your issue"
msgstr ""
@ -19774,9 +19813,6 @@ msgstr ""
msgid "Stars"
msgstr ""
msgid "Start GitLab Ultimate trial"
msgstr ""
msgid "Start Web Terminal"
msgstr ""
@ -22643,9 +22679,6 @@ msgstr ""
msgid "Upgrade plan to unlock Canary Deployments feature"
msgstr ""
msgid "Upgrade your plan"
msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""

View File

@ -9,7 +9,6 @@ describe('ActiveToggle', () => {
const defaultProps = {
initialActivated: true,
disabled: false,
};
const createComponent = props => {

View File

@ -0,0 +1,81 @@
import { shallowMount } from '@vue/test-utils';
import IntegrationForm from '~/integrations/edit/components/integration_form.vue';
import ActiveToggle from '~/integrations/edit/components/active_toggle.vue';
import JiraTriggerFields from '~/integrations/edit/components/jira_trigger_fields.vue';
describe('IntegrationForm', () => {
let wrapper;
const defaultProps = {
activeToggleProps: {
initialActivated: true,
},
showActive: true,
triggerFieldsProps: {
initialTriggerCommit: false,
initialTriggerMergeRequest: false,
initialEnableComments: false,
},
type: '',
};
const createComponent = props => {
wrapper = shallowMount(IntegrationForm, {
propsData: { ...defaultProps, ...props },
stubs: {
ActiveToggle,
JiraTriggerFields,
},
});
};
afterEach(() => {
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
});
const findActiveToggle = () => wrapper.find(ActiveToggle);
const findJiraTriggerFields = () => wrapper.find(JiraTriggerFields);
describe('template', () => {
describe('showActive is true', () => {
it('renders ActiveToggle', () => {
createComponent();
expect(findActiveToggle().exists()).toBe(true);
});
});
describe('showActive is false', () => {
it('does not render ActiveToggle', () => {
createComponent({
showActive: false,
});
expect(findActiveToggle().exists()).toBe(false);
});
});
describe('type is "slack"', () => {
it('does not render JiraTriggerFields', () => {
createComponent({
type: 'slack',
});
expect(findJiraTriggerFields().exists()).toBe(false);
});
});
describe('type is "jira"', () => {
it('renders JiraTriggerFields', () => {
createComponent({
type: 'jira',
});
expect(findJiraTriggerFields().exists()).toBe(true);
});
});
});
});

View File

@ -0,0 +1,94 @@
import { mount } from '@vue/test-utils';
import JiraTriggerFields from '~/integrations/edit/components/jira_trigger_fields.vue';
import { GlFormCheckbox } from '@gitlab/ui';
describe('JiraTriggerFields', () => {
let wrapper;
const defaultProps = {
initialTriggerCommit: false,
initialTriggerMergeRequest: false,
initialEnableComments: false,
};
const createComponent = props => {
wrapper = mount(JiraTriggerFields, {
propsData: Object.assign({}, defaultProps, props),
});
};
afterEach(() => {
if (wrapper) wrapper.destroy();
});
const findCommentSettings = () => wrapper.find('[data-testid="comment-settings"]');
const findCommentDetail = () => wrapper.find('[data-testid="comment-detail"]');
const findCommentSettingsCheckbox = () => findCommentSettings().find(GlFormCheckbox);
describe('template', () => {
describe('initialTriggerCommit and initialTriggerMergeRequest are false', () => {
it('does not show comment settings', () => {
createComponent();
expect(findCommentSettings().isVisible()).toBe(false);
expect(findCommentDetail().isVisible()).toBe(false);
});
});
describe('initialTriggerCommit is true', () => {
beforeEach(() => {
createComponent({
initialTriggerCommit: true,
});
});
it('shows comment settings', () => {
expect(findCommentSettings().isVisible()).toBe(true);
expect(findCommentDetail().isVisible()).toBe(false);
});
// As per https://vuejs.org/v2/guide/forms.html#Checkbox-1,
// browsers don't include unchecked boxes in form submissions.
it('includes comment settings as false even if unchecked', () => {
expect(
findCommentSettings()
.find('input[name="service[comment_on_event_enabled]"]')
.exists(),
).toBe(true);
});
describe('on enable comments', () => {
it('shows comment detail', () => {
findCommentSettingsCheckbox().vm.$emit('input', true);
return wrapper.vm.$nextTick().then(() => {
expect(findCommentDetail().isVisible()).toBe(true);
});
});
});
});
describe('initialTriggerMergeRequest is true', () => {
it('shows comment settings', () => {
createComponent({
initialTriggerMergeRequest: true,
});
expect(findCommentSettings().isVisible()).toBe(true);
expect(findCommentDetail().isVisible()).toBe(false);
});
});
describe('initialTriggerCommit is true, initialEnableComments is true', () => {
it('shows comment settings and comment detail', () => {
createComponent({
initialTriggerCommit: true,
initialEnableComments: true,
});
expect(findCommentSettings().isVisible()).toBe(true);
expect(findCommentDetail().isVisible()).toBe(true);
});
});
});
});

View File

@ -57,4 +57,22 @@ describe X509Helper do
end
end
end
describe '#x509_signature?' do
let(:x509_signature) { create(:x509_commit_signature) }
let(:gpg_signature) { create(:gpg_signature) }
it 'detects a x509 signed commit' do
signature = Gitlab::X509::Signature.new(
X509Helpers::User1.signed_commit_signature,
X509Helpers::User1.signed_commit_base_data,
X509Helpers::User1.certificate_email,
X509Helpers::User1.signed_commit_time
)
expect(x509_signature?(x509_signature)).to be_truthy
expect(x509_signature?(signature)).to be_truthy
expect(x509_signature?(gpg_signature)).to be_falsey
end
end
end

View File

@ -146,14 +146,6 @@ describe Feature do
expect(described_class.enabled?(:enabled_feature_flag)).to be_truthy
end
context 'with USE_THREAD_MEMORY_CACHE defined' do
before do
stub_env('USE_THREAD_MEMORY_CACHE', '1')
end
it { expect(described_class.l1_cache_backend).to eq(Gitlab::ThreadMemoryCache.cache_backend) }
end
it { expect(described_class.l1_cache_backend).to eq(Gitlab::ProcessMemoryCache.cache_backend) }
it { expect(described_class.l2_cache_backend).to eq(Rails.cache) }

View File

@ -13,6 +13,13 @@ describe Gitlab::Git::Tag, :seed_helper do
it { expect(tag.target).to eq("f4e6814c3e4e7a0de82a9e7cd20c626cc963a2f8") }
it { expect(tag.dereferenced_target.sha).to eq("6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9") }
it { expect(tag.message).to eq("Release") }
it { expect(tag.has_signature?).to be_falsey }
it { expect(tag.signature_type).to eq(:NONE) }
it { expect(tag.signature).to be_nil }
it { expect(tag.tagger.name).to eq("Dmitriy Zaporozhets") }
it { expect(tag.tagger.email).to eq("dmitriy.zaporozhets@gmail.com") }
it { expect(tag.tagger.date).to eq(Google::Protobuf::Timestamp.new(seconds: 1393491299)) }
it { expect(tag.tagger.timezone).to eq("+0200") }
end
describe 'last tag' do
@ -22,6 +29,29 @@ describe Gitlab::Git::Tag, :seed_helper do
it { expect(tag.target).to eq("2ac1f24e253e08135507d0830508febaaccf02ee") }
it { expect(tag.dereferenced_target.sha).to eq("fa1b1e6c004a68b7d8763b86455da9e6b23e36d6") }
it { expect(tag.message).to eq("Version 1.2.1") }
it { expect(tag.has_signature?).to be_falsey }
it { expect(tag.signature_type).to eq(:NONE) }
it { expect(tag.signature).to be_nil }
it { expect(tag.tagger.name).to eq("Douwe Maan") }
it { expect(tag.tagger.email).to eq("douwe@selenight.nl") }
it { expect(tag.tagger.date).to eq(Google::Protobuf::Timestamp.new(seconds: 1427789449)) }
it { expect(tag.tagger.timezone).to eq("+0200") }
end
describe 'signed tag' do
let(:project) { create(:project, :repository) }
let(:tag) { project.repository.find_tag('v1.1.1') }
it { expect(tag.target).to eq("8f03acbcd11c53d9c9468078f32a2622005a4841") }
it { expect(tag.dereferenced_target.sha).to eq("189a6c924013fc3fe40d6f1ec1dc20214183bc97") }
it { expect(tag.message).to eq("x509 signed tag" + "\n" + X509Helpers::User1.signed_tag_signature.chomp) }
it { expect(tag.has_signature?).to be_truthy }
it { expect(tag.signature_type).to eq(:X509) }
it { expect(tag.signature).not_to be_nil }
it { expect(tag.tagger.name).to eq("Roger Meier") }
it { expect(tag.tagger.email).to eq("r.meier@siemens.com") }
it { expect(tag.tagger.date).to eq(Google::Protobuf::Timestamp.new(seconds: 1574261780)) }
it { expect(tag.tagger.timezone).to eq("+0100") }
end
it { expect(repository.tags.size).to eq(SeedRepo::Repo::TAGS.size) }

View File

@ -3,47 +3,103 @@
require "spec_helper"
RSpec.describe Gitlab::Json do
before do
stub_feature_flags(json_wrapper_legacy_mode: true)
end
describe ".parse" do
it "parses an object" do
expect(subject.parse('{ "foo": "bar" }')).to eq({ "foo" => "bar" })
context "legacy_mode is disabled by default" do
it "parses an object" do
expect(subject.parse('{ "foo": "bar" }')).to eq({ "foo" => "bar" })
end
it "parses an array" do
expect(subject.parse('[{ "foo": "bar" }]')).to eq([{ "foo" => "bar" }])
end
# These tests will change expectations when the gem is upgraded
it "raises an error on a string" do
expect { subject.parse('"foo"') }.to raise_error(JSON::ParserError)
end
it "raises an error on a true bool" do
expect { subject.parse("true") }.to raise_error(JSON::ParserError)
end
it "raises an error on a false bool" do
expect { subject.parse("false") }.to raise_error(JSON::ParserError)
end
end
it "parses an array" do
expect(subject.parse('[{ "foo": "bar" }]')).to eq([{ "foo" => "bar" }])
end
context "legacy_mode is enabled" do
it "parses an object" do
expect(subject.parse('{ "foo": "bar" }', legacy_mode: true)).to eq({ "foo" => "bar" })
end
it "raises an error on a string" do
expect { subject.parse('"foo"') }.to raise_error(JSON::ParserError)
end
it "parses an array" do
expect(subject.parse('[{ "foo": "bar" }]', legacy_mode: true)).to eq([{ "foo" => "bar" }])
end
it "raises an error on a true bool" do
expect { subject.parse("true") }.to raise_error(JSON::ParserError)
end
it "raises an error on a string" do
expect { subject.parse('"foo"', legacy_mode: true) }.to raise_error(JSON::ParserError)
end
it "raises an error on a false bool" do
expect { subject.parse("false") }.to raise_error(JSON::ParserError)
it "raises an error on a true bool" do
expect { subject.parse("true", legacy_mode: true) }.to raise_error(JSON::ParserError)
end
it "raises an error on a false bool" do
expect { subject.parse("false", legacy_mode: true) }.to raise_error(JSON::ParserError)
end
end
end
describe ".parse!" do
it "parses an object" do
expect(subject.parse!('{ "foo": "bar" }')).to eq({ "foo" => "bar" })
context "legacy_mode is disabled by default" do
it "parses an object" do
expect(subject.parse!('{ "foo": "bar" }')).to eq({ "foo" => "bar" })
end
it "parses an array" do
expect(subject.parse!('[{ "foo": "bar" }]')).to eq([{ "foo" => "bar" }])
end
# These tests will change expectations when the gem is upgraded
it "raises an error on a string" do
expect { subject.parse!('"foo"') }.to raise_error(JSON::ParserError)
end
it "raises an error on a true bool" do
expect { subject.parse!("true") }.to raise_error(JSON::ParserError)
end
it "raises an error on a false bool" do
expect { subject.parse!("false") }.to raise_error(JSON::ParserError)
end
end
it "parses an array" do
expect(subject.parse!('[{ "foo": "bar" }]')).to eq([{ "foo" => "bar" }])
end
context "legacy_mode is enabled" do
it "parses an object" do
expect(subject.parse!('{ "foo": "bar" }', legacy_mode: true)).to eq({ "foo" => "bar" })
end
it "raises an error on a string" do
expect { subject.parse!('"foo"') }.to raise_error(JSON::ParserError)
end
it "parses an array" do
expect(subject.parse!('[{ "foo": "bar" }]', legacy_mode: true)).to eq([{ "foo" => "bar" }])
end
it "raises an error on a true bool" do
expect { subject.parse!("true") }.to raise_error(JSON::ParserError)
end
it "raises an error on a string" do
expect { subject.parse!('"foo"', legacy_mode: true) }.to raise_error(JSON::ParserError)
end
it "raises an error on a false bool" do
expect { subject.parse!("false") }.to raise_error(JSON::ParserError)
it "raises an error on a true bool" do
expect { subject.parse!("true", legacy_mode: true) }.to raise_error(JSON::ParserError)
end
it "raises an error on a false bool" do
expect { subject.parse!("false", legacy_mode: true) }.to raise_error(JSON::ParserError)
end
end
end

View File

@ -38,7 +38,7 @@ describe Gitlab::PerformanceBar do
end
end
it { expect(described_class.l1_cache_backend).to eq(Gitlab::ThreadMemoryCache.cache_backend) }
it { expect(described_class.l1_cache_backend).to eq(Gitlab::ProcessMemoryCache.cache_backend) }
it { expect(described_class.l2_cache_backend).to eq(Rails.cache) }
describe '.enabled_for_user?' do

View File

@ -229,4 +229,164 @@ describe Gitlab::X509::Signature do
end
end
end
describe '#user' do
signature = described_class.new(
X509Helpers::User1.signed_tag_signature,
X509Helpers::User1.signed_tag_base_data,
X509Helpers::User1.certificate_email,
X509Helpers::User1.signed_commit_time
)
context 'if email is assigned to a user' do
let!(:user) { create(:user, email: X509Helpers::User1.certificate_email) }
it 'returns user' do
expect(signature.user).to eq(user)
end
end
it 'if email is not assigned to a user, return nil' do
expect(signature.user).to be_nil
end
end
context 'tag signature' do
let(:certificate_attributes) do
{
subject_key_identifier: X509Helpers::User1.tag_certificate_subject_key_identifier,
subject: X509Helpers::User1.certificate_subject,
email: X509Helpers::User1.certificate_email,
serial_number: X509Helpers::User1.tag_certificate_serial
}
end
let(:issuer_attributes) do
{
subject_key_identifier: X509Helpers::User1.tag_issuer_subject_key_identifier,
subject: X509Helpers::User1.tag_certificate_issuer,
crl_url: X509Helpers::User1.tag_certificate_crl
}
end
context 'verified signature' do
context 'with trusted certificate store' do
before do
store = OpenSSL::X509::Store.new
certificate = OpenSSL::X509::Certificate.new X509Helpers::User1.trust_cert
store.add_cert(certificate)
allow(OpenSSL::X509::Store).to receive(:new).and_return(store)
end
it 'returns a verified signature if email does match' do
signature = described_class.new(
X509Helpers::User1.signed_tag_signature,
X509Helpers::User1.signed_tag_base_data,
X509Helpers::User1.certificate_email,
X509Helpers::User1.signed_commit_time
)
expect(signature.x509_certificate).to have_attributes(certificate_attributes)
expect(signature.x509_certificate.x509_issuer).to have_attributes(issuer_attributes)
expect(signature.verified_signature).to be_truthy
expect(signature.verification_status).to eq(:verified)
end
it 'returns an unverified signature if email does not match' do
signature = described_class.new(
X509Helpers::User1.signed_tag_signature,
X509Helpers::User1.signed_tag_base_data,
"gitlab@example.com",
X509Helpers::User1.signed_commit_time
)
expect(signature.x509_certificate).to have_attributes(certificate_attributes)
expect(signature.x509_certificate.x509_issuer).to have_attributes(issuer_attributes)
expect(signature.verified_signature).to be_truthy
expect(signature.verification_status).to eq(:unverified)
end
it 'returns an unverified signature if email does match and time is wrong' do
signature = described_class.new(
X509Helpers::User1.signed_tag_signature,
X509Helpers::User1.signed_tag_base_data,
X509Helpers::User1.certificate_email,
Time.new(2020, 2, 22)
)
expect(signature.x509_certificate).to have_attributes(certificate_attributes)
expect(signature.x509_certificate.x509_issuer).to have_attributes(issuer_attributes)
expect(signature.verified_signature).to be_falsey
expect(signature.verification_status).to eq(:unverified)
end
it 'returns an unverified signature if certificate is revoked' do
signature = described_class.new(
X509Helpers::User1.signed_tag_signature,
X509Helpers::User1.signed_tag_base_data,
X509Helpers::User1.certificate_email,
X509Helpers::User1.signed_commit_time
)
expect(signature.verification_status).to eq(:verified)
signature.x509_certificate.revoked!
expect(signature.verification_status).to eq(:unverified)
end
end
context 'without trusted certificate within store' do
before do
store = OpenSSL::X509::Store.new
allow(OpenSSL::X509::Store).to receive(:new)
.and_return(
store
)
end
it 'returns an unverified signature' do
signature = described_class.new(
X509Helpers::User1.signed_tag_signature,
X509Helpers::User1.signed_tag_base_data,
X509Helpers::User1.certificate_email,
X509Helpers::User1.signed_commit_time
)
expect(signature.x509_certificate).to have_attributes(certificate_attributes)
expect(signature.x509_certificate.x509_issuer).to have_attributes(issuer_attributes)
expect(signature.verified_signature).to be_falsey
expect(signature.verification_status).to eq(:unverified)
end
end
end
context 'invalid signature' do
it 'returns nil' do
signature = described_class.new(
X509Helpers::User1.signed_tag_signature.tr('A', 'B'),
X509Helpers::User1.signed_tag_base_data,
X509Helpers::User1.certificate_email,
X509Helpers::User1.signed_commit_time
)
expect(signature.x509_certificate).to be_nil
expect(signature.verified_signature).to be_falsey
expect(signature.verification_status).to eq(:unverified)
end
end
context 'invalid message' do
it 'returns nil' do
signature = described_class.new(
X509Helpers::User1.signed_tag_signature,
'x',
X509Helpers::User1.certificate_email,
X509Helpers::User1.signed_commit_time
)
expect(signature.x509_certificate).to be_nil
expect(signature.verified_signature).to be_falsey
expect(signature.verification_status).to eq(:unverified)
end
end
end
end

View File

@ -0,0 +1,42 @@
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::X509::Tag do
subject(:signature) { described_class.new(tag).signature }
describe '#signature' do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
let(:project) { create(:project, :repository) }
describe 'signed tag' do
let(:tag) { project.repository.find_tag('v1.1.1') }
let(:certificate_attributes) do
{
subject_key_identifier: X509Helpers::User1.tag_certificate_subject_key_identifier,
subject: X509Helpers::User1.certificate_subject,
email: X509Helpers::User1.certificate_email,
serial_number: X509Helpers::User1.tag_certificate_serial
}
end
let(:issuer_attributes) do
{
subject_key_identifier: X509Helpers::User1.tag_issuer_subject_key_identifier,
subject: X509Helpers::User1.tag_certificate_issuer,
crl_url: X509Helpers::User1.tag_certificate_crl
}
end
it { expect(signature).not_to be_nil }
it { expect(signature.verification_status).to eq(:unverified) }
it { expect(signature.x509_certificate).to have_attributes(certificate_attributes) }
it { expect(signature.x509_certificate.x509_issuer).to have_attributes(issuer_attributes) }
end
context 'unsigned tag' do
let(:tag) { project.repository.find_tag('v1.0.0') }
it { expect(signature).to be_nil }
end
end
end

View File

@ -205,11 +205,11 @@ describe CacheableAttributes do
end
end
it 'uses RequestStore in addition to Thread memory cache', :request_store do
it 'uses RequestStore in addition to process memory cache', :request_store do
# Warm up the cache
create(:application_setting).cache!
expect(ApplicationSetting.cache_backend).to eq(Gitlab::ThreadMemoryCache.cache_backend)
expect(ApplicationSetting.cache_backend).to eq(Gitlab::ProcessMemoryCache.cache_backend)
expect(ApplicationSetting.cache_backend).to receive(:read).with(ApplicationSetting.cache_key).once.and_call_original
2.times { ApplicationSetting.current }

View File

@ -3,6 +3,8 @@
require 'spec_helper'
describe MergeRequestDiff do
using RSpec::Parameterized::TableSyntax
include RepoHelpers
let(:diff_with_commits) { create(:merge_request).merge_request_diff }
@ -125,18 +127,71 @@ describe MergeRequestDiff do
end
end
describe '#update_external_diff_store' do
let_it_be(:merge_request) { create(:merge_request) }
let(:diff) { merge_request.merge_request_diff }
let(:store) { diff.external_diff.object_store }
where(:change_stored_externally, :change_external_diff) do
false | false
false | true
true | false
true | true
end
with_them do
it do
diff.stored_externally = true if change_stored_externally
diff.external_diff = "new-filename" if change_external_diff
update_store = receive(:update_column).with(:external_diff_store, store)
if change_stored_externally || change_external_diff
expect(diff).to update_store
else
expect(diff).not_to update_store
end
diff.save!
end
end
end
describe '#migrate_files_to_external_storage!' do
let(:uploader) { ExternalDiffUploader }
let(:file_store) { uploader::Store::LOCAL }
let(:remote_store) { uploader::Store::REMOTE }
let(:diff) { create(:merge_request).merge_request_diff }
it 'converts from in-database to external storage' do
it 'converts from in-database to external file storage' do
expect(diff).not_to be_stored_externally
stub_external_diffs_setting(enabled: true)
expect(diff).to receive(:save!)
expect(diff).to receive(:save!).and_call_original
diff.migrate_files_to_external_storage!
expect(diff).to be_stored_externally
expect(diff.external_diff_store).to eq(file_store)
end
it 'converts from in-database to external object storage' do
expect(diff).not_to be_stored_externally
stub_external_diffs_setting(enabled: true)
# Without direct_upload: true, the files would be saved to disk, and a
# background job would be enqueued to move the file to object storage
stub_external_diffs_object_storage(uploader, direct_upload: true)
expect(diff).to receive(:save!).and_call_original
diff.migrate_files_to_external_storage!
expect(diff).to be_stored_externally
expect(diff.external_diff_store).to eq(remote_store)
end
it 'does nothing with an external diff' do

View File

@ -582,6 +582,79 @@ describe JiraService do
end
end
describe '#create_cross_reference_note' do
let_it_be(:user) { build_stubbed(:user) }
let_it_be(:project) { create(:project, :repository) }
let(:jira_service) do
described_class.new(
project: project,
url: url,
username: username,
password: password
)
end
let(:jira_issue) { ExternalIssue.new('JIRA-123', project) }
subject { jira_service.create_cross_reference_note(jira_issue, resource, user) }
shared_examples 'creates a comment on Jira' do
let(:issue_url) { "#{url}/rest/api/2/issue/JIRA-123" }
let(:comment_url) { "#{issue_url}/comment" }
let(:remote_link_url) { "#{issue_url}/remotelink" }
before do
allow(JIRA::Resource::Remotelink).to receive(:all).and_return([])
stub_request(:get, issue_url).with(basic_auth: [username, password])
stub_request(:post, comment_url).with(basic_auth: [username, password])
stub_request(:post, remote_link_url).with(basic_auth: [username, password])
end
it 'creates a comment on Jira' do
subject
expect(WebMock).to have_requested(:post, comment_url).with(
body: /mentioned this issue in/
).once
end
end
context 'when resource is a commit' do
let(:resource) { project.commit('master') }
context 'when disabled' do
before do
allow_next_instance_of(JiraService) do |instance|
allow(instance).to receive(:commit_events) { false }
end
end
it { is_expected.to eq('Events for commits are disabled.') }
end
context 'when enabled' do
it_behaves_like 'creates a comment on Jira'
end
end
context 'when resource is a merge request' do
let(:resource) { build_stubbed(:merge_request, source_project: project) }
context 'when disabled' do
before do
allow_next_instance_of(JiraService) do |instance|
allow(instance).to receive(:merge_requests_events) { false }
end
end
it { is_expected.to eq('Events for merge requests are disabled.') }
end
context 'when enabled' do
it_behaves_like 'creates a comment on Jira'
end
end
end
describe '#test' do
let(:jira_service) do
described_class.new(

View File

@ -202,6 +202,22 @@ describe SnippetRepository do
it_behaves_like 'snippet repository with file names', 'snippetfile10.txt', 'snippetfile11.txt'
end
shared_examples 'snippet repository with git errors' do |path, error|
let(:new_file) { { file_path: path, content: 'bar' } }
it 'raises a path specific error' do
expect do
snippet_repository.multi_files_action(user, data, commit_opts)
end.to raise_error(error)
end
end
context 'with git errors' do
it_behaves_like 'snippet repository with git errors', 'invalid://path/here', described_class::InvalidPathError
it_behaves_like 'snippet repository with git errors', '../../path/traversal/here', described_class::InvalidPathError
it_behaves_like 'snippet repository with git errors', 'README', described_class::CommitError
end
end
def blob_at(snippet, path)

View File

@ -9,6 +9,15 @@ RSpec.describe X509CommitSignature do
let(:x509_certificate) { create(:x509_certificate) }
let(:x509_signature) { create(:x509_commit_signature, commit_sha: commit_sha) }
let(:attributes) do
{
commit_sha: commit_sha,
project: project,
x509_certificate_id: x509_certificate.id,
verification_status: "verified"
}
end
it_behaves_like 'having unique enum values'
describe 'validation' do
@ -23,15 +32,6 @@ RSpec.describe X509CommitSignature do
end
describe '.safe_create!' do
let(:attributes) do
{
commit_sha: commit_sha,
project: project,
x509_certificate_id: x509_certificate.id,
verification_status: "verified"
}
end
it 'finds a signature by commit sha if it existed' do
x509_signature
@ -50,4 +50,18 @@ RSpec.describe X509CommitSignature do
expect(signature.x509_certificate_id).to eq(x509_certificate.id)
end
end
describe '#user' do
context 'if email is assigned to a user' do
let!(:user) { create(:user, email: X509Helpers::User1.certificate_email) }
it 'returns user' do
expect(described_class.safe_create!(attributes).user).to eq(user)
end
end
it 'if email is not assigned to a user, return nil' do
expect(described_class.safe_create!(attributes).user).to be_nil
end
end
end

View File

@ -462,7 +462,8 @@ describe SystemNoteService do
describe "existing reference" do
before do
allow(JIRA::Resource::Remotelink).to receive(:all).and_return([])
message = "[#{author.name}|http://localhost/#{author.username}] mentioned this issue in [a commit of #{project.full_path}|http://localhost/#{project.full_path}/-/commit/#{commit.id}]:\n'#{commit.title.chomp}'"
message = double('message')
allow(message).to receive(:include?) { true }
allow_next_instance_of(JIRA::Resource::Issue) do |instance|
allow(instance).to receive(:comments).and_return([OpenStruct.new(body: message)])
end

View File

@ -209,9 +209,7 @@ RSpec.configure do |config|
# expect(Gitlab::Git::KeepAround).to receive(:execute).and_call_original
allow(Gitlab::Git::KeepAround).to receive(:execute)
[Gitlab::ThreadMemoryCache, Gitlab::ProcessMemoryCache].each do |cache|
cache.cache_backend.clear
end
Gitlab::ProcessMemoryCache.cache_backend.clear
Sidekiq::Worker.clear_all

View File

@ -45,7 +45,7 @@ module StubObjectStorage
def stub_external_diffs_object_storage(uploader = described_class, **params)
stub_object_storage_uploader(config: Gitlab.config.external_diffs.object_store,
uploader: uploader,
remote_directory: 'external_diffs',
remote_directory: 'external-diffs',
**params)
end

View File

@ -173,22 +173,155 @@ module X509Helpers
Time.at(1561027326)
end
def signed_tag_signature
<<~SIGNATURE
-----BEGIN SIGNED MESSAGE-----
MIISfwYJKoZIhvcNAQcCoIIScDCCEmwCAQExDTALBglghkgBZQMEAgEwCwYJKoZI
hvcNAQcBoIIP8zCCB3QwggVcoAMCAQICBBXXLOIwDQYJKoZIhvcNAQELBQAwgbYx
CzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVu
MRAwDgYDVQQKDAdTaWVtZW5zMREwDwYDVQQFEwhaWlpaWlpBNjEdMBsGA1UECwwU
U2llbWVucyBUcnVzdCBDZW50ZXIxPzA9BgNVBAMMNlNpZW1lbnMgSXNzdWluZyBD
QSBNZWRpdW0gU3RyZW5ndGggQXV0aGVudGljYXRpb24gMjAxNjAeFw0xNzAyMDMw
NjU4MzNaFw0yMDAyMDMwNjU4MzNaMFsxETAPBgNVBAUTCFowMDBOV0RIMQ4wDAYD
VQQqDAVSb2dlcjEOMAwGA1UEBAwFTWVpZXIxEDAOBgNVBAoMB1NpZW1lbnMxFDAS
BgNVBAMMC01laWVyIFJvZ2VyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAuBNea/68ZCnHYQjpm/k3ZBG0wBpEKSwG6lk9CEQlSxsqVLQHAoAKBIlJm1in
YVLcK/Sq1yhYJ/qWcY/M53DhK2rpPuhtrWJUdOUy8EBWO20F4bd4Fw9pO7jt8bme
u33TSrK772vKjuppzB6SeG13Cs08H+BIeD106G27h7ufsO00pvsxoSDL+uc4slnr
pBL+2TAL7nSFnB9QHWmRIK27SPqJE+lESdb0pse11x1wjvqKy2Q7EjL9fpqJdHzX
NLKHXd2r024TOORTa05DFTNR+kQEKKV96XfpYdtSBomXNQ44cisiPBJjFtYvfnFE
wgrHa8fogn/b0C+A+HAoICN12wIDAQABo4IC4jCCAt4wHQYDVR0OBBYEFCF+gkUp
XQ6xGc0kRWXuDFxzA14zMEMGA1UdEQQ8MDqgIwYKKwYBBAGCNxQCA6AVDBNyLm1l
aWVyQHNpZW1lbnMuY29tgRNyLm1laWVyQHNpZW1lbnMuY29tMA4GA1UdDwEB/wQE
AwIHgDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwgcoGA1UdHwSBwjCB
vzCBvKCBuaCBtoYmaHR0cDovL2NoLnNpZW1lbnMuY29tL3BraT9aWlpaWlpBNi5j
cmyGQWxkYXA6Ly9jbC5zaWVtZW5zLm5ldC9DTj1aWlpaWlpBNixMPVBLST9jZXJ0
aWZpY2F0ZVJldm9jYXRpb25MaXN0hklsZGFwOi8vY2wuc2llbWVucy5jb20vQ049
WlpaWlpaQTYsbz1UcnVzdGNlbnRlcj9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0
MEUGA1UdIAQ+MDwwOgYNKwYBBAGhaQcCAgMBAzApMCcGCCsGAQUFBwIBFhtodHRw
Oi8vd3d3LnNpZW1lbnMuY29tL3BraS8wDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAW
gBT4FV1HDGx3e3LEAheRaKK292oJRDCCAQQGCCsGAQUFBwEBBIH3MIH0MDIGCCsG
AQUFBzAChiZodHRwOi8vYWguc2llbWVucy5jb20vcGtpP1paWlpaWkE2LmNydDBB
BggrBgEFBQcwAoY1bGRhcDovL2FsLnNpZW1lbnMubmV0L0NOPVpaWlpaWkE2LEw9
UEtJP2NBQ2VydGlmaWNhdGUwSQYIKwYBBQUHMAKGPWxkYXA6Ly9hbC5zaWVtZW5z
LmNvbS9DTj1aWlpaWlpBNixvPVRydXN0Y2VudGVyP2NBQ2VydGlmaWNhdGUwMAYI
KwYBBQUHMAGGJGh0dHA6Ly9vY3NwLnBraS1zZXJ2aWNlcy5zaWVtZW5zLmNvbTAN
BgkqhkiG9w0BAQsFAAOCAgEAXPVcX6vaEcszJqg5IemF9aFTlwTrX5ITNIpzcqG+
kD5haOf2mZYLjl+MKtLC1XfmIsGCUZNb8bjP6QHQEI+2d6x/ZOqPq7Kd7PwVu6x6
xZrkDjUyhUbUntT5+RBy++l3Wf6Cq6Kx+K8ambHBP/bu90/p2U8KfFAG3Kr2gI2q
fZrnNMOxmJfZ3/sXxssgLkhbZ7hRa+MpLfQ6uFsSiat3vlawBBvTyHnoZ/7oRc8y
qi6QzWcd76CPpMElYWibl+hJzKbBZUWvc71AzHR6i1QeZ6wubYz7vr+FF5Y7tnxB
Vz6omPC9XAg0F+Dla6Zlz3Awj5imCzVXa+9SjtnsidmJdLcKzTAKyDewewoxYOOJ
j3cJU7VSjJPl+2fVmDBaQwcNcUcu/TPAKApkegqO7tRF9IPhjhW8QkRnkqMetO3D
OXmAFVIsEI0Hvb2cdb7B6jSpjGUuhaFm9TCKhQtCk2p8JCDTuaENLm1x34rrJKbT
2vzyYN0CZtSkUdgD4yQxK9VWXGEzexRisWb4AnZjD2NAquLPpXmw8N0UwFD7MSpC
dpaX7FktdvZmMXsnGiAdtLSbBgLVWOD1gmJFDjrhNbI8NOaOaNk4jrfGqNh5lhGU
4DnBT2U6Cie1anLmFH/oZooAEXR2o3Nu+1mNDJChnJp0ovs08aa3zZvBdcloOvfU
qdowggh3MIIGX6ADAgECAgQtyi/nMA0GCSqGSIb3DQEBCwUAMIGZMQswCQYDVQQG
EwJERTEPMA0GA1UECAwGQmF5ZXJuMREwDwYDVQQHDAhNdWVuY2hlbjEQMA4GA1UE
CgwHU2llbWVuczERMA8GA1UEBRMIWlpaWlpaQTExHTAbBgNVBAsMFFNpZW1lbnMg
VHJ1c3QgQ2VudGVyMSIwIAYDVQQDDBlTaWVtZW5zIFJvb3QgQ0EgVjMuMCAyMDE2
MB4XDTE2MDcyMDEzNDYxMFoXDTIyMDcyMDEzNDYxMFowgbYxCzAJBgNVBAYTAkRF
MQ8wDQYDVQQIDAZCYXllcm4xETAPBgNVBAcMCE11ZW5jaGVuMRAwDgYDVQQKDAdT
aWVtZW5zMREwDwYDVQQFEwhaWlpaWlpBNjEdMBsGA1UECwwUU2llbWVucyBUcnVz
dCBDZW50ZXIxPzA9BgNVBAMMNlNpZW1lbnMgSXNzdWluZyBDQSBNZWRpdW0gU3Ry
ZW5ndGggQXV0aGVudGljYXRpb24gMjAxNjCCAiIwDQYJKoZIhvcNAQEBBQADggIP
ADCCAgoCggIBAL9UfK+JAZEqVMVvECdYF9IK4KSw34AqyNl3rYP5x03dtmKaNu+2
0fQqNESA1NGzw3s6LmrKLh1cR991nB2cvKOXu7AvEGpSuxzIcOROd4NpvRx+Ej1p
JIPeqf+ScmVK7lMSO8QL/QzjHOpGV3is9sG+ZIxOW9U1ESooy4Hal6ZNs4DNItsz
piCKqm6G3et4r2WqCy2RRuSqvnmMza7Y8BZsLy0ZVo5teObQ37E/FxqSrbDI8nxn
B7nVUve5ZjrqoIGSkEOtyo11003dVO1vmWB9A0WQGDqE/q3w178hGhKfxzRaqzyi
SoADUYS2sD/CglGTUxVq6u0pGLLsCFjItcCWqW+T9fPYfJ2CEd5b3hvqdCn+pXjZ
/gdX1XAcdUF5lRnGWifaYpT9n4s4adzX8q6oHSJxTppuAwLRKH6eXALbGQ1I9lGQ
DSOipD/09xkEsPw6HOepmf2U3YxZK1VU2sHqugFJboeLcHMzp6E1n2ctlNG1GKE9
FDHmdyFzDi0Nnxtf/GgVjnHF68hByEE1MYdJ4nJLuxoT9hyjYdRW9MpeNNxxZnmz
W3zh7QxIqP0ZfIz6XVhzrI9uZiqwwojDiM5tEOUkQ7XyW6grNXe75yt6mTj89LlB
H5fOW2RNmCy/jzBXDjgyskgK7kuCvUYTuRv8ITXbBY5axFA+CpxZqokpAgMBAAGj
ggKmMIICojCCAQUGCCsGAQUFBwEBBIH4MIH1MEEGCCsGAQUFBzAChjVsZGFwOi8v
YWwuc2llbWVucy5uZXQvQ049WlpaWlpaQTEsTD1QS0k/Y0FDZXJ0aWZpY2F0ZTAy
BggrBgEFBQcwAoYmaHR0cDovL2FoLnNpZW1lbnMuY29tL3BraT9aWlpaWlpBMS5j
cnQwSgYIKwYBBQUHMAKGPmxkYXA6Ly9hbC5zaWVtZW5zLmNvbS91aWQ9WlpaWlpa
QTEsbz1UcnVzdGNlbnRlcj9jQUNlcnRpZmljYXRlMDAGCCsGAQUFBzABhiRodHRw
Oi8vb2NzcC5wa2ktc2VydmljZXMuc2llbWVucy5jb20wHwYDVR0jBBgwFoAUcG2g
UOyp0CxnnRkV/v0EczXD4tQwEgYDVR0TAQH/BAgwBgEB/wIBADBABgNVHSAEOTA3
MDUGCCsGAQQBoWkHMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuc2llbWVucy5j
b20vcGtpLzCBxwYDVR0fBIG/MIG8MIG5oIG2oIGzhj9sZGFwOi8vY2wuc2llbWVu
cy5uZXQvQ049WlpaWlpaQTEsTD1QS0k/YXV0aG9yaXR5UmV2b2NhdGlvbkxpc3SG
Jmh0dHA6Ly9jaC5zaWVtZW5zLmNvbS9wa2k/WlpaWlpaQTEuY3JshkhsZGFwOi8v
Y2wuc2llbWVucy5jb20vdWlkPVpaWlpaWkExLG89VHJ1c3RjZW50ZXI/YXV0aG9y
aXR5UmV2b2NhdGlvbkxpc3QwJwYDVR0lBCAwHgYIKwYBBQUHAwIGCCsGAQUFBwME
BggrBgEFBQcDCTAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPgVXUcMbHd7csQC
F5Foorb3aglEMA0GCSqGSIb3DQEBCwUAA4ICAQBw+sqMp3SS7DVKcILEmXbdRAg3
lLO1r457KY+YgCT9uX4VG5EdRKcGfWXK6VHGCi4Dos5eXFV34Mq/p8nu1sqMuoGP
YjHn604eWDprhGy6GrTYdxzcE/GGHkpkuE3Ir/45UcmZlOU41SJ9SNjuIVrSHMOf
ccSY42BCspR/Q1Z/ykmIqQecdT3/Kkx02GzzSN2+HlW6cEO4GBW5RMqsvd2n0h2d
fe2zcqOgkLtx7u2JCR/U77zfyxG3qXtcymoz0wgSHcsKIl+GUjITLkHfS9Op8V7C
Gr/dX437sIg5pVHmEAWadjkIzqdHux+EF94Z6kaHywohc1xG0KvPYPX7iSNjkvhz
4NY53DHmxl4YEMLffZnaS/dqyhe1GTpcpyN8WiR4KuPfxrkVDOsuzWFtMSvNdlOV
gdI0MXcLMP+EOeANZWX6lGgJ3vWyemo58nzgshKd24MY3w3i6masUkxJH2KvI7UH
/1Db3SC8oOUjInvSRej6M3ZhYWgugm6gbpUgFoDw/o9Cg6Qm71hY0JtcaPC13rzm
N8a2Br0+Fa5e2VhwLmAxyfe1JKzqPwuHT0S5u05SQghL5VdzqfA8FCL/j4XC9yI6
csZTAQi73xFQYVjZt3+aoSz84lOlTmVo/jgvGMY/JzH9I4mETGgAJRNj34Z/0meh
M+pKWCojNH/dgyJSwDGCAlIwggJOAgEBMIG/MIG2MQswCQYDVQQGEwJERTEPMA0G
A1UECAwGQmF5ZXJuMREwDwYDVQQHDAhNdWVuY2hlbjEQMA4GA1UECgwHU2llbWVu
czERMA8GA1UEBRMIWlpaWlpaQTYxHTAbBgNVBAsMFFNpZW1lbnMgVHJ1c3QgQ2Vu
dGVyMT8wPQYDVQQDDDZTaWVtZW5zIElzc3VpbmcgQ0EgTWVkaXVtIFN0cmVuZ3Ro
IEF1dGhlbnRpY2F0aW9uIDIwMTYCBBXXLOIwCwYJYIZIAWUDBAIBoGkwHAYJKoZI
hvcNAQkFMQ8XDTE5MTEyMDE0NTYyMFowLwYJKoZIhvcNAQkEMSIEIJDnZUpcVLzC
OdtpkH8gtxwLPIDE0NmAmFC9uM8q2z+OMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0B
BwEwCwYJKoZIhvcNAQEBBIIBAH/Pqv2xp3a0jSPkwU1K3eGA/1lfoNJMUny4d/PS
LVWlkgrmedXdLmuBzAGEaaZOJS0lEpNd01pR/reHs7xxZ+RZ0olTs2ufM0CijQSx
OL9HDl2O3OoD77NWx4tl3Wy1yJCeV3XH/cEI7AkKHCmKY9QMoMYWh16ORBtr+YcS
YK+gONOjpjgcgTJgZ3HSFgQ50xiD4WT1kFBHsuYsLqaOSbTfTN6Ayyg4edjrPQqa
VcVf1OQcIrfWA3yMQrnEZfOYfN/D4EPjTfxBV+VCi/F2bdZmMbJ7jNk1FbewSwWO
SDH1i0K32NyFbnh0BSos7njq7ELqKlYBsoB/sZfaH2vKy5U=
-----END SIGNED MESSAGE-----
SIGNATURE
end
def signed_tag_base_data
<<~SIGNEDDATA
object 189a6c924013fc3fe40d6f1ec1dc20214183bc97
type commit
tag v1.1.1
tagger Roger Meier <r.meier@siemens.com> 1574261780 +0100
x509 signed tag
SIGNEDDATA
end
def certificate_crl
'http://ch.siemens.com/pki?ZZZZZZA2.crl'
end
def tag_certificate_crl
'http://ch.siemens.com/pki?ZZZZZZA6.crl'
end
def certificate_serial
1810356222
end
def tag_certificate_serial
3664232660
end
def certificate_subject_key_identifier
'EC:00:B5:28:02:5C:D3:A5:A1:AB:C2:A1:34:81:84:AA:BF:9B:CF:F8'
end
def tag_certificate_subject_key_identifier
'21:7E:82:45:29:5D:0E:B1:19:CD:24:45:65:EE:0C:5C:73:03:5E:33'
end
def issuer_subject_key_identifier
'BD:BD:2A:43:22:3D:48:4A:57:7E:98:31:17:A9:70:9D:EE:9F:A8:99'
end
def tag_issuer_subject_key_identifier
'F8:15:5D:47:0C:6C:77:7B:72:C4:02:17:91:68:A2:B6:F7:6A:09:44'
end
def certificate_email
'r.meier@siemens.com'
end
@ -197,6 +330,10 @@ module X509Helpers
'CN=Siemens Issuing CA EE Auth 2016,OU=Siemens Trust Center,serialNumber=ZZZZZZA2,O=Siemens,L=Muenchen,ST=Bayern,C=DE'
end
def tag_certificate_issuer
'CN=Siemens Issuing CA Medium Strength Authentication 2016,OU=Siemens Trust Center,serialNumber=ZZZZZZA6,O=Siemens,L=Muenchen,ST=Bayern,C=DE'
end
def certificate_subject
'CN=Meier Roger,O=Siemens,SN=Meier,GN=Roger,serialNumber=Z000NWDH'
end

View File

@ -29,20 +29,5 @@ describe 'projects/services/_form' do
expect(rendered).to have_content('Event will be triggered when a commit is created/updated')
expect(rendered).to have_content('Event will be triggered when a merge request is created/updated/merged')
end
context 'when service is Jira' do
let(:project) { create(:jira_project) }
before do
assign(:service, project.jira_service)
end
it 'display merge_request_events and commit_events descriptions' do
render
expect(rendered).to have_content('Jira comments will be created when an issue gets referenced in a commit.')
expect(rendered).to have_content('Jira comments will be created when an issue gets referenced in a merge request.')
end
end
end
end