Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-11-23 06:07:43 +00:00
parent 26eeb803e0
commit 9d15ef5c5c
42 changed files with 289 additions and 90 deletions

View File

@ -585,6 +585,36 @@
]
}
},
"id_tokens": {
"type": "object",
"markdownDescription": "Defines JWTs to be injected as environment variables.",
"patternProperties": {
".*": {
"type": "object",
"properties": {
"aud": {
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
}
]
}
},
"required": [
"aud"
],
"additionalProperties": false
}
}
},
"secrets": {
"type": "object",
"markdownDescription": "Defines secrets to be injected as environment variables. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#secrets).",
@ -1209,6 +1239,9 @@
"cache": {
"$ref": "#/definitions/cache"
},
"id_tokens": {
"$ref": "#/definitions/id_tokens"
},
"secrets": {
"$ref": "#/definitions/secrets"
},

View File

@ -1,5 +1,6 @@
import { spriteIcon } from '~/lib/utils/common_utils';
import { objectToQuery } from '~/lib/utils/url_utility';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearchContainer from './container';
import VisualTokenValue from './visual_token_value';
@ -38,7 +39,7 @@ export default class FilteredSearchVisualTokens {
lastVisualToken,
isLastVisualTokenValid:
lastVisualToken === null ||
lastVisualToken.className.indexOf('filtered-search-term') !== -1 ||
lastVisualToken.className.indexOf(FILTERED_SEARCH_TERM) !== -1 ||
(lastVisualToken &&
lastVisualToken.querySelector('.operator') !== null &&
lastVisualToken.querySelector('.value') !== null),
@ -113,7 +114,7 @@ export default class FilteredSearchVisualTokens {
} = options;
const li = document.createElement('li');
li.classList.add('js-visual-token');
li.classList.add(isSearchTerm ? 'filtered-search-term' : 'filtered-search-token');
li.classList.add(isSearchTerm ? FILTERED_SEARCH_TERM : 'filtered-search-token');
if (!isSearchTerm) {
li.classList.add(tokenClass);
@ -239,7 +240,7 @@ export default class FilteredSearchVisualTokens {
static addSearchVisualToken(searchTerm) {
const { lastVisualToken } = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
if (lastVisualToken && lastVisualToken.classList.contains('filtered-search-term')) {
if (lastVisualToken && lastVisualToken.classList.contains(FILTERED_SEARCH_TERM)) {
lastVisualToken.querySelector('.name').textContent += ` ${searchTerm}`;
} else {
FilteredSearchVisualTokens.addVisualTokenElement({

View File

@ -7,11 +7,11 @@ import {
redirectTo,
} from '~/lib/utils/url_utility';
import {
SEARCH_TOKEN_TYPE,
SORT_QUERY_PARAM_NAME,
ACTIVE_TAB_QUERY_PARAM_NAME,
AVAILABLE_FILTERED_SEARCH_TOKENS,
} from 'ee_else_ce/members/constants';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
export default {
@ -65,7 +65,7 @@ export default {
if (query[this.filteredSearchBar.searchParam]) {
tokens.push({
type: SEARCH_TOKEN_TYPE,
type: FILTERED_SEARCH_TERM,
value: {
data: query[this.filteredSearchBar.searchParam],
},
@ -83,7 +83,7 @@ export default {
return accumulator;
}
if (type === SEARCH_TOKEN_TYPE) {
if (type === FILTERED_SEARCH_TERM) {
if (value.data !== '') {
const { searchParam } = this.filteredSearchBar;
const { [searchParam]: searchParamValue } = accumulator;

View File

@ -187,8 +187,6 @@ export const LEAVE_MODAL_ID = 'member-leave-modal';
export const REMOVE_GROUP_LINK_MODAL_ID = 'remove-group-link-modal-id';
export const SEARCH_TOKEN_TYPE = 'filtered-search-term';
export const SORT_QUERY_PARAM_NAME = 'sort';
export const ACTIVE_TAB_QUERY_PARAM_NAME = 'tab';

View File

@ -6,8 +6,8 @@ import { joinPaths } from '~/lib/utils/url_utility';
import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue';
import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue';
import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
import TagsLoader from '~/packages_and_registries/shared/components/tags_loader.vue';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import {
REMOVE_TAGS_BUTTON_TITLE,
TAGS_LIST_TITLE,

View File

@ -11,9 +11,9 @@ import {
import { get } from 'lodash';
import getContainerRepositoriesQuery from 'shared_queries/container_registry/get_container_repositories.query.graphql';
import { createAlert } from '~/flash';
import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
import Tracking from '~/tracking';
import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import DeleteImage from '../components/delete_image.vue';
import RegistryHeader from '../components/list_page/registry_header.vue';

View File

@ -3,8 +3,8 @@ import {
SORT_FIELD_MAPPING,
TOKEN_TYPE_TAG_NAME,
} from '~/packages_and_registries/harbor_registry/constants';
import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
import { normalizeHeaders, parseIntPagination } from '~/lib/utils/common_utils';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
export const extractSortingDetail = (parsedSorting = '') => {
const [orderBy, sortOrder] = parsedSorting.split('_');

View File

@ -4,16 +4,14 @@ import { mapActions, mapState } from 'vuex';
import { createAlert, VARIANT_INFO } from '~/flash';
import { historyReplaceState } from '~/lib/utils/common_utils';
import { s__ } from '~/locale';
import {
SHOW_DELETE_SUCCESS_ALERT,
FILTERED_SEARCH_TERM,
} from '~/packages_and_registries/shared/constants';
import { SHOW_DELETE_SUCCESS_ALERT } from '~/packages_and_registries/shared/constants';
import { getQueryParams, extractFilterAndSorting } from '~/packages_and_registries/shared/utils';
import InfrastructureTitle from '~/packages_and_registries/infrastructure_registry/list/components/infrastructure_title.vue';
import InfrastructureSearch from '~/packages_and_registries/infrastructure_registry/list/components/infrastructure_search.vue';
import PackageList from '~/packages_and_registries/infrastructure_registry/list/components/packages_list.vue';
import { DELETE_PACKAGE_SUCCESS_MESSAGE } from '~/packages_and_registries/infrastructure_registry/list/constants';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
export default {
components: {

View File

@ -2,6 +2,7 @@ import Api from '~/api';
import { createAlert, VARIANT_SUCCESS } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { DELETE_PACKAGE_ERROR_MESSAGE } from '~/packages_and_registries/shared/constants';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import {
FETCH_PACKAGES_LIST_ERROR_MESSAGE,
DELETE_PACKAGE_SUCCESS_MESSAGE,
@ -31,7 +32,7 @@ export const requestPackagesList = ({ dispatch, state }, params = {}) => {
const type = state.config.forceTerraform
? TERRAFORM_SEARCH_TYPE
: state.filter.find((f) => f.type === 'type');
const name = state.filter.find((f) => f.type === 'filtered-search-term');
const name = state.filter.find((f) => f.type === FILTERED_SEARCH_TERM);
const packageFilters = { package_type: type?.value?.data, package_name: name?.value?.data };
const apiMethod = state.config.isGroupPage ? 'groupPackages' : 'projectPackages';

View File

@ -1,14 +1,14 @@
<script>
import { s__ } from '~/locale';
import { sortableFields } from '~/packages_and_registries/package_registry/utils';
import { OPERATORS_IS } from '~/vue_shared/components/filtered_search_bar/constants';
import {
FILTERED_SEARCH_TERM,
OPERATORS_IS,
TOKEN_TITLE_TYPE,
TOKEN_TYPE_TYPE,
} from '~/vue_shared/components/filtered_search_bar/constants';
import RegistrySearch from '~/vue_shared/components/registry/registry_search.vue';
import UrlSync from '~/vue_shared/components/url_sync.vue';
import { getQueryParams, extractFilterAndSorting } from '~/packages_and_registries/shared/utils';
import {
FILTERED_SEARCH_TERM,
FILTERED_SEARCH_TYPE,
} from '~/packages_and_registries/shared/constants';
import { LIST_KEY_CREATED_AT } from '~/packages_and_registries/package_registry/constants';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import PackageTypeToken from './tokens/package_type_token.vue';
@ -16,9 +16,9 @@ import PackageTypeToken from './tokens/package_type_token.vue';
export default {
tokens: [
{
type: 'type',
type: TOKEN_TYPE_TYPE,
icon: 'package',
title: s__('PackageRegistry|Type'),
title: TOKEN_TITLE_TYPE,
unique: true,
token: PackageTypeToken,
operators: OPERATORS_IS,
@ -51,7 +51,7 @@ export default {
};
return this.filters.reduce((acc, filter) => {
if (filter.type === FILTERED_SEARCH_TYPE && filter.value?.data) {
if (filter.type === TOKEN_TYPE_TYPE && filter.value?.data) {
return {
...acc,
packageType: filter.value.data.toUpperCase(),

View File

@ -1,7 +1,5 @@
import { s__ } from '~/locale';
export const FILTERED_SEARCH_TERM = 'filtered-search-term';
export const FILTERED_SEARCH_TYPE = 'type';
export const HISTORY_PIPELINES_LIMIT = 5;
export const DELETE_PACKAGE_TRACKING_ACTION = 'delete_package';

View File

@ -1,6 +1,6 @@
import Vue from 'vue';
import { queryToObject } from '~/lib/utils/url_utility';
import { FILTERED_SEARCH_TERM } from './constants';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
export const getQueryParams = (query) =>
queryToObject(query, { gatherArrays: true, legacySpacesDecode: true });

View File

@ -50,7 +50,6 @@ export const SORT_DIRECTION = {
ascending: 'ascending',
};
export const FILTERED_SEARCH_LABELS = 'labels';
export const FILTERED_SEARCH_TERM = 'filtered-search-term';
export const TOKEN_TITLE_APPROVED_BY = __('Approved-By');

View File

@ -5,6 +5,7 @@ import Api from '~/api';
import { updateHistory, setUrlParams } from '~/lib/utils/url_utility';
import Tracking from '~/tracking';
import {
FILTERED_SEARCH_TERM,
OPERATORS_IS,
TOKEN_TITLE_ASSIGNEE,
TOKEN_TITLE_AUTHOR,
@ -226,7 +227,7 @@ export default {
case 'assignee_username':
filterParams.assigneeUsername = isAny(filter.value.data);
break;
case 'filtered-search-term':
case FILTERED_SEARCH_TERM:
if (filter.value.data !== '') filterParams.search = filter.value.data;
break;
default:

View File

@ -1,7 +1,7 @@
<script>
import { GlSorting, GlSortingItem, GlFilteredSearch } from '@gitlab/ui';
import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
import { SORT_DIRECTION_UI } from '~/search/sort/constants';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
const ASCENDING_ORDER = 'asc';
const DESCENDING_ORDER = 'desc';

View File

@ -1208,11 +1208,11 @@ module Ci
if project.ci_cd_settings.opt_in_jwt?
id_tokens_variables
else
legacy_jwt_variables.concat(id_tokens_variables)
predefined_jwt_variables.concat(id_tokens_variables)
end
end
def legacy_jwt_variables
def predefined_jwt_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
jwt = Gitlab::Ci::Jwt.for_build(self)
jwt_v2 = Gitlab::Ci::JwtV2.for_build(self)
@ -1229,7 +1229,7 @@ module Ci
Gitlab::Ci::Variables::Collection.new.tap do |variables|
id_tokens.each do |var_name, token_data|
token = Gitlab::Ci::JwtV2.for_build(self, aud: token_data['id_token']['aud'])
token = Gitlab::Ci::JwtV2.for_build(self, aud: token_data['aud'])
variables.append(key: var_name, value: token, public: false, masked: true)
end

View File

@ -5,18 +5,27 @@
"patternProperties": {
".*": {
"type": "object",
"patternProperties": {
"^id_token$": {
"type": "object",
"required": ["aud"],
"properties": {
"aud": { "type": "string" },
"field": { "type": "string" }
},
"additionalProperties": false
"required": [
"aud"
],
"properties": {
"aud": {
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"uniqueItems": true
}
]
}
},
"additionalProperties": false
}
}
}
}

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
module Gitlab
module Ci
class Config
module Entry
##
# Entry that represents a JWT definition.
#
class IdToken < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Attributable
include ::Gitlab::Config::Entry::Validatable
attributes %i[aud]
validations do
validates :config, required_keys: %i[aud], allowed_keys: %i[aud]
validates :aud, array_of_strings_or_string: true
end
def value
{ aud: aud }
end
end
end
end
end
end

View File

@ -14,7 +14,7 @@ module Gitlab
ALLOWED_KEYS = %i[tags script image services start_in artifacts
cache dependencies before_script after_script
environment coverage retry parallel interruptible timeout
release].freeze
release id_tokens].freeze
validations do
validates :config, allowed_keys: Gitlab::Ci::Config::Entry::Job.allowed_keys + PROCESSABLE_ALLOWED_KEYS
@ -116,6 +116,11 @@ module Gitlab
description: 'Indicates whether this job is allowed to fail or not.',
inherit: false
entry :id_tokens, ::Gitlab::Config::Entry::ComposableHash,
description: 'Configured JWTs for this job',
inherit: false,
metadata: { composable_class: ::Gitlab::Ci::Config::Entry::IdToken }
attributes :script, :tags, :when, :dependencies,
:needs, :retry, :parallel, :start_in,
:interruptible, :timeout,
@ -158,7 +163,8 @@ module Gitlab
ignore: ignored?,
allow_failure_criteria: allow_failure_criteria,
needs: needs_defined? ? needs_value : nil,
scheduling_type: needs_defined? ? :dag : :stage
scheduling_type: needs_defined? ? :dag : :stage,
id_tokens: id_tokens_value
).compact
end

View File

@ -107,6 +107,7 @@ module Gitlab
cache: job[:cache],
resource_group_key: job[:resource_group],
scheduling_type: job[:scheduling_type],
id_tokens: job[:id_tokens],
options: {
image: job[:image],
services: job[:services],

View File

@ -13907,6 +13907,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
msgid "DevOps metrics comparison (Alpha)"
msgstr ""
msgid "Developer"
msgstr ""
@ -16206,9 +16209,6 @@ msgstr ""
msgid "Execution time"
msgstr ""
msgid "Executive Dashboard"
msgstr ""
msgid "Existing branch name, tag, or commit SHA"
msgstr ""
@ -29295,9 +29295,6 @@ msgstr ""
msgid "PackageRegistry|To widen your search, change or remove the filters above."
msgstr ""
msgid "PackageRegistry|Type"
msgstr ""
msgid "PackageRegistry|Unable to fetch package version information."
msgstr ""

View File

@ -1,3 +1,5 @@
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
export default class FilteredSearchSpecHelper {
static createFilterVisualTokenHTML(name, operator, value, isSelected) {
return FilteredSearchSpecHelper.createFilterVisualToken(name, operator, value, isSelected)
@ -43,7 +45,7 @@ export default class FilteredSearchSpecHelper {
static createSearchVisualToken(name) {
const li = document.createElement('li');
li.classList.add('js-visual-token', 'filtered-search-term');
li.classList.add('js-visual-token', FILTERED_SEARCH_TERM);
li.innerHTML = `<div class="name">${name}</div>`;
return li;
}

View File

@ -13,6 +13,7 @@ import {
DEFAULT_SORT,
CONTACTED_DESC,
} from '~/ci/runner/constants';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
@ -34,7 +35,7 @@ describe('RunnerList', () => {
const mockOtherSort = CONTACTED_DESC;
const mockFilters = [
{ type: PARAM_KEY_STATUS, value: { data: STATUS_ONLINE, operator: '=' } },
{ type: 'filtered-search-term', value: { data: '' } },
{ type: FILTERED_SEARCH_TERM, value: { data: '' } },
];
const expectToHaveLastEmittedInput = (value) => {

View File

@ -18,6 +18,7 @@ import groupRunnersDataPaginated from 'test_fixtures/graphql/ci/runner/list/grou
import groupRunnersCountData from 'test_fixtures/graphql/ci/runner/list/group_runners_count.query.graphql.json';
import { DEFAULT_MEMBERSHIP, RUNNER_PAGE_SIZE } from '~/ci/runner/constants';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
const emptyPageInfo = {
__typename: 'PageInfo',
@ -73,7 +74,7 @@ export const mockSearchExamples = [
membership: DEFAULT_MEMBERSHIP,
filters: [
{
type: 'filtered-search-term',
type: FILTERED_SEARCH_TERM,
value: { data: 'something' },
},
],
@ -95,11 +96,11 @@ export const mockSearchExamples = [
membership: DEFAULT_MEMBERSHIP,
filters: [
{
type: 'filtered-search-term',
type: FILTERED_SEARCH_TERM,
value: { data: 'something' },
},
{
type: 'filtered-search-term',
type: FILTERED_SEARCH_TERM,
value: { data: 'else' },
},
],

View File

@ -6,6 +6,7 @@ import {
fromSearchToVariables,
isSearchFiltered,
} from 'ee_else_ce/ci/runner/runner_search_utils';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import { mockSearchExamples } from './mock_data';
describe('search_params.js', () => {
@ -48,8 +49,8 @@ describe('search_params.js', () => {
it('When search params appear as array, they are concatenated', () => {
expect(fromUrlQueryToSearch('?search[]=my&search[]=text').filters).toEqual([
{ type: 'filtered-search-term', value: { data: 'my' } },
{ type: 'filtered-search-term', value: { data: 'text' } },
{ type: FILTERED_SEARCH_TERM, value: { data: 'my' } },
{ type: FILTERED_SEARCH_TERM, value: { data: 'text' } },
]);
});
});
@ -93,7 +94,7 @@ describe('search_params.js', () => {
fromSearchToVariables({
filters: [
{
type: 'filtered-search-term',
type: FILTERED_SEARCH_TERM,
value: { data: '' },
},
],
@ -106,11 +107,11 @@ describe('search_params.js', () => {
fromSearchToVariables({
filters: [
{
type: 'filtered-search-term',
type: FILTERED_SEARCH_TERM,
value: { data: 'something' },
},
{
type: 'filtered-search-term',
type: FILTERED_SEARCH_TERM,
value: { data: '' },
},
],

View File

@ -30,6 +30,7 @@ import RulesYaml from './yaml_tests/positive_tests/rules.yml';
import ProjectPathYaml from './yaml_tests/positive_tests/project_path.yml';
import VariablesYaml from './yaml_tests/positive_tests/variables.yml';
import JobWhenYaml from './yaml_tests/positive_tests/job_when.yml';
import IdTokensYaml from './yaml_tests/positive_tests/id_tokens.yml';
// YAML NEGATIVE TEST
import ArtifactsNegativeYaml from './yaml_tests/negative_tests/artifacts.yml';
@ -45,6 +46,7 @@ import RulesNegativeYaml from './yaml_tests/negative_tests/rules.yml';
import TriggerNegative from './yaml_tests/negative_tests/trigger.yml';
import VariablesInvalidSyntaxDescYaml from './yaml_tests/negative_tests/variables/invalid_syntax_desc.yml';
import VariablesWrongSyntaxUsageExpand from './yaml_tests/negative_tests/variables/wrong_syntax_usage_expand.yml';
import IdTokensNegativeYaml from './yaml_tests/negative_tests/id_tokens.yml';
const ajv = new Ajv({
strictTypes: false,
@ -80,6 +82,7 @@ describe('positive tests', () => {
RulesYaml,
VariablesYaml,
ProjectPathYaml,
IdTokensYaml,
}),
)('schema validates %s', (_, input) => {
// We construct a new "JSON" from each main key that is inside a
@ -103,6 +106,7 @@ describe('negative tests', () => {
// YAML
ArtifactsNegativeYaml,
CacheKeyNeative,
IdTokensNegativeYaml,
IncludeNegativeYaml,
JobWhenNegativeYaml,
RulesNegativeYaml,

View File

@ -0,0 +1,11 @@
id_token_with_wrong_aud_type:
id_tokens:
INVALID_ID_TOKEN:
aud:
invalid_prop: invalid
id_token_with_extra_properties:
id_tokens:
INVALID_ID_TOKEN:
aud: 'https://gitlab.com'
sub: 'not a valid property'

View File

@ -0,0 +1,11 @@
valid_id_tokens:
script:
- echo $ID_TOKEN_1
- echo $ID_TOKEN_2
id_tokens:
ID_TOKEN_1:
aud: 'https://gitlab.com'
ID_TOKEN_2:
aud:
- 'https://aws.com'
- 'https://google.com'

View File

@ -4,6 +4,7 @@ import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import FilteredSearchSpecHelper from 'helpers/filtered_search_spec_helper';
import waitForPromises from 'helpers/wait_for_promises';
import FilteredSearchVisualTokens from '~/filtered_search/filtered_search_visual_tokens';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
describe('Filtered Search Visual Tokens', () => {
let mock;
@ -302,7 +303,7 @@ describe('Filtered Search Visual Tokens', () => {
});
const token = tokensContainer.querySelector('.js-visual-token');
expect(token.classList.contains('filtered-search-term')).toEqual(true);
expect(token.classList.contains(FILTERED_SEARCH_TERM)).toEqual(true);
expect(token.querySelector('.name').innerText).toEqual('search term');
expect(token.querySelector('.operator').innerText).toEqual('=');
expect(token.querySelector('.value')).toEqual(null);
@ -430,7 +431,7 @@ describe('Filtered Search Visual Tokens', () => {
subject.addSearchVisualToken('search term');
const token = tokensContainer.querySelector('.js-visual-token');
expect(token.classList.contains('filtered-search-term')).toEqual(true);
expect(token.classList.contains(FILTERED_SEARCH_TERM)).toEqual(true);
expect(token.querySelector('.name').innerText).toEqual('search term');
expect(token.querySelector('.value')).toEqual(null);
});

View File

@ -9,6 +9,7 @@ import {
FILTERED_SEARCH_TOKEN_TWO_FACTOR,
FILTERED_SEARCH_TOKEN_WITH_INHERITED_PERMISSIONS,
} from '~/members/constants';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
jest.mock('~/lib/utils/url_utility', () => {
@ -130,7 +131,7 @@ describe('MembersFilteredSearchBar', () => {
expect(findFilteredSearchBar().props('initialFilterValue')).toEqual([
{
type: 'filtered-search-term',
type: FILTERED_SEARCH_TERM,
value: {
data: 'foobar',
},
@ -145,7 +146,7 @@ describe('MembersFilteredSearchBar', () => {
expect(findFilteredSearchBar().props('initialFilterValue')).toEqual([
{
type: 'filtered-search-term',
type: FILTERED_SEARCH_TERM,
value: {
data: 'foo bar baz',
},
@ -174,7 +175,7 @@ describe('MembersFilteredSearchBar', () => {
findFilteredSearchBar().vm.$emit('onFilter', [
{ type: FILTERED_SEARCH_TOKEN_TWO_FACTOR.type, value: { data: 'enabled', operator: '=' } },
{ type: 'filtered-search-term', value: { data: 'foobar' } },
{ type: FILTERED_SEARCH_TERM, value: { data: 'foobar' } },
]);
expect(redirectTo).toHaveBeenCalledWith(
@ -187,7 +188,7 @@ describe('MembersFilteredSearchBar', () => {
findFilteredSearchBar().vm.$emit('onFilter', [
{ type: FILTERED_SEARCH_TOKEN_TWO_FACTOR.type, value: { data: 'enabled', operator: '=' } },
{ type: 'filtered-search-term', value: { data: 'foo bar baz' } },
{ type: FILTERED_SEARCH_TERM, value: { data: 'foo bar baz' } },
]);
expect(redirectTo).toHaveBeenCalledWith(
@ -202,7 +203,7 @@ describe('MembersFilteredSearchBar', () => {
findFilteredSearchBar().vm.$emit('onFilter', [
{ type: FILTERED_SEARCH_TOKEN_TWO_FACTOR.type, value: { data: 'enabled', operator: '=' } },
{ type: 'filtered-search-term', value: { data: 'foobar' } },
{ type: FILTERED_SEARCH_TERM, value: { data: 'foobar' } },
]);
expect(redirectTo).toHaveBeenCalledWith(
@ -216,7 +217,7 @@ describe('MembersFilteredSearchBar', () => {
createComponent();
findFilteredSearchBar().vm.$emit('onFilter', [
{ type: 'filtered-search-term', value: { data: 'foobar' } },
{ type: FILTERED_SEARCH_TERM, value: { data: 'foobar' } },
]);
expect(redirectTo).toHaveBeenCalledWith('https://localhost/?search=foobar&tab=invited');

View File

@ -18,7 +18,7 @@ import {
NO_TAGS_MATCHING_FILTERS_TITLE,
NO_TAGS_MATCHING_FILTERS_DESCRIPTION,
} from '~/packages_and_registries/container_registry/explorer/constants/index';
import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import { tagsMock, imageTagsMock, tagsPageInfo } from '../../mock_data';
describe('Tags List', () => {

View File

@ -6,7 +6,6 @@ import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import getContainerRepositoriesQuery from 'shared_queries/container_registry/get_container_repositories.query.graphql';
import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
import DeleteImage from '~/packages_and_registries/container_registry/explorer/components/delete_image.vue';
import CliCommands from '~/packages_and_registries/shared/components/cli_commands.vue';
import GroupEmptyState from '~/packages_and_registries/container_registry/explorer/components/list_page/group_empty_state.vue';
@ -23,6 +22,7 @@ import getContainerRepositoriesDetails from '~/packages_and_registries/container
import component from '~/packages_and_registries/container_registry/explorer/pages/list.vue';
import Tracking from '~/tracking';
import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
import { $toast } from 'jest/packages_and_registries/shared/mocks';

View File

@ -7,13 +7,11 @@ import { createAlert, VARIANT_INFO } from '~/flash';
import * as commonUtils from '~/lib/utils/common_utils';
import PackageListApp from '~/packages_and_registries/infrastructure_registry/list/components/packages_list_app.vue';
import { DELETE_PACKAGE_SUCCESS_MESSAGE } from '~/packages_and_registries/infrastructure_registry/list/constants';
import {
SHOW_DELETE_SUCCESS_ALERT,
FILTERED_SEARCH_TERM,
} from '~/packages_and_registries/shared/constants';
import { SHOW_DELETE_SUCCESS_ALERT } from '~/packages_and_registries/shared/constants';
import * as packageUtils from '~/packages_and_registries/shared/utils';
import InfrastructureSearch from '~/packages_and_registries/infrastructure_registry/list/components/infrastructure_search.vue';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
jest.mock('~/lib/utils/common_utils');
jest.mock('~/flash');

View File

@ -1,4 +1,3 @@
import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
import {
getQueryParams,
keyValueToFilterToken,
@ -7,6 +6,7 @@ import {
beautifyPath,
getCommitLink,
} from '~/packages_and_registries/shared/utils';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import { packageList } from 'jest/packages_and_registries/infrastructure_registry/components/mock_data';

View File

@ -6,7 +6,10 @@ import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import Api from '~/api';
import axios from '~/lib/utils/axios_utils';
import PipelinesFilteredSearch from '~/pipelines/components/pipelines_list/pipelines_filtered_search.vue';
import { OPERATORS_IS } from '~/vue_shared/components/filtered_search_bar/constants';
import {
FILTERED_SEARCH_TERM,
OPERATORS_IS,
} from '~/vue_shared/components/filtered_search_bar/constants';
import { TRACKING_CATEGORIES } from '~/pipelines/constants';
import { users, mockSearch, branches, tags } from '../mock_data';
@ -111,7 +114,7 @@ describe('Pipelines filtered search', () => {
it('disables tag name token when branch name token is active', async () => {
findFilteredSearch().vm.$emit('input', [
{ type: 'ref', value: { data: 'branch-1', operator: '=' } },
{ type: 'filtered-search-term', value: { data: '' } },
{ type: FILTERED_SEARCH_TERM, value: { data: '' } },
]);
await nextTick();
@ -122,7 +125,7 @@ describe('Pipelines filtered search', () => {
it('disables branch name token when tag name token is active', async () => {
findFilteredSearch().vm.$emit('input', [
{ type: 'tag', value: { data: 'tag-1', operator: '=' } },
{ type: 'filtered-search-term', value: { data: '' } },
{ type: FILTERED_SEARCH_TERM, value: { data: '' } },
]);
await nextTick();
@ -139,7 +142,7 @@ describe('Pipelines filtered search', () => {
});
it('resets tokens disabled state when clearing tokens by backspace', async () => {
findFilteredSearch().vm.$emit('input', [{ type: 'filtered-search-term', value: { data: '' } }]);
findFilteredSearch().vm.$emit('input', [{ type: FILTERED_SEARCH_TERM, value: { data: '' } }]);
await nextTick();
expect(findBranchToken().disabled).toBe(false);
@ -172,7 +175,7 @@ describe('Pipelines filtered search', () => {
operator: '=',
},
},
{ type: 'filtered-search-term', value: { data: '' } },
{ type: FILTERED_SEARCH_TERM, value: { data: '' } },
];
expect(findFilteredSearch().props('value')).toMatchObject(expectedValueProp);

View File

@ -1,6 +1,6 @@
import { GlSorting, GlSortingItem, GlFilteredSearch } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
import component from '~/vue_shared/components/registry/registry_search.vue';
describe('Registry Search', () => {

View File

@ -0,0 +1,58 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::Entry::IdToken do
context 'when given `aud` as a string' do
it 'is valid' do
config = { aud: 'https://gitlab.com' }
id_token = described_class.new(config)
id_token.compose!
expect(id_token).to be_valid
expect(id_token.value).to eq(aud: 'https://gitlab.com')
end
end
context 'when given `aud` as an array' do
it 'is valid and concatenates the values' do
config = { aud: ['https://gitlab.com', 'https://aws.com'] }
id_token = described_class.new(config)
id_token.compose!
expect(id_token).to be_valid
expect(id_token.value).to eq(aud: ['https://gitlab.com', 'https://aws.com'])
end
end
context 'when not given an `aud`' do
it 'is invalid' do
config = {}
id_token = described_class.new(config)
id_token.compose!
expect(id_token).not_to be_valid
expect(id_token.errors).to match_array([
'id token config missing required keys: aud',
'id token aud should be an array of strings or a string'
])
end
end
context 'when given an unknown keyword' do
it 'is invalid' do
config = { aud: 'https://gitlab.com', unknown: 'test' }
id_token = described_class.new(config)
id_token.compose!
expect(id_token).not_to be_valid
expect(id_token.errors).to match_array([
'id token config contains unknown keys: unknown'
])
end
end
end

View File

@ -716,7 +716,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
let(:config) do
{ before_script: %w[ls pwd],
script: 'rspec',
after_script: %w[cleanup] }
after_script: %w[cleanup],
id_tokens: { TEST_ID_TOKEN: { aud: 'https://gitlab.com' } } }
end
it 'returns correct value' do
@ -730,7 +731,8 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
only: { refs: %w[branches tags] },
job_variables: {},
root_variables_inheritance: true,
scheduling_type: :stage)
scheduling_type: :stage,
id_tokens: { TEST_ID_TOKEN: { aud: 'https://gitlab.com' } })
end
end
end

View File

@ -12,6 +12,20 @@ module Gitlab
let(:ci_config) { Gitlab::Ci::Config.new(config_content, user: user) }
let(:result) { described_class.new(ci_config: ci_config, warnings: ci_config&.warnings) }
describe '#builds' do
context 'when a job has ID tokens' do
let(:config_content) do
YAML.dump(
test: { stage: 'test', script: 'echo', id_tokens: { TEST_ID_TOKEN: { aud: 'https://gitlab.com' } } }
)
end
it 'includes `id_tokens`' do
expect(result.builds.first[:id_tokens]).to eq({ TEST_ID_TOKEN: { aud: 'https://gitlab.com' } })
end
end
end
describe '#config_metadata' do
subject(:config_metadata) { result.config_metadata }

View File

@ -107,9 +107,7 @@ RSpec.describe Ci::BuildMetadata do
}
metadata.id_tokens = {
TEST_JWT_TOKEN: {
id_token: {
aud: 'https://gitlab.test'
}
aud: 'https://gitlab.test'
}
}

View File

@ -3539,8 +3539,8 @@ RSpec.describe Ci::Build do
rsa_key = OpenSSL::PKey::RSA.generate(3072).to_s
stub_application_setting(ci_jwt_signing_key: rsa_key)
build.metadata.update!(id_tokens: {
'ID_TOKEN_1' => { id_token: { aud: 'developers' } },
'ID_TOKEN_2' => { id_token: { aud: 'maintainers' } }
'ID_TOKEN_1' => { aud: 'developers' },
'ID_TOKEN_2' => { aud: 'maintainers' }
})
end

View File

@ -710,6 +710,29 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
end
end
context 'when the configuration includes ID tokens' do
it 'creates variables for the ID tokens' do
config = YAML.dump({
job_with_id_tokens: {
script: 'ls',
id_tokens: {
'TEST_ID_TOKEN' => {
aud: 'https://gitlab.com'
}
}
}
})
stub_ci_pipeline_yaml_file(config)
result = execute_service.payload
expect(result).to be_persisted
expect(result.builds.first.id_tokens).to eq({
'TEST_ID_TOKEN' => { 'aud' => 'https://gitlab.com' }
})
end
end
context 'with manual actions' do
before do
config = YAML.dump({ deploy: { script: 'ls', when: 'manual' } })