Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
959e358268
commit
d221c6bf4a
|
|
@ -108,6 +108,9 @@
|
|||
.if-merge-request-labels-run-observability-e2e-tests-current-branch: &if-merge-request-labels-run-observability-e2e-tests-current-branch
|
||||
if: '($CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_EVENT_TYPE != "merge_train") && $CI_MERGE_REQUEST_LABELS =~ /pipeline:run-observability-e2e-tests-current-branch/'
|
||||
|
||||
.if-merge-request-labels-skip-observability-e2e-tests: &if-merge-request-labels-skip-observability-e2e-tests
|
||||
if: '($CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_EVENT_TYPE != "merge_train") && $CI_MERGE_REQUEST_LABELS =~ /pipeline:skip-observability-e2e-tests/'
|
||||
|
||||
.if-merge-request-labels-run-single-db: &if-merge-request-labels-run-single-db
|
||||
if: '($CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_EVENT_TYPE != "merge_train") && $CI_MERGE_REQUEST_LABELS =~ /pipeline:run-single-db/'
|
||||
|
||||
|
|
@ -3397,6 +3400,8 @@
|
|||
rules:
|
||||
- <<: *if-observability-skip-e2e-jobs
|
||||
when: never
|
||||
- <<: *if-merge-request-labels-skip-observability-e2e-tests
|
||||
when: never
|
||||
- <<: *if-merge-request-labels-run-observability-e2e-tests-current-branch
|
||||
when: never
|
||||
- <<: *if-merge-request-labels-run-observability-e2e-tests-main-branch
|
||||
|
|
@ -3407,6 +3412,8 @@
|
|||
rules:
|
||||
- <<: *if-observability-skip-e2e-jobs
|
||||
when: never
|
||||
- <<: *if-merge-request-labels-skip-observability-e2e-tests
|
||||
when: never
|
||||
- <<: *if-merge-request-labels-run-observability-e2e-tests-main-branch
|
||||
when: never
|
||||
- <<: *if-merge-request-labels-run-observability-e2e-tests-current-branch
|
||||
|
|
|
|||
2
Gemfile
2
Gemfile
|
|
@ -256,7 +256,7 @@ gem 'creole', '~> 0.5.0', feature_category: :markdown
|
|||
gem 'wikicloth', '0.8.1', feature_category: :markdown
|
||||
gem 'asciidoctor', '~> 2.0.18', feature_category: :markdown
|
||||
gem 'asciidoctor-include-ext', '~> 0.4.0', require: false, feature_category: :markdown
|
||||
gem 'asciidoctor-plantuml', '~> 0.0.16', feature_category: :markdown
|
||||
gem 'asciidoctor-plantuml', '~> 0.1.1', feature_category: :markdown
|
||||
gem 'asciidoctor-kroki', '~> 0.10.0', require: false, feature_category: :markdown
|
||||
gem 'rouge', '~> 4.4.0', feature_category: :shared
|
||||
gem 'truncato', '~> 0.7.12', feature_category: :team_planning
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
{"name":"asciidoctor","version":"2.0.23","platform":"ruby","checksum":"52208807f237dfa0ca29882f8b13d60b820496116ad191cf197ca56f2b7fddf3"},
|
||||
{"name":"asciidoctor-include-ext","version":"0.4.0","platform":"ruby","checksum":"406adb9d2fbfc25536609ca13b787ed704dc06a4e49d6709b83f3bad578f7878"},
|
||||
{"name":"asciidoctor-kroki","version":"0.10.0","platform":"ruby","checksum":"8e4225d88f120e2e7b5d3f5ddb67c5e69496d7344a16c57db5036ac900123062"},
|
||||
{"name":"asciidoctor-plantuml","version":"0.0.16","platform":"ruby","checksum":"407e47cd1186ded5ccc75f0c812e5524c26c571d542247c5132abb8f47bd1793"},
|
||||
{"name":"asciidoctor-plantuml","version":"0.1.1","platform":"ruby","checksum":"2bfa1a79349aa3fff611cdc54ade674e91423e38c20a21b24d8ca59006a0b0ae"},
|
||||
{"name":"ast","version":"2.4.2","platform":"ruby","checksum":"1e280232e6a33754cde542bc5ef85520b74db2aac73ec14acef453784447cc12"},
|
||||
{"name":"async","version":"2.12.1","platform":"ruby","checksum":"146fb3acf6d05ad40abb9ae659dd3b574067a3420fe7d6d5d6a3cf5413de3ea5"},
|
||||
{"name":"atlassian-jwt","version":"0.2.1","platform":"ruby","checksum":"2fd2d87418773f2e140c038cb22e049069708aff2bd0a423a7e1740574e97823"},
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ GEM
|
|||
asciidoctor (>= 1.5.6, < 3.0.0)
|
||||
asciidoctor-kroki (0.10.0)
|
||||
asciidoctor (~> 2.0)
|
||||
asciidoctor-plantuml (0.0.16)
|
||||
asciidoctor-plantuml (0.1.1)
|
||||
asciidoctor (>= 2.0.17, < 3.0.0)
|
||||
ast (2.4.2)
|
||||
async (2.12.1)
|
||||
|
|
@ -1985,7 +1985,7 @@ DEPENDENCIES
|
|||
asciidoctor (~> 2.0.18)
|
||||
asciidoctor-include-ext (~> 0.4.0)
|
||||
asciidoctor-kroki (~> 0.10.0)
|
||||
asciidoctor-plantuml (~> 0.0.16)
|
||||
asciidoctor-plantuml (~> 0.1.1)
|
||||
async (~> 2.12.1)
|
||||
atlassian-jwt (~> 0.2.1)
|
||||
attr_encrypted (~> 3.2.4)!
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
{"name":"asciidoctor","version":"2.0.23","platform":"ruby","checksum":"52208807f237dfa0ca29882f8b13d60b820496116ad191cf197ca56f2b7fddf3"},
|
||||
{"name":"asciidoctor-include-ext","version":"0.4.0","platform":"ruby","checksum":"406adb9d2fbfc25536609ca13b787ed704dc06a4e49d6709b83f3bad578f7878"},
|
||||
{"name":"asciidoctor-kroki","version":"0.10.0","platform":"ruby","checksum":"8e4225d88f120e2e7b5d3f5ddb67c5e69496d7344a16c57db5036ac900123062"},
|
||||
{"name":"asciidoctor-plantuml","version":"0.0.16","platform":"ruby","checksum":"407e47cd1186ded5ccc75f0c812e5524c26c571d542247c5132abb8f47bd1793"},
|
||||
{"name":"asciidoctor-plantuml","version":"0.1.1","platform":"ruby","checksum":"2bfa1a79349aa3fff611cdc54ade674e91423e38c20a21b24d8ca59006a0b0ae"},
|
||||
{"name":"ast","version":"2.4.2","platform":"ruby","checksum":"1e280232e6a33754cde542bc5ef85520b74db2aac73ec14acef453784447cc12"},
|
||||
{"name":"async","version":"2.12.1","platform":"ruby","checksum":"146fb3acf6d05ad40abb9ae659dd3b574067a3420fe7d6d5d6a3cf5413de3ea5"},
|
||||
{"name":"atlassian-jwt","version":"0.2.1","platform":"ruby","checksum":"2fd2d87418773f2e140c038cb22e049069708aff2bd0a423a7e1740574e97823"},
|
||||
|
|
|
|||
|
|
@ -340,7 +340,7 @@ GEM
|
|||
asciidoctor (>= 1.5.6, < 3.0.0)
|
||||
asciidoctor-kroki (0.10.0)
|
||||
asciidoctor (~> 2.0)
|
||||
asciidoctor-plantuml (0.0.16)
|
||||
asciidoctor-plantuml (0.1.1)
|
||||
asciidoctor (>= 2.0.17, < 3.0.0)
|
||||
ast (2.4.2)
|
||||
async (2.12.1)
|
||||
|
|
@ -2012,7 +2012,7 @@ DEPENDENCIES
|
|||
asciidoctor (~> 2.0.18)
|
||||
asciidoctor-include-ext (~> 0.4.0)
|
||||
asciidoctor-kroki (~> 0.10.0)
|
||||
asciidoctor-plantuml (~> 0.0.16)
|
||||
asciidoctor-plantuml (~> 0.1.1)
|
||||
async (~> 2.12.1)
|
||||
atlassian-jwt (~> 0.2.1)
|
||||
attr_encrypted (~> 3.2.4)!
|
||||
|
|
|
|||
|
|
@ -80,14 +80,23 @@ class Ability
|
|||
|
||||
before_check(policy, ability.to_sym, user, subject, opts)
|
||||
|
||||
case opts[:scope]
|
||||
when :user
|
||||
DeclarativePolicy.user_scope { policy.allowed?(ability) }
|
||||
when :subject
|
||||
DeclarativePolicy.subject_scope { policy.allowed?(ability) }
|
||||
result = case opts[:scope]
|
||||
when :user
|
||||
DeclarativePolicy.user_scope { policy.allowed?(ability) }
|
||||
when :subject
|
||||
DeclarativePolicy.subject_scope { policy.allowed?(ability) }
|
||||
else
|
||||
policy.allowed?(ability)
|
||||
end
|
||||
|
||||
identity = ::Gitlab::Auth::Identity.fabricate(user)
|
||||
|
||||
if identity.present? && identity.composite?
|
||||
result && allowed?(identity.scoped_user, ability, subject, **opts)
|
||||
else
|
||||
policy.allowed?(ability)
|
||||
result
|
||||
end
|
||||
|
||||
ensure
|
||||
# TODO: replace with runner invalidation:
|
||||
# See: https://gitlab.com/gitlab-org/declarative-policy/-/merge_requests/24
|
||||
|
|
|
|||
|
|
@ -886,10 +886,6 @@ class ApplicationSetting < ApplicationRecord
|
|||
validate_url(parsed_kroki_url, :kroki_url, KROKI_URL_ERROR_MESSAGE)
|
||||
end
|
||||
|
||||
def kroki_url_absolute?
|
||||
parsed_kroki_url&.absolute?
|
||||
end
|
||||
|
||||
def sourcegraph_url_is_com?
|
||||
!!(sourcegraph_url =~ %r{\Ahttps://(www\.)?sourcegraph\.com})
|
||||
end
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ class NotificationRecipient
|
|||
end
|
||||
|
||||
def notifiable?
|
||||
return false if has_composite_identity?
|
||||
return false unless has_access?
|
||||
return false if emails_disabled?
|
||||
return false if own_activity?
|
||||
|
|
@ -121,6 +122,10 @@ class NotificationRecipient
|
|||
end
|
||||
end
|
||||
|
||||
def has_composite_identity?
|
||||
user.has_composite_identity?
|
||||
end
|
||||
|
||||
def excluded_watcher_action?
|
||||
return false unless notification_level == :watch
|
||||
return false unless @custom_action
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class OauthAccessToken < Doorkeeper::AccessToken
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
belongs_to :resource_owner, class_name: 'User'
|
||||
belongs_to :application, class_name: 'Doorkeeper::Application'
|
||||
belongs_to :organization, class_name: 'Organizations::Organization'
|
||||
|
|
@ -12,6 +14,9 @@ class OauthAccessToken < Doorkeeper::AccessToken
|
|||
scope :latest_per_application, -> { select('distinct on(application_id) *').order(application_id: :desc, created_at: :desc) }
|
||||
scope :preload_application, -> { preload(:application) }
|
||||
|
||||
# user scope format is: `user:$USER_ID`
|
||||
SCOPED_USER_REGEX = /user:(\d+)(?:\s|$)/
|
||||
|
||||
def scopes=(value)
|
||||
if value.is_a?(Array)
|
||||
super(Doorkeeper::OAuth::Scopes.from_array(value).to_s)
|
||||
|
|
@ -36,4 +41,20 @@ class OauthAccessToken < Doorkeeper::AccessToken
|
|||
def self.matching_token_for(application, resource_owner, scopes)
|
||||
# no-op
|
||||
end
|
||||
|
||||
def scope_user
|
||||
user_id = extract_user_id_from_scopes
|
||||
return unless user_id
|
||||
|
||||
::User.find_by_id(user_id)
|
||||
end
|
||||
strong_memoize_attr :scope_user
|
||||
|
||||
private
|
||||
|
||||
def extract_user_id_from_scopes
|
||||
return false unless scopes.present?
|
||||
|
||||
scopes.to_s[SCOPED_USER_REGEX, 1]&.to_i
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2522,6 +2522,10 @@ class User < ApplicationRecord
|
|||
true
|
||||
end
|
||||
|
||||
def has_composite_identity?
|
||||
false
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# override, from Devise::Validatable
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ module Webauthn
|
|||
#
|
||||
def verify_webauthn_credential(webauthn_credential, stored_credential, challenge, encoder)
|
||||
# We need to adjust the relaying party id (RP id) we verify against if the registration in question
|
||||
# is a migrated U2F registration. This is beacuse the appid of U2F and the rp id of WebAuthn differ.
|
||||
# is a migrated U2F registration. This is because the appid of U2F and the rp id of WebAuthn differ.
|
||||
rp_id = webauthn_credential.client_extension_outputs['appid'] ? WebAuthn.configuration.origin : URI(WebAuthn.configuration.origin).host
|
||||
webauthn_credential.response.verify(
|
||||
encoder.decode(challenge),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
name: composite_identity
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/468370
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/173006
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/506473
|
||||
milestone: "17.7"
|
||||
group: group::authorization
|
||||
type: wip
|
||||
default_enabled: false
|
||||
|
|
@ -9,18 +9,23 @@ module Banzai
|
|||
#
|
||||
class PlantumlFilter < HTML::Pipeline::Filter
|
||||
prepend Concerns::PipelineTimingCheck
|
||||
include ActionView::Helpers::TagHelper
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
def call
|
||||
return doc unless settings.plantuml_enabled? && doc.at_xpath(lang_tag)
|
||||
return doc unless plantuml_url_valid?
|
||||
|
||||
Gitlab::Plantuml.configure
|
||||
|
||||
doc.xpath(lang_tag).each do |node|
|
||||
img_tag = Nokogiri::HTML::DocumentFragment.parse(
|
||||
Asciidoctor::PlantUml::Processor.plantuml_content(node.content, {})).css('img').first
|
||||
next if node.content.blank?
|
||||
|
||||
next if img_tag.nil?
|
||||
image_src = create_image_src('png', node.content)
|
||||
img_tag = Nokogiri::HTML::DocumentFragment.parse(content_tag(:img, nil, src: image_src))
|
||||
img_tag = img_tag.children.first
|
||||
|
||||
img_tag.add_class('plantuml')
|
||||
img_tag.set_attribute('data-diagram', 'plantuml')
|
||||
img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(node.content)}")
|
||||
|
||||
|
|
@ -40,6 +45,15 @@ module Banzai
|
|||
def settings
|
||||
Gitlab::CurrentSettings.current_application_settings
|
||||
end
|
||||
strong_memoize_attr :settings
|
||||
|
||||
def create_image_src(format, text)
|
||||
Asciidoctor::PlantUml::Processor.gen_url(text, format)
|
||||
end
|
||||
|
||||
def plantuml_url_valid?
|
||||
::Gitlab::UrlSanitizer.valid_web?(settings.plantuml_url)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -71,8 +71,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def find_user_from_bearer_token
|
||||
find_user_from_job_bearer_token ||
|
||||
find_user_from_access_token
|
||||
find_user_from_job_bearer_token || find_user_from_access_token
|
||||
end
|
||||
|
||||
def find_user_from_job_token
|
||||
|
|
@ -299,6 +298,11 @@ module Gitlab
|
|||
raise UnauthorizedError unless oauth_token
|
||||
|
||||
oauth_token.revoke_previous_refresh_token!
|
||||
|
||||
::Gitlab::Auth::Identity.link_from_oauth_token(oauth_token).tap do |identity|
|
||||
raise UnauthorizedError if identity && !identity.valid?
|
||||
end
|
||||
|
||||
oauth_token
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module Auth
|
||||
##
|
||||
# Identity class represents identity which we want to use in authorization policies.
|
||||
#
|
||||
# It decides if an identity is a single or composite identity and finds identity scope.
|
||||
#
|
||||
class Identity
|
||||
COMPOSITE_IDENTITY_USERS_KEY = 'composite_identities'
|
||||
COMPOSITE_IDENTITY_KEY_FORMAT = 'user:%s:composite_identity'
|
||||
|
||||
IdentityError = Class.new(StandardError)
|
||||
IdentityLinkMismatchError = Class.new(IdentityError)
|
||||
UnexpectedIdentityError = Class.new(IdentityError)
|
||||
TooManyIdentitiesLinkedError = Class.new(IdentityError)
|
||||
MissingCompositeIdentityError = Class.new(::Gitlab::Access::AccessDeniedError)
|
||||
|
||||
# TODO: why is this called 3 times in doorkeeper_access_spec.rb specs?
|
||||
def self.link_from_oauth_token(oauth_token)
|
||||
fabricate(oauth_token.user).tap do |identity|
|
||||
identity.link!(oauth_token.scope_user) if identity&.composite?
|
||||
end
|
||||
end
|
||||
|
||||
def self.fabricate(user)
|
||||
new(user) if user.is_a?(::User)
|
||||
end
|
||||
|
||||
def initialize(user, store: ::Gitlab::SafeRequestStore)
|
||||
raise UnexpectedIdentityError unless user.is_a?(::User)
|
||||
|
||||
@user = user
|
||||
@request_store = store
|
||||
end
|
||||
|
||||
def composite?
|
||||
return false unless Feature.enabled?(:composite_identity, @user)
|
||||
|
||||
@user.has_composite_identity?
|
||||
end
|
||||
|
||||
def linked?
|
||||
@request_store.exist?(store_key)
|
||||
end
|
||||
|
||||
def valid?
|
||||
return true unless composite?
|
||||
|
||||
linked?
|
||||
end
|
||||
|
||||
def scoped_user
|
||||
@request_store.fetch(store_key) do
|
||||
raise MissingCompositeIdentityError, 'composite identity missing'
|
||||
end
|
||||
end
|
||||
|
||||
def link!(scope_user)
|
||||
return unless scope_user
|
||||
|
||||
validate_link!(scope_user)
|
||||
store_identity_link!(scope_user)
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def scoped_user_id
|
||||
scoped_user.id
|
||||
end
|
||||
|
||||
def scoped_user_present?
|
||||
@request_store.exist?(store_key)
|
||||
end
|
||||
|
||||
def validate_link!(scope_user)
|
||||
return unless scoped_user_present? && saved_scoped_user_different_from_new_scope_user?(scope_user)
|
||||
|
||||
raise IdentityLinkMismatchError, 'identity link change detected'
|
||||
end
|
||||
|
||||
def saved_scoped_user_different_from_new_scope_user?(scope_user)
|
||||
scoped_user_id != scope_user.id
|
||||
end
|
||||
|
||||
def store_identity_link!(scope_user)
|
||||
@request_store.store[store_key] = scope_user
|
||||
|
||||
composite_identities.add(@user)
|
||||
|
||||
raise TooManyIdentitiesLinkedError if composite_identities.size > 1
|
||||
end
|
||||
|
||||
def composite_identities
|
||||
@request_store.store[COMPOSITE_IDENTITY_USERS_KEY] ||= Set.new
|
||||
end
|
||||
|
||||
def store_key
|
||||
@store_key ||= format(COMPOSITE_IDENTITY_KEY_FORMAT, @user.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -28996,7 +28996,7 @@ msgstr ""
|
|||
msgid "Insights|Configure a custom report for insights into your group processes such as amount of issues, bugs, and merge requests per month. %{linkStart}How do I configure an insights report?%{linkEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Insights|Some items are not visible beacuse the project was filtered out in the insights.yml file (see the projects.only config in the YAML file or the enabled project features (issues, merge requests) in the project settings)."
|
||||
msgid "Insights|Some items are not visible because the project was filtered out in the insights.yml file (see the projects.only config in the YAML file or the enabled project features (issues, merge requests) in the project settings)."
|
||||
msgstr ""
|
||||
|
||||
msgid "Insights|This project is filtered out in the insights.yml file (see the projects.only config for more information)."
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@
|
|||
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
|
||||
"@rails/actioncable": "7.0.8-4",
|
||||
"@rails/ujs": "7.0.8-4",
|
||||
"@sentry/browser": "8.38.0",
|
||||
"@sentry/browser": "8.41.0",
|
||||
"@snowplow/browser-plugin-client-hints": "^3.24.2",
|
||||
"@snowplow/browser-plugin-form-tracking": "^3.24.2",
|
||||
"@snowplow/browser-plugin-ga-cookies": "^3.24.2",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
const { spawnSync } = require('node:child_process');
|
||||
const { readFile, open } = require('node:fs/promises');
|
||||
const { readFile, open, stat } = require('node:fs/promises');
|
||||
const parser = require('fast-xml-parser');
|
||||
const defaultChalk = require('chalk');
|
||||
const { getLocalQuarantinedFiles } = require('./jest_vue3_quarantine_utils');
|
||||
|
|
@ -7,6 +7,9 @@ const { getLocalQuarantinedFiles } = require('./jest_vue3_quarantine_utils');
|
|||
// Always use basic color output
|
||||
const chalk = new defaultChalk.constructor({ level: 1 });
|
||||
|
||||
let quarantinedFiles;
|
||||
let filesThatChanged;
|
||||
|
||||
async function parseJUnitReport() {
|
||||
let junit;
|
||||
try {
|
||||
|
|
@ -21,7 +24,7 @@ async function parseJUnitReport() {
|
|||
console.warn(e);
|
||||
// No JUnit report exists, or there was a parsing error. Either way, we
|
||||
// should not block the MR.
|
||||
return { passed: [], total: 0 };
|
||||
return [];
|
||||
}
|
||||
|
||||
const failuresByFile = new Map();
|
||||
|
|
@ -41,32 +44,28 @@ async function parseJUnitReport() {
|
|||
}
|
||||
}
|
||||
|
||||
const quarantinedFiles = new Set(await getLocalQuarantinedFiles());
|
||||
const passed = [];
|
||||
for (const [file, failures] of failuresByFile.entries()) {
|
||||
if (failures === 0 && quarantinedFiles.has(file)) passed.push(file);
|
||||
}
|
||||
|
||||
return {
|
||||
passed,
|
||||
total: failuresByFile.size,
|
||||
};
|
||||
return passed;
|
||||
}
|
||||
|
||||
function reportPassingSpecsShouldBeUnquarantined(passed) {
|
||||
function reportSpecsShouldBeUnquarantined(files) {
|
||||
const docsLink =
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
'https://docs.gitlab.com/ee/development/testing_guide/testing_vue3.html#quarantine-list';
|
||||
console.warn(' ');
|
||||
console.warn(
|
||||
`The following ${passed.length} spec file(s) now pass(es) under Vue 3, and so must be removed from quarantine:`,
|
||||
`The following ${files.length} spec files either now pass under Vue 3, or no longer exist, and so must be removed from quarantine:`,
|
||||
);
|
||||
console.warn(' ');
|
||||
console.warn(passed.join('\n'));
|
||||
console.warn(files.join('\n'));
|
||||
console.warn(' ');
|
||||
console.warn(
|
||||
chalk.red(
|
||||
`To fix this job, remove the file(s) listed above from the file ${chalk.underline('scripts/frontend/quarantined_vue3_specs.txt')}.`,
|
||||
`To fix this job, remove the files listed above from the file ${chalk.underline('scripts/frontend/quarantined_vue3_specs.txt')}.`,
|
||||
),
|
||||
);
|
||||
console.warn(`For more information, please see ${docsLink}.`);
|
||||
|
|
@ -84,8 +83,34 @@ async function changedFiles() {
|
|||
return files.flat();
|
||||
}
|
||||
|
||||
function intersection(a, b) {
|
||||
const result = new Set();
|
||||
|
||||
for (const element of a) {
|
||||
if (b.has(element)) result.add(element);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function getRemovedQuarantinedSpecs() {
|
||||
const removedQuarantinedSpecs = [];
|
||||
|
||||
for (const file of intersection(filesThatChanged, quarantinedFiles)) {
|
||||
try {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await stat(file);
|
||||
} catch (e) {
|
||||
if (e.code === 'ENOENT') removedQuarantinedSpecs.push(file);
|
||||
}
|
||||
}
|
||||
|
||||
return removedQuarantinedSpecs;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const filesThatChanged = await changedFiles();
|
||||
filesThatChanged = await changedFiles();
|
||||
quarantinedFiles = new Set(await getLocalQuarantinedFiles());
|
||||
const jestStdout = (await open('jest_stdout', 'w')).createWriteStream();
|
||||
const jestStderr = (await open('jest_stderr', 'w')).createWriteStream();
|
||||
|
||||
|
|
@ -126,18 +151,18 @@ async function main() {
|
|||
},
|
||||
);
|
||||
|
||||
const { passed, total } = await parseJUnitReport();
|
||||
const passed = await parseJUnitReport();
|
||||
const removedQuarantinedSpecs = await getRemovedQuarantinedSpecs();
|
||||
const filesToReport = [...passed, ...removedQuarantinedSpecs];
|
||||
|
||||
if (total.length === 0) {
|
||||
if (filesToReport.length === 0) {
|
||||
// No tests ran, or there was some unexpected error. Either way, exit
|
||||
// successfully.
|
||||
return;
|
||||
}
|
||||
|
||||
if (passed.length > 0) {
|
||||
process.exitCode = 1;
|
||||
reportPassingSpecsShouldBeUnquarantined(passed);
|
||||
}
|
||||
process.exitCode = 1;
|
||||
reportSpecsShouldBeUnquarantined(filesToReport);
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ ee/spec/frontend/analytics/analytics_dashboards/components/analytics_dashboards_
|
|||
ee/spec/frontend/analytics/analytics_dashboards/components/analytics_data_explorer_spec.js
|
||||
ee/spec/frontend/analytics/analytics_dashboards/components/list/feature_list_item_spec.js
|
||||
ee/spec/frontend/analytics/cycle_analytics/components/value_stream_aggregation_status_spec.js
|
||||
ee/spec/frontend/analytics/cycle_analytics/components/value_stream_form_content_spec.js
|
||||
ee/spec/frontend/analytics/cycle_analytics/vsa_settings/components/value_stream_form_content_spec.js
|
||||
ee/spec/frontend/analytics/dashboards/ai_impact/components/metric_table_spec.js
|
||||
ee/spec/frontend/analytics/devops_reports/devops_adoption/components/devops_adoption_app_spec.js
|
||||
|
|
@ -46,7 +45,6 @@ ee/spec/frontend/clusters/components/environments_spec.js
|
|||
ee/spec/frontend/compliance_dashboard/components/frameworks_report/edit_framework/components/policies_section_spec.js
|
||||
ee/spec/frontend/compliance_dashboard/components/frameworks_report/report_spec.js
|
||||
ee/spec/frontend/compliance_dashboard/components/standards_adherence_report/base_table_spec.js
|
||||
ee/spec/frontend/contextual_sidebar/components/trial_status_widget_spec.js
|
||||
ee/spec/frontend/dependencies/components/app_spec.js
|
||||
ee/spec/frontend/dependencies/components/dependency_location_spec.js
|
||||
ee/spec/frontend/dependencies/components/dependency_path_viewer_spec.js
|
||||
|
|
@ -171,7 +169,6 @@ spec/frontend/ci/pipeline_editor/components/job_assistant_drawer/accordion_items
|
|||
spec/frontend/ci/pipeline_editor/components/lint/ci_lint_warnings_spec.js
|
||||
spec/frontend/ci/pipeline_editor/components/pipeline_editor_tabs_spec.js
|
||||
spec/frontend/ci/pipeline_editor/pipeline_editor_app_spec.js
|
||||
spec/frontend/ci/pipeline_mini_graph/legacy_pipeline_mini_graph/legacy_pipeline_stage_spec.js
|
||||
spec/frontend/ci/pipeline_mini_graph/pipeline_mini_graph_spec.js
|
||||
spec/frontend/ci/pipelines_page/components/pipeline_multi_actions_spec.js
|
||||
spec/frontend/ci/pipelines_page/components/pipelines_artifacts_spec.js
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ RSpec.describe Banzai::Filter::PlantumlFilter, feature_category: :markdown do
|
|||
stub_application_setting(plantuml_enabled: true, plantuml_url: "http://localhost:8080")
|
||||
|
||||
input = '<pre data-canonical-lang="plantuml"><code>Bob -> Sara : Hello</code></pre>'
|
||||
output = '<img class="plantuml" src="http://localhost:8080/png/U9npoazIqBLJ24uiIbImKl18pSd91m0rkGMq" data-diagram="plantuml" data-diagram-src="data:text/plain;base64,Qm9iIC0+IFNhcmEgOiBIZWxsbw==">'
|
||||
output = '<img src="http://localhost:8080/png/U9npoazIqBLJ24uiIbImKl18pSd91m0rkGMq" class="plantuml" data-diagram="plantuml" data-diagram-src="data:text/plain;base64,Qm9iIC0+IFNhcmEgOiBIZWxsbw==">'
|
||||
doc = filter(input)
|
||||
|
||||
expect(doc.to_s).to eq output
|
||||
|
|
@ -19,7 +19,7 @@ RSpec.describe Banzai::Filter::PlantumlFilter, feature_category: :markdown do
|
|||
stub_application_setting(plantuml_enabled: true, plantuml_url: "http://localhost:8080")
|
||||
|
||||
input = '<pre><code data-canonical-lang="plantuml">Bob -> Sara : Hello</code></pre>'
|
||||
output = '<img class="plantuml" src="http://localhost:8080/png/U9npoazIqBLJ24uiIbImKl18pSd91m0rkGMq" data-diagram="plantuml" data-diagram-src="data:text/plain;base64,Qm9iIC0+IFNhcmEgOiBIZWxsbw==">'
|
||||
output = '<img src="http://localhost:8080/png/U9npoazIqBLJ24uiIbImKl18pSd91m0rkGMq" class="plantuml" data-diagram="plantuml" data-diagram-src="data:text/plain;base64,Qm9iIC0+IFNhcmEgOiBIZWxsbw==">'
|
||||
doc = filter(input)
|
||||
|
||||
expect(doc.to_s).to eq output
|
||||
|
|
@ -35,8 +35,17 @@ RSpec.describe Banzai::Filter::PlantumlFilter, feature_category: :markdown do
|
|||
expect(doc.to_s).to eq output
|
||||
end
|
||||
|
||||
it 'does not replace plantuml pre tag with img tag if url is invalid' do
|
||||
stub_application_setting(plantuml_enabled: true, plantuml_url: "invalid")
|
||||
it 'does not replace plantuml pre tag if there is no content' do
|
||||
stub_application_setting(plantuml_enabled: true, plantuml_url: "http://localhost:8080")
|
||||
|
||||
input = '<pre><code data-canonical-lang="plantuml"></code></pre>'
|
||||
doc = filter(input)
|
||||
|
||||
expect(doc.to_s).to eq input
|
||||
end
|
||||
|
||||
it 'does not replace plantuml pre tag if the url is not valid' do
|
||||
stub_application_setting(plantuml_enabled: true, plantuml_url: "invalid://localhost:8080")
|
||||
|
||||
input = '<pre data-canonical-lang="plantuml"><code>Bob -> Sara : Hello</code></pre>'
|
||||
output = '<pre data-canonical-lang="plantuml"><code>Bob -> Sara : Hello</code></pre>'
|
||||
|
|
|
|||
|
|
@ -708,8 +708,22 @@ RSpec.describe Gitlab::Auth::AuthFinders, feature_category: :system_access do
|
|||
end
|
||||
|
||||
describe '#find_oauth_access_token' do
|
||||
let(:application) { Doorkeeper::Application.create!(name: 'MyApp', redirect_uri: 'https://app.com', owner: user) }
|
||||
let(:doorkeeper_access_token) { Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: 'api', organization_id: organization.id) }
|
||||
let(:scopes) { 'api' }
|
||||
|
||||
let(:application) do
|
||||
Doorkeeper::Application.create!(
|
||||
name: 'MyApp',
|
||||
redirect_uri: 'https://app.com',
|
||||
owner: user)
|
||||
end
|
||||
|
||||
let(:doorkeeper_access_token) do
|
||||
Doorkeeper::AccessToken.create!(
|
||||
application_id: application.id,
|
||||
resource_owner_id: user.id,
|
||||
scopes: scopes,
|
||||
organization_id: organization.id)
|
||||
end
|
||||
|
||||
context 'passed as header' do
|
||||
before do
|
||||
|
|
@ -742,6 +756,48 @@ RSpec.describe Gitlab::Auth::AuthFinders, feature_category: :system_access do
|
|||
expect { find_oauth_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with composite identity', :request_store do
|
||||
let(:user) { create(:user, username: 'gitlab-duo') }
|
||||
|
||||
before do
|
||||
allow_any_instance_of(::User).to receive(:has_composite_identity?) do |user|
|
||||
user.username == 'gitlab-duo'
|
||||
end
|
||||
|
||||
set_bearer_token(doorkeeper_access_token.plaintext_token)
|
||||
end
|
||||
|
||||
context 'when scoped user is specified' do
|
||||
let(:scopes) { "user:#{user.id}" }
|
||||
|
||||
context 'when linking composite identitiy succeeds' do
|
||||
it 'returns the oauth token' do
|
||||
expect(find_oauth_access_token.token).to eq(doorkeeper_access_token.token)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when linking composite identity raises an error' do
|
||||
before do
|
||||
allow(Gitlab::Auth::Identity).to(
|
||||
receive(:link_from_oauth_token).and_raise(::Gitlab::Auth::Identity::IdentityLinkMismatchError)
|
||||
)
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expect { find_oauth_access_token }.to raise_error(::Gitlab::Auth::Identity::IdentityLinkMismatchError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when composite identity link can not be created' do
|
||||
let(:scopes) { 'api' }
|
||||
|
||||
it 'raises an exception' do
|
||||
expect { find_oauth_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#find_personal_access_token_from_http_basic_auth' do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Auth::Identity, :request_store, feature_category: :system_access do
|
||||
describe '.link_from_oauth_token' do
|
||||
let_it_be(:actor_user) { create(:user) }
|
||||
let_it_be(:delegating_user) { create(:user) }
|
||||
let_it_be(:token_scopes) { [:api, :"user:#{delegating_user.id}"] }
|
||||
let_it_be(:oauth_access_token) { create(:oauth_access_token, user: actor_user, scopes: token_scopes) }
|
||||
|
||||
subject(:identity) { described_class.link_from_oauth_token(oauth_access_token) }
|
||||
|
||||
context 'when composite identity is required for the actor' do
|
||||
before do
|
||||
allow(actor_user).to receive(:has_composite_identity?).and_return(true)
|
||||
end
|
||||
|
||||
it 'returns an identity' do
|
||||
expect(identity).to be_composite
|
||||
expect(identity).to be_linked
|
||||
expect(identity).to be_valid
|
||||
|
||||
expect(identity.scoped_user).to eq(delegating_user)
|
||||
end
|
||||
|
||||
context 'when oauth token does not have required scopes' do
|
||||
let(:oauth_access_token) { create(:oauth_access_token, user: actor_user, scopes: [:api]) }
|
||||
|
||||
it 'fabricates a composite identity which is not valid' do
|
||||
expect(identity).to be_composite
|
||||
expect(identity).not_to be_linked
|
||||
expect(identity).not_to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an identity link was already done for a different composite user' do
|
||||
let_it_be(:different_user) { create(:user) }
|
||||
let_it_be(:new_token_scopes) { [:api, :"user:#{different_user.id}"] }
|
||||
let_it_be(:new_oauth_access_token) { create(:oauth_access_token, user: actor_user, scopes: new_token_scopes) }
|
||||
|
||||
it 'raises an error' do
|
||||
expect(identity).to be_valid
|
||||
|
||||
expect { described_class.link_from_oauth_token(new_oauth_access_token) }
|
||||
.to raise_error(::Gitlab::Auth::Identity::IdentityLinkMismatchError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when actor user does not have composite identity enforced' do
|
||||
before do
|
||||
allow(actor_user).to receive(:has_composite_identity?).and_return(false)
|
||||
end
|
||||
|
||||
context 'when token has composite user scope' do
|
||||
it 'returns an identity' do
|
||||
expect(identity).not_to be_composite
|
||||
expect(identity).not_to be_linked
|
||||
end
|
||||
end
|
||||
|
||||
context 'when token does not have composite user scope' do
|
||||
let_it_be(:token_scopes) { [:api] }
|
||||
let_it_be(:oauth_access_token) { create(:oauth_access_token, user: actor_user, scopes: token_scopes) }
|
||||
|
||||
it 'returns an identity' do
|
||||
expect(identity).not_to be_composite
|
||||
expect(identity).not_to be_linked
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when composite identity is not required for the actor' do
|
||||
it 'fabricates a valid identity' do
|
||||
expect(identity).not_to be_composite
|
||||
expect(identity).to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.fabricate' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
subject(:identity) { described_class.fabricate(user) }
|
||||
|
||||
it 'returns a valid identity without a scoped user' do
|
||||
expect(identity).to be_valid
|
||||
|
||||
expect { identity.scoped_user }
|
||||
.to raise_error(::Gitlab::Auth::Identity::MissingCompositeIdentityError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -14,7 +14,7 @@ RSpec.describe Gitlab::Auth::OAuth::IdentityLinker do
|
|||
let!(:identity) { user.identities.create!(provider: provider, extern_uid: uid) }
|
||||
|
||||
it "doesn't create new identity" do
|
||||
expect { subject.link }.not_to change { Identity.count }
|
||||
expect { subject.link }.not_to change { ::Identity.count }
|
||||
end
|
||||
|
||||
it "sets #changed? to false" do
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ RSpec.describe Gitlab::Database::Count::ExactCountStrategy do
|
|||
create(:identity)
|
||||
end
|
||||
|
||||
let(:models) { [Project, Identity] }
|
||||
let(:models) { [::Project, ::Identity] }
|
||||
|
||||
subject { described_class.new(models).count }
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ RSpec.describe Gitlab::Database::Count::ExactCountStrategy do
|
|||
it 'counts all models' do
|
||||
expect(models).to all(receive(:count).and_call_original)
|
||||
|
||||
expect(subject).to eq({ Project => 3, Identity => 1 })
|
||||
expect(subject).to eq({ ::Project => 3, ::Identity => 1 })
|
||||
end
|
||||
|
||||
it 'returns default value if count times out' do
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ RSpec.describe Gitlab::Database::Count::TablesampleCountStrategy do
|
|||
create(:group)
|
||||
end
|
||||
|
||||
let(:models) { [Project, Ci::InstanceVariable, Identity, Group, Namespace] }
|
||||
let(:models) { [Project, Ci::InstanceVariable, ::Identity, Group, Namespace] }
|
||||
let(:strategy) { described_class.new(models) }
|
||||
|
||||
subject { strategy.count }
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ RSpec.describe Gitlab::Database::Count do
|
|||
create(:identity)
|
||||
end
|
||||
|
||||
let(:models) { [Project, Identity] }
|
||||
let(:models) { [::Project, ::Identity] }
|
||||
|
||||
describe '.approximate_counts' do
|
||||
context 'fallbacks' do
|
||||
|
|
@ -32,15 +32,15 @@ RSpec.describe Gitlab::Database::Count do
|
|||
end
|
||||
|
||||
it 'gets more results from second strategy if some counts are missing' do
|
||||
expect(first_strategy).to receive(:count).and_return({ Project => 3 })
|
||||
expect(strategies[1]).to receive(:new).with([Identity]).and_return(second_strategy)
|
||||
expect(second_strategy).to receive(:count).and_return({ Identity => 1 })
|
||||
expect(first_strategy).to receive(:count).and_return({ ::Project => 3 })
|
||||
expect(strategies[1]).to receive(:new).with([::Identity]).and_return(second_strategy)
|
||||
expect(second_strategy).to receive(:count).and_return({ ::Identity => 1 })
|
||||
|
||||
expect(subject).to eq({ Project => 3, Identity => 1 })
|
||||
expect(subject).to eq({ ::Project => 3, ::Identity => 1 })
|
||||
end
|
||||
|
||||
it 'does not get more results as soon as all counts are present' do
|
||||
expect(first_strategy).to receive(:count).and_return({ Project => 3, Identity => 1 })
|
||||
expect(first_strategy).to receive(:count).and_return({ ::Project => 3, ::Identity => 1 })
|
||||
expect(strategies[1]).not_to receive(:new)
|
||||
|
||||
subject
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ RSpec.describe ::Gitlab::Graphql::Authorize::ObjectAuthorization do
|
|||
describe '#ok?' do
|
||||
subject(:authorization) { described_class.new(%i[go_fast go_slow]) }
|
||||
|
||||
let(:user) { double(:User, id: 10001) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let(:scope_validator) { instance_double(::Gitlab::Auth::ScopeValidator, valid_for?: true) }
|
||||
|
||||
let(:policy) do
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ RSpec.describe Gitlab::OtherMarkup, feature_category: :wiki do
|
|||
RST
|
||||
|
||||
output = <<~HTML
|
||||
<img class="plantuml" src="https://plantuml.com/plantuml/png/U9npoazIqBLJSCp9J4wrKiX8pSd9vm9pGA9E-Kb0iKm0o4SAt000" data-diagram="plantuml" data-diagram-src="data:text/plain;base64,Qm9iIC0+IEFsaWNlOiBoZWxsbwpBbGljZSAtPiBCb2I6IGhp">
|
||||
<img src="https://plantuml.com/plantuml/png/U9npoazIqBLJSCp9J4wrKiX8pSd9vm9pGA9E-Kb0iKm0o4SAt000" class="plantuml" data-diagram="plantuml" data-diagram-src="data:text/plain;base64,Qm9iIC0+IEFsaWNlOiBoZWxsbwpBbGljZSAtPiBCb2I6IGhp">
|
||||
<p>Caption with <strong>bold</strong> and <em>italic</em></p>
|
||||
HTML
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Ability do
|
||||
RSpec.describe Ability, feature_category: :system_access do
|
||||
describe '#policy_for' do
|
||||
subject(:policy) { described_class.policy_for(user, subject, **options) }
|
||||
|
||||
|
|
@ -476,6 +476,89 @@ RSpec.describe Ability do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.allowed?' do
|
||||
let_it_be(:group) { create(:group, :private) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:delegated_user) { create(:user) }
|
||||
let(:request_store_key) { format(::Gitlab::Auth::Identity::COMPOSITE_IDENTITY_KEY_FORMAT, user.id) }
|
||||
|
||||
subject { described_class.allowed?(user, :read_group, group) }
|
||||
|
||||
context 'with composite identity', :request_store do
|
||||
before do
|
||||
::Gitlab::SafeRequestStore[request_store_key] = delegated_user
|
||||
allow(user).to receive(:has_composite_identity?).and_return(true)
|
||||
end
|
||||
|
||||
context 'when both users are members' do
|
||||
before_all do
|
||||
group.add_developer(delegated_user)
|
||||
group.add_developer(user)
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
expect(subject).to be_truthy
|
||||
end
|
||||
|
||||
context 'when delegating user is not set in RequestStore' do
|
||||
before do
|
||||
::Gitlab::SafeRequestStore.delete(request_store_key)
|
||||
end
|
||||
|
||||
it 'raises an exception' do
|
||||
expect { subject }.to raise_exception(::Gitlab::Auth::Identity::MissingCompositeIdentityError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when only delegating user is a member' do
|
||||
before_all do
|
||||
group.add_developer(user)
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(subject).to be_falsey
|
||||
end
|
||||
|
||||
context 'with disabled composite_identity feature flag' do
|
||||
before do
|
||||
stub_feature_flags(composite_identity: false)
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
expect(subject).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'with unenforced composite identity' do
|
||||
before do
|
||||
allow(user).to receive(:has_composite_identity?).and_return(false)
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
expect(subject).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when only delegated user is a member' do
|
||||
before_all do
|
||||
group.add_developer(delegated_user)
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(subject).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'when neither user is a member' do
|
||||
it 'returns false' do
|
||||
expect(subject).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'forgetting', :request_store do
|
||||
it 'allows us to discard specific values from the DeclarativePolicy cache' do
|
||||
user_a = build_stubbed(:user)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,16 @@ RSpec.describe NotificationRecipient, feature_category: :team_planning do
|
|||
describe '#notifiable?' do
|
||||
let(:recipient) { described_class.new(user, :mention, target: target, project: project) }
|
||||
|
||||
context 'when user has a composite identity' do
|
||||
before do
|
||||
allow(recipient).to receive(:has_composite_identity?).and_return(true)
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(recipient.notifiable?).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when emails are disabled' do
|
||||
it 'returns false if group disabled' do
|
||||
expect(project.namespace).to receive(:emails_enabled?).and_return(false)
|
||||
|
|
|
|||
|
|
@ -77,4 +77,66 @@ RSpec.describe OauthAccessToken, feature_category: :system_access do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#scope_user' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be_with_refind(:oauth_access_token) { create(:oauth_access_token) }
|
||||
let(:user_id) { user.id }
|
||||
|
||||
before do
|
||||
allow(oauth_access_token).to receive(:scopes).and_return(scopes)
|
||||
end
|
||||
|
||||
context 'when scopes match expected format' do
|
||||
context 'when scopes only include the composite scope' do
|
||||
let(:scopes) { "user:#{user_id}" }
|
||||
|
||||
it 'returns the user' do
|
||||
expect(oauth_access_token.scope_user).to eq user
|
||||
end
|
||||
end
|
||||
|
||||
context 'when scopes include another scope before composite scope' do
|
||||
let(:scopes) { "other:scope user:#{user_id}" }
|
||||
|
||||
it 'returns the user' do
|
||||
expect(oauth_access_token.scope_user).to eq user
|
||||
end
|
||||
end
|
||||
|
||||
context "when scopes include another scope after composite scope" do
|
||||
let(:scopes) { "user:#{user_id} other:scope" }
|
||||
|
||||
it 'returns the user' do
|
||||
expect(oauth_access_token.scope_user).to eq user
|
||||
end
|
||||
end
|
||||
|
||||
context 'when scopes include another scope before and after composite scope' do
|
||||
let(:scopes) { "api user:#{user_id} read_api" }
|
||||
|
||||
it 'returns the user' do
|
||||
expect(oauth_access_token.scope_user).to eq user
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when scopes do not match composite scope format' do
|
||||
where(:scopes) do
|
||||
[
|
||||
"user:#{non_existing_record_id}",
|
||||
'user:not_a_number',
|
||||
'some:other:scope',
|
||||
nil,
|
||||
""
|
||||
]
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'returns false' do
|
||||
expect(oauth_access_token.scope_user).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8828,6 +8828,12 @@ RSpec.describe User, feature_category: :user_profile do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#has_composite_identity?' do
|
||||
it 'is false' do
|
||||
expect(build(:user).has_composite_identity?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe 'color_mode_id' do
|
||||
context 'when theme_id is 11' do
|
||||
let(:user) { build(:user, theme_id: 11) }
|
||||
|
|
|
|||
|
|
@ -8,6 +8,44 @@ RSpec.describe 'doorkeeper access', feature_category: :system_access do
|
|||
let!(:application) { Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user) }
|
||||
let!(:token) { Doorkeeper::AccessToken.create! application_id: application.id, resource_owner_id: user.id, scopes: "api", organization_id: organization.id }
|
||||
|
||||
describe 'access token with composite identity scope', :request_store do
|
||||
let!(:scopes) { "user:#{scope_user.id} api" }
|
||||
let!(:scoped_token) { OauthAccessToken.create! application_id: application.id, user: user, scopes: scopes, organization_id: organization.id }
|
||||
|
||||
let(:scope_user) { create(:user) }
|
||||
let(:group) { create(:group, :private) }
|
||||
let(:user) do
|
||||
user = create(:user)
|
||||
allow(user).to receive(:has_composite_identity?).and_return(true)
|
||||
user
|
||||
end
|
||||
|
||||
context 'when user one has a composite identity token scoped to user two' do
|
||||
before do
|
||||
group.add_developer(user) # scoped user doesn't have access
|
||||
allow(OauthAccessToken).to receive(:by_token).and_return(scoped_token) # this is required for the has_composite_identity? stub to work
|
||||
end
|
||||
|
||||
it 'restricts user access permissions' do
|
||||
get api("/groups/#{group.id}"), params: { access_token: scoped_token.plaintext_token }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
|
||||
context 'when both users have access to a resource' do
|
||||
before do
|
||||
group.add_developer(scope_user)
|
||||
end
|
||||
|
||||
it 'allows access' do
|
||||
get api("/groups/#{group.id}"), params: { access_token: token.plaintext_token }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "unauthenticated" do
|
||||
it "returns authentication success" do
|
||||
get api("/user"), params: { access_token: token.plaintext_token }
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ RSpec.describe API::VirtualRegistries::Packages::Maven, :aggregate_failures, fea
|
|||
group.add_maintainer(user)
|
||||
end
|
||||
|
||||
it 'returns a bad request beacuse it is not a top level group' do
|
||||
it 'returns a bad request because it is not a top level group' do
|
||||
api_request
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
|
|
|
|||
|
|
@ -211,8 +211,7 @@ RSpec.describe ::Ci::Runners::SetRunnerAssociatedProjectsService, '#execute', fe
|
|||
let(:new_projects) { [project3, project4] }
|
||||
let(:owner_project) { new_projects.first }
|
||||
|
||||
it 'assigns associated projects and returns success response',
|
||||
quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/481411' do
|
||||
it 'assigns associated projects and returns success response' do
|
||||
expect(execute).to be_success
|
||||
|
||||
runner.reload
|
||||
|
|
|
|||
|
|
@ -535,6 +535,8 @@ module TestEnv
|
|||
end
|
||||
|
||||
def component_ahead_of_target?(component_folder, expected_version)
|
||||
return false unless Dir.exist?(File.join(component_folder, '.git'))
|
||||
|
||||
# The HEAD of the component_folder will be used as heuristic for the version
|
||||
# of the binaries, allowing to use Git to determine if HEAD is later than
|
||||
# the expected version. Note: Git considers HEAD to be an anchestor of HEAD.
|
||||
|
|
@ -556,6 +558,8 @@ module TestEnv
|
|||
|
||||
return false unless Dir.exist?(component_folder)
|
||||
|
||||
return false unless Dir.exist?(File.join(component_folder, '.git'))
|
||||
|
||||
sha, exit_status = Gitlab::Popen.popen(%W[#{Gitlab.config.git.bin_path} rev-parse HEAD], component_folder)
|
||||
return false if exit_status != 0
|
||||
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ module MarkdownMatchers
|
|||
end
|
||||
end
|
||||
|
||||
# PLantumlFilter
|
||||
# PlantumlFilter
|
||||
matcher :parse_plantuml do
|
||||
set_default_markdown_messages
|
||||
|
||||
|
|
|
|||
103
yarn.lock
103
yarn.lock
|
|
@ -2502,76 +2502,63 @@
|
|||
resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8"
|
||||
integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==
|
||||
|
||||
"@sentry-internal/browser-utils@8.38.0":
|
||||
version "8.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.38.0.tgz#d7f6d398778906efb0c017e63d3c59d3203dfa7d"
|
||||
integrity sha512-5QMVcssrAcmjKT0NdFYcX0b0wwZovGAZ9L2GajErXtHkBenjI2sgR2+5J7n+QZGuk2SC1qhGmT1O9i3p3UEwew==
|
||||
"@sentry-internal/browser-utils@8.41.0":
|
||||
version "8.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.41.0.tgz#9dc30a8c88aa6e1e542e5acae29ceabd1b377cc4"
|
||||
integrity sha512-nU7Bn3jEUmf1QXRUT3j2ewUBlFJpe9vnAnjqpeVPDWTsVI52BwVNcJHuE37PrGs66OZ1ZkGMfKnQk43oCAa+oQ==
|
||||
dependencies:
|
||||
"@sentry/core" "8.38.0"
|
||||
"@sentry/types" "8.38.0"
|
||||
"@sentry/utils" "8.38.0"
|
||||
"@sentry/core" "8.41.0"
|
||||
"@sentry/types" "8.41.0"
|
||||
|
||||
"@sentry-internal/feedback@8.38.0":
|
||||
version "8.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.38.0.tgz#726661a01f7ff40b93c8ee05c985fd0436a1c033"
|
||||
integrity sha512-AW5HCCAlc3T1jcSuNhbFVNO1CHyJ5g5tsGKEP4VKgu+D1Gg2kZ5S2eFatLBUP/BD5JYb1A7p6XPuzYp1XfMq0A==
|
||||
"@sentry-internal/feedback@8.41.0":
|
||||
version "8.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.41.0.tgz#9c3c95e6f7738a0d00fcb89061c284baef313ba2"
|
||||
integrity sha512-bw+BrSNw8abOnu/IpD8YSbYubXkkT8jyNS7TM4e4UPZMuXcbtia7/r5d7kAiUfKv/sV5PNMlZLOk+EYJeLTANg==
|
||||
dependencies:
|
||||
"@sentry/core" "8.38.0"
|
||||
"@sentry/types" "8.38.0"
|
||||
"@sentry/utils" "8.38.0"
|
||||
"@sentry/core" "8.41.0"
|
||||
"@sentry/types" "8.41.0"
|
||||
|
||||
"@sentry-internal/replay-canvas@8.38.0":
|
||||
version "8.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.38.0.tgz#26e9bc937dab73e1a26d57dc1015b7ff1f2d76c5"
|
||||
integrity sha512-OxmlWzK9J8mRM+KxdSnQ5xuxq+p7TiBzTz70FT3HltxmeugvDkyp6803UcFqHOPHR35OYeVLOalym+FmvNn9kw==
|
||||
"@sentry-internal/replay-canvas@8.41.0":
|
||||
version "8.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.41.0.tgz#9da984adc54fcd8ebe07cbbc13132fa78396dd01"
|
||||
integrity sha512-lpgOBHWr1ZNxidD72A2pfoUMjIpwonOPYoQZWAHr86Oa3eIVQOyfklZlHW+gKPFl2/IEl9Lbtcke0JiDp3dkIQ==
|
||||
dependencies:
|
||||
"@sentry-internal/replay" "8.38.0"
|
||||
"@sentry/core" "8.38.0"
|
||||
"@sentry/types" "8.38.0"
|
||||
"@sentry/utils" "8.38.0"
|
||||
"@sentry-internal/replay" "8.41.0"
|
||||
"@sentry/core" "8.41.0"
|
||||
"@sentry/types" "8.41.0"
|
||||
|
||||
"@sentry-internal/replay@8.38.0":
|
||||
version "8.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.38.0.tgz#9a9b945a3c066f5610a363774e3c99420c3f4fce"
|
||||
integrity sha512-mQPShKnIab7oKwkwrRxP/D8fZYHSkDY+cvqORzgi+wAwgnunytJQjz9g6Ww2lJu98rHEkr5SH4V4rs6PZYZmnQ==
|
||||
"@sentry-internal/replay@8.41.0":
|
||||
version "8.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.41.0.tgz#b1112a52a0cf1727589ad4d42a8fac9f98f6d733"
|
||||
integrity sha512-ByXEY7JI95y4Qr9fS3d28l9uuVU5Qa0HgL+xDmYElNx7CXz3Q9hFN6ibgUeC3h8BO5pDULxWNgAppl7FRY8N5w==
|
||||
dependencies:
|
||||
"@sentry-internal/browser-utils" "8.38.0"
|
||||
"@sentry/core" "8.38.0"
|
||||
"@sentry/types" "8.38.0"
|
||||
"@sentry/utils" "8.38.0"
|
||||
"@sentry-internal/browser-utils" "8.41.0"
|
||||
"@sentry/core" "8.41.0"
|
||||
"@sentry/types" "8.41.0"
|
||||
|
||||
"@sentry/browser@8.38.0":
|
||||
version "8.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.38.0.tgz#c562accdc2bbe0b0074d98bfe7ff460e39ce3109"
|
||||
integrity sha512-AZR+b0EteNZEGv6JSdBD22S9VhQ7nrljKsSnzxobBULf3BpwmhmCzTbDrqWszKDAIDYmL+yQJIR2glxbknneWQ==
|
||||
"@sentry/browser@8.41.0":
|
||||
version "8.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.41.0.tgz#f594012e6377a92db72127ef39aee812efe3a3b7"
|
||||
integrity sha512-FfAU55eYwW2lG4M3dEw2472RvHrD5YWSfHCZvuRf/4skX38kFvKghZQ+epL+CVHTzvIRHOrbj8qQK6YLTGl9ew==
|
||||
dependencies:
|
||||
"@sentry-internal/browser-utils" "8.38.0"
|
||||
"@sentry-internal/feedback" "8.38.0"
|
||||
"@sentry-internal/replay" "8.38.0"
|
||||
"@sentry-internal/replay-canvas" "8.38.0"
|
||||
"@sentry/core" "8.38.0"
|
||||
"@sentry/types" "8.38.0"
|
||||
"@sentry/utils" "8.38.0"
|
||||
"@sentry-internal/browser-utils" "8.41.0"
|
||||
"@sentry-internal/feedback" "8.41.0"
|
||||
"@sentry-internal/replay" "8.41.0"
|
||||
"@sentry-internal/replay-canvas" "8.41.0"
|
||||
"@sentry/core" "8.41.0"
|
||||
"@sentry/types" "8.41.0"
|
||||
|
||||
"@sentry/core@8.38.0":
|
||||
version "8.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.38.0.tgz#5d1b74770c79e489e786018a3e514cddeb777bcb"
|
||||
integrity sha512-sGD+5TEHU9G7X7zpyaoJxpOtwjTjvOd1f/MKBrWW2vf9UbYK+GUJrOzLhMoSWp/pHSYgvObkJkDb/HwieQjvhQ==
|
||||
"@sentry/core@8.41.0":
|
||||
version "8.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.41.0.tgz#e8a25cacd25fe4358f3179e85f3c2697427fe5a6"
|
||||
integrity sha512-3v7u3t4LozCA5SpZY4yqUN2U3jSrkXNoLgz6L2SUUiydyCuSwXZIFEwpLJfgQyidpNDifeQbBI5E1O910XkPsA==
|
||||
dependencies:
|
||||
"@sentry/types" "8.38.0"
|
||||
"@sentry/utils" "8.38.0"
|
||||
"@sentry/types" "8.41.0"
|
||||
|
||||
"@sentry/types@8.38.0":
|
||||
version "8.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.38.0.tgz#9c48734a8b4055bfd553a0141efec78e9680ed09"
|
||||
integrity sha512-fP5H9ZX01W4Z/EYctk3mkSHi7d06cLcX2/UWqwdWbyPWI+pL2QpUPICeO/C+8SnmYx//wFj3qWDhyPCh1PdFAA==
|
||||
|
||||
"@sentry/utils@8.38.0":
|
||||
version "8.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.38.0.tgz#2f91ca7d044f6e17b993c866ca02a981c4c1bc25"
|
||||
integrity sha512-3X7MgIKIx+2q5Al7QkhaRB4wV6DvzYsaeIwdqKUzGLuRjXmNgJrLoU87TAwQRmZ6Wr3IoEpThZZMNrzYPXxArw==
|
||||
dependencies:
|
||||
"@sentry/types" "8.38.0"
|
||||
"@sentry/types@8.41.0":
|
||||
version "8.41.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.41.0.tgz#db40c93bcedad26569c5dfe10a4b31253c349a10"
|
||||
integrity sha512-eqdnGr9k9H++b9CjVUoTNUVahPVWeNnMy0YGkqS5+cjWWC+x43p56202oidGFmWo6702ub/xwUNH6M5PC4kq6A==
|
||||
|
||||
"@sinclair/typebox@^0.27.8":
|
||||
version "0.27.8"
|
||||
|
|
|
|||
Loading…
Reference in New Issue