Update upstream source from tag 'upstream/14.5.3+ds1'
Update to upstream version '14.5.3+ds1'
with Debian dir 61802afe6d
This commit is contained in:
commit
5b1839d50c
81 changed files with 1198 additions and 868 deletions
|
@ -2,6 +2,10 @@
|
||||||
documentation](doc/development/changelog.md) for instructions on adding your own
|
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||||
entry.
|
entry.
|
||||||
|
|
||||||
|
## 14.5.3 (2022-01-11)
|
||||||
|
|
||||||
|
No changes.
|
||||||
|
|
||||||
## 14.5.2 (2021-12-03)
|
## 14.5.2 (2021-12-03)
|
||||||
|
|
||||||
No changes.
|
No changes.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
14.5.2
|
14.5.3
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
14.5.2
|
14.5.3
|
|
@ -64,10 +64,12 @@ class GlEmoji extends HTMLElement {
|
||||||
this.classList.add('emoji-icon');
|
this.classList.add('emoji-icon');
|
||||||
this.classList.add(fallbackSpriteClass);
|
this.classList.add(fallbackSpriteClass);
|
||||||
} else if (hasImageFallback) {
|
} else if (hasImageFallback) {
|
||||||
this.innerHTML = emojiImageTag(name, fallbackSrc);
|
this.innerHTML = '';
|
||||||
|
this.appendChild(emojiImageTag(name, fallbackSrc));
|
||||||
} else {
|
} else {
|
||||||
const src = emojiFallbackImageSrc(name);
|
const src = emojiFallbackImageSrc(name);
|
||||||
this.innerHTML = emojiImageTag(name, src);
|
this.innerHTML = '';
|
||||||
|
this.appendChild(emojiImageTag(name, src));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -211,7 +211,17 @@ export function emojiFallbackImageSrc(inputName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function emojiImageTag(name, src) {
|
export function emojiImageTag(name, src) {
|
||||||
return `<img class="emoji" title=":${name}:" alt=":${name}:" src="${src}" width="20" height="20" align="absmiddle" />`;
|
const img = document.createElement('img');
|
||||||
|
|
||||||
|
img.className = 'emoji';
|
||||||
|
img.setAttribute('title', `:${name}:`);
|
||||||
|
img.setAttribute('alt', `:${name}:`);
|
||||||
|
img.setAttribute('src', src);
|
||||||
|
img.setAttribute('width', '20');
|
||||||
|
img.setAttribute('height', '20');
|
||||||
|
img.setAttribute('align', 'absmiddle');
|
||||||
|
|
||||||
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function glEmojiTag(inputName, options) {
|
export function glEmojiTag(inputName, options) {
|
||||||
|
|
|
@ -20,7 +20,7 @@ module SessionlessAuthentication
|
||||||
end
|
end
|
||||||
|
|
||||||
def sessionless_sign_in(user)
|
def sessionless_sign_in(user)
|
||||||
if user && can?(user, :log_in)
|
if can?(user, :log_in) && !user.password_expired_if_applicable?
|
||||||
# Notice we are passing store false, so the user is not
|
# Notice we are passing store false, so the user is not
|
||||||
# actually stored in the session and a token is needed
|
# actually stored in the session and a token is needed
|
||||||
# for every request. If you want the token to work as a
|
# for every request. If you want the token to work as a
|
||||||
|
|
|
@ -28,9 +28,15 @@ class Import::GithubController < Import::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def callback
|
def callback
|
||||||
|
auth_state = session[auth_state_key]
|
||||||
|
session[auth_state_key] = nil
|
||||||
|
if auth_state.blank? || !ActiveSupport::SecurityUtils.secure_compare(auth_state, params[:state])
|
||||||
|
provider_unauthorized
|
||||||
|
else
|
||||||
session[access_token_key] = get_token(params[:code])
|
session[access_token_key] = get_token(params[:code])
|
||||||
redirect_to status_import_url
|
redirect_to status_import_url
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def personal_access_token
|
def personal_access_token
|
||||||
session[access_token_key] = params[:personal_access_token]&.strip
|
session[access_token_key] = params[:personal_access_token]&.strip
|
||||||
|
@ -154,13 +160,16 @@ class Import::GithubController < Import::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def authorize_url
|
def authorize_url
|
||||||
|
state = SecureRandom.base64(64)
|
||||||
|
session[auth_state_key] = state
|
||||||
if Feature.enabled?(:remove_legacy_github_client)
|
if Feature.enabled?(:remove_legacy_github_client)
|
||||||
oauth_client.auth_code.authorize_url(
|
oauth_client.auth_code.authorize_url(
|
||||||
redirect_uri: callback_import_url,
|
redirect_uri: callback_import_url,
|
||||||
scope: 'repo, user, user:email'
|
scope: 'repo, user, user:email',
|
||||||
|
state: state
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
client.authorize_url(callback_import_url)
|
client.authorize_url(callback_import_url, state)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -219,6 +228,10 @@ class Import::GithubController < Import::BaseController
|
||||||
alert: _('Missing OAuth configuration for GitHub.')
|
alert: _('Missing OAuth configuration for GitHub.')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def auth_state_key
|
||||||
|
:"#{provider_name}_auth_state_key"
|
||||||
|
end
|
||||||
|
|
||||||
def access_token_key
|
def access_token_key
|
||||||
:"#{provider_name}_access_token"
|
:"#{provider_name}_access_token"
|
||||||
end
|
end
|
||||||
|
|
|
@ -36,7 +36,7 @@ module Resolvers
|
||||||
def unconditional_includes
|
def unconditional_includes
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
project: [:project_feature]
|
project: [:project_feature, :group]
|
||||||
},
|
},
|
||||||
:author
|
:author
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,6 +6,13 @@ module Integrations
|
||||||
|
|
||||||
def notify(message, opts)
|
def notify(message, opts)
|
||||||
# See https://gitlab.com/gitlab-org/slack-notifier/#custom-http-client
|
# See https://gitlab.com/gitlab-org/slack-notifier/#custom-http-client
|
||||||
|
#
|
||||||
|
# TODO: By default both Markdown and HTML links are converted into Slack "mrkdwn" syntax,
|
||||||
|
# but it seems we only need to support Markdown and could disable HTML.
|
||||||
|
#
|
||||||
|
# See:
|
||||||
|
# - https://gitlab.com/gitlab-org/slack-notifier#middleware
|
||||||
|
# - https://gitlab.com/gitlab-org/gitlab/-/issues/347048
|
||||||
notifier = ::Slack::Messenger.new(webhook, opts.merge(http_client: HTTPClient))
|
notifier = ::Slack::Messenger.new(webhook, opts.merge(http_client: HTTPClient))
|
||||||
notifier.ping(
|
notifier.ping(
|
||||||
message.pretext,
|
message.pretext,
|
||||||
|
|
|
@ -23,7 +23,7 @@ module Integrations
|
||||||
|
|
||||||
def attachments
|
def attachments
|
||||||
[{
|
[{
|
||||||
title: title,
|
title: strip_markup(title),
|
||||||
title_link: alert_url,
|
title_link: alert_url,
|
||||||
color: attachment_color,
|
color: attachment_color,
|
||||||
fields: attachment_fields
|
fields: attachment_fields
|
||||||
|
@ -31,7 +31,7 @@ module Integrations
|
||||||
end
|
end
|
||||||
|
|
||||||
def message
|
def message
|
||||||
"Alert firing in #{project_name}"
|
"Alert firing in #{strip_markup(project_name)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -5,6 +5,10 @@ module Integrations
|
||||||
class BaseMessage
|
class BaseMessage
|
||||||
RELATIVE_LINK_REGEX = %r{!\[[^\]]*\]\((/uploads/[^\)]*)\)}.freeze
|
RELATIVE_LINK_REGEX = %r{!\[[^\]]*\]\((/uploads/[^\)]*)\)}.freeze
|
||||||
|
|
||||||
|
# Markup characters which are used for links in HTML, Markdown,
|
||||||
|
# and Slack "mrkdwn" syntax (`<http://example.com|Label>`).
|
||||||
|
UNSAFE_MARKUP_CHARACTERS = '<>[]|'
|
||||||
|
|
||||||
attr_reader :markdown
|
attr_reader :markdown
|
||||||
attr_reader :user_full_name
|
attr_reader :user_full_name
|
||||||
attr_reader :user_name
|
attr_reader :user_name
|
||||||
|
@ -65,12 +69,26 @@ module Integrations
|
||||||
string.gsub(RELATIVE_LINK_REGEX, "#{project_url}\\1")
|
string.gsub(RELATIVE_LINK_REGEX, "#{project_url}\\1")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Remove unsafe markup from user input, which can be used to hijack links in our own markup,
|
||||||
|
# or insert new ones.
|
||||||
|
#
|
||||||
|
# This currently removes Markdown and Slack "mrkdwn" links (keeping the link label),
|
||||||
|
# and all HTML markup (keeping the text nodes).
|
||||||
|
# We can't just escape the markup characters, because each chat app handles this differently.
|
||||||
|
#
|
||||||
|
# See:
|
||||||
|
# - https://api.slack.com/reference/surfaces/formatting#escaping
|
||||||
|
# - https://gitlab.com/gitlab-org/slack-notifier#escaping
|
||||||
|
def strip_markup(string)
|
||||||
|
string&.delete(UNSAFE_MARKUP_CHARACTERS)
|
||||||
|
end
|
||||||
|
|
||||||
def attachment_color
|
def attachment_color
|
||||||
'#345'
|
'#345'
|
||||||
end
|
end
|
||||||
|
|
||||||
def link(text, url)
|
def link(text, url)
|
||||||
"[#{text}](#{url})"
|
"[#{strip_markup(text)}](#{url})"
|
||||||
end
|
end
|
||||||
|
|
||||||
def pretty_duration(seconds)
|
def pretty_duration(seconds)
|
||||||
|
|
|
@ -27,7 +27,7 @@ module Integrations
|
||||||
|
|
||||||
def attachments
|
def attachments
|
||||||
[{
|
[{
|
||||||
text: "#{project_link} with job #{deployment_link} by #{user_link}\n#{commit_link}: #{commit_title}",
|
text: "#{project_link} with job #{deployment_link} by #{user_link}\n#{commit_link}: #{strip_markup(commit_title)}",
|
||||||
color: color
|
color: color
|
||||||
}]
|
}]
|
||||||
end
|
end
|
||||||
|
@ -40,9 +40,9 @@ module Integrations
|
||||||
|
|
||||||
def message
|
def message
|
||||||
if running?
|
if running?
|
||||||
"Starting deploy to #{environment}"
|
"Starting deploy to #{strip_markup(environment)}"
|
||||||
else
|
else
|
||||||
"Deploy to #{environment} #{humanized_status}"
|
"Deploy to #{strip_markup(environment)} #{humanized_status}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ module Integrations
|
||||||
|
|
||||||
def activity
|
def activity
|
||||||
{
|
{
|
||||||
title: "Issue #{state} by #{user_combined_name}",
|
title: "Issue #{state} by #{strip_markup(user_combined_name)}",
|
||||||
subtitle: "in #{project_link}",
|
subtitle: "in #{project_link}",
|
||||||
text: issue_link,
|
text: issue_link,
|
||||||
image: user_avatar
|
image: user_avatar
|
||||||
|
@ -42,7 +42,7 @@ module Integrations
|
||||||
private
|
private
|
||||||
|
|
||||||
def message
|
def message
|
||||||
"[#{project_link}] Issue #{issue_link} #{state} by #{user_combined_name}"
|
"[#{project_link}] Issue #{issue_link} #{state} by #{strip_markup(user_combined_name)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def opened_issue?
|
def opened_issue?
|
||||||
|
@ -67,7 +67,7 @@ module Integrations
|
||||||
end
|
end
|
||||||
|
|
||||||
def issue_title
|
def issue_title
|
||||||
"#{Issue.reference_prefix}#{issue_iid} #{title}"
|
"#{Issue.reference_prefix}#{issue_iid} #{strip_markup(title)}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,7 +29,7 @@ module Integrations
|
||||||
|
|
||||||
def activity
|
def activity
|
||||||
{
|
{
|
||||||
title: "Merge request #{state_or_action_text} by #{user_combined_name}",
|
title: "Merge request #{state_or_action_text} by #{strip_markup(user_combined_name)}",
|
||||||
subtitle: "in #{project_link}",
|
subtitle: "in #{project_link}",
|
||||||
text: merge_request_link,
|
text: merge_request_link,
|
||||||
image: user_avatar
|
image: user_avatar
|
||||||
|
@ -39,7 +39,7 @@ module Integrations
|
||||||
private
|
private
|
||||||
|
|
||||||
def format_title(title)
|
def format_title(title)
|
||||||
'*' + title.lines.first.chomp + '*'
|
'*' + strip_markup(title.lines.first.chomp) + '*'
|
||||||
end
|
end
|
||||||
|
|
||||||
def message
|
def message
|
||||||
|
@ -51,7 +51,7 @@ module Integrations
|
||||||
end
|
end
|
||||||
|
|
||||||
def merge_request_message
|
def merge_request_message
|
||||||
"#{user_combined_name} #{state_or_action_text} merge request #{merge_request_link} in #{project_link}"
|
"#{strip_markup(user_combined_name)} #{state_or_action_text} merge request #{merge_request_link} in #{project_link}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def merge_request_link
|
def merge_request_link
|
||||||
|
@ -59,7 +59,7 @@ module Integrations
|
||||||
end
|
end
|
||||||
|
|
||||||
def merge_request_title
|
def merge_request_title
|
||||||
"#{MergeRequest.reference_prefix}#{merge_request_iid} #{title}"
|
"#{MergeRequest.reference_prefix}#{merge_request_iid} #{strip_markup(title)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def merge_request_url
|
def merge_request_url
|
||||||
|
|
|
@ -35,9 +35,9 @@ module Integrations
|
||||||
|
|
||||||
def activity
|
def activity
|
||||||
{
|
{
|
||||||
title: "#{user_combined_name} #{link('commented on ' + target, note_url)}",
|
title: "#{strip_markup(user_combined_name)} #{link('commented on ' + target, note_url)}",
|
||||||
subtitle: "in #{project_link}",
|
subtitle: "in #{project_link}",
|
||||||
text: formatted_title,
|
text: strip_markup(formatted_title),
|
||||||
image: user_avatar
|
image: user_avatar
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -45,7 +45,7 @@ module Integrations
|
||||||
private
|
private
|
||||||
|
|
||||||
def message
|
def message
|
||||||
"#{user_combined_name} #{link('commented on ' + target, note_url)} in #{project_link}: *#{formatted_title}*"
|
"#{strip_markup(user_combined_name)} #{link('commented on ' + target, note_url)} in #{project_link}: *#{strip_markup(formatted_title)}*"
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_title(title)
|
def format_title(title)
|
||||||
|
|
|
@ -56,7 +56,7 @@ module Integrations
|
||||||
[{
|
[{
|
||||||
fallback: format(message),
|
fallback: format(message),
|
||||||
color: attachment_color,
|
color: attachment_color,
|
||||||
author_name: user_combined_name,
|
author_name: strip_markup(user_combined_name),
|
||||||
author_icon: user_avatar,
|
author_icon: user_avatar,
|
||||||
author_link: author_url,
|
author_link: author_url,
|
||||||
title: s_("ChatMessage|Pipeline #%{pipeline_id} %{humanized_status} in %{duration}") %
|
title: s_("ChatMessage|Pipeline #%{pipeline_id} %{humanized_status} in %{duration}") %
|
||||||
|
@ -80,7 +80,7 @@ module Integrations
|
||||||
pipeline_link: pipeline_link,
|
pipeline_link: pipeline_link,
|
||||||
ref_type: ref_type,
|
ref_type: ref_type,
|
||||||
ref_link: ref_link,
|
ref_link: ref_link,
|
||||||
user_combined_name: user_combined_name,
|
user_combined_name: strip_markup(user_combined_name),
|
||||||
humanized_status: humanized_status
|
humanized_status: humanized_status
|
||||||
},
|
},
|
||||||
subtitle: s_("ChatMessage|in %{project_link}") % { project_link: project_link },
|
subtitle: s_("ChatMessage|in %{project_link}") % { project_link: project_link },
|
||||||
|
@ -154,7 +154,7 @@ module Integrations
|
||||||
pipeline_link: pipeline_link,
|
pipeline_link: pipeline_link,
|
||||||
ref_type: ref_type,
|
ref_type: ref_type,
|
||||||
ref_link: ref_link,
|
ref_link: ref_link,
|
||||||
user_combined_name: user_combined_name,
|
user_combined_name: strip_markup(user_combined_name),
|
||||||
humanized_status: humanized_status,
|
humanized_status: humanized_status,
|
||||||
duration: pretty_duration(duration)
|
duration: pretty_duration(duration)
|
||||||
}
|
}
|
||||||
|
@ -189,7 +189,7 @@ module Integrations
|
||||||
end
|
end
|
||||||
|
|
||||||
def ref_link
|
def ref_link
|
||||||
"[#{ref}](#{ref_url})"
|
link(ref, ref_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_url
|
def project_url
|
||||||
|
@ -197,7 +197,7 @@ module Integrations
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_link
|
def project_link
|
||||||
"[#{project.name}](#{project_url})"
|
link(project.name, project_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def pipeline_failed_jobs_url
|
def pipeline_failed_jobs_url
|
||||||
|
@ -213,7 +213,7 @@ module Integrations
|
||||||
end
|
end
|
||||||
|
|
||||||
def pipeline_link
|
def pipeline_link
|
||||||
"[##{pipeline_id}](#{pipeline_url})"
|
link("##{pipeline_id}", pipeline_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def job_url(job)
|
def job_url(job)
|
||||||
|
@ -221,7 +221,7 @@ module Integrations
|
||||||
end
|
end
|
||||||
|
|
||||||
def job_link(job)
|
def job_link(job)
|
||||||
"[#{job[:name]}](#{job_url(job)})"
|
link(job[:name], job_url(job))
|
||||||
end
|
end
|
||||||
|
|
||||||
def failed_jobs_links
|
def failed_jobs_links
|
||||||
|
@ -242,7 +242,7 @@ module Integrations
|
||||||
|
|
||||||
def stage_link(stage)
|
def stage_link(stage)
|
||||||
# All stages link to the pipeline page
|
# All stages link to the pipeline page
|
||||||
"[#{stage}](#{pipeline_url})"
|
link(stage, pipeline_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def failed_stages_links
|
def failed_stages_links
|
||||||
|
@ -254,7 +254,7 @@ module Integrations
|
||||||
end
|
end
|
||||||
|
|
||||||
def commit_link
|
def commit_link
|
||||||
"[#{commit.title}](#{commit_url})"
|
link(commit.title, commit_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def author_url
|
def author_url
|
||||||
|
|
|
@ -39,7 +39,7 @@ module Integrations
|
||||||
|
|
||||||
def humanized_action(short: false)
|
def humanized_action(short: false)
|
||||||
action, ref_link, target_link = compose_action_details
|
action, ref_link, target_link = compose_action_details
|
||||||
text = [user_combined_name, action, ref_type, ref_link]
|
text = [strip_markup(user_combined_name), action, ref_type, ref_link]
|
||||||
text << target_link unless short
|
text << target_link unless short
|
||||||
text.join(' ')
|
text.join(' ')
|
||||||
end
|
end
|
||||||
|
@ -67,7 +67,7 @@ module Integrations
|
||||||
|
|
||||||
url = commit[:url]
|
url = commit[:url]
|
||||||
|
|
||||||
"[#{id}](#{url}): #{title} - #{author}"
|
"#{link(id, url)}: #{strip_markup(title)} - #{strip_markup(author)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def new_branch?
|
def new_branch?
|
||||||
|
@ -91,15 +91,15 @@ module Integrations
|
||||||
end
|
end
|
||||||
|
|
||||||
def ref_link
|
def ref_link
|
||||||
"[#{ref}](#{ref_url})"
|
link(ref, ref_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_link
|
def project_link
|
||||||
"[#{project_name}](#{project_url})"
|
link(project_name, project_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def compare_link
|
def compare_link
|
||||||
"[Compare changes](#{compare_url})"
|
link('Compare changes', compare_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def compose_action_details
|
def compose_action_details
|
||||||
|
|
|
@ -36,9 +36,9 @@ module Integrations
|
||||||
|
|
||||||
def activity
|
def activity
|
||||||
{
|
{
|
||||||
title: "#{user_combined_name} #{action} #{wiki_page_link}",
|
title: "#{strip_markup(user_combined_name)} #{action} #{wiki_page_link}",
|
||||||
subtitle: "in #{project_link}",
|
subtitle: "in #{project_link}",
|
||||||
text: title,
|
text: strip_markup(title),
|
||||||
image: user_avatar
|
image: user_avatar
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -46,7 +46,7 @@ module Integrations
|
||||||
private
|
private
|
||||||
|
|
||||||
def message
|
def message
|
||||||
"#{user_combined_name} #{action} #{wiki_page_link} (#{diff_link}) in #{project_link}: *#{title}*"
|
"#{strip_markup(user_combined_name)} #{action} #{wiki_page_link} (#{diff_link}) in #{project_link}: *#{strip_markup(title)}*"
|
||||||
end
|
end
|
||||||
|
|
||||||
def description_message
|
def description_message
|
||||||
|
|
|
@ -72,10 +72,24 @@ module Packages
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# TODO (technical debt): Extract the package size calculation to its own component and unit test it separately.
|
||||||
|
def calculated_package_file_size
|
||||||
|
strong_memoize(:calculated_package_file_size) do
|
||||||
|
# This calculation is based on:
|
||||||
|
# 1. 4 chars in a Base64 encoded string are 3 bytes in the original string. Meaning 1 char is 0.75 bytes.
|
||||||
|
# 2. The encoded string may have 1 or 2 extra '=' chars used for padding. Each padding char means 1 byte less in the original string.
|
||||||
|
# Reference:
|
||||||
|
# - https://blog.aaronlenoir.com/2017/11/10/get-original-length-from-base-64-string/
|
||||||
|
# - https://en.wikipedia.org/wiki/Base64#Decoding_Base64_with_padding
|
||||||
|
encoded_data = attachment['data']
|
||||||
|
((encoded_data.length * 0.75 ) - encoded_data[-2..].count('=')).to_i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def file_params
|
def file_params
|
||||||
{
|
{
|
||||||
file: CarrierWaveStringFile.new(Base64.decode64(attachment['data'])),
|
file: CarrierWaveStringFile.new(Base64.decode64(attachment['data'])),
|
||||||
size: attachment['length'],
|
size: calculated_package_file_size,
|
||||||
file_sha1: version_data[:dist][:shasum],
|
file_sha1: version_data[:dist][:shasum],
|
||||||
file_name: package_file_name,
|
file_name: package_file_name,
|
||||||
build: params[:build]
|
build: params[:build]
|
||||||
|
@ -88,7 +102,7 @@ module Packages
|
||||||
end
|
end
|
||||||
|
|
||||||
def file_size_exceeded?
|
def file_size_exceeded?
|
||||||
project.actual_limits.exceeded?(:npm_max_file_size, attachment['length'].to_i)
|
project.actual_limits.exceeded?(:npm_max_file_size, calculated_package_file_size)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -291,6 +291,15 @@
|
||||||
:weight: 1
|
:weight: 1
|
||||||
:idempotent: true
|
:idempotent: true
|
||||||
:tags: []
|
:tags: []
|
||||||
|
- :name: cronjob:dependency_proxy_cleanup_dependency_proxy
|
||||||
|
:worker_name: DependencyProxy::CleanupDependencyProxyWorker
|
||||||
|
:feature_category: :dependency_proxy
|
||||||
|
:has_external_dependencies:
|
||||||
|
:urgency: :low
|
||||||
|
:resource_boundary: :unknown
|
||||||
|
:weight: 1
|
||||||
|
:idempotent: true
|
||||||
|
:tags: []
|
||||||
- :name: cronjob:dependency_proxy_image_ttl_group_policy
|
- :name: cronjob:dependency_proxy_image_ttl_group_policy
|
||||||
:worker_name: DependencyProxy::ImageTtlGroupPolicyWorker
|
:worker_name: DependencyProxy::ImageTtlGroupPolicyWorker
|
||||||
:feature_category: :dependency_proxy
|
:feature_category: :dependency_proxy
|
||||||
|
|
17
app/workers/concerns/dependency_proxy/expireable.rb
Normal file
17
app/workers/concerns/dependency_proxy/expireable.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module DependencyProxy
|
||||||
|
module Expireable
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
UPDATE_BATCH_SIZE = 100
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def expire_artifacts(collection)
|
||||||
|
collection.each_batch(of: UPDATE_BATCH_SIZE) do |batch|
|
||||||
|
batch.update_all(status: :expired)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,28 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module DependencyProxy
|
||||||
|
class CleanupDependencyProxyWorker
|
||||||
|
include ApplicationWorker
|
||||||
|
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||||
|
|
||||||
|
data_consistency :always
|
||||||
|
idempotent!
|
||||||
|
|
||||||
|
feature_category :dependency_proxy
|
||||||
|
|
||||||
|
def perform
|
||||||
|
enqueue_blob_cleanup_job if DependencyProxy::Blob.expired.any?
|
||||||
|
enqueue_manifest_cleanup_job if DependencyProxy::Manifest.expired.any?
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def enqueue_blob_cleanup_job
|
||||||
|
DependencyProxy::CleanupBlobWorker.perform_with_capacity
|
||||||
|
end
|
||||||
|
|
||||||
|
def enqueue_manifest_cleanup_job
|
||||||
|
DependencyProxy::CleanupManifestWorker.perform_with_capacity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,20 +4,19 @@ module DependencyProxy
|
||||||
class ImageTtlGroupPolicyWorker # rubocop:disable Scalability/IdempotentWorker
|
class ImageTtlGroupPolicyWorker # rubocop:disable Scalability/IdempotentWorker
|
||||||
include ApplicationWorker
|
include ApplicationWorker
|
||||||
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
|
||||||
|
include DependencyProxy::Expireable
|
||||||
|
|
||||||
data_consistency :always
|
data_consistency :always
|
||||||
|
|
||||||
feature_category :dependency_proxy
|
feature_category :dependency_proxy
|
||||||
|
|
||||||
UPDATE_BATCH_SIZE = 100
|
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
DependencyProxy::ImageTtlGroupPolicy.enabled.each do |policy|
|
DependencyProxy::ImageTtlGroupPolicy.enabled.each do |policy|
|
||||||
qualified_blobs = policy.group.dependency_proxy_blobs.active.read_before(policy.ttl)
|
qualified_blobs = policy.group.dependency_proxy_blobs.active.read_before(policy.ttl)
|
||||||
qualified_manifests = policy.group.dependency_proxy_manifests.active.read_before(policy.ttl)
|
qualified_manifests = policy.group.dependency_proxy_manifests.active.read_before(policy.ttl)
|
||||||
|
|
||||||
enqueue_blob_cleanup_job if expire_artifacts(qualified_blobs, DependencyProxy::Blob)
|
expire_artifacts(qualified_blobs)
|
||||||
enqueue_manifest_cleanup_job if expire_artifacts(qualified_manifests, DependencyProxy::Manifest)
|
expire_artifacts(qualified_manifests)
|
||||||
end
|
end
|
||||||
|
|
||||||
log_counts
|
log_counts
|
||||||
|
@ -25,25 +24,6 @@ module DependencyProxy
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def expire_artifacts(artifacts, model)
|
|
||||||
rows_updated = false
|
|
||||||
|
|
||||||
artifacts.each_batch(of: UPDATE_BATCH_SIZE) do |batch|
|
|
||||||
rows = batch.update_all(status: :expired)
|
|
||||||
rows_updated ||= rows > 0
|
|
||||||
end
|
|
||||||
|
|
||||||
rows_updated
|
|
||||||
end
|
|
||||||
|
|
||||||
def enqueue_blob_cleanup_job
|
|
||||||
DependencyProxy::CleanupBlobWorker.perform_with_capacity
|
|
||||||
end
|
|
||||||
|
|
||||||
def enqueue_manifest_cleanup_job
|
|
||||||
DependencyProxy::CleanupManifestWorker.perform_with_capacity
|
|
||||||
end
|
|
||||||
|
|
||||||
def log_counts
|
def log_counts
|
||||||
use_replica_if_available do
|
use_replica_if_available do
|
||||||
expired_blob_count = DependencyProxy::Blob.expired.count
|
expired_blob_count = DependencyProxy::Blob.expired.count
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class PurgeDependencyProxyCacheWorker
|
class PurgeDependencyProxyCacheWorker
|
||||||
include ApplicationWorker
|
include ApplicationWorker
|
||||||
|
include DependencyProxy::Expireable
|
||||||
|
|
||||||
data_consistency :always
|
data_consistency :always
|
||||||
|
|
||||||
|
@ -18,8 +19,8 @@ class PurgeDependencyProxyCacheWorker
|
||||||
|
|
||||||
return unless valid?
|
return unless valid?
|
||||||
|
|
||||||
@group.dependency_proxy_blobs.destroy_all # rubocop:disable Cop/DestroyAll
|
expire_artifacts(@group.dependency_proxy_blobs)
|
||||||
@group.dependency_proxy_manifests.destroy_all # rubocop:disable Cop/DestroyAll
|
expire_artifacts(@group.dependency_proxy_manifests)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -534,6 +534,9 @@ Settings.cron_jobs['container_expiration_policy_worker']['job_class'] = 'Contain
|
||||||
Settings.cron_jobs['image_ttl_group_policy_worker'] ||= Settingslogic.new({})
|
Settings.cron_jobs['image_ttl_group_policy_worker'] ||= Settingslogic.new({})
|
||||||
Settings.cron_jobs['image_ttl_group_policy_worker']['cron'] ||= '40 0 * * *'
|
Settings.cron_jobs['image_ttl_group_policy_worker']['cron'] ||= '40 0 * * *'
|
||||||
Settings.cron_jobs['image_ttl_group_policy_worker']['job_class'] = 'DependencyProxy::ImageTtlGroupPolicyWorker'
|
Settings.cron_jobs['image_ttl_group_policy_worker']['job_class'] = 'DependencyProxy::ImageTtlGroupPolicyWorker'
|
||||||
|
Settings.cron_jobs['cleanup_dependency_proxy_worker'] ||= Settingslogic.new({})
|
||||||
|
Settings.cron_jobs['cleanup_dependency_proxy_worker']['cron'] ||= '20 3,15 * * *'
|
||||||
|
Settings.cron_jobs['cleanup_dependency_proxy_worker']['job_class'] = 'DependencyProxy::CleanupDependencyProxyWorker'
|
||||||
Settings.cron_jobs['x509_issuer_crl_check_worker'] ||= Settingslogic.new({})
|
Settings.cron_jobs['x509_issuer_crl_check_worker'] ||= Settingslogic.new({})
|
||||||
Settings.cron_jobs['x509_issuer_crl_check_worker']['cron'] ||= '30 1 * * *'
|
Settings.cron_jobs['x509_issuer_crl_check_worker']['cron'] ||= '30 1 * * *'
|
||||||
Settings.cron_jobs['x509_issuer_crl_check_worker']['job_class'] = 'X509IssuerCrlCheckWorker'
|
Settings.cron_jobs['x509_issuer_crl_check_worker']['job_class'] = 'X509IssuerCrlCheckWorker'
|
||||||
|
|
|
@ -11,7 +11,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
||||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11631) in GitLab 12.10.
|
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11631) in GitLab 12.10.
|
||||||
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/273655) from GitLab Premium to GitLab Free in 13.6.
|
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/273655) from GitLab Premium to GitLab Free in 13.6.
|
||||||
|
|
||||||
Deletes the cached manifests and blobs for a group. This endpoint requires the [Owner role](../user/permissions.md)
|
Schedules for deletion the cached manifests and blobs for a group. This endpoint requires the
|
||||||
|
[Owner role](../user/permissions.md)
|
||||||
for the group.
|
for the group.
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
|
|
|
@ -6,15 +6,6 @@ module API
|
||||||
|
|
||||||
feature_category :dependency_proxy
|
feature_category :dependency_proxy
|
||||||
|
|
||||||
helpers do
|
|
||||||
def obtain_new_purge_cache_lease
|
|
||||||
Gitlab::ExclusiveLease
|
|
||||||
.new("dependency_proxy:delete_group_blobs:#{user_group.id}",
|
|
||||||
timeout: 1.hour)
|
|
||||||
.try_obtain
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
after_validation do
|
after_validation do
|
||||||
authorize! :admin_group, user_group
|
authorize! :admin_group, user_group
|
||||||
end
|
end
|
||||||
|
@ -29,9 +20,6 @@ module API
|
||||||
delete ':id/dependency_proxy/cache' do
|
delete ':id/dependency_proxy/cache' do
|
||||||
not_found! unless user_group.dependency_proxy_feature_available?
|
not_found! unless user_group.dependency_proxy_feature_available?
|
||||||
|
|
||||||
message = 'This request has already been made. It may take some time to purge the cache. You can run this at most once an hour for a given group'
|
|
||||||
render_api_error!(message, 409) unless obtain_new_purge_cache_lease
|
|
||||||
|
|
||||||
# rubocop:disable CodeReuse/Worker
|
# rubocop:disable CodeReuse/Worker
|
||||||
PurgeDependencyProxyCacheWorker.perform_async(current_user.id, user_group.id)
|
PurgeDependencyProxyCacheWorker.perform_async(current_user.id, user_group.id)
|
||||||
# rubocop:enable CodeReuse/Worker
|
# rubocop:enable CodeReuse/Worker
|
||||||
|
|
|
@ -613,6 +613,7 @@ module API
|
||||||
|
|
||||||
source_project = Project.find_by_id(params[:project_id])
|
source_project = Project.find_by_id(params[:project_id])
|
||||||
not_found!('Project') unless source_project && can?(current_user, :read_project, source_project)
|
not_found!('Project') unless source_project && can?(current_user, :read_project, source_project)
|
||||||
|
forbidden!('Project') unless source_project && can?(current_user, :admin_project_member, source_project)
|
||||||
|
|
||||||
result = ::Members::ImportProjectTeamService.new(current_user, params).execute
|
result = ::Members::ImportProjectTeamService.new(current_user, params).execute
|
||||||
|
|
||||||
|
|
|
@ -48,10 +48,11 @@ module Gitlab
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def authorize_url(redirect_uri)
|
def authorize_url(redirect_uri, state = nil)
|
||||||
client.auth_code.authorize_url({
|
client.auth_code.authorize_url({
|
||||||
redirect_uri: redirect_uri,
|
redirect_uri: redirect_uri,
|
||||||
scope: "repo, user, user:email"
|
scope: "repo, user, user:email",
|
||||||
|
state: state
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -252,13 +252,13 @@ module Gitlab
|
||||||
def internal_web?(uri)
|
def internal_web?(uri)
|
||||||
uri.scheme == config.gitlab.protocol &&
|
uri.scheme == config.gitlab.protocol &&
|
||||||
uri.hostname == config.gitlab.host &&
|
uri.hostname == config.gitlab.host &&
|
||||||
(uri.port.blank? || uri.port == config.gitlab.port)
|
get_port(uri) == config.gitlab.port
|
||||||
end
|
end
|
||||||
|
|
||||||
def internal_shell?(uri)
|
def internal_shell?(uri)
|
||||||
uri.scheme == 'ssh' &&
|
uri.scheme == 'ssh' &&
|
||||||
uri.hostname == config.gitlab_shell.ssh_host &&
|
uri.hostname == config.gitlab_shell.ssh_host &&
|
||||||
(uri.port.blank? || uri.port == config.gitlab_shell.ssh_port)
|
get_port(uri) == config.gitlab_shell.ssh_port
|
||||||
end
|
end
|
||||||
|
|
||||||
def domain_allowed?(uri)
|
def domain_allowed?(uri)
|
||||||
|
|
|
@ -8,10 +8,6 @@ RSpec.describe Dashboard::ProjectsController, :aggregate_failures do
|
||||||
let_it_be(:user) { create(:user) }
|
let_it_be(:user) { create(:user) }
|
||||||
|
|
||||||
describe '#index' do
|
describe '#index' do
|
||||||
context 'user not logged in' do
|
|
||||||
it_behaves_like 'authenticates sessionless user', :index, :atom
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'user logged in' do
|
context 'user logged in' do
|
||||||
let_it_be(:project) { create(:project, name: 'Project 1') }
|
let_it_be(:project) { create(:project, name: 'Project 1') }
|
||||||
let_it_be(:project2) { create(:project, name: 'Project Two') }
|
let_it_be(:project2) { create(:project, name: 'Project Two') }
|
||||||
|
|
|
@ -72,9 +72,6 @@ RSpec.describe DashboardController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'authenticates sessionless user', :issues, :atom, author_id: User.first
|
|
||||||
it_behaves_like 'authenticates sessionless user', :issues_calendar, :ics
|
|
||||||
|
|
||||||
describe "#check_filters_presence!" do
|
describe "#check_filters_presence!" do
|
||||||
let(:user) { create(:user) }
|
let(:user) { create(:user) }
|
||||||
|
|
||||||
|
|
|
@ -1219,26 +1219,6 @@ RSpec.describe GroupsController, factory_default: :keep do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'token authentication' do
|
|
||||||
it_behaves_like 'authenticates sessionless user', :show, :atom, public: true do
|
|
||||||
before do
|
|
||||||
default_params.merge!(id: group)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'authenticates sessionless user', :issues, :atom, public: true do
|
|
||||||
before do
|
|
||||||
default_params.merge!(id: group, author_id: user.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'authenticates sessionless user', :issues_calendar, :ics, public: true do
|
|
||||||
before do
|
|
||||||
default_params.merge!(id: group)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'external authorization' do
|
describe 'external authorization' do
|
||||||
before do
|
before do
|
||||||
group.add_owner(user)
|
group.add_owner(user)
|
||||||
|
|
|
@ -6,6 +6,7 @@ RSpec.describe Import::GithubController do
|
||||||
include ImportSpecHelper
|
include ImportSpecHelper
|
||||||
|
|
||||||
let(:provider) { :github }
|
let(:provider) { :github }
|
||||||
|
let(:new_import_url) { public_send("new_import_#{provider}_url") }
|
||||||
|
|
||||||
include_context 'a GitHub-ish import controller'
|
include_context 'a GitHub-ish import controller'
|
||||||
|
|
||||||
|
@ -50,14 +51,38 @@ RSpec.describe Import::GithubController do
|
||||||
stub_omniauth_provider('github')
|
stub_omniauth_provider('github')
|
||||||
end
|
end
|
||||||
|
|
||||||
it "updates access token" do
|
context "when auth state param is missing from session" do
|
||||||
|
it "reports an error" do
|
||||||
|
get :callback
|
||||||
|
|
||||||
|
expect(controller).to redirect_to(new_import_url)
|
||||||
|
expect(flash[:alert]).to eq('Access denied to your GitHub account.')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when auth state param is present in session" do
|
||||||
|
let(:valid_auth_state) { "secret-state" }
|
||||||
|
|
||||||
|
before do
|
||||||
|
session[:github_auth_state_key] = valid_auth_state
|
||||||
|
end
|
||||||
|
|
||||||
|
it "updates access token if state param is valid" do
|
||||||
token = "asdasd12345"
|
token = "asdasd12345"
|
||||||
|
|
||||||
get :callback
|
get :callback, params: { state: valid_auth_state }
|
||||||
|
|
||||||
expect(session[:github_access_token]).to eq(token)
|
expect(session[:github_access_token]).to eq(token)
|
||||||
expect(controller).to redirect_to(status_import_github_url)
|
expect(controller).to redirect_to(status_import_github_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "reports an error if state param is invalid" do
|
||||||
|
get :callback, params: { state: "different-state" }
|
||||||
|
|
||||||
|
expect(controller).to redirect_to(new_import_url)
|
||||||
|
expect(flash[:alert]).to eq('Access denied to your GitHub account.')
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST personal_access_token" do
|
describe "POST personal_access_token" do
|
||||||
|
@ -71,8 +96,6 @@ RSpec.describe Import::GithubController do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when OAuth config is missing' do
|
context 'when OAuth config is missing' do
|
||||||
let(:new_import_url) { public_send("new_import_#{provider}_url") }
|
|
||||||
|
|
||||||
before do
|
before do
|
||||||
allow(controller).to receive(:oauth_config).and_return(nil)
|
allow(controller).to receive(:oauth_config).and_return(nil)
|
||||||
end
|
end
|
||||||
|
@ -108,6 +131,16 @@ RSpec.describe Import::GithubController do
|
||||||
|
|
||||||
get :status
|
get :status
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'gets authorization url using legacy client' do
|
||||||
|
allow(controller).to receive(:logged_in_with_provider?).and_return(true)
|
||||||
|
expect(controller).to receive(:go_to_provider_for_permissions).and_call_original
|
||||||
|
expect_next_instance_of(Gitlab::LegacyGithubImport::Client) do |client|
|
||||||
|
expect(client).to receive(:authorize_url).and_call_original
|
||||||
|
end
|
||||||
|
|
||||||
|
get :new
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when feature remove_legacy_github_client is enabled' do
|
context 'when feature remove_legacy_github_client is enabled' do
|
||||||
|
@ -130,6 +163,16 @@ RSpec.describe Import::GithubController do
|
||||||
get :status
|
get :status
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'gets authorization url using oauth client' do
|
||||||
|
allow(controller).to receive(:logged_in_with_provider?).and_return(true)
|
||||||
|
expect(controller).to receive(:go_to_provider_for_permissions).and_call_original
|
||||||
|
expect_next_instance_of(OAuth2::Client) do |client|
|
||||||
|
expect(client.auth_code).to receive(:authorize_url).and_call_original
|
||||||
|
end
|
||||||
|
|
||||||
|
get :new
|
||||||
|
end
|
||||||
|
|
||||||
context 'pagination' do
|
context 'pagination' do
|
||||||
context 'when no page is specified' do
|
context 'when no page is specified' do
|
||||||
it 'requests first page' do
|
it 'requests first page' do
|
||||||
|
|
|
@ -162,27 +162,4 @@ RSpec.describe Projects::CommitsController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'token authentication' do
|
|
||||||
context 'public project' do
|
|
||||||
it_behaves_like 'authenticates sessionless user', :show, :atom, { public: true, ignore_incrementing: true } do
|
|
||||||
before do
|
|
||||||
public_project = create(:project, :repository, :public)
|
|
||||||
|
|
||||||
default_params.merge!(namespace_id: public_project.namespace, project_id: public_project, id: "master.atom")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'private project' do
|
|
||||||
it_behaves_like 'authenticates sessionless user', :show, :atom, { public: false, ignore_incrementing: true } do
|
|
||||||
before do
|
|
||||||
private_project = create(:project, :repository, :private)
|
|
||||||
private_project.add_maintainer(user)
|
|
||||||
|
|
||||||
default_params.merge!(namespace_id: private_project.namespace, project_id: private_project, id: "master.atom")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1965,40 +1965,4 @@ RSpec.describe Projects::IssuesController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'private project with token authentication' do
|
|
||||||
let_it_be(:private_project) { create(:project, :private) }
|
|
||||||
|
|
||||||
it_behaves_like 'authenticates sessionless user', :index, :atom, ignore_incrementing: true do
|
|
||||||
before do
|
|
||||||
default_params.merge!(project_id: private_project, namespace_id: private_project.namespace)
|
|
||||||
|
|
||||||
private_project.add_maintainer(user)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'authenticates sessionless user', :calendar, :ics, ignore_incrementing: true do
|
|
||||||
before do
|
|
||||||
default_params.merge!(project_id: private_project, namespace_id: private_project.namespace)
|
|
||||||
|
|
||||||
private_project.add_maintainer(user)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'public project with token authentication' do
|
|
||||||
let_it_be(:public_project) { create(:project, :public) }
|
|
||||||
|
|
||||||
it_behaves_like 'authenticates sessionless user', :index, :atom, public: true do
|
|
||||||
before do
|
|
||||||
default_params.merge!(project_id: public_project, namespace_id: public_project.namespace)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'authenticates sessionless user', :calendar, :ics, public: true do
|
|
||||||
before do
|
|
||||||
default_params.merge!(project_id: public_project, namespace_id: public_project.namespace)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -193,6 +193,8 @@ RSpec.describe Projects::RawController do
|
||||||
let_it_be(:user) { create(:user, static_object_token: 'very-secure-token') }
|
let_it_be(:user) { create(:user, static_object_token: 'very-secure-token') }
|
||||||
let_it_be(:file_path) { 'master/README.md' }
|
let_it_be(:file_path) { 'master/README.md' }
|
||||||
|
|
||||||
|
let(:token) { user.static_object_token }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
project.add_developer(user)
|
project.add_developer(user)
|
||||||
end
|
end
|
||||||
|
@ -207,17 +209,46 @@ RSpec.describe Projects::RawController do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when a token param is present' do
|
context 'when a token param is present' do
|
||||||
|
subject(:execute_raw_request_with_token_in_params) do
|
||||||
|
execute_raw_requests(requests: 1, project: project, file_path: file_path, token: token)
|
||||||
|
end
|
||||||
|
|
||||||
context 'when token is correct' do
|
context 'when token is correct' do
|
||||||
it 'calls the action normally' do
|
it 'calls the action normally' do
|
||||||
execute_raw_requests(requests: 1, project: project, file_path: file_path, token: user.static_object_token)
|
execute_raw_request_with_token_in_params
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user with expired password' do
|
||||||
|
let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) }
|
||||||
|
|
||||||
|
it 'redirects to sign in page' do
|
||||||
|
execute_raw_request_with_token_in_params
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:found)
|
||||||
|
expect(response.location).to end_with('/users/sign_in')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when password expiration is not applicable' do
|
||||||
|
context 'when ldap user' do
|
||||||
|
let_it_be(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) }
|
||||||
|
|
||||||
|
it 'calls the action normally' do
|
||||||
|
execute_raw_request_with_token_in_params
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when token is incorrect' do
|
context 'when token is incorrect' do
|
||||||
|
let(:token) { 'foobar' }
|
||||||
|
|
||||||
it 'redirects to sign in page' do
|
it 'redirects to sign in page' do
|
||||||
execute_raw_requests(requests: 1, project: project, file_path: file_path, token: 'foobar')
|
execute_raw_request_with_token_in_params
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:found)
|
expect(response).to have_gitlab_http_status(:found)
|
||||||
expect(response.location).to end_with('/users/sign_in')
|
expect(response.location).to end_with('/users/sign_in')
|
||||||
|
@ -226,19 +257,47 @@ RSpec.describe Projects::RawController do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when a token header is present' do
|
context 'when a token header is present' do
|
||||||
|
subject(:execute_raw_request_with_token_in_headers) do
|
||||||
|
request.headers['X-Gitlab-Static-Object-Token'] = token
|
||||||
|
execute_raw_requests(requests: 1, project: project, file_path: file_path)
|
||||||
|
end
|
||||||
|
|
||||||
context 'when token is correct' do
|
context 'when token is correct' do
|
||||||
it 'calls the action normally' do
|
it 'calls the action normally' do
|
||||||
request.headers['X-Gitlab-Static-Object-Token'] = user.static_object_token
|
execute_raw_request_with_token_in_headers
|
||||||
execute_raw_requests(requests: 1, project: project, file_path: file_path)
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user with expired password' do
|
||||||
|
let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) }
|
||||||
|
|
||||||
|
it 'redirects to sign in page' do
|
||||||
|
execute_raw_request_with_token_in_headers
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:found)
|
||||||
|
expect(response.location).to end_with('/users/sign_in')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when password expiration is not applicable' do
|
||||||
|
context 'when ldap user' do
|
||||||
|
let_it_be(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) }
|
||||||
|
|
||||||
|
it 'calls the action normally' do
|
||||||
|
execute_raw_request_with_token_in_headers
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when token is incorrect' do
|
context 'when token is incorrect' do
|
||||||
|
let(:token) { 'foobar' }
|
||||||
|
|
||||||
it 'redirects to sign in page' do
|
it 'redirects to sign in page' do
|
||||||
request.headers['X-Gitlab-Static-Object-Token'] = 'foobar'
|
execute_raw_request_with_token_in_headers
|
||||||
execute_raw_requests(requests: 1, project: project, file_path: file_path)
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:found)
|
expect(response).to have_gitlab_http_status(:found)
|
||||||
expect(response.location).to end_with('/users/sign_in')
|
expect(response.location).to end_with('/users/sign_in')
|
||||||
|
|
|
@ -178,6 +178,29 @@ RSpec.describe Projects::RepositoriesController do
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when user with expired password' do
|
||||||
|
let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) }
|
||||||
|
|
||||||
|
it 'redirects to sign in page' do
|
||||||
|
get :archive, params: { namespace_id: project.namespace, project_id: project, id: 'master', token: user.static_object_token }, format: 'zip'
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:found)
|
||||||
|
expect(response.location).to end_with('/users/sign_in')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when password expiration is not applicable' do
|
||||||
|
context 'when ldap user' do
|
||||||
|
let_it_be(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) }
|
||||||
|
|
||||||
|
it 'calls the action normally' do
|
||||||
|
get :archive, params: { namespace_id: project.namespace, project_id: project, id: 'master', token: user.static_object_token }, format: 'zip'
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when token is incorrect' do
|
context 'when token is incorrect' do
|
||||||
|
@ -197,6 +220,31 @@ RSpec.describe Projects::RepositoriesController do
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when user with expired password' do
|
||||||
|
let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) }
|
||||||
|
|
||||||
|
it 'redirects to sign in page' do
|
||||||
|
request.headers['X-Gitlab-Static-Object-Token'] = user.static_object_token
|
||||||
|
get :archive, params: { namespace_id: project.namespace, project_id: project, id: 'master' }, format: 'zip'
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:found)
|
||||||
|
expect(response.location).to end_with('/users/sign_in')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when password expiration is not applicable' do
|
||||||
|
context 'when ldap user' do
|
||||||
|
let_it_be(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) }
|
||||||
|
|
||||||
|
it 'calls the action normally' do
|
||||||
|
request.headers['X-Gitlab-Static-Object-Token'] = user.static_object_token
|
||||||
|
get :archive, params: { namespace_id: project.namespace, project_id: project, id: 'master' }, format: 'zip'
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when token is incorrect' do
|
context 'when token is incorrect' do
|
||||||
|
|
|
@ -117,28 +117,6 @@ RSpec.describe Projects::TagsController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'private project with token authentication' do
|
|
||||||
let(:private_project) { create(:project, :repository, :private) }
|
|
||||||
|
|
||||||
it_behaves_like 'authenticates sessionless user', :index, :atom, ignore_incrementing: true do
|
|
||||||
before do
|
|
||||||
default_params.merge!(project_id: private_project, namespace_id: private_project.namespace)
|
|
||||||
|
|
||||||
private_project.add_maintainer(user)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'public project with token authentication' do
|
|
||||||
let(:public_project) { create(:project, :repository, :public) }
|
|
||||||
|
|
||||||
it_behaves_like 'authenticates sessionless user', :index, :atom, public: true do
|
|
||||||
before do
|
|
||||||
default_params.merge!(project_id: public_project, namespace_id: public_project.namespace)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'POST #create' do
|
describe 'POST #create' do
|
||||||
before do
|
before do
|
||||||
project.add_developer(user)
|
project.add_developer(user)
|
||||||
|
|
|
@ -1555,28 +1555,6 @@ RSpec.describe ProjectsController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'private project with token authentication' do
|
|
||||||
let_it_be(:private_project) { create(:project, :private) }
|
|
||||||
|
|
||||||
it_behaves_like 'authenticates sessionless user', :show, :atom, ignore_incrementing: true do
|
|
||||||
before do
|
|
||||||
default_params.merge!(id: private_project, namespace_id: private_project.namespace)
|
|
||||||
|
|
||||||
private_project.add_maintainer(user)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'public project with token authentication' do
|
|
||||||
let_it_be(:public_project) { create(:project, :public) }
|
|
||||||
|
|
||||||
it_behaves_like 'authenticates sessionless user', :show, :atom, public: true do
|
|
||||||
before do
|
|
||||||
default_params.merge!(id: public_project, namespace_id: public_project.namespace)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'GET show.atom' do
|
context 'GET show.atom' do
|
||||||
let_it_be(:public_project) { create(:project, :public) }
|
let_it_be(:public_project) { create(:project, :public) }
|
||||||
let_it_be(:event) { create(:event, :commented, project: public_project, target: create(:note, project: public_project)) }
|
let_it_be(:event) { create(:event, :commented, project: public_project, target: create(:note, project: public_project)) }
|
||||||
|
|
|
@ -258,18 +258,6 @@ ZDXgrA==
|
||||||
certificate_source { :gitlab_provided }
|
certificate_source { :gitlab_provided }
|
||||||
end
|
end
|
||||||
|
|
||||||
# This contains:
|
|
||||||
# webdioxide.com
|
|
||||||
# Let's Encrypt R3
|
|
||||||
# ISRG Root X1 (issued by DST Root CA X3)
|
|
||||||
#
|
|
||||||
# DST Root CA X3 expired on 2021-09-30, but ISRG Root X1 should be trusted on most systems.
|
|
||||||
trait :letsencrypt_expired_x3_root do
|
|
||||||
certificate do
|
|
||||||
File.read(Rails.root.join('spec/fixtures/ssl', 'letsencrypt_expired_x3.pem'))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
trait :explicit_ecdsa do
|
trait :explicit_ecdsa do
|
||||||
certificate do
|
certificate do
|
||||||
'-----BEGIN CERTIFICATE-----
|
'-----BEGIN CERTIFICATE-----
|
||||||
|
|
|
@ -31,7 +31,6 @@ RSpec.describe 'factories' do
|
||||||
[:pages_domain, :with_trusted_chain],
|
[:pages_domain, :with_trusted_chain],
|
||||||
[:pages_domain, :with_trusted_expired_chain],
|
[:pages_domain, :with_trusted_expired_chain],
|
||||||
[:pages_domain, :explicit_ecdsa],
|
[:pages_domain, :explicit_ecdsa],
|
||||||
[:pages_domain, :letsencrypt_expired_x3_root],
|
|
||||||
[:project_member, :blocked],
|
[:project_member, :blocked],
|
||||||
[:remote_mirror, :ssh],
|
[:remote_mirror, :ssh],
|
||||||
[:user_preference, :only_comments],
|
[:user_preference, :only_comments],
|
||||||
|
|
23
spec/fixtures/clusters/ca_certificate.pem
vendored
23
spec/fixtures/clusters/ca_certificate.pem
vendored
|
@ -1,23 +0,0 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
|
|
||||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
|
||||||
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
|
|
||||||
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
|
|
||||||
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
|
|
||||||
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
|
|
||||||
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
|
|
||||||
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
|
|
||||||
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
|
|
||||||
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
|
|
||||||
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
|
|
||||||
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
|
|
||||||
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
|
|
||||||
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
|
|
||||||
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
|
|
||||||
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
|
|
||||||
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
|
|
||||||
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
|
|
||||||
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
|
|
||||||
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
|
|
||||||
+OkuE6N36B9K
|
|
||||||
-----END CERTIFICATE-----
|
|
174
spec/fixtures/clusters/chain_certificates.pem
vendored
174
spec/fixtures/clusters/chain_certificates.pem
vendored
|
@ -1,100 +1,86 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIItjCCB56gAwIBAgIQCu5Ga1hR41iahM0SWhyeNjANBgkqhkiG9w0BAQsFADB1
|
MIIGYzCCBUugAwIBAgIQAaQHyOeT/PBR4ioLKYneZDANBgkqhkiG9w0BAQsFADBY
|
||||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEuMCwGA1UE
|
||||||
d3cuZGlnaWNlcnQuY29tMTQwMgYDVQQDEytEaWdpQ2VydCBTSEEyIEV4dGVuZGVk
|
AxMlR2xvYmFsU2lnbiBBdGxhcyBSMyBEViBUTFMgQ0EgSDIgMjAyMTAeFw0yMTEw
|
||||||
IFZhbGlkYXRpb24gU2VydmVyIENBMB4XDTE5MTIwNDAwMDAwMFoXDTIxMTIwODEy
|
MTgxODUwMDRaFw0yMjExMTkxODUwMDNaMBsxGTAXBgNVBAMMEGFib3V0LmdpdGxh
|
||||||
MDAwMFowgb0xHTAbBgNVBA8MFFByaXZhdGUgT3JnYW5pemF0aW9uMRMwEQYLKwYB
|
Yi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDWSo0eziN/0lq5
|
||||||
BAGCNzwCAQMTAlVTMRUwEwYLKwYBBAGCNzwCAQITBFV0YWgxFTATBgNVBAUTDDUy
|
dIcS7ZceJw2odzZeT0tRkcKEW8iagNul6JetrFlk6h5lxoLEu35+MK6/fWHNmt7u
|
||||||
OTk1MzctMDE0MjELMAkGA1UEBhMCVVMxDTALBgNVBAgTBFV0YWgxDTALBgNVBAcT
|
eQk7HS0uRipskAzeGrL1Hvk8EjIcHXXTxpRu7JqWOu7ZSXwNxW5cqn7L9/N2gYwt
|
||||||
BExlaGkxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMRUwEwYDVQQDEwxkaWdpY2Vy
|
Jg/sfkv9AFQiNOdKrarKfbcBstxmra6rQbh5ggLG5UBT23N4ZrA3XnzvEx3+GjtO
|
||||||
dC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAeRYb/RLbljGZ
|
u/a5izbk7FQP3gyXKyfm/SQRpNsytYa9jJqu5Hmyzfap5KaueOJbtJEOk8dR/HWR
|
||||||
IB//DrEdyKYMQqqaJwBlrr3t2paAWNuDJizvVkTMIzdJesI1pA58Myenxp5Dp8GJ
|
i/gmAUevq62MNxorYbz8YU/P1468tS7iORkD31Tc2QWCMQSPya5qGaCGnz7dVgWy
|
||||||
u/VhBf//v/HAZHUE4xwu104Fg6A1BwUEKgVKERf+7kTt17Lf9fcMIjMyL+FeyPXb
|
E1xTPbBXAgMBAAGjggNkMIIDYDAbBgNVHREEFDASghBhYm91dC5naXRsYWIuY29t
|
||||||
DOFbH+ej/nYaneFLch2j2xWZg1+Thk0qBlGE8WWAK+fvbEuM0SOeH9RkYFCNGPRS
|
MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
|
||||||
KsLn0GvaCnnD4LfNDyMqYop0IpaqXoREEnkRv1MVSOw+hBj497wnnO+/GZegfzwU
|
HQYDVR0OBBYEFJFVruwpjWeUfGJXl3m5grAjhAwPMFcGA1UdIARQME4wCAYGZ4EM
|
||||||
iS60h+PjlDfmdCP18qOS7tRd0qnfU3N3S+PYEd3R63LMcIfbgXNEEWBNKpiH9+8f
|
AQIBMEIGCisGAQQBoDIKAQMwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xv
|
||||||
eXq6bXKPAgMBAAGjggT3MIIE8zAfBgNVHSMEGDAWgBQ901Cl1qCt7vNKYApl0yHU
|
YmFsc2lnbi5jb20vcmVwb3NpdG9yeS8wDAYDVR0TAQH/BAIwADCBngYIKwYBBQUH
|
||||||
+PjWDzAdBgNVHQ4EFgQUTx0XO7HqD5DOhwlm2p+70uYPBmgwggGjBgNVHREEggGa
|
AQEEgZEwgY4wQAYIKwYBBQUHMAGGNGh0dHA6Ly9vY3NwLmdsb2JhbHNpZ24uY29t
|
||||||
MIIBloIMZGlnaWNlcnQuY29tggl0aGF3dGUuZGWCC2ZyZWVzc2wuY29tggxyYXBp
|
L2NhL2dzYXRsYXNyM2R2dGxzY2FoMjIwMjEwSgYIKwYBBQUHMAKGPmh0dHA6Ly9z
|
||||||
ZHNzbC5jb22CDGdlb3RydXN0LmNvbYIJdGhhd3RlLmZyggp0aGF3dGUuY29tghB3
|
ZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2dzYXRsYXNyM2R2dGxzY2FoMjIw
|
||||||
d3cucmFwaWRzc2wuY29tghB3d3cuZ2VvdHJ1c3QuY29tgg13d3cudGhhd3RlLmZy
|
MjEuY3J0MB8GA1UdIwQYMBaAFCo0uar6vzyI8Ufy0hJ4vsXlqrBpMEgGA1UdHwRB
|
||||||
gg13d3cudGhhd3RlLmRlgg53d3cudGhhd3RlLmNvbYIQd3d3LmRpZ2ljZXJ0LmNv
|
MD8wPaA7oDmGN2h0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vY2EvZ3NhdGxhc3Iz
|
||||||
bYIYa2ItaW50ZXJuYWwuZGlnaWNlcnQuY29tghprbm93bGVkZ2ViYXNlLmRpZ2lj
|
ZHZ0bHNjYWgyMjAyMS5jcmwwggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB3AG9T
|
||||||
ZXJ0LmNvbYIWa25vd2xlZGdlLmRpZ2ljZXJ0LmNvbYIPa2guZGlnaWNlcnQuY29t
|
dqwx8DEZ2JkApFEV/3cVHBHZAsEAKQaNsgiaN9kTAAABfJS9R5YAAAQDAEgwRgIh
|
||||||
ghlrbm93bGVkZ2VodWIuZGlnaWNlcnQuY29tghh3ZWJzZWN1cml0eS5kaWdpY2Vy
|
AOOZmc41vB2ICwkwEB5Bmpm/X8UHfjbxwrCXEdeRmO+qAiEAg/JugZIrG2PeV4bA
|
||||||
dC5jb22CFGNvbnRlbnQuZGlnaWNlcnQuY29tgg93d3cuZnJlZXNzbC5jb22CHHd3
|
Gm6rry7HUfB954bQJ4p0PeQVmwsAdABGpVXrdfqRIDC1oolp9PN9ESxBdL79SbiF
|
||||||
dy53ZWJzZWN1cml0eS5kaWdpY2VydC5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1Ud
|
q/L8cP5tRwAAAXyUvUeOAAAEAwBFMEMCHyRAiTz2fZ8DuQF6hrVP+IMTCPBtjB3D
|
||||||
JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjB1BgNVHR8EbjBsMDSgMqAwhi5odHRw
|
m4naI8tC/foCIDXFCRIYjRb00CFI6piLYGihRy+GYF5nMQhQ9uE6hltzAHcAUaOw
|
||||||
Oi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1ldi1zZXJ2ZXItZzIuY3JsMDSgMqAw
|
9f0BeZxWbbg3eI8MpHrMGyfL956IQpoN/tSLBeUAAAF8lL1ICgAABAMASDBGAiEA
|
||||||
hi5odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1ldi1zZXJ2ZXItZzIuY3Js
|
5d/bXb9TPZWhwSH8GGji/LDFL6OJnZtOV94sBaDiFgMCIQCtl00oCRMFFnqsvBo6
|
||||||
MEsGA1UdIAREMEIwNwYJYIZIAYb9bAIBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v
|
SRtnDqJkEHYBS12I4LyC+D1onjANBgkqhkiG9w0BAQsFAAOCAQEAE5xcno79J+Ec
|
||||||
d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwBwYFZ4EMAQEwgYgGCCsGAQUFBwEBBHwwejAk
|
DIPJKnJCugKiM7yKjCjCp/63osCbRC+jUwRyXBIe/oTdY3geKwDOQAvyEeJPSWP1
|
||||||
BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFIGCCsGAQUFBzAC
|
LbNp0l3yHbYXfsYl/NMTrJpjrJrrRO5BxG/d3IPwXIlcZrrdDSoGfGYIF9N23iqB
|
||||||
hkZodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyRXh0ZW5k
|
in15L7B+PodTl8/mSQZTjbLoecPvl+AOcLyStcWCKYQUlQb3x4UV3R4Z1ukwGbBC
|
||||||
ZWRWYWxpZGF0aW9uU2VydmVyQ0EuY3J0MAwGA1UdEwEB/wQCMAAwggF8BgorBgEE
|
cDbTR2XOSJzA9ECJcxKnWjQRQUc54pdG3pt13Wu2dVapX5sWZpV05rga3bBDjCqw
|
||||||
AdZ5AgQCBIIBbASCAWgBZgB1AKS5CZC0GFgUh7sTosxncAo8NZgE+RvfuON3zQ7I
|
DcfKuYbOChm2i6CQ578lAntPTIS02EkGFHrmYxrIAvlhGksHpJNJtRoff1KkQKni
|
||||||
DdwQAAABbtLkOs4AAAQDAEYwRAIgQ7gh393PInhYfPOhg/lF9yZNRdvjBeufFoG8
|
r8emWp7D2Q==
|
||||||
VnBuPNMCIBP8YGC83ig5ttw3ipSRjH0bKj4Ak5O4rynoql9Dy8x3AHYAVhQGmi/X
|
|
||||||
wuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFu0uQ7VgAABAMARzBFAiEAhzE7
|
|
||||||
1c48wn3s/30IB4WgxfpLburH0Ku8cchv8QeqcgACIBrWpUlDD18AOfkPCOcB2kWU
|
|
||||||
vRXsdptVm3jPeU5TtDSoAHUAu9nfvB+KcbWTlCOXqpJ7RzhXlQqrUugakJZkNo4e
|
|
||||||
0YUAAAFu0uQ60gAABAMARjBEAiBBpH5m7ntGKFTOFgSLcFXRDg66xJqerMy0gOHj
|
|
||||||
4TIBYAIgfFABPNy6P61hjiOWwjq73lvoEdAyh18GeFHIp0BgsWEwDQYJKoZIhvcN
|
|
||||||
AQELBQADggEBAInaSEqteyQA1zUKiXVqgffhHKZsUq9UnMows6X+UoFPoby9xqm6
|
|
||||||
IaY/77zaFZYwXJlP/SvrlbgTLHAdir3y38uhAlfPX4iRuwggOpFFF5hqDckzCm91
|
|
||||||
ocGnoG6sUY5mOqKu2vIcZkUQDe+K5gOxI6ME/4YwzWCIcTmBPQ6NQmqiFLPoQty1
|
|
||||||
gdbGCcLQNFCuNq4n5OK2NmBjcbtyT4gglat7C4+KV8RkEubZ+MkXzyDkpEXjjzsK
|
|
||||||
7iuNB0hRgyyhGzHrlZ/l0OLoT0Cb4I5PzzRSseFEyPKCC1WSF7aE9rFfUqhpqSAT
|
|
||||||
7NV7SEijYyFFtuZfz9RGglcqnRlAfgTy+tU=
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIEtjCCA56gAwIBAgIQDHmpRLCMEZUgkmFf4msdgzANBgkqhkiG9w0BAQsFADBs
|
MIIExTCCA62gAwIBAgIQeimFGrf0XWZ5UGZBtv/XHTANBgkqhkiG9w0BAQsFADBM
|
||||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
MSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xv
|
||||||
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
|
YmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0yMTA2MTYxMjAwMDBaFw0y
|
||||||
ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowdTEL
|
NDA2MTYwMDAwMDBaMFgxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWdu
|
||||||
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
|
IG52LXNhMS4wLAYDVQQDEyVHbG9iYWxTaWduIEF0bGFzIFIzIERWIFRMUyBDQSBI
|
||||||
LmRpZ2ljZXJ0LmNvbTE0MDIGA1UEAxMrRGlnaUNlcnQgU0hBMiBFeHRlbmRlZCBW
|
MiAyMDIxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1JTAQMj+QUYF
|
||||||
YWxpZGF0aW9uIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
3d9X5eOWFOphbB6GpHE3J0uvUXcQwxnd8Jz26aQCE1ZYxJFEc2WmsxuVeVXU+rZj
|
||||||
ggEBANdTpARR+JmmFkhLZyeqk0nQOe0MsLAAh/FnKIaFjI5j2ryxQDji0/XspQUY
|
7+MYD7Mg72bhuiwUdwRGRN4a2N122LfIQlTFlHu/fwcNqYX/fe3phvZt9upnH4oJ
|
||||||
uD0+xZkXMuwYjPrxDKZkIYXLBxA0sFKIKx9om9KxjxKws9LniB8f7zh3VFNfgHk/
|
aLBbay+t+HPPC4em74x2WKaIl31ZXzgzllLomnlLISLOKiQe1rEHp4yy3/yE2a4G
|
||||||
LhqqqB5LKw2rt2O5Nbd9FLxZS99RStKh4gzikIKHaq7q12TWmFXo/a8aUGxUvBHy
|
1l/lprA49dcyM/oylm9Bbkum2F4C+EOjHgTAoDVJrJpdWvPj0CU+HkmftujfFp4S
|
||||||
/Urynbt/DvTVvo4WiRJV2MBxNO723C3sxIclho3YIeSwTQyJ3DkmF93215SF2AQh
|
55LECSr2TfJt7xjgR3eLUx12nlpoauWEzZ0/i6OIDPfbmqcksw4ani/YO07LbRM6
|
||||||
cJ1vb/9cuhnhRctWVyh+HA1BV6q3uCe7seT6Ku8hI3UarS2bhjWMnHe1c63YlC3k
|
cY9VZzkAvwIDAQABo4IBlTCCAZEwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQG
|
||||||
8wyd7sFOYn4XwHGeLN7x+RAoGTMCAwEAAaOCAUkwggFFMBIGA1UdEwEB/wQIMAYB
|
CCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQW
|
||||||
Af8CAQAwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF
|
BBQqNLmq+r88iPFH8tISeL7F5aqwaTAfBgNVHSMEGDAWgBSP8Et/qC5FJK5NUPpj
|
||||||
BQcDAjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp
|
move4t0bvDB7BggrBgEFBQcBAQRvMG0wLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3Nw
|
||||||
Z2ljZXJ0LmNvbTBLBgNVHR8ERDBCMECgPqA8hjpodHRwOi8vY3JsNC5kaWdpY2Vy
|
Mi5nbG9iYWxzaWduLmNvbS9yb290cjMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9zZWN1
|
||||||
dC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3JsMD0GA1UdIAQ2
|
cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L3Jvb3QtcjMuY3J0MDYGA1UdHwQvMC0w
|
||||||
MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5j
|
K6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vcm9vdC1yMy5jcmwwVwYD
|
||||||
b20vQ1BTMB0GA1UdDgQWBBQ901Cl1qCt7vNKYApl0yHU+PjWDzAfBgNVHSMEGDAW
|
VR0gBFAwTjAIBgZngQwBAgEwQgYKKwYBBAGgMgoBAzA0MDIGCCsGAQUFBwIBFiZo
|
||||||
gBSxPsNpA/i/RwHUmCYaCALvY2QrwzANBgkqhkiG9w0BAQsFAAOCAQEAnbbQkIbh
|
dHRwczovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBvc2l0b3J5LzANBgkqhkiG9w0B
|
||||||
hgLtxaDwNBx0wY12zIYKqPBKikLWP8ipTa18CK3mtlC4ohpNiAexKSHc59rGPCHg
|
AQsFAAOCAQEAEsIwXEhdAfoUGaKAnYfVI7zsOY7Sx8bpC/obGxXa4Kyu8CVx+TtT
|
||||||
4xFJcKx6HQGkyhE6V6t9VypAdP3THYUYUN9XR3WhfVUgLkc3UHKMf4Ib0mKPLQNa
|
g8WmKNF7+I7C51NZEmhvb8UDI1G9ny7iYIRDajQD5AeZowbfC69aHQSI9LiOeAZb
|
||||||
2sPIoc4sUqIAY+tzunHISScjl2SFnjgOrWNoPLpSgVh5oywM395t6zHyuqB8bPEs
|
YaRDJfWps9redPwoaC0iT5R4xLOnWwCtmIho1bv/YG3pMAvaQ+qn04kuUvWO7LEp
|
||||||
1OG9d4Q3A84ytciagRpKkk47RpqF/oOi+Z6Mo8wNXrM9zwR4jxQUezKcxwCmXMS1
|
u7FdHmx1DdgkefcqYgN/rAZ8E39S9VxWV+64PNUDey8vkAIH8FCTxbWiITty6dsH
|
||||||
oVWNWlZopCJwqjyBcdmdqEU79OX2olHdx3ti6G8MdOu42vi/hw15UJGQmxg7kVkn
|
SulKQ9pSa93k9PHTf+di08mMQBq5WBWTiFeMYZEWyE/z7NHdU3eLMZjq6y/nKlF9
|
||||||
8TUoE6smftX3eg==
|
nywrToh4AgdZK6JnbU+lqbNiexJbaBoA3w==
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
|
MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
|
||||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
|
||||||
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
|
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
|
||||||
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
|
MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
|
||||||
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
|
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
|
||||||
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
|
hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
|
||||||
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
|
RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
|
||||||
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
|
gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
|
||||||
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
|
KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
|
||||||
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
|
QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
|
||||||
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
|
XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
|
||||||
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
|
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
|
||||||
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
|
LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
|
||||||
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
|
RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
|
||||||
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
|
jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
|
||||||
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
|
6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
|
||||||
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
|
mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
|
||||||
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
|
Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
|
||||||
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
|
WD9f
|
||||||
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
|
|
||||||
+OkuE6N36B9K
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIEtjCCA56gAwIBAgIQDHmpRLCMEZUgkmFf4msdgzANBgkqhkiG9w0BAQsFADBs
|
MIIExTCCA62gAwIBAgIQeimFGrf0XWZ5UGZBtv/XHTANBgkqhkiG9w0BAQsFADBM
|
||||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
MSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xv
|
||||||
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
|
YmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0yMTA2MTYxMjAwMDBaFw0y
|
||||||
ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowdTEL
|
NDA2MTYwMDAwMDBaMFgxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWdu
|
||||||
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
|
IG52LXNhMS4wLAYDVQQDEyVHbG9iYWxTaWduIEF0bGFzIFIzIERWIFRMUyBDQSBI
|
||||||
LmRpZ2ljZXJ0LmNvbTE0MDIGA1UEAxMrRGlnaUNlcnQgU0hBMiBFeHRlbmRlZCBW
|
MiAyMDIxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1JTAQMj+QUYF
|
||||||
YWxpZGF0aW9uIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
3d9X5eOWFOphbB6GpHE3J0uvUXcQwxnd8Jz26aQCE1ZYxJFEc2WmsxuVeVXU+rZj
|
||||||
ggEBANdTpARR+JmmFkhLZyeqk0nQOe0MsLAAh/FnKIaFjI5j2ryxQDji0/XspQUY
|
7+MYD7Mg72bhuiwUdwRGRN4a2N122LfIQlTFlHu/fwcNqYX/fe3phvZt9upnH4oJ
|
||||||
uD0+xZkXMuwYjPrxDKZkIYXLBxA0sFKIKx9om9KxjxKws9LniB8f7zh3VFNfgHk/
|
aLBbay+t+HPPC4em74x2WKaIl31ZXzgzllLomnlLISLOKiQe1rEHp4yy3/yE2a4G
|
||||||
LhqqqB5LKw2rt2O5Nbd9FLxZS99RStKh4gzikIKHaq7q12TWmFXo/a8aUGxUvBHy
|
1l/lprA49dcyM/oylm9Bbkum2F4C+EOjHgTAoDVJrJpdWvPj0CU+HkmftujfFp4S
|
||||||
/Urynbt/DvTVvo4WiRJV2MBxNO723C3sxIclho3YIeSwTQyJ3DkmF93215SF2AQh
|
55LECSr2TfJt7xjgR3eLUx12nlpoauWEzZ0/i6OIDPfbmqcksw4ani/YO07LbRM6
|
||||||
cJ1vb/9cuhnhRctWVyh+HA1BV6q3uCe7seT6Ku8hI3UarS2bhjWMnHe1c63YlC3k
|
cY9VZzkAvwIDAQABo4IBlTCCAZEwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQG
|
||||||
8wyd7sFOYn4XwHGeLN7x+RAoGTMCAwEAAaOCAUkwggFFMBIGA1UdEwEB/wQIMAYB
|
CCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQW
|
||||||
Af8CAQAwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF
|
BBQqNLmq+r88iPFH8tISeL7F5aqwaTAfBgNVHSMEGDAWgBSP8Et/qC5FJK5NUPpj
|
||||||
BQcDAjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp
|
move4t0bvDB7BggrBgEFBQcBAQRvMG0wLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3Nw
|
||||||
Z2ljZXJ0LmNvbTBLBgNVHR8ERDBCMECgPqA8hjpodHRwOi8vY3JsNC5kaWdpY2Vy
|
Mi5nbG9iYWxzaWduLmNvbS9yb290cjMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9zZWN1
|
||||||
dC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3JsMD0GA1UdIAQ2
|
cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L3Jvb3QtcjMuY3J0MDYGA1UdHwQvMC0w
|
||||||
MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5j
|
K6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vcm9vdC1yMy5jcmwwVwYD
|
||||||
b20vQ1BTMB0GA1UdDgQWBBQ901Cl1qCt7vNKYApl0yHU+PjWDzAfBgNVHSMEGDAW
|
VR0gBFAwTjAIBgZngQwBAgEwQgYKKwYBBAGgMgoBAzA0MDIGCCsGAQUFBwIBFiZo
|
||||||
gBSxPsNpA/i/RwHUmCYaCALvY2QrwzANBgkqhkiG9w0BAQsFAAOCAQEAnbbQkIbh
|
dHRwczovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBvc2l0b3J5LzANBgkqhkiG9w0B
|
||||||
hgLtxaDwNBx0wY12zIYKqPBKikLWP8ipTa18CK3mtlC4ohpNiAexKSHc59rGPCHg
|
AQsFAAOCAQEAEsIwXEhdAfoUGaKAnYfVI7zsOY7Sx8bpC/obGxXa4Kyu8CVx+TtT
|
||||||
4xFJcKx6HQGkyhE6V6t9VypAdP3THYUYUN9XR3WhfVUgLkc3UHKMf4Ib0mKPLQNa
|
g8WmKNF7+I7C51NZEmhvb8UDI1G9ny7iYIRDajQD5AeZowbfC69aHQSI9LiOeAZb
|
||||||
2sPIoc4sUqIAY+tzunHISScjl2SFnjgOrWNoPLpSgVh5oywM395t6zHyuqB8bPEs
|
YaRDJfWps9redPwoaC0iT5R4xLOnWwCtmIho1bv/YG3pMAvaQ+qn04kuUvWO7LEp
|
||||||
1OG9d4Q3A84ytciagRpKkk47RpqF/oOi+Z6Mo8wNXrM9zwR4jxQUezKcxwCmXMS1
|
u7FdHmx1DdgkefcqYgN/rAZ8E39S9VxWV+64PNUDey8vkAIH8FCTxbWiITty6dsH
|
||||||
oVWNWlZopCJwqjyBcdmdqEU79OX2olHdx3ti6G8MdOu42vi/hw15UJGQmxg7kVkn
|
SulKQ9pSa93k9PHTf+di08mMQBq5WBWTiFeMYZEWyE/z7NHdU3eLMZjq6y/nKlF9
|
||||||
8TUoE6smftX3eg==
|
nywrToh4AgdZK6JnbU+lqbNiexJbaBoA3w==
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
37
spec/fixtures/clusters/leaf_certificate.pem
vendored
Normal file
37
spec/fixtures/clusters/leaf_certificate.pem
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIGYzCCBUugAwIBAgIQAaQHyOeT/PBR4ioLKYneZDANBgkqhkiG9w0BAQsFADBY
|
||||||
|
MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEuMCwGA1UE
|
||||||
|
AxMlR2xvYmFsU2lnbiBBdGxhcyBSMyBEViBUTFMgQ0EgSDIgMjAyMTAeFw0yMTEw
|
||||||
|
MTgxODUwMDRaFw0yMjExMTkxODUwMDNaMBsxGTAXBgNVBAMMEGFib3V0LmdpdGxh
|
||||||
|
Yi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDWSo0eziN/0lq5
|
||||||
|
dIcS7ZceJw2odzZeT0tRkcKEW8iagNul6JetrFlk6h5lxoLEu35+MK6/fWHNmt7u
|
||||||
|
eQk7HS0uRipskAzeGrL1Hvk8EjIcHXXTxpRu7JqWOu7ZSXwNxW5cqn7L9/N2gYwt
|
||||||
|
Jg/sfkv9AFQiNOdKrarKfbcBstxmra6rQbh5ggLG5UBT23N4ZrA3XnzvEx3+GjtO
|
||||||
|
u/a5izbk7FQP3gyXKyfm/SQRpNsytYa9jJqu5Hmyzfap5KaueOJbtJEOk8dR/HWR
|
||||||
|
i/gmAUevq62MNxorYbz8YU/P1468tS7iORkD31Tc2QWCMQSPya5qGaCGnz7dVgWy
|
||||||
|
E1xTPbBXAgMBAAGjggNkMIIDYDAbBgNVHREEFDASghBhYm91dC5naXRsYWIuY29t
|
||||||
|
MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
|
||||||
|
HQYDVR0OBBYEFJFVruwpjWeUfGJXl3m5grAjhAwPMFcGA1UdIARQME4wCAYGZ4EM
|
||||||
|
AQIBMEIGCisGAQQBoDIKAQMwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xv
|
||||||
|
YmFsc2lnbi5jb20vcmVwb3NpdG9yeS8wDAYDVR0TAQH/BAIwADCBngYIKwYBBQUH
|
||||||
|
AQEEgZEwgY4wQAYIKwYBBQUHMAGGNGh0dHA6Ly9vY3NwLmdsb2JhbHNpZ24uY29t
|
||||||
|
L2NhL2dzYXRsYXNyM2R2dGxzY2FoMjIwMjEwSgYIKwYBBQUHMAKGPmh0dHA6Ly9z
|
||||||
|
ZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2dzYXRsYXNyM2R2dGxzY2FoMjIw
|
||||||
|
MjEuY3J0MB8GA1UdIwQYMBaAFCo0uar6vzyI8Ufy0hJ4vsXlqrBpMEgGA1UdHwRB
|
||||||
|
MD8wPaA7oDmGN2h0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vY2EvZ3NhdGxhc3Iz
|
||||||
|
ZHZ0bHNjYWgyMjAyMS5jcmwwggF+BgorBgEEAdZ5AgQCBIIBbgSCAWoBaAB3AG9T
|
||||||
|
dqwx8DEZ2JkApFEV/3cVHBHZAsEAKQaNsgiaN9kTAAABfJS9R5YAAAQDAEgwRgIh
|
||||||
|
AOOZmc41vB2ICwkwEB5Bmpm/X8UHfjbxwrCXEdeRmO+qAiEAg/JugZIrG2PeV4bA
|
||||||
|
Gm6rry7HUfB954bQJ4p0PeQVmwsAdABGpVXrdfqRIDC1oolp9PN9ESxBdL79SbiF
|
||||||
|
q/L8cP5tRwAAAXyUvUeOAAAEAwBFMEMCHyRAiTz2fZ8DuQF6hrVP+IMTCPBtjB3D
|
||||||
|
m4naI8tC/foCIDXFCRIYjRb00CFI6piLYGihRy+GYF5nMQhQ9uE6hltzAHcAUaOw
|
||||||
|
9f0BeZxWbbg3eI8MpHrMGyfL956IQpoN/tSLBeUAAAF8lL1ICgAABAMASDBGAiEA
|
||||||
|
5d/bXb9TPZWhwSH8GGji/LDFL6OJnZtOV94sBaDiFgMCIQCtl00oCRMFFnqsvBo6
|
||||||
|
SRtnDqJkEHYBS12I4LyC+D1onjANBgkqhkiG9w0BAQsFAAOCAQEAE5xcno79J+Ec
|
||||||
|
DIPJKnJCugKiM7yKjCjCp/63osCbRC+jUwRyXBIe/oTdY3geKwDOQAvyEeJPSWP1
|
||||||
|
LbNp0l3yHbYXfsYl/NMTrJpjrJrrRO5BxG/d3IPwXIlcZrrdDSoGfGYIF9N23iqB
|
||||||
|
in15L7B+PodTl8/mSQZTjbLoecPvl+AOcLyStcWCKYQUlQb3x4UV3R4Z1ukwGbBC
|
||||||
|
cDbTR2XOSJzA9ECJcxKnWjQRQUc54pdG3pt13Wu2dVapX5sWZpV05rga3bBDjCqw
|
||||||
|
DcfKuYbOChm2i6CQ578lAntPTIS02EkGFHrmYxrIAvlhGksHpJNJtRoff1KkQKni
|
||||||
|
r8emWp7D2Q==
|
||||||
|
-----END CERTIFICATE-----
|
66
spec/fixtures/clusters/root_certificate.pem
vendored
66
spec/fixtures/clusters/root_certificate.pem
vendored
|
@ -1,49 +1,21 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIItjCCB56gAwIBAgIQCu5Ga1hR41iahM0SWhyeNjANBgkqhkiG9w0BAQsFADB1
|
MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
|
||||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
|
||||||
d3cuZGlnaWNlcnQuY29tMTQwMgYDVQQDEytEaWdpQ2VydCBTSEEyIEV4dGVuZGVk
|
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
|
||||||
IFZhbGlkYXRpb24gU2VydmVyIENBMB4XDTE5MTIwNDAwMDAwMFoXDTIxMTIwODEy
|
MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
|
||||||
MDAwMFowgb0xHTAbBgNVBA8MFFByaXZhdGUgT3JnYW5pemF0aW9uMRMwEQYLKwYB
|
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
|
||||||
BAGCNzwCAQMTAlVTMRUwEwYLKwYBBAGCNzwCAQITBFV0YWgxFTATBgNVBAUTDDUy
|
hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
|
||||||
OTk1MzctMDE0MjELMAkGA1UEBhMCVVMxDTALBgNVBAgTBFV0YWgxDTALBgNVBAcT
|
RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
|
||||||
BExlaGkxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMRUwEwYDVQQDEwxkaWdpY2Vy
|
gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
|
||||||
dC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAeRYb/RLbljGZ
|
KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
|
||||||
IB//DrEdyKYMQqqaJwBlrr3t2paAWNuDJizvVkTMIzdJesI1pA58Myenxp5Dp8GJ
|
QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
|
||||||
u/VhBf//v/HAZHUE4xwu104Fg6A1BwUEKgVKERf+7kTt17Lf9fcMIjMyL+FeyPXb
|
XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
|
||||||
DOFbH+ej/nYaneFLch2j2xWZg1+Thk0qBlGE8WWAK+fvbEuM0SOeH9RkYFCNGPRS
|
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
|
||||||
KsLn0GvaCnnD4LfNDyMqYop0IpaqXoREEnkRv1MVSOw+hBj497wnnO+/GZegfzwU
|
LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
|
||||||
iS60h+PjlDfmdCP18qOS7tRd0qnfU3N3S+PYEd3R63LMcIfbgXNEEWBNKpiH9+8f
|
RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
|
||||||
eXq6bXKPAgMBAAGjggT3MIIE8zAfBgNVHSMEGDAWgBQ901Cl1qCt7vNKYApl0yHU
|
jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
|
||||||
+PjWDzAdBgNVHQ4EFgQUTx0XO7HqD5DOhwlm2p+70uYPBmgwggGjBgNVHREEggGa
|
6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
|
||||||
MIIBloIMZGlnaWNlcnQuY29tggl0aGF3dGUuZGWCC2ZyZWVzc2wuY29tggxyYXBp
|
mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
|
||||||
ZHNzbC5jb22CDGdlb3RydXN0LmNvbYIJdGhhd3RlLmZyggp0aGF3dGUuY29tghB3
|
Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
|
||||||
d3cucmFwaWRzc2wuY29tghB3d3cuZ2VvdHJ1c3QuY29tgg13d3cudGhhd3RlLmZy
|
WD9f
|
||||||
gg13d3cudGhhd3RlLmRlgg53d3cudGhhd3RlLmNvbYIQd3d3LmRpZ2ljZXJ0LmNv
|
|
||||||
bYIYa2ItaW50ZXJuYWwuZGlnaWNlcnQuY29tghprbm93bGVkZ2ViYXNlLmRpZ2lj
|
|
||||||
ZXJ0LmNvbYIWa25vd2xlZGdlLmRpZ2ljZXJ0LmNvbYIPa2guZGlnaWNlcnQuY29t
|
|
||||||
ghlrbm93bGVkZ2VodWIuZGlnaWNlcnQuY29tghh3ZWJzZWN1cml0eS5kaWdpY2Vy
|
|
||||||
dC5jb22CFGNvbnRlbnQuZGlnaWNlcnQuY29tgg93d3cuZnJlZXNzbC5jb22CHHd3
|
|
||||||
dy53ZWJzZWN1cml0eS5kaWdpY2VydC5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1Ud
|
|
||||||
JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjB1BgNVHR8EbjBsMDSgMqAwhi5odHRw
|
|
||||||
Oi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1ldi1zZXJ2ZXItZzIuY3JsMDSgMqAw
|
|
||||||
hi5odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1ldi1zZXJ2ZXItZzIuY3Js
|
|
||||||
MEsGA1UdIAREMEIwNwYJYIZIAYb9bAIBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v
|
|
||||||
d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwBwYFZ4EMAQEwgYgGCCsGAQUFBwEBBHwwejAk
|
|
||||||
BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFIGCCsGAQUFBzAC
|
|
||||||
hkZodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyRXh0ZW5k
|
|
||||||
ZWRWYWxpZGF0aW9uU2VydmVyQ0EuY3J0MAwGA1UdEwEB/wQCMAAwggF8BgorBgEE
|
|
||||||
AdZ5AgQCBIIBbASCAWgBZgB1AKS5CZC0GFgUh7sTosxncAo8NZgE+RvfuON3zQ7I
|
|
||||||
DdwQAAABbtLkOs4AAAQDAEYwRAIgQ7gh393PInhYfPOhg/lF9yZNRdvjBeufFoG8
|
|
||||||
VnBuPNMCIBP8YGC83ig5ttw3ipSRjH0bKj4Ak5O4rynoql9Dy8x3AHYAVhQGmi/X
|
|
||||||
wuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFu0uQ7VgAABAMARzBFAiEAhzE7
|
|
||||||
1c48wn3s/30IB4WgxfpLburH0Ku8cchv8QeqcgACIBrWpUlDD18AOfkPCOcB2kWU
|
|
||||||
vRXsdptVm3jPeU5TtDSoAHUAu9nfvB+KcbWTlCOXqpJ7RzhXlQqrUugakJZkNo4e
|
|
||||||
0YUAAAFu0uQ60gAABAMARjBEAiBBpH5m7ntGKFTOFgSLcFXRDg66xJqerMy0gOHj
|
|
||||||
4TIBYAIgfFABPNy6P61hjiOWwjq73lvoEdAyh18GeFHIp0BgsWEwDQYJKoZIhvcN
|
|
||||||
AQELBQADggEBAInaSEqteyQA1zUKiXVqgffhHKZsUq9UnMows6X+UoFPoby9xqm6
|
|
||||||
IaY/77zaFZYwXJlP/SvrlbgTLHAdir3y38uhAlfPX4iRuwggOpFFF5hqDckzCm91
|
|
||||||
ocGnoG6sUY5mOqKu2vIcZkUQDe+K5gOxI6ME/4YwzWCIcTmBPQ6NQmqiFLPoQty1
|
|
||||||
gdbGCcLQNFCuNq4n5OK2NmBjcbtyT4gglat7C4+KV8RkEubZ+MkXzyDkpEXjjzsK
|
|
||||||
7iuNB0hRgyyhGzHrlZ/l0OLoT0Cb4I5PzzRSseFEyPKCC1WSF7aE9rFfUqhpqSAT
|
|
||||||
7NV7SEijYyFFtuZfz9RGglcqnRlAfgTy+tU=
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
98
spec/fixtures/ssl/letsencrypt_expired_x3.pem
vendored
98
spec/fixtures/ssl/letsencrypt_expired_x3.pem
vendored
|
@ -1,98 +0,0 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIGJDCCBQygAwIBAgISBOSAE/WwQGsTbDJI1vDL9+eKMA0GCSqGSIb3DQEBCwUA
|
|
||||||
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
|
|
||||||
EwJSMzAeFw0yMTEwMDEyMjIxMTlaFw0yMTEyMzAyMjIxMThaMBkxFzAVBgNVBAMT
|
|
||||||
DndlYmRpb3hpZGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
|
|
||||||
wf/TpE5AjzoLXMFQ+WHle7Dn5rlEe0bPee2JU386cZmMYnGFS5DR251FerSX28U4
|
|
||||||
pqk2yS8oefHGi2PS6h8/MWxr+Zy/6hk3WkgwdIK3uPiUcfCdPV/btXDd4YqikEDm
|
|
||||||
BoOE4fQlqKQwtLOnhEZu9y8FQoxxoQ+7DndHrDixDoMbpUloxpqUZwziQnH4QHXE
|
|
||||||
32rQhq25+NUK/lVFGKOFnmZ2s/yUildKafqulHrLHOhumKMOEivzlFDZbtqP+RKt
|
|
||||||
nsrJ3i9O+nSQz6j5dv3Du6eaResrtK7tT1MFDNhcg2cgjNW64VLXQdFXYXE1OYsw
|
|
||||||
yAuXUnHNzWFhinyf80qeh2046YR21dlG8voIDQH4fGG5GmWLyu7glsWYVwQQ36VA
|
|
||||||
TTxPmAoaqUTl8A7cnlJpAo+BJ00mS/9DwJ7pkgGC7dYOhJzWlI7lPqzEfmJ+o8pj
|
|
||||||
CJlLIuqsn0vcCZQlmqCFMxK4asn+puLLnMjRLHIYEJKDNyPGHQEr2e5t4GUYZKaN
|
|
||||||
MEpXMwJd97tUamUKWeBPNIND/kOuqexe+okbOTRp34VAsK5oCpawEJckoNkK+sv0
|
|
||||||
OrSWFOdfLBHv66p9qsrz8LQXxmN5JUBUe51SBSUo1Ul4/vGYdhuKd/8KcLw9/Al+
|
|
||||||
HJN2hAeo3v+2fVey4hgGna7XNe8e3+E+OEQb4zpQDLkCAwEAAaOCAkswggJHMA4G
|
|
||||||
A1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYD
|
|
||||||
VR0TAQH/BAIwADAdBgNVHQ4EFgQU4PbvqCKatjx6GZMXy7v9GwykZq4wHwYDVR0j
|
|
||||||
BBgwFoAUFC6zF7dYVsuuUAlA5h+vnYsUwsYwVQYIKwYBBQUHAQEESTBHMCEGCCsG
|
|
||||||
AQUFBzABhhVodHRwOi8vcjMuby5sZW5jci5vcmcwIgYIKwYBBQUHMAKGFmh0dHA6
|
|
||||||
Ly9yMy5pLmxlbmNyLm9yZy8wGQYDVR0RBBIwEIIOd2ViZGlveGlkZS5jb20wTAYD
|
|
||||||
VR0gBEUwQzAIBgZngQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcCARYa
|
|
||||||
aHR0cDovL2Nwcy5sZXRzZW5jcnlwdC5vcmcwggEGBgorBgEEAdZ5AgQCBIH3BIH0
|
|
||||||
APIAdwBc3EOS/uarRUSxXprUVuYQN/vV+kfcoXOUsl7m9scOygAAAXw+KYGHAAAE
|
|
||||||
AwBIMEYCIQCqD6jMtHrGlE02Qh1FzFd4+qYzJTrChHmHBFIncPGQKAIhALeYk0Vf
|
|
||||||
/Lw2tX2beVlKN4/h1o8srNJv+06xkr1N6XmiAHcAfT7y+I//iFVoJMLAyp5SiXkr
|
|
||||||
xQ54CX8uapdomX4i8NcAAAF8PimBogAABAMASDBGAiEA0h883FFj1dSYKGym9+Wa
|
|
||||||
XgJRj526X7YlkhkZ5J1TjioCIQDyjMPrbo5liVi/e5b8gfDw5Fd9WNiTu1W1LKKu
|
|
||||||
UpE/qTANBgkqhkiG9w0BAQsFAAOCAQEAcx10nqp1kh2awwoqwf7Jo8Gycqx2bA2O
|
|
||||||
E2rveQ/BK9UhwvrNeEpE9SG6liMsYJKxGar0vbbBHvxzuMU00bhGjXFtUT5XuQ8q
|
|
||||||
FcU0OdycyZj8fjZmUNsJr82l8HvfJ50jfxFORTgj8Ln5MWVUFlbl0nD+06l28sDc
|
|
||||||
V+r/B4394fkoMsKXtiTA4/ZeOD1tHNsdxQ7sNQtEfqCG0wFCYHK3rs7XTZ1K0F3c
|
|
||||||
M051JShko1UKP/k5blrendOwVRwLtq+9pavGnJBeqNIVgugTER/IHlp4427WyhdY
|
|
||||||
KYjKoytW+XQyWqxU/Mh/O4rxkD8cZaE+FdZpP67VZ185AuZMbn+LcQ==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw
|
|
||||||
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
|
||||||
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw
|
|
||||||
WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
|
|
||||||
RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
|
|
||||||
AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP
|
|
||||||
R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx
|
|
||||||
sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm
|
|
||||||
NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg
|
|
||||||
Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG
|
|
||||||
/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC
|
|
||||||
AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB
|
|
||||||
Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA
|
|
||||||
FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw
|
|
||||||
AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw
|
|
||||||
Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB
|
|
||||||
gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W
|
|
||||||
PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl
|
|
||||||
ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz
|
|
||||||
CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm
|
|
||||||
lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4
|
|
||||||
avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2
|
|
||||||
yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O
|
|
||||||
yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids
|
|
||||||
hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+
|
|
||||||
HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv
|
|
||||||
MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX
|
|
||||||
nLRbwHOoq7hHwg==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/
|
|
||||||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
|
||||||
DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow
|
|
||||||
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
|
||||||
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB
|
|
||||||
AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC
|
|
||||||
ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL
|
|
||||||
wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D
|
|
||||||
LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK
|
|
||||||
4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5
|
|
||||||
bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y
|
|
||||||
sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ
|
|
||||||
Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4
|
|
||||||
FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc
|
|
||||||
SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql
|
|
||||||
PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND
|
|
||||||
TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
|
|
||||||
SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1
|
|
||||||
c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx
|
|
||||||
+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB
|
|
||||||
ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu
|
|
||||||
b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E
|
|
||||||
U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu
|
|
||||||
MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC
|
|
||||||
5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW
|
|
||||||
9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG
|
|
||||||
WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O
|
|
||||||
he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC
|
|
||||||
Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5
|
|
||||||
-----END CERTIFICATE-----
|
|
|
@ -102,6 +102,18 @@ describe('gl_emoji', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('escapes gl-emoji name', async () => {
|
||||||
|
const glEmojiElement = markupToDomElement(
|
||||||
|
"<gl-emoji data-name='"x="y" onload="alert(document.location.href)"' data-unicode-version='x'>abc</gl-emoji>",
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitForPromises();
|
||||||
|
|
||||||
|
expect(glEmojiElement.outerHTML).toBe(
|
||||||
|
'<gl-emoji data-name=""x="y" onload="alert(document.location.href)"" data-unicode-version="x"><img class="emoji" title=":"x="y" onload="alert(document.location.href)":" alt=":"x="y" onload="alert(document.location.href)":" src="/-/emojis/1/grey_question.png" width="20" height="20" align="absmiddle"></gl-emoji>',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('Adds sprite CSS if emojis are not supported', async () => {
|
it('Adds sprite CSS if emojis are not supported', async () => {
|
||||||
const testPath = '/test-path.css';
|
const testPath = '/test-path.css';
|
||||||
jest.spyOn(EmojiUnicodeSupport, 'default').mockReturnValue(false);
|
jest.spyOn(EmojiUnicodeSupport, 'default').mockReturnValue(false);
|
||||||
|
|
|
@ -560,13 +560,13 @@ RSpec.describe Resolvers::IssuesResolver do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'finds a specific issue with iid', :request_store do
|
it 'finds a specific issue with iid', :request_store do
|
||||||
result = batch_sync(max_queries: 5) { resolve_issues(iid: issue1.iid).to_a }
|
result = batch_sync(max_queries: 6) { resolve_issues(iid: issue1.iid).to_a }
|
||||||
|
|
||||||
expect(result).to contain_exactly(issue1)
|
expect(result).to contain_exactly(issue1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'batches queries that only include IIDs', :request_store do
|
it 'batches queries that only include IIDs', :request_store do
|
||||||
result = batch_sync(max_queries: 5) do
|
result = batch_sync(max_queries: 6) do
|
||||||
[issue1, issue2]
|
[issue1, issue2]
|
||||||
.map { |issue| resolve_issues(iid: issue.iid.to_s) }
|
.map { |issue| resolve_issues(iid: issue.iid.to_s) }
|
||||||
.flat_map(&:to_a)
|
.flat_map(&:to_a)
|
||||||
|
@ -576,7 +576,7 @@ RSpec.describe Resolvers::IssuesResolver do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'finds a specific issue with iids', :request_store do
|
it 'finds a specific issue with iids', :request_store do
|
||||||
result = batch_sync(max_queries: 5) do
|
result = batch_sync(max_queries: 6) do
|
||||||
resolve_issues(iids: [issue1.iid]).to_a
|
resolve_issues(iids: [issue1.iid]).to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -531,24 +531,6 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def stub_domain_resolv(domain, ip, port = 80, &block)
|
|
||||||
address = instance_double(Addrinfo,
|
|
||||||
ip_address: ip,
|
|
||||||
ipv4_private?: true,
|
|
||||||
ipv6_linklocal?: false,
|
|
||||||
ipv4_loopback?: false,
|
|
||||||
ipv6_loopback?: false,
|
|
||||||
ipv4?: false,
|
|
||||||
ip_port: port
|
|
||||||
)
|
|
||||||
allow(Addrinfo).to receive(:getaddrinfo).with(domain, port, any_args).and_return([address])
|
|
||||||
allow(address).to receive(:ipv6_v4mapped?).and_return(false)
|
|
||||||
|
|
||||||
yield
|
|
||||||
|
|
||||||
allow(Addrinfo).to receive(:getaddrinfo).and_call_original
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when enforce_user is' do
|
context 'when enforce_user is' do
|
||||||
|
@ -611,6 +593,44 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
|
||||||
|
|
||||||
expect(described_class).to be_blocked_url('http://foobar.x')
|
expect(described_class).to be_blocked_url('http://foobar.x')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when gitlab is running on a non-default port' do
|
||||||
|
let(:gitlab_port) { 3000 }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_config(gitlab: { protocol: 'http', host: 'gitlab.local', port: gitlab_port })
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns true for url targeting the wrong port' do
|
||||||
|
stub_domain_resolv('gitlab.local', '127.0.0.1') do
|
||||||
|
expect(described_class).to be_blocked_url("http://gitlab.local/foo")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not block url on gitlab port' do
|
||||||
|
stub_domain_resolv('gitlab.local', '127.0.0.1') do
|
||||||
|
expect(described_class).not_to be_blocked_url("http://gitlab.local:#{gitlab_port}/foo")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def stub_domain_resolv(domain, ip, port = 80, &block)
|
||||||
|
address = instance_double(Addrinfo,
|
||||||
|
ip_address: ip,
|
||||||
|
ipv4_private?: true,
|
||||||
|
ipv6_linklocal?: false,
|
||||||
|
ipv4_loopback?: false,
|
||||||
|
ipv6_loopback?: false,
|
||||||
|
ipv4?: false,
|
||||||
|
ip_port: port
|
||||||
|
)
|
||||||
|
allow(Addrinfo).to receive(:getaddrinfo).with(domain, port, any_args).and_return([address])
|
||||||
|
allow(address).to receive(:ipv6_v4mapped?).and_return(false)
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
allow(Addrinfo).to receive(:getaddrinfo).and_call_original
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#validate_hostname' do
|
describe '#validate_hostname' do
|
||||||
|
|
|
@ -201,7 +201,7 @@ RSpec.describe Clusters::Platforms::Kubernetes do
|
||||||
it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::KubeClient) }
|
it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::KubeClient) }
|
||||||
|
|
||||||
context 'ca_pem is a single certificate' do
|
context 'ca_pem is a single certificate' do
|
||||||
let(:ca_pem) { File.read(Rails.root.join('spec/fixtures/clusters/ca_certificate.pem')) }
|
let(:ca_pem) { File.read(Rails.root.join('spec/fixtures/clusters/root_certificate.pem')) }
|
||||||
let(:kubernetes) do
|
let(:kubernetes) do
|
||||||
build(:cluster_platform_kubernetes,
|
build(:cluster_platform_kubernetes,
|
||||||
:configured,
|
:configured,
|
||||||
|
@ -228,21 +228,22 @@ RSpec.describe Clusters::Platforms::Kubernetes do
|
||||||
ca_pem: cert_chain)
|
ca_pem: cert_chain)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
where(:fixture_path) do
|
||||||
|
%w[
|
||||||
|
spec/fixtures/clusters/root_certificate.pem
|
||||||
|
spec/fixtures/clusters/intermediate_certificate.pem
|
||||||
|
spec/fixtures/clusters/leaf_certificate.pem
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
with_them do
|
||||||
it 'includes chain of certificates' do
|
it 'includes chain of certificates' do
|
||||||
cert1_file = File.read(Rails.root.join('spec/fixtures/clusters/root_certificate.pem'))
|
|
||||||
cert1 = OpenSSL::X509::Certificate.new(cert1_file)
|
|
||||||
|
|
||||||
cert2_file = File.read(Rails.root.join('spec/fixtures/clusters/intermediate_certificate.pem'))
|
|
||||||
cert2 = OpenSSL::X509::Certificate.new(cert2_file)
|
|
||||||
|
|
||||||
cert3_file = File.read(Rails.root.join('spec/fixtures/clusters/ca_certificate.pem'))
|
|
||||||
cert3 = OpenSSL::X509::Certificate.new(cert3_file)
|
|
||||||
|
|
||||||
cert_store = kubernetes.kubeclient.kubeclient_options[:ssl_options][:cert_store]
|
cert_store = kubernetes.kubeclient.kubeclient_options[:ssl_options][:cert_store]
|
||||||
|
cert_file = File.read(Rails.root.join(fixture_path))
|
||||||
|
certificate = OpenSSL::X509::Certificate.new(cert_file)
|
||||||
|
|
||||||
expect(cert_store.verify(cert1)).to be true
|
expect(cert_store.verify(certificate)).to be true
|
||||||
expect(cert_store.verify(cert2)).to be true
|
end
|
||||||
expect(cert_store.verify(cert3)).to be true
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,6 +16,8 @@ RSpec.describe Integrations::ChatMessage::AlertMessage do
|
||||||
}.merge(Gitlab::DataBuilder::Alert.build(alert))
|
}.merge(Gitlab::DataBuilder::Alert.build(alert))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it_behaves_like Integrations::ChatMessage
|
||||||
|
|
||||||
describe '#message' do
|
describe '#message' do
|
||||||
it 'returns the correct message' do
|
it 'returns the correct message' do
|
||||||
expect(subject.message).to eq("Alert firing in #{args[:project_name]}")
|
expect(subject.message).to eq("Alert firing in #{args[:project_name]}")
|
||||||
|
|
|
@ -31,4 +31,22 @@ RSpec.describe Integrations::ChatMessage::BaseMessage do
|
||||||
it { is_expected.to eq('Check this out https://gitlab-domain.com/uploads/Screenshot1.png. And this https://gitlab-domain.com/uploads/Screenshot2.png') }
|
it { is_expected.to eq('Check this out https://gitlab-domain.com/uploads/Screenshot1.png. And this https://gitlab-domain.com/uploads/Screenshot2.png') }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#strip_markup' do
|
||||||
|
using RSpec::Parameterized::TableSyntax
|
||||||
|
|
||||||
|
where(:input, :output) do
|
||||||
|
nil | nil
|
||||||
|
'' | ''
|
||||||
|
'[label](url)' | 'label(url)'
|
||||||
|
'<url|label>' | 'urllabel'
|
||||||
|
'<a href="url">label</a>' | 'a href="url"label/a'
|
||||||
|
end
|
||||||
|
|
||||||
|
with_them do
|
||||||
|
it 'returns the expected output' do
|
||||||
|
expect(base_message.send(:strip_markup, input)).to eq(output)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,83 +3,79 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe Integrations::ChatMessage::DeploymentMessage do
|
RSpec.describe Integrations::ChatMessage::DeploymentMessage do
|
||||||
|
subject { described_class.new(args) }
|
||||||
|
|
||||||
|
let_it_be(:user) { create(:user, name: 'John Smith', username: 'smith') }
|
||||||
|
let_it_be(:namespace) { create(:namespace, name: 'myspace') }
|
||||||
|
let_it_be(:project) { create(:project, :repository, namespace: namespace, name: 'myproject') }
|
||||||
|
let_it_be(:commit) { project.commit('HEAD') }
|
||||||
|
let_it_be(:ci_build) { create(:ci_build, project: project) }
|
||||||
|
let_it_be(:environment) { create(:environment, name: 'myenvironment', project: project) }
|
||||||
|
let_it_be(:deployment) { create(:deployment, status: :success, deployable: ci_build, environment: environment, project: project, user: user, sha: commit.sha) }
|
||||||
|
|
||||||
|
let(:args) do
|
||||||
|
Gitlab::DataBuilder::Deployment.build(deployment, Time.current)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like Integrations::ChatMessage
|
||||||
|
|
||||||
describe '#pretext' do
|
describe '#pretext' do
|
||||||
it 'returns a message with the data returned by the deployment data builder' do
|
it 'returns a message with the data returned by the deployment data builder' do
|
||||||
environment = create(:environment, name: "myenvironment")
|
expect(subject.pretext).to eq("Deploy to myenvironment succeeded")
|
||||||
project = create(:project, :repository)
|
|
||||||
commit = project.commit('HEAD')
|
|
||||||
deployment = create(:deployment, status: :success, environment: environment, project: project, sha: commit.sha)
|
|
||||||
data = Gitlab::DataBuilder::Deployment.build(deployment, Time.current)
|
|
||||||
|
|
||||||
message = described_class.new(data)
|
|
||||||
|
|
||||||
expect(message.pretext).to eq("Deploy to myenvironment succeeded")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a message for a successful deployment' do
|
it 'returns a message for a successful deployment' do
|
||||||
data = {
|
args.merge!(
|
||||||
status: 'success',
|
status: 'success',
|
||||||
environment: 'production'
|
environment: 'production'
|
||||||
}
|
)
|
||||||
|
|
||||||
message = described_class.new(data)
|
expect(subject.pretext).to eq('Deploy to production succeeded')
|
||||||
|
|
||||||
expect(message.pretext).to eq('Deploy to production succeeded')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a message for a failed deployment' do
|
it 'returns a message for a failed deployment' do
|
||||||
data = {
|
args.merge!(
|
||||||
status: 'failed',
|
status: 'failed',
|
||||||
environment: 'production'
|
environment: 'production'
|
||||||
}
|
)
|
||||||
|
|
||||||
message = described_class.new(data)
|
expect(subject.pretext).to eq('Deploy to production failed')
|
||||||
|
|
||||||
expect(message.pretext).to eq('Deploy to production failed')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a message for a canceled deployment' do
|
it 'returns a message for a canceled deployment' do
|
||||||
data = {
|
args.merge!(
|
||||||
status: 'canceled',
|
status: 'canceled',
|
||||||
environment: 'production'
|
environment: 'production'
|
||||||
}
|
)
|
||||||
|
|
||||||
message = described_class.new(data)
|
expect(subject.pretext).to eq('Deploy to production canceled')
|
||||||
|
|
||||||
expect(message.pretext).to eq('Deploy to production canceled')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a message for a deployment to another environment' do
|
it 'returns a message for a deployment to another environment' do
|
||||||
data = {
|
args.merge!(
|
||||||
status: 'success',
|
status: 'success',
|
||||||
environment: 'staging'
|
environment: 'staging'
|
||||||
}
|
)
|
||||||
|
|
||||||
message = described_class.new(data)
|
expect(subject.pretext).to eq('Deploy to staging succeeded')
|
||||||
|
|
||||||
expect(message.pretext).to eq('Deploy to staging succeeded')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a message for a deployment with any other status' do
|
it 'returns a message for a deployment with any other status' do
|
||||||
data = {
|
args.merge!(
|
||||||
status: 'unknown',
|
status: 'unknown',
|
||||||
environment: 'staging'
|
environment: 'staging'
|
||||||
}
|
)
|
||||||
|
|
||||||
message = described_class.new(data)
|
expect(subject.pretext).to eq('Deploy to staging unknown')
|
||||||
|
|
||||||
expect(message.pretext).to eq('Deploy to staging unknown')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a message for a running deployment' do
|
it 'returns a message for a running deployment' do
|
||||||
data = {
|
args.merge!(
|
||||||
status: 'running',
|
status: 'running',
|
||||||
environment: 'production'
|
environment: 'production'
|
||||||
}
|
)
|
||||||
|
|
||||||
message = described_class.new(data)
|
expect(subject.pretext).to eq('Starting deploy to production')
|
||||||
|
|
||||||
expect(message.pretext).to eq('Starting deploy to production')
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -108,21 +104,11 @@ RSpec.describe Integrations::ChatMessage::DeploymentMessage do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns attachments with the data returned by the deployment data builder' do
|
it 'returns attachments with the data returned by the deployment data builder' do
|
||||||
user = create(:user, name: "John Smith", username: "smith")
|
|
||||||
namespace = create(:namespace, name: "myspace")
|
|
||||||
project = create(:project, :repository, namespace: namespace, name: "myproject")
|
|
||||||
commit = project.commit('HEAD')
|
|
||||||
environment = create(:environment, name: "myenvironment", project: project)
|
|
||||||
ci_build = create(:ci_build, project: project)
|
|
||||||
deployment = create(:deployment, :success, deployable: ci_build, environment: environment, project: project, user: user, sha: commit.sha)
|
|
||||||
job_url = Gitlab::Routing.url_helpers.project_job_url(project, ci_build)
|
job_url = Gitlab::Routing.url_helpers.project_job_url(project, ci_build)
|
||||||
commit_url = Gitlab::UrlBuilder.build(deployment.commit)
|
commit_url = Gitlab::UrlBuilder.build(deployment.commit)
|
||||||
user_url = Gitlab::Routing.url_helpers.user_url(user)
|
user_url = Gitlab::Routing.url_helpers.user_url(user)
|
||||||
data = Gitlab::DataBuilder::Deployment.build(deployment, Time.current)
|
|
||||||
|
|
||||||
message = described_class.new(data)
|
expect(subject.attachments).to eq([{
|
||||||
|
|
||||||
expect(message.attachments).to eq([{
|
|
||||||
text: "[myspace/myproject](#{project.web_url}) with job [##{ci_build.id}](#{job_url}) by [John Smith (smith)](#{user_url})\n[#{deployment.short_sha}](#{commit_url}): #{commit.title}",
|
text: "[myspace/myproject](#{project.web_url}) with job [##{ci_build.id}](#{job_url}) by [John Smith (smith)](#{user_url})\n[#{deployment.short_sha}](#{commit_url}): #{commit.title}",
|
||||||
color: "good"
|
color: "good"
|
||||||
}])
|
}])
|
||||||
|
|
|
@ -28,6 +28,8 @@ RSpec.describe Integrations::ChatMessage::IssueMessage do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it_behaves_like Integrations::ChatMessage
|
||||||
|
|
||||||
context 'without markdown' do
|
context 'without markdown' do
|
||||||
let(:color) { '#C95823' }
|
let(:color) { '#C95823' }
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,8 @@ RSpec.describe Integrations::ChatMessage::MergeMessage do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it_behaves_like Integrations::ChatMessage
|
||||||
|
|
||||||
context 'without markdown' do
|
context 'without markdown' do
|
||||||
let(:color) { '#345' }
|
let(:color) { '#345' }
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,10 @@ RSpec.describe Integrations::ChatMessage::NoteMessage do
|
||||||
name: 'project_name',
|
name: 'project_name',
|
||||||
url: 'http://somewhere.com'
|
url: 'http://somewhere.com'
|
||||||
},
|
},
|
||||||
|
commit: {
|
||||||
|
id: '5f163b2b95e6f53cbd428f5f0b103702a52b9a23',
|
||||||
|
message: "Added a commit message\ndetails\n123\n"
|
||||||
|
},
|
||||||
object_attributes: {
|
object_attributes: {
|
||||||
id: 10,
|
id: 10,
|
||||||
note: 'comment on a commit',
|
note: 'comment on a commit',
|
||||||
|
@ -28,16 +32,9 @@ RSpec.describe Integrations::ChatMessage::NoteMessage do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'commit notes' do
|
it_behaves_like Integrations::ChatMessage
|
||||||
before do
|
|
||||||
args[:object_attributes][:note] = 'comment on a commit'
|
|
||||||
args[:object_attributes][:noteable_type] = 'Commit'
|
|
||||||
args[:commit] = {
|
|
||||||
id: '5f163b2b95e6f53cbd428f5f0b103702a52b9a23',
|
|
||||||
message: "Added a commit message\ndetails\n123\n"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
context 'commit notes' do
|
||||||
context 'without markdown' do
|
context 'without markdown' do
|
||||||
it 'returns a message regarding notes on commits' do
|
it 'returns a message regarding notes on commits' do
|
||||||
expect(subject.pretext).to eq("Test User (test.user) <http://url.com|commented on " \
|
expect(subject.pretext).to eq("Test User (test.user) <http://url.com|commented on " \
|
||||||
|
|
|
@ -40,6 +40,8 @@ RSpec.describe Integrations::ChatMessage::PipelineMessage do
|
||||||
|
|
||||||
let(:has_yaml_errors) { false }
|
let(:has_yaml_errors) { false }
|
||||||
|
|
||||||
|
it_behaves_like Integrations::ChatMessage
|
||||||
|
|
||||||
before do
|
before do
|
||||||
test_commit = double("A test commit", committer: args[:user], title: "A test commit message")
|
test_commit = double("A test commit", committer: args[:user], title: "A test commit message")
|
||||||
test_project = double("A test project", commit_by: test_commit, name: args[:project][:name], web_url: args[:project][:web_url])
|
test_project = double("A test project", commit_by: test_commit, name: args[:project][:name], web_url: args[:project][:web_url])
|
||||||
|
|
|
@ -19,6 +19,8 @@ RSpec.describe Integrations::ChatMessage::PushMessage do
|
||||||
|
|
||||||
let(:color) { '#345' }
|
let(:color) { '#345' }
|
||||||
|
|
||||||
|
it_behaves_like Integrations::ChatMessage
|
||||||
|
|
||||||
context 'push' do
|
context 'push' do
|
||||||
before do
|
before do
|
||||||
args[:commits] = [
|
args[:commits] = [
|
||||||
|
|
|
@ -33,6 +33,8 @@ RSpec.describe Integrations::ChatMessage::WikiPageMessage do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it_behaves_like Integrations::ChatMessage
|
||||||
|
|
||||||
context 'without markdown' do
|
context 'without markdown' do
|
||||||
describe '#pretext' do
|
describe '#pretext' do
|
||||||
context 'when :action == "create"' do
|
context 'when :action == "create"' do
|
||||||
|
|
|
@ -287,19 +287,6 @@ RSpec.describe PagesDomain do
|
||||||
|
|
||||||
it { is_expected.to be_truthy }
|
it { is_expected.to be_truthy }
|
||||||
end
|
end
|
||||||
|
|
||||||
# The LetsEncrypt DST Root CA X3 expired on 2021-09-30, but the
|
|
||||||
# cross-sign in ISRG Root X1 enables it to function provided a chain
|
|
||||||
# of trust can be established with the system store. See:
|
|
||||||
#
|
|
||||||
# 1. https://community.letsencrypt.org/t/production-chain-changes/150739
|
|
||||||
# 2. https://letsencrypt.org/2020/12/21/extending-android-compatibility.html
|
|
||||||
# 3. https://www.openssl.org/blog/blog/2021/09/13/LetsEncryptRootCertExpire/
|
|
||||||
context 'with a LetsEncrypt bundle with an expired DST Root CA X3' do
|
|
||||||
let(:domain) { build(:pages_domain, :letsencrypt_expired_x3_root) }
|
|
||||||
|
|
||||||
it { is_expected.to be_truthy }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#expired?' do
|
describe '#expired?' do
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe API::DependencyProxy, api: true do
|
RSpec.describe API::DependencyProxy, api: true do
|
||||||
include ExclusiveLeaseHelpers
|
|
||||||
|
|
||||||
let_it_be(:user) { create(:user) }
|
let_it_be(:user) { create(:user) }
|
||||||
let_it_be(:blob) { create(:dependency_proxy_blob )}
|
let_it_be(:blob) { create(:dependency_proxy_blob )}
|
||||||
let_it_be(:group, reload: true) { blob.group }
|
let_it_be(:group, reload: true) { blob.group }
|
||||||
|
@ -20,11 +18,8 @@ RSpec.describe API::DependencyProxy, api: true do
|
||||||
|
|
||||||
shared_examples 'responding to purge requests' do
|
shared_examples 'responding to purge requests' do
|
||||||
context 'with feature available and enabled' do
|
context 'with feature available and enabled' do
|
||||||
let_it_be(:lease_key) { "dependency_proxy:delete_group_blobs:#{group.id}" }
|
|
||||||
|
|
||||||
context 'an admin user' do
|
context 'an admin user' do
|
||||||
it 'deletes the blobs and returns no content' do
|
it 'deletes the blobs and returns no content' do
|
||||||
stub_exclusive_lease(lease_key, timeout: 1.hour)
|
|
||||||
expect(PurgeDependencyProxyCacheWorker).to receive(:perform_async)
|
expect(PurgeDependencyProxyCacheWorker).to receive(:perform_async)
|
||||||
|
|
||||||
subject
|
subject
|
||||||
|
@ -32,23 +27,6 @@ RSpec.describe API::DependencyProxy, api: true do
|
||||||
expect(response).to have_gitlab_http_status(:accepted)
|
expect(response).to have_gitlab_http_status(:accepted)
|
||||||
expect(response.body).to eq('202')
|
expect(response.body).to eq('202')
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'called multiple times in one hour', :clean_gitlab_redis_shared_state do
|
|
||||||
it 'returns 409 with an error message' do
|
|
||||||
stub_exclusive_lease_taken(lease_key, timeout: 1.hour)
|
|
||||||
|
|
||||||
subject
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:conflict)
|
|
||||||
expect(response.body).to include('This request has already been made.')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'executes service only for the first time' do
|
|
||||||
expect(PurgeDependencyProxyCacheWorker).to receive(:perform_async).once
|
|
||||||
|
|
||||||
2.times { subject }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'a non-admin' do
|
context 'a non-admin' do
|
||||||
|
|
|
@ -253,7 +253,7 @@ RSpec.describe 'GraphQL' do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with token authentication' do
|
context 'with token authentication' do
|
||||||
let(:token) { create(:personal_access_token) }
|
let(:token) { create(:personal_access_token, user: user) }
|
||||||
|
|
||||||
it 'authenticates users with a PAT' do
|
it 'authenticates users with a PAT' do
|
||||||
stub_authentication_activity_metrics(debug: false)
|
stub_authentication_activity_metrics(debug: false)
|
||||||
|
@ -276,6 +276,32 @@ RSpec.describe 'GraphQL' do
|
||||||
expect(graphql_errors).to include({ 'message' => /API not accessible/ })
|
expect(graphql_errors).to include({ 'message' => /API not accessible/ })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when user with expired password' do
|
||||||
|
let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) }
|
||||||
|
|
||||||
|
it 'does not authenticate user' do
|
||||||
|
post_graphql(query, headers: { 'PRIVATE-TOKEN' => token.token })
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
||||||
|
expect(graphql_data['echo']).to eq('nil says: Hello world')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when password expiration is not applicable' do
|
||||||
|
context 'when ldap user' do
|
||||||
|
let_it_be(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) }
|
||||||
|
|
||||||
|
it 'authenticates user' do
|
||||||
|
post_graphql(query, headers: { 'PRIVATE-TOKEN' => token.token })
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
||||||
|
expect(graphql_data['echo']).to eq("\"#{token.user.username}\" says: Hello world")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when the personal access token has no api scope' do
|
context 'when the personal access token has no api scope' do
|
||||||
it 'does not log the user in' do
|
it 'does not log the user in' do
|
||||||
token.update!(scopes: [:read_user])
|
token.update!(scopes: [:read_user])
|
||||||
|
|
|
@ -3115,6 +3115,29 @@ RSpec.describe API::Projects do
|
||||||
expect(json_response['message']).to eq('404 Project Not Found')
|
expect(json_response['message']).to eq('404 Project Not Found')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'returns 404 if the source project members cannot be viewed by the requester' do
|
||||||
|
private_project = create(:project, :private)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
post api("/projects/#{project.id}/import_project_members/#{private_project.id}", user)
|
||||||
|
end.not_to change { project.members.count }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:not_found)
|
||||||
|
expect(json_response['message']).to eq('404 Project Not Found')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 403 if the source project members cannot be administered by the requester' do
|
||||||
|
project.add_maintainer(user2)
|
||||||
|
project2.add_developer(user2)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
post api("/projects/#{project.id}/import_project_members/#{project2.id}", user2)
|
||||||
|
end.not_to change { project.members.count }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:forbidden)
|
||||||
|
expect(json_response['message']).to eq('403 Forbidden - Project')
|
||||||
|
end
|
||||||
|
|
||||||
it 'returns 422 if the import failed for valid projects' do
|
it 'returns 422 if the import failed for valid projects' do
|
||||||
allow_next_instance_of(::ProjectTeam) do |project_team|
|
allow_next_instance_of(::ProjectTeam) do |project_team|
|
||||||
allow(project_team).to receive(:import).and_return(false)
|
allow(project_team).to receive(:import).and_return(false)
|
||||||
|
|
11
spec/requests/dashboard/projects_controller_spec.rb
Normal file
11
spec/requests/dashboard/projects_controller_spec.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe Dashboard::ProjectsController do
|
||||||
|
context 'token authentication' do
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: false do
|
||||||
|
let(:url) { dashboard_projects_url(:atom) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
15
spec/requests/dashboard_controller_spec.rb
Normal file
15
spec/requests/dashboard_controller_spec.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe DashboardController do
|
||||||
|
context 'token authentication' do
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'issues atom', public_resource: false do
|
||||||
|
let(:url) { issues_dashboard_url(:atom, assignee_username: user.username) }
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'issues_calendar ics', public_resource: false do
|
||||||
|
let(:url) { issues_dashboard_url(:ics, assignee_username: user.username) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
51
spec/requests/groups_controller_spec.rb
Normal file
51
spec/requests/groups_controller_spec.rb
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe GroupsController do
|
||||||
|
context 'token authentication' do
|
||||||
|
context 'when public group' do
|
||||||
|
let_it_be(:public_group) { create(:group, :public) }
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: true do
|
||||||
|
let(:url) { group_path(public_group, format: :atom) }
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'issues atom', public_resource: true do
|
||||||
|
let(:url) { issues_group_path(public_group, format: :atom) }
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'issues_calendar ics', public_resource: true do
|
||||||
|
let(:url) { issues_group_calendar_url(public_group, format: :ics) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when private project' do
|
||||||
|
let_it_be(:private_group) { create(:group, :private) }
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: false, ignore_metrics: true do
|
||||||
|
let(:url) { group_path(private_group, format: :atom) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
private_group.add_maintainer(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'issues atom', public_resource: false, ignore_metrics: true do
|
||||||
|
let(:url) { issues_group_path(private_group, format: :atom) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
private_group.add_maintainer(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'issues_calendar ics', public_resource: false, ignore_metrics: true do
|
||||||
|
let(:url) { issues_group_calendar_url(private_group, format: :ics) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
private_group.add_maintainer(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
27
spec/requests/projects/commits_controller_spec.rb
Normal file
27
spec/requests/projects/commits_controller_spec.rb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe Projects::CommitsController do
|
||||||
|
context 'token authentication' do
|
||||||
|
context 'when public project' do
|
||||||
|
let_it_be(:public_project) { create(:project, :repository, :public) }
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: true do
|
||||||
|
let(:url) { project_commits_url(public_project, public_project.default_branch, format: :atom) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when private project' do
|
||||||
|
let_it_be(:private_project) { create(:project, :repository, :private) }
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: false, ignore_metrics: true do
|
||||||
|
let(:url) { project_commits_url(private_project, private_project.default_branch, format: :atom) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
private_project.add_maintainer(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -8,11 +8,11 @@ RSpec.describe Projects::IssuesController do
|
||||||
let_it_be(:project) { issue.project }
|
let_it_be(:project) { issue.project }
|
||||||
let_it_be(:user) { issue.author }
|
let_it_be(:user) { issue.author }
|
||||||
|
|
||||||
|
describe 'GET #discussions' do
|
||||||
before do
|
before do
|
||||||
login_as(user)
|
login_as(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET #discussions' do
|
|
||||||
let_it_be(:discussion) { create(:discussion_note_on_issue, noteable: issue, project: issue.project) }
|
let_it_be(:discussion) { create(:discussion_note_on_issue, noteable: issue, project: issue.project) }
|
||||||
let_it_be(:discussion_reply) { create(:discussion_note_on_issue, noteable: issue, project: issue.project, in_reply_to: discussion) }
|
let_it_be(:discussion_reply) { create(:discussion_note_on_issue, noteable: issue, project: issue.project, in_reply_to: discussion) }
|
||||||
let_it_be(:state_event) { create(:resource_state_event, issue: issue) }
|
let_it_be(:state_event) { create(:resource_state_event, issue: issue) }
|
||||||
|
@ -68,4 +68,38 @@ RSpec.describe Projects::IssuesController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'token authentication' do
|
||||||
|
context 'when public project' do
|
||||||
|
let_it_be(:public_project) { create(:project, :public) }
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: true do
|
||||||
|
let(:url) { project_issues_url(public_project, format: :atom) }
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'calendar ics', public_resource: true do
|
||||||
|
let(:url) { project_issues_url(public_project, format: :ics) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when private project' do
|
||||||
|
let_it_be(:private_project) { create(:project, :private) }
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: false, ignore_metrics: true do
|
||||||
|
let(:url) { project_issues_url(private_project, format: :atom) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
private_project.add_maintainer(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'calendar ics', public_resource: false, ignore_metrics: true do
|
||||||
|
let(:url) { project_issues_url(private_project, format: :ics) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
private_project.add_maintainer(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
27
spec/requests/projects/merge_requests_controller_spec.rb
Normal file
27
spec/requests/projects/merge_requests_controller_spec.rb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe Projects::MergeRequestsController do
|
||||||
|
context 'token authentication' do
|
||||||
|
context 'when public project' do
|
||||||
|
let_it_be(:public_project) { create(:project, :public) }
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: true do
|
||||||
|
let(:url) { project_merge_requests_url(public_project, format: :atom) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when private project' do
|
||||||
|
let_it_be(:private_project) { create(:project, :private) }
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: false, ignore_metrics: true do
|
||||||
|
let(:url) { project_merge_requests_url(private_project, format: :atom) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
private_project.add_maintainer(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
27
spec/requests/projects/tags_controller_spec.rb
Normal file
27
spec/requests/projects/tags_controller_spec.rb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe Projects::TagsController do
|
||||||
|
context 'token authentication' do
|
||||||
|
context 'when public project' do
|
||||||
|
let_it_be(:public_project) { create(:project, :repository, :public) }
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: true do
|
||||||
|
let(:url) { project_tags_url(public_project, format: :atom) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when private project' do
|
||||||
|
let_it_be(:private_project) { create(:project, :repository, :private) }
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'index atom', public_resource: false, ignore_metrics: true do
|
||||||
|
let(:url) { project_tags_url(private_project, format: :atom) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
private_project.add_maintainer(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
27
spec/requests/projects_controller_spec.rb
Normal file
27
spec/requests/projects_controller_spec.rb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe ProjectsController do
|
||||||
|
context 'token authentication' do
|
||||||
|
context 'when public project' do
|
||||||
|
let_it_be(:public_project) { create(:project, :public) }
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: true do
|
||||||
|
let(:url) { project_url(public_project, format: :atom) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when private project' do
|
||||||
|
let_it_be(:private_project) { create(:project, :private) }
|
||||||
|
|
||||||
|
it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: false, ignore_metrics: true do
|
||||||
|
let(:url) { project_url(private_project, format: :atom) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
private_project.add_maintainer(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -792,9 +792,9 @@ RSpec.describe UsersController do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'token authentication' do
|
context 'token authentication' do
|
||||||
let(:url) { user_url(user.username, format: :atom) }
|
it_behaves_like 'authenticates sessionless user for the request spec', 'show atom', public_resource: true do
|
||||||
|
let(:url) { user_url(user, format: :atom) }
|
||||||
it_behaves_like 'authenticates sessionless user for the request spec', public: true
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_moved_message(redirect_route, user)
|
def user_moved_message(redirect_route, user)
|
||||||
|
|
|
@ -11,10 +11,8 @@ RSpec.describe Packages::Npm::CreatePackageService do
|
||||||
Gitlab::Json.parse(fixture_file('packages/npm/payload.json')
|
Gitlab::Json.parse(fixture_file('packages/npm/payload.json')
|
||||||
.gsub('@root/npm-test', package_name)
|
.gsub('@root/npm-test', package_name)
|
||||||
.gsub('1.0.1', version)).with_indifferent_access
|
.gsub('1.0.1', version)).with_indifferent_access
|
||||||
.merge!(override)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:override) { {} }
|
|
||||||
let(:package_name) { "@#{namespace.path}/my-app" }
|
let(:package_name) { "@#{namespace.path}/my-app" }
|
||||||
let(:version_data) { params.dig('versions', '1.0.1') }
|
let(:version_data) { params.dig('versions', '1.0.1') }
|
||||||
|
|
||||||
|
@ -127,13 +125,53 @@ RSpec.describe Packages::Npm::CreatePackageService do
|
||||||
it { expect(subject[:message]).to be 'Package already exists.' }
|
it { expect(subject[:message]).to be 'Package already exists.' }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'file size above maximum limit' do
|
describe 'max file size validation' do
|
||||||
before do
|
let(:max_file_size) { 5.bytes}
|
||||||
params['_attachments']["#{package_name}-#{version}.tgz"]['length'] = project.actual_limits.npm_max_file_size + 1
|
|
||||||
|
shared_examples_for 'max file size validation failure' do
|
||||||
|
it 'returns a 400 error', :aggregate_failures do
|
||||||
|
expect(subject[:http_status]).to eq 400
|
||||||
|
expect(subject[:message]).to be 'File is too large.'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect(subject[:http_status]).to eq 400 }
|
before do
|
||||||
it { expect(subject[:message]).to be 'File is too large.' }
|
project.actual_limits.update!(npm_max_file_size: max_file_size)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when max file size is exceeded' do
|
||||||
|
# NOTE: The base64 encoded package data in the fixture file is the "hello\n" string, whose byte size is 6.
|
||||||
|
it_behaves_like 'max file size validation failure'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when file size is faked by setting the attachment length param to a lower size' do
|
||||||
|
let(:params) { super().deep_merge!( { _attachments: { "#{package_name}-#{version}.tgz" => { data: encoded_package_data, length: 1 } } }) }
|
||||||
|
|
||||||
|
# TODO (technical debt): Extract the package size calculation outside the service and add separate specs for it.
|
||||||
|
# Right now we have several contexts here to test the calculation's different scenarios.
|
||||||
|
context "when encoded package data is not padded" do
|
||||||
|
# 'Hello!' (size = 6 bytes) => 'SGVsbG8h'
|
||||||
|
let(:encoded_package_data) { 'SGVsbG8h' }
|
||||||
|
|
||||||
|
it_behaves_like 'max file size validation failure'
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when encoded package data is padded with '='" do
|
||||||
|
let(:max_file_size) { 4.bytes}
|
||||||
|
# 'Hello' (size = 5 bytes) => 'SGVsbG8='
|
||||||
|
let(:encoded_package_data) { 'SGVsbG8=' }
|
||||||
|
|
||||||
|
it_behaves_like 'max file size validation failure'
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when encoded package data is padded with '=='" do
|
||||||
|
let(:max_file_size) { 3.bytes}
|
||||||
|
# 'Hell' (size = 4 bytes) => 'SGVsbA=='
|
||||||
|
let(:encoded_package_data) { 'SGVsbA==' }
|
||||||
|
|
||||||
|
it_behaves_like 'max file size validation failure'
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
[
|
[
|
||||||
|
@ -152,7 +190,7 @@ RSpec.describe Packages::Npm::CreatePackageService do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with empty versions' do
|
context 'with empty versions' do
|
||||||
let(:override) { { versions: {} } }
|
let(:params) { super().merge!({ versions: {} } ) }
|
||||||
|
|
||||||
it { expect(subject[:http_status]).to eq 400 }
|
it { expect(subject[:http_status]).to eq 400 }
|
||||||
it { expect(subject[:message]).to eq 'Version is empty.' }
|
it { expect(subject[:message]).to eq 'Version is empty.' }
|
||||||
|
|
|
@ -1,112 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# This controller shared examples will be migrated to
|
|
||||||
# spec/support/shared_examples/requests/sessionless_auth_request_shared_examples.rb
|
|
||||||
# See also https://gitlab.com/groups/gitlab-org/-/epics/5076
|
|
||||||
|
|
||||||
RSpec.shared_examples 'authenticates sessionless user' do |path, format, params|
|
|
||||||
params ||= {}
|
|
||||||
|
|
||||||
before do
|
|
||||||
stub_authentication_activity_metrics(debug: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:user) { create(:user) }
|
|
||||||
let(:personal_access_token) { create(:personal_access_token, user: user) }
|
|
||||||
let(:default_params) { { format: format }.merge(params.except(:public) || {}) }
|
|
||||||
|
|
||||||
context "when the 'personal_access_token' param is populated with the personal access token" do
|
|
||||||
it 'logs the user in' do
|
|
||||||
expect(authentication_metrics)
|
|
||||||
.to increment(:user_authenticated_counter)
|
|
||||||
.and increment(:user_session_override_counter)
|
|
||||||
.and increment(:user_sessionless_authentication_counter)
|
|
||||||
|
|
||||||
get path, params: default_params.merge(private_token: personal_access_token.token)
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
|
||||||
expect(controller.current_user).to eq(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not log the user in if page is public', if: params[:public] do
|
|
||||||
get path, params: default_params
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
|
||||||
expect(controller.current_user).to be_nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when the personal access token has no api scope', unless: params[:public] do
|
|
||||||
it 'does not log the user in' do
|
|
||||||
# Several instances of where these specs are shared route the request
|
|
||||||
# through ApplicationController#route_not_found which does not involve
|
|
||||||
# the usual auth code from Devise, so does not increment the
|
|
||||||
# :user_unauthenticated_counter
|
|
||||||
#
|
|
||||||
unless params[:ignore_incrementing]
|
|
||||||
expect(authentication_metrics)
|
|
||||||
.to increment(:user_unauthenticated_counter)
|
|
||||||
end
|
|
||||||
|
|
||||||
personal_access_token.update!(scopes: [:read_user])
|
|
||||||
|
|
||||||
get path, params: default_params.merge(private_token: personal_access_token.token)
|
|
||||||
|
|
||||||
expect(response).not_to have_gitlab_http_status(:ok)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the 'PERSONAL_ACCESS_TOKEN' header is populated with the personal access token" do
|
|
||||||
it 'logs the user in' do
|
|
||||||
expect(authentication_metrics)
|
|
||||||
.to increment(:user_authenticated_counter)
|
|
||||||
.and increment(:user_session_override_counter)
|
|
||||||
.and increment(:user_sessionless_authentication_counter)
|
|
||||||
|
|
||||||
@request.headers['PRIVATE-TOKEN'] = personal_access_token.token
|
|
||||||
get path, params: default_params
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the 'feed_token' param is populated with the feed token", if: format == :rss do
|
|
||||||
it "logs the user in" do
|
|
||||||
expect(authentication_metrics)
|
|
||||||
.to increment(:user_authenticated_counter)
|
|
||||||
.and increment(:user_session_override_counter)
|
|
||||||
.and increment(:user_sessionless_authentication_counter)
|
|
||||||
|
|
||||||
get path, params: default_params.merge(feed_token: user.feed_token)
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the 'feed_token' param is populated with an invalid feed token", if: format == :rss, unless: params[:public] do
|
|
||||||
it "logs the user" do
|
|
||||||
expect(authentication_metrics)
|
|
||||||
.to increment(:user_unauthenticated_counter)
|
|
||||||
|
|
||||||
get path, params: default_params.merge(feed_token: 'token')
|
|
||||||
|
|
||||||
expect(response).not_to have_gitlab_http_status(:ok)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't log the user in otherwise", unless: params[:public] do
|
|
||||||
# Several instances of where these specs are shared route the request
|
|
||||||
# through ApplicationController#route_not_found which does not involve
|
|
||||||
# the usual auth code from Devise, so does not increment the
|
|
||||||
# :user_unauthenticated_counter
|
|
||||||
#
|
|
||||||
unless params[:ignore_incrementing]
|
|
||||||
expect(authentication_metrics)
|
|
||||||
.to increment(:user_unauthenticated_counter)
|
|
||||||
end
|
|
||||||
|
|
||||||
get path, params: default_params.merge(private_token: 'token')
|
|
||||||
|
|
||||||
expect(response).not_to have_gitlab_http_status(:ok)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.shared_examples Integrations::ChatMessage do
|
||||||
|
context 'when input contains link markup' do
|
||||||
|
let(:evil_input) { '[Markdown](http://evil.com) <a href="http://evil.com">HTML</a> <http://evil.com|Slack>' }
|
||||||
|
|
||||||
|
# Attributes returned from #activity and #attributes which should be sanitized.
|
||||||
|
let(:sanitized_attributes) do
|
||||||
|
%i[title subtitle text fallback author_name]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Attributes passed to #initialize which can contain user input.
|
||||||
|
before do
|
||||||
|
args.deep_merge!(
|
||||||
|
project_name: evil_input,
|
||||||
|
user_name: evil_input,
|
||||||
|
user_full_name: evil_input,
|
||||||
|
commit_title: evil_input,
|
||||||
|
environment: evil_input,
|
||||||
|
project: {
|
||||||
|
name: evil_input
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
name: evil_input,
|
||||||
|
username: evil_input
|
||||||
|
},
|
||||||
|
object_attributes: {
|
||||||
|
title: evil_input
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# NOTE: The `include` matcher is used here so the RSpec error messages will tell us
|
||||||
|
# which method or attribute is failing, even though it makes the spec a bit less readable.
|
||||||
|
it 'strips all link markup characters', :aggregate_failures do
|
||||||
|
expect(subject).not_to have_attributes(
|
||||||
|
pretext: include(evil_input),
|
||||||
|
summary: include(evil_input)
|
||||||
|
)
|
||||||
|
|
||||||
|
begin
|
||||||
|
sanitized_attributes.each do |attribute|
|
||||||
|
expect(subject.activity).not_to include(attribute => include(evil_input))
|
||||||
|
end
|
||||||
|
rescue NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
sanitized_attributes.each do |attribute|
|
||||||
|
expect(subject.attachments).not_to include(include(attribute => include(evil_input)))
|
||||||
|
end
|
||||||
|
rescue NotImplementedError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,84 +1,160 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.shared_examples 'authenticates sessionless user for the request spec' do |params|
|
RSpec.shared_examples 'authenticates sessionless user for the request spec' do |name, public_resource:, ignore_metrics: false, params: {}|
|
||||||
params ||= {}
|
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_authentication_activity_metrics(debug: false)
|
stub_authentication_activity_metrics(debug: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:user) { create(:user) }
|
let_it_be(:user) { create(:user) }
|
||||||
let(:personal_access_token) { create(:personal_access_token, user: user) }
|
let(:personal_access_token) { create(:personal_access_token, user: user) }
|
||||||
let(:default_params) { params.except(:public) || {} }
|
|
||||||
|
|
||||||
context "when the 'personal_access_token' param is populated with the personal access token" do
|
shared_examples 'authenticates user and returns response with ok status' do
|
||||||
it 'logs the user in' do
|
it 'authenticates user and returns response with ok status' do
|
||||||
expect(authentication_metrics)
|
expect(authentication_metrics)
|
||||||
.to increment(:user_authenticated_counter)
|
.to increment(:user_authenticated_counter)
|
||||||
.and increment(:user_session_override_counter)
|
.and increment(:user_session_override_counter)
|
||||||
.and increment(:user_sessionless_authentication_counter)
|
.and increment(:user_sessionless_authentication_counter)
|
||||||
|
|
||||||
get url, params: default_params.merge(private_token: personal_access_token.token)
|
subject
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
|
||||||
expect(controller.current_user).to eq(user)
|
expect(controller.current_user).to eq(user)
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not log the user in if page is public', if: params[:public] do
|
shared_examples 'does not authenticate user and returns response with ok status' do
|
||||||
get url, params: default_params
|
it 'does not authenticate user and returns response with ok status' do
|
||||||
|
subject
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
|
||||||
expect(controller.current_user).to be_nil
|
expect(controller.current_user).to be_nil
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when the personal access token has no api scope', unless: params[:public] do
|
|
||||||
it 'does not log the user in' do
|
|
||||||
# Several instances of where these specs are shared route the request
|
|
||||||
# through ApplicationController#route_not_found which does not involve
|
|
||||||
# the usual auth code from Devise, so does not increment the
|
|
||||||
# :user_unauthenticated_counter
|
|
||||||
#
|
|
||||||
unless params[:ignore_incrementing]
|
|
||||||
expect(authentication_metrics)
|
|
||||||
.to increment(:user_unauthenticated_counter)
|
|
||||||
end
|
|
||||||
|
|
||||||
personal_access_token.update!(scopes: [:read_user])
|
|
||||||
|
|
||||||
get url, params: default_params.merge(private_token: personal_access_token.token)
|
|
||||||
|
|
||||||
expect(response).not_to have_gitlab_http_status(:ok)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the 'PERSONAL_ACCESS_TOKEN' header is populated with the personal access token" do
|
|
||||||
it 'logs the user in' do
|
|
||||||
expect(authentication_metrics)
|
|
||||||
.to increment(:user_authenticated_counter)
|
|
||||||
.and increment(:user_session_override_counter)
|
|
||||||
.and increment(:user_sessionless_authentication_counter)
|
|
||||||
|
|
||||||
headers = { 'PRIVATE-TOKEN': personal_access_token.token }
|
|
||||||
get url, params: default_params, headers: headers
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't log the user in otherwise", unless: params[:public] do
|
shared_examples 'does not return response with ok status' do
|
||||||
|
it 'does not return response with ok status' do
|
||||||
# Several instances of where these specs are shared route the request
|
# Several instances of where these specs are shared route the request
|
||||||
# through ApplicationController#route_not_found which does not involve
|
# through ApplicationController#route_not_found which does not involve
|
||||||
# the usual auth code from Devise, so does not increment the
|
# the usual auth code from Devise, so does not increment the
|
||||||
# :user_unauthenticated_counter
|
# :user_unauthenticated_counter
|
||||||
#
|
unless ignore_metrics
|
||||||
unless params[:ignore_incrementing]
|
|
||||||
expect(authentication_metrics)
|
expect(authentication_metrics)
|
||||||
.to increment(:user_unauthenticated_counter)
|
.to increment(:user_unauthenticated_counter)
|
||||||
end
|
end
|
||||||
|
|
||||||
get url, params: default_params.merge(private_token: 'token')
|
subject
|
||||||
|
|
||||||
expect(response).not_to have_gitlab_http_status(:ok)
|
expect(response).not_to have_gitlab_http_status(:ok)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
shared_examples 'using valid token' do
|
||||||
|
context 'when resource is private', unless: public_resource do
|
||||||
|
include_examples 'authenticates user and returns response with ok status'
|
||||||
|
|
||||||
|
context 'when user with expired password' do
|
||||||
|
let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) }
|
||||||
|
|
||||||
|
include_examples 'does not return response with ok status'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when password expiration is not applicable' do
|
||||||
|
context 'when ldap user' do
|
||||||
|
let_it_be(:user) { create(:omniauth_user, provider: 'ldap', password_expires_at: 2.minutes.ago) }
|
||||||
|
|
||||||
|
include_examples 'authenticates user and returns response with ok status'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when resource is public', if: public_resource do
|
||||||
|
include_examples 'authenticates user and returns response with ok status'
|
||||||
|
|
||||||
|
context 'when user with expired password' do
|
||||||
|
let_it_be(:user) { create(:user, password_expires_at: 2.minutes.ago) }
|
||||||
|
|
||||||
|
include_examples 'does not authenticate user and returns response with ok status'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'using invalid token' do
|
||||||
|
context 'when resource is private', unless: public_resource do
|
||||||
|
include_examples 'does not return response with ok status'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when resource is public', if: public_resource do
|
||||||
|
include_examples 'does not authenticate user and returns response with ok status'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'personal access token has no api scope' do
|
||||||
|
context 'when the personal access token has no api scope' do
|
||||||
|
before do
|
||||||
|
personal_access_token.update!(scopes: [:read_user])
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when resource is private', unless: public_resource do
|
||||||
|
include_examples 'does not return response with ok status'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when resource is public', if: public_resource do
|
||||||
|
include_examples 'does not authenticate user and returns response with ok status'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe name do
|
||||||
|
context "when the 'private_token' param is populated with the personal access token" do
|
||||||
|
context 'when valid token' do
|
||||||
|
subject { get url, params: params.merge(private_token: personal_access_token.token) }
|
||||||
|
|
||||||
|
include_examples 'using valid token'
|
||||||
|
|
||||||
|
include_examples 'personal access token has no api scope'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when invalid token' do
|
||||||
|
subject { get url, params: params.merge(private_token: 'invalid token') }
|
||||||
|
|
||||||
|
include_examples 'using invalid token'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the 'PRIVATE-TOKEN' header is populated with the personal access token" do
|
||||||
|
context 'when valid token' do
|
||||||
|
subject do
|
||||||
|
headers = { 'PRIVATE-TOKEN': personal_access_token.token }
|
||||||
|
get url, params: params, headers: headers
|
||||||
|
end
|
||||||
|
|
||||||
|
include_examples 'using valid token'
|
||||||
|
|
||||||
|
include_examples 'personal access token has no api scope'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when invalid token' do
|
||||||
|
subject do
|
||||||
|
headers = { 'PRIVATE-TOKEN': 'invalid token' }
|
||||||
|
get url, params: params, headers: headers
|
||||||
|
end
|
||||||
|
|
||||||
|
include_examples 'using invalid token'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the 'feed_token' param is populated with the feed token" do
|
||||||
|
context 'when valid token' do
|
||||||
|
subject { get url, params: params.merge(feed_token: user.feed_token) }
|
||||||
|
|
||||||
|
include_examples 'using valid token'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when invalid token' do
|
||||||
|
subject { get url, params: params.merge(feed_token: 'invalid token') }
|
||||||
|
|
||||||
|
include_examples 'using invalid token'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe DependencyProxy::CleanupDependencyProxyWorker do
|
||||||
|
describe '#perform' do
|
||||||
|
subject { described_class.new.perform }
|
||||||
|
|
||||||
|
context 'when there are records to be deleted' do
|
||||||
|
it_behaves_like 'an idempotent worker' do
|
||||||
|
it 'queues the cleanup jobs', :aggregate_failures do
|
||||||
|
create(:dependency_proxy_blob, :expired)
|
||||||
|
create(:dependency_proxy_manifest, :expired)
|
||||||
|
|
||||||
|
expect(DependencyProxy::CleanupBlobWorker).to receive(:perform_with_capacity).twice
|
||||||
|
expect(DependencyProxy::CleanupManifestWorker).to receive(:perform_with_capacity).twice
|
||||||
|
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are not records to be deleted' do
|
||||||
|
it_behaves_like 'an idempotent worker' do
|
||||||
|
it 'does not queue the cleanup jobs', :aggregate_failures do
|
||||||
|
expect(DependencyProxy::CleanupBlobWorker).not_to receive(:perform_with_capacity)
|
||||||
|
expect(DependencyProxy::CleanupManifestWorker).not_to receive(:perform_with_capacity)
|
||||||
|
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -17,13 +17,6 @@ RSpec.describe DependencyProxy::ImageTtlGroupPolicyWorker do
|
||||||
let_it_be_with_reload(:new_blob) { create(:dependency_proxy_blob, group: group) }
|
let_it_be_with_reload(:new_blob) { create(:dependency_proxy_blob, group: group) }
|
||||||
let_it_be_with_reload(:new_manifest) { create(:dependency_proxy_manifest, group: group) }
|
let_it_be_with_reload(:new_manifest) { create(:dependency_proxy_manifest, group: group) }
|
||||||
|
|
||||||
it 'calls the limited capacity workers', :aggregate_failures do
|
|
||||||
expect(DependencyProxy::CleanupBlobWorker).to receive(:perform_with_capacity)
|
|
||||||
expect(DependencyProxy::CleanupManifestWorker).to receive(:perform_with_capacity)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'updates the old images to expired' do
|
it 'updates the old images to expired' do
|
||||||
expect { subject }
|
expect { subject }
|
||||||
.to change { old_blob.reload.status }.from('default').to('expired')
|
.to change { old_blob.reload.status }.from('default').to('expired')
|
||||||
|
@ -33,15 +26,6 @@ RSpec.describe DependencyProxy::ImageTtlGroupPolicyWorker do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when there are no images to expire' do
|
|
||||||
it 'does not do anything', :aggregate_failures do
|
|
||||||
expect(DependencyProxy::CleanupBlobWorker).not_to receive(:perform_with_capacity)
|
|
||||||
expect(DependencyProxy::CleanupManifestWorker).not_to receive(:perform_with_capacity)
|
|
||||||
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'counts logging' do
|
context 'counts logging' do
|
||||||
let_it_be(:expired_blob) { create(:dependency_proxy_blob, :expired, group: group) }
|
let_it_be(:expired_blob) { create(:dependency_proxy_blob, :expired, group: group) }
|
||||||
let_it_be(:expired_blob2) { create(:dependency_proxy_blob, :expired, group: group) }
|
let_it_be(:expired_blob2) { create(:dependency_proxy_blob, :expired, group: group) }
|
||||||
|
|
|
@ -4,18 +4,18 @@ require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe PurgeDependencyProxyCacheWorker do
|
RSpec.describe PurgeDependencyProxyCacheWorker do
|
||||||
let_it_be(:user) { create(:admin) }
|
let_it_be(:user) { create(:admin) }
|
||||||
let_it_be(:blob) { create(:dependency_proxy_blob )}
|
let_it_be_with_refind(:blob) { create(:dependency_proxy_blob )}
|
||||||
let_it_be(:group, reload: true) { blob.group }
|
let_it_be_with_reload(:group) { blob.group }
|
||||||
let_it_be(:manifest) { create(:dependency_proxy_manifest, group: group )}
|
let_it_be_with_refind(:manifest) { create(:dependency_proxy_manifest, group: group )}
|
||||||
let_it_be(:group_id) { group.id }
|
let_it_be(:group_id) { group.id }
|
||||||
|
|
||||||
subject { described_class.new.perform(user.id, group_id) }
|
subject { described_class.new.perform(user.id, group_id) }
|
||||||
|
|
||||||
describe '#perform' do
|
describe '#perform' do
|
||||||
shared_examples 'not removing blobs and manifests' do
|
shared_examples 'not expiring blobs and manifests' do
|
||||||
it 'does not remove blobs and manifests', :aggregate_failures do
|
it 'does not expire blobs and manifests', :aggregate_failures do
|
||||||
expect { subject }.not_to change { group.dependency_proxy_blobs.size }
|
expect { subject }.not_to change { blob.status }
|
||||||
expect { subject }.not_to change { group.dependency_proxy_manifests.size }
|
expect { subject }.not_to change { manifest.status }
|
||||||
expect(subject).to be_nil
|
expect(subject).to be_nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -25,39 +25,36 @@ RSpec.describe PurgeDependencyProxyCacheWorker do
|
||||||
include_examples 'an idempotent worker' do
|
include_examples 'an idempotent worker' do
|
||||||
let(:job_args) { [user.id, group_id] }
|
let(:job_args) { [user.id, group_id] }
|
||||||
|
|
||||||
it 'deletes the blobs and returns ok', :aggregate_failures do
|
it 'expires the blobs and returns ok', :aggregate_failures do
|
||||||
expect(group.dependency_proxy_blobs.size).to eq(1)
|
|
||||||
expect(group.dependency_proxy_manifests.size).to eq(1)
|
|
||||||
|
|
||||||
subject
|
subject
|
||||||
|
|
||||||
expect(group.dependency_proxy_blobs.size).to eq(0)
|
expect(blob).to be_expired
|
||||||
expect(group.dependency_proxy_manifests.size).to eq(0)
|
expect(manifest).to be_expired
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when admin mode is disabled' do
|
context 'when admin mode is disabled' do
|
||||||
it_behaves_like 'not removing blobs and manifests'
|
it_behaves_like 'not expiring blobs and manifests'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'a non-admin user' do
|
context 'a non-admin user' do
|
||||||
let(:user) { create(:user) }
|
let(:user) { create(:user) }
|
||||||
|
|
||||||
it_behaves_like 'not removing blobs and manifests'
|
it_behaves_like 'not expiring blobs and manifests'
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'an invalid user id' do
|
context 'an invalid user id' do
|
||||||
let(:user) { double('User', id: 99999 ) }
|
let(:user) { double('User', id: 99999 ) }
|
||||||
|
|
||||||
it_behaves_like 'not removing blobs and manifests'
|
it_behaves_like 'not expiring blobs and manifests'
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'an invalid group' do
|
context 'an invalid group' do
|
||||||
let(:group_id) { 99999 }
|
let(:group_id) { 99999 }
|
||||||
|
|
||||||
it_behaves_like 'not removing blobs and manifests'
|
it_behaves_like 'not expiring blobs and manifests'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue