diff options
53 files changed, 920 insertions, 377 deletions
diff --git a/.rubocop_todo/layout/first_array_element_indentation.yml b/.rubocop_todo/layout/first_array_element_indentation.yml index a30ed786e5d..b90a4621be9 100644 --- a/.rubocop_todo/layout/first_array_element_indentation.yml +++ b/.rubocop_todo/layout/first_array_element_indentation.yml @@ -4,25 +4,6 @@ Layout/FirstArrayElementIndentation: Exclude: - 'lib/gitlab/email/message/in_product_marketing/trial.rb' - 'qa/qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb' - - 'qa/qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_commit_spec.rb' - - 'qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb' - - 'qa/qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb' - - 'qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb' - - 'qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb' - - 'qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb' - - 'qa/qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb' - - 'qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb' - - 'qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb' - - 'qa/qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb' - - 'qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb' - - 'qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb' - - 'qa/qa/specs/features/ee/api/1_manage/user/minimal_access_user_spec.rb' - - 'qa/qa/specs/features/ee/api/9_enablement/elasticsearch/advanced_global_advanced_syntax_search_spec.rb' - - 'qa/qa/specs/features/ee/api/9_enablement/elasticsearch/elasticsearch_api_spec.rb' - - 'qa/qa/specs/features/ee/api/9_enablement/elasticsearch/index_tests/main_index/blob_index_spec.rb' - - 'qa/qa/specs/features/ee/api/9_enablement/elasticsearch/nightly_elasticsearch_test_spec.rb' - - 'qa/qa/specs/features/ee/browser_ui/3_create/repository/code_owners_with_protected_branch_and_squashed_commits_spec.rb' - - 'qa/qa/specs/features/ee/browser_ui/4_verify/new_discussion_not_dropping_merge_trains_mr_spec.rb' - 'spec/controllers/concerns/send_file_upload_spec.rb' - 'spec/graphql/types/packages/tag_type_spec.rb' - 'spec/helpers/application_settings_helper_spec.rb' diff --git a/app/assets/javascripts/ide/utils.js b/app/assets/javascripts/ide/utils.js index a7e6506b045..83a3d7f2ac3 100644 --- a/app/assets/javascripts/ide/utils.js +++ b/app/assets/javascripts/ide/utils.js @@ -1,5 +1,6 @@ import { flatten, isString } from 'lodash'; import { languages } from 'monaco-editor'; +import { setDiagnosticsOptions as yamlDiagnosticsOptions } from 'monaco-yaml'; import { performanceMarkAndMeasure } from '~/performance/utils'; import { SIDE_LEFT, SIDE_RIGHT } from './constants'; @@ -82,17 +83,16 @@ export function registerLanguages(def, ...defs) { } export function registerSchema(schema, options = {}) { - const defaults = [languages.json.jsonDefaults, languages.yaml.yamlDefaults]; - defaults.forEach((d) => - d.setDiagnosticsOptions({ - validate: true, - enableSchemaRequest: true, - hover: true, - completion: true, - schemas: [schema], - ...options, - }), - ); + const defaultOptions = { + validate: true, + enableSchemaRequest: true, + hover: true, + completion: true, + schemas: [schema], + ...options, + }; + languages.json.jsonDefaults.setDiagnosticsOptions(defaultOptions); + yamlDiagnosticsOptions(defaultOptions); } export const otherSide = (side) => (side === SIDE_RIGHT ? SIDE_LEFT : SIDE_RIGHT); diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/branch_dropdown.vue b/app/assets/javascripts/projects/settings/branch_rules/components/edit/branch_dropdown.vue index 6ba2ef7da99..f2b1c749abc 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/components/branch_dropdown.vue +++ b/app/assets/javascripts/projects/settings/branch_rules/components/edit/branch_dropdown.vue @@ -10,7 +10,7 @@ import { import { createAlert } from '~/flash'; import { s__, sprintf } from '~/locale'; import { helpPagePath } from '~/helpers/help_page_helper'; -import branchesQuery from '../queries/branches.query.graphql'; +import branchesQuery from '../../queries/branches.query.graphql'; export const i18n = { fetchBranchesError: s__('BranchRules|An error occurred while fetching branches.'), diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/rule_edit.vue b/app/assets/javascripts/projects/settings/branch_rules/components/edit/index.vue index ad3eb7d2899..ad3eb7d2899 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/components/rule_edit.vue +++ b/app/assets/javascripts/projects/settings/branch_rules/components/edit/index.vue diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/protections/index.vue b/app/assets/javascripts/projects/settings/branch_rules/components/edit/protections/index.vue index bcc0f64d667..bcc0f64d667 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/components/protections/index.vue +++ b/app/assets/javascripts/projects/settings/branch_rules/components/edit/protections/index.vue diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/protections/merge_protections.vue b/app/assets/javascripts/projects/settings/branch_rules/components/edit/protections/merge_protections.vue index 85f168af4a8..85f168af4a8 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/components/protections/merge_protections.vue +++ b/app/assets/javascripts/projects/settings/branch_rules/components/edit/protections/merge_protections.vue diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/protections/push_protections.vue b/app/assets/javascripts/projects/settings/branch_rules/components/edit/protections/push_protections.vue index 541923bb735..541923bb735 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/components/protections/push_protections.vue +++ b/app/assets/javascripts/projects/settings/branch_rules/components/edit/protections/push_protections.vue diff --git a/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js b/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js index 8452542540e..10de5f12757 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js +++ b/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js @@ -1,7 +1,7 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createDefaultClient from '~/lib/graphql'; -import RuleEdit from './components/rule_edit.vue'; +import RuleEdit from './components/edit/index.vue'; export default function mountBranchRules(el) { if (!el) { diff --git a/app/finders/personal_access_tokens_finder.rb b/app/finders/personal_access_tokens_finder.rb index 7d356c1014c..8403c531945 100644 --- a/app/finders/personal_access_tokens_finder.rb +++ b/app/finders/personal_access_tokens_finder.rb @@ -18,6 +18,12 @@ class PersonalAccessTokensFinder tokens = by_impersonation(tokens) tokens = by_state(tokens) tokens = by_owner_type(tokens) + tokens = by_revoked_state(tokens) + tokens = by_created_before(tokens) + tokens = by_created_after(tokens) + tokens = by_last_used_before(tokens) + tokens = by_last_used_after(tokens) + tokens = by_search(tokens) sort(tokens) end @@ -83,4 +89,40 @@ class PersonalAccessTokensFinder tokens end end + + def by_revoked_state(tokens) + return tokens unless params.has_key?(:revoked) + + params[:revoked] ? tokens.revoked : tokens.not_revoked + end + + def by_created_before(tokens) + return tokens unless params[:created_before] + + tokens.created_before(params[:created_before]) + end + + def by_created_after(tokens) + return tokens unless params[:created_after] + + tokens.created_after(params[:created_after]) + end + + def by_last_used_before(tokens) + return tokens unless params[:last_used_before] + + tokens.last_used_before(params[:last_used_before]) + end + + def by_last_used_after(tokens) + return tokens unless params[:last_used_after] + + tokens.last_used_after(params[:last_used_after]) + end + + def by_search(tokens) + return tokens unless params[:search] + + tokens.search(params[:search]) + end end diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb index 9ed25c56ed6..f0ed1822da6 100644 --- a/app/models/personal_access_token.rb +++ b/app/models/personal_access_token.rb @@ -5,6 +5,8 @@ class PersonalAccessToken < ApplicationRecord include TokenAuthenticatable include Sortable include EachBatch + include CreatedAtFilterable + include Gitlab::SQL::Pattern extend ::Gitlab::Utils::Override add_authentication_token_field :token, digest: true @@ -24,7 +26,6 @@ class PersonalAccessToken < ApplicationRecord scope :expiring_and_not_notified, ->(date) { where(["revoked = false AND expire_notification_delivered = false AND expires_at >= CURRENT_DATE AND expires_at <= ?", date]) } scope :expired_today_and_not_notified, -> { where(["revoked = false AND expires_at = CURRENT_DATE AND after_expiry_notification_delivered = false"]) } scope :inactive, -> { where("revoked = true OR expires_at < CURRENT_DATE") } - scope :created_before, -> (date) { where("personal_access_tokens.created_at < :date", date: date) } scope :last_used_before_or_unused, -> (date) { where("personal_access_tokens.created_at < :date AND (last_used_at < :date OR last_used_at IS NULL)", date: date) } scope :with_impersonation, -> { where(impersonation: true) } scope :without_impersonation, -> { where(impersonation: false) } @@ -38,6 +39,8 @@ class PersonalAccessToken < ApplicationRecord scope :order_expires_at_asc_id_desc, -> { reorder(expires_at: :asc, id: :desc) } scope :project_access_token, -> { includes(:user).where(user: { user_type: :project_bot }) } scope :owner_is_human, -> { includes(:user).where(user: { user_type: :human }) } + scope :last_used_before, -> (date) { where("last_used_at <= ?", date) } + scope :last_used_after, -> (date) { where("last_used_at >= ?", date) } validates :scopes, presence: true validate :validate_scopes @@ -90,6 +93,10 @@ class PersonalAccessToken < ApplicationRecord Gitlab::CurrentSettings.current_application_settings.personal_access_token_prefix end + def self.search(query) + fuzzy_search(query, [:name]) + end + override :format_token def format_token(token) "#{self.class.token_prefix}#{token}" diff --git a/app/models/preloaders/project_root_ancestor_preloader.rb b/app/models/preloaders/project_root_ancestor_preloader.rb index 8d04e71774c..1e935249407 100644 --- a/app/models/preloaders/project_root_ancestor_preloader.rb +++ b/app/models/preloaders/project_root_ancestor_preloader.rb @@ -21,7 +21,8 @@ module Preloaders ActiveRecord::Associations::Preloader.new.preload(@projects, :namespace) @projects.each do |project| - project.namespace.root_ancestor = root_ancestors_by_id[project.id]&.first + root_ancestor = root_ancestors_by_id[project.id]&.first + project.namespace.root_ancestor = root_ancestor if root_ancestor.present? end end diff --git a/config/webpack.config.js b/config/webpack.config.js index 146e9b48442..5a38f278855 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -20,13 +20,13 @@ const webpack = require('webpack'); const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); const { StatsWriterPlugin } = require('webpack-stats-plugin'); const WEBPACK_VERSION = require('webpack/package.json').version; +const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); const createIncrementalWebpackCompiler = require('./helpers/incremental_webpack_compiler'); const IS_EE = require('./helpers/is_ee_env'); const IS_JH = require('./helpers/is_jh_env'); const vendorDllHash = require('./helpers/vendor_dll_hash'); -const MonacoWebpackPlugin = require('./plugins/monaco_webpack'); const GraphqlKnownOperationsPlugin = require('./plugins/graphql_known_operations_plugin'); const ROOT_PATH = path.resolve(__dirname, '..'); @@ -250,6 +250,20 @@ if (VENDOR_DLL && !IS_PRODUCTION) { }; } +const defaultJsOptions = { + cacheDirectory: path.join(CACHE_PATH, 'babel-loader'), + cacheIdentifier: [ + process.env.BABEL_ENV || process.env.NODE_ENV || 'development', + webpack.version, + BABEL_VERSION, + BABEL_LOADER_VERSION, + // Ensure that changing supported browsers will refresh the cache + // in order to not pull in outdated files that import core-js + SUPPORTED_BROWSERS_HASH, + ].join('|'), + cacheCompression: false, +}; + module.exports = { mode: IS_PRODUCTION ? 'production' : 'development', @@ -285,18 +299,18 @@ module.exports = { exclude: (modulePath) => /node_modules|vendor[\\/]assets/.test(modulePath) && !/\.vue\.js/.test(modulePath), loader: 'babel-loader', + options: defaultJsOptions, + }, + { + test: /\.js$/, + include: (modulePath) => + /node_modules\/(monaco-worker-manager|monaco-marker-data-provider)\/index\.js/.test( + modulePath, + ) || /node_modules\/yaml/.test(modulePath), + loader: 'babel-loader', options: { - cacheDirectory: path.join(CACHE_PATH, 'babel-loader'), - cacheIdentifier: [ - process.env.BABEL_ENV || process.env.NODE_ENV || 'development', - webpack.version, - BABEL_VERSION, - BABEL_LOADER_VERSION, - // Ensure that changing supported browsers will refresh the cache - // in order to not pull in outdated files that import core-js - SUPPORTED_BROWSERS_HASH, - ].join('|'), - cacheCompression: false, + plugins: ['@babel/plugin-proposal-numeric-separator'], + ...defaultJsOptions, }, }, { @@ -492,6 +506,16 @@ module.exports = { // automatically configure monaco editor web workers new MonacoWebpackPlugin({ filename: '[name].[contenthash:8].worker.js', + customLanguages: [ + { + label: 'yaml', + entry: 'monaco-yaml', + worker: { + id: 'monaco-yaml/yamlWorker', + entry: 'monaco-yaml/yaml.worker', + }, + }, + ], }), new GraphqlKnownOperationsPlugin({ filename: 'graphql_known_operations.yml' }), diff --git a/doc/administration/merge_request_diffs.md b/doc/administration/merge_request_diffs.md index fe1c74b0e24..fd0991b1771 100644 --- a/doc/administration/merge_request_diffs.md +++ b/doc/administration/merge_request_diffs.md @@ -271,3 +271,11 @@ By default, `sudo` does not preserve existing environment variables. You should ```shell sudo gitlab-rake gitlab:external_diffs:force_object_storage START_ID=59946109 END_ID=59946109 UPDATE_DELAY=5 ``` + +## Switching from external storage to object storage + +Automatic migration moves diffs stored in the database, but it does not move diffs between storage types. +To switch from external storage to object storage: + +1. Move files stored on local or NFS storage to object storage manually. +1. Run the Rake task in the [previous section](#correcting-incorrectly-migrated-diffs) to change their location in the database. diff --git a/doc/api/personal_access_tokens.md b/doc/api/personal_access_tokens.md index 849b5c75684..e7b491059ba 100644 --- a/doc/api/personal_access_tokens.md +++ b/doc/api/personal_access_tokens.md @@ -12,24 +12,56 @@ You can read more about [personal access tokens](../user/profile/personal_access > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227264) in GitLab 13.3. > - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/270200) from GitLab Ultimate to GitLab Free in 13.6. +> - `created_after`, `created_before`, `last_used_after`, `last_used_before`, `revoked`, `search` and `state` filters were [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/362248) in GitLab 15.4. -Get a list of personal access tokens. +Get all personal access tokens the authenticated user has access to. By default, returns an unfiltered list of: + +- Only personal access tokens created by the current user to a non-administrator. +- All personal access tokens to an administrator. + +Administrators: + +- Can use the `user_id` parameter to filter by a user. +- Can use other filters on all personal access tokens (GitLab 15.3 and later). + +Non-administrators: + +- Cannot use the `user_id` parameter to filter on any user except themselves, otherwise they receive a `401 Unauthorized` response. +- Can only filter on their own personal access tokens (GitLab 15.3 and later). ```plaintext GET /personal_access_tokens +GET /personal_access_tokens?created_after=2022-01-01T00:00:00 +GET /personal_access_tokens?created_before=2022-01-01T00:00:00 +GET /personal_access_tokens?last_used_after=2022-01-01T00:00:00 +GET /personal_access_tokens?last_used_before=2022-01-01T00:00:00 +GET /personal_access_tokens?revoked=true +GET /personal_access_tokens?search=name +GET /personal_access_tokens?state=inactive +GET /personal_access_tokens?user_id=1 ``` -| Attribute | Type | required | Description | -|-----------|---------|----------|---------------------| -| `user_id` | integer/string | no | The ID of the user to filter by | +Supported attributes: -NOTE: -Administrators can use the `user_id` parameter to filter by a user. Non-administrators cannot filter by any user except themselves. Attempting to do so will result in a `401 Unauthorized` response. +| Attribute | Type | Required | Description | +|---------------------|----------------|----------|---------------------| +| `created_after` | datetime (ISO 8601) | **{dotted-circle}** No | Limit results to PATs created after specified time. | +| `created_before` | datetime (ISO 8601) | **{dotted-circle}** No | Limit results to PATs created before specified time. | +| `last_used_after` | datetime (ISO 8601) | **{dotted-circle}** No | Limit results to PATs last used after specified time. | +| `last_used_before` | datetime (ISO 8601) | **{dotted-circle}** No | Limit results to PATs last used before specified time. | +| `revoked` | boolean | **{dotted-circle}** No | Limit results to PATs with specified revoked state. Valid values are `true` and `false`. | +| `search` | string | **{dotted-circle}** No | Limit results to PATs with name containing search string. | +| `state` | string | **{dotted-circle}** No | Limit results to PATs with specified state. Valid values are `active` and `inactive`. | +| `user_id` | integer or string | **{dotted-circle}** No | Limit results to PATs owned by specified user. | + +Example request: ```shell curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/personal_access_tokens" ``` +Example response: + ```json [ { @@ -48,10 +80,14 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a ] ``` +Example request: + ```shell curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/personal_access_tokens?user_id=3" ``` +Example response: + ```json [ { @@ -70,6 +106,38 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a ] ``` +Example request: + +```shell +curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/personal_access_tokens?revoked=true" +``` + +Example response: + +```json +[ + { + "id": 41, + "name": "Revoked Test Token", + "revoked": true, + "created_at": "2022-01-01T14:31:47.729Z", + "scopes": [ + "api" + ], + "user_id": 8, + "last_used_at": "2022-05-18T17:58:37.550Z", + "active": false, + "expires_at": null + } +] +``` + +You can filter by merged attributes with: + +```plaintext +GET /personal_access_tokens?revoked=true&created_before=2022-01-01 +``` + ## Get single personal access token by ID > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/362239) in GitLab 15.1. @@ -81,7 +149,7 @@ Administrators can get any token. GET /personal_access_tokens/:id ``` -| Attribute | Type | required | Description | +| Attribute | Type | Required | Description | |-----------|---------|----------|---------------------| | `id` | integer/string | yes | ID of personal access token | @@ -116,7 +184,7 @@ Revoke a personal access token using its ID. DELETE /personal_access_tokens/:id ``` -| Attribute | Type | required | Description | +| Attribute | Type | Required | Description | |-----------|---------|----------|---------------------| | `id` | integer/string | yes | ID of personal access token | diff --git a/doc/raketasks/import.md b/doc/raketasks/import.md index 5c95fe2eca1..9ccf238769d 100644 --- a/doc/raketasks/import.md +++ b/doc/raketasks/import.md @@ -19,7 +19,8 @@ Note that: - Existing projects are skipped. - Projects in hashed storage may be skipped. For more information, see [Importing bare repositories from hashed storage](#importing-bare-repositories-from-hashed-storage). -- The existing Git repositories ware moved from disk (removed from the original path). +- The existing Git repositories are moved from disk (removed from the original path). +- You must manually [push Git LFS objects](#push-git-lfs-objects). To import bare repositories into a GitLab instance: @@ -152,3 +153,12 @@ projects (this may take a while if there are 1000s of projects in a namespace): namespace = Namespace.find_by_full_path('gitlab-org') namespace.send(:write_projects_repository_config) ``` + +## Push Git LFS objects + +The import task doesn't import Git LFS objects. You must manually push the LFS objects to the newly +created GitLab repository using the following command: + +```shell +git lfs push --all +``` diff --git a/doc/subscriptions/index.md b/doc/subscriptions/index.md index e71954f1968..aedbeb8481e 100644 --- a/doc/subscriptions/index.md +++ b/doc/subscriptions/index.md @@ -73,26 +73,30 @@ click E "./self_managed/index.html#view-your-subscription" With the [Customers Portal](https://customers.gitlab.com/) you can: -- [Change your personal details](#change-your-personal-details) +- [Change account owner information](#change-account-owner-information) - [Change your company details](#change-your-company-details) - [Change your payment method](#change-your-payment-method) - [Change the linked account](#change-the-linked-account) - [Change the namespace the subscription is linked to](#change-the-linked-namespace) - [Change customers portal account password](#change-customers-portal-account-password) -### Change your personal details +### Change account owner information -Your personal details are used on invoices. Your email address is used for the Customers Portal -login and license-related email. +Account owner personal details are used on invoices. The account owner email +address is used for the Customers Portal login and license-related email. -To change your personal details, including name, billing address, and email address: +To change account owner information, including name, billing address, and email address: 1. Log in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in). 1. Select **My account > Account details**. 1. Expand the **Personal details** section. -1. Edit your personal details. +1. Edit the personal details. 1. Select **Save changes**. +If you want to transfer ownership of the Customers Portal account +to another person, after you enter that person's personal details, you must also +[change the Customers Portal account password](#change-customers-portal-account-password). + ### Change your company details To change your company details, including company name and VAT number: diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md index 207b7e6f2d8..30849d2754b 100644 --- a/doc/user/admin_area/index.md +++ b/doc/user/admin_area/index.md @@ -270,10 +270,12 @@ To [Create a new group](../group/manage.md#create-a-group) select **New group**. ### Administering Topics -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/340920) in GitLab 14.4. +- > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/340920) in GitLab 14.4. +- > Merging topics [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/366884) in GitLab 15.5. -You can administer all [topics](../project/working_with_projects.md#explore-topics) in the -GitLab instance from the Admin Area's Topics page. +[Topics](../project/working_with_projects.md#explore-topics) are used to categorize and find similar projects. + +You can administer all topics in the GitLab instance from the Admin Area's Topics page. To access the Topics page: @@ -295,7 +297,7 @@ insensitive and applies partial matching. NOTE: The assigned topics are visible only to everyone with access to the project, -but everyone can see which topics exist at all on the GitLab instance. +but everyone can see which topics exist on the GitLab instance. Do not include sensitive information in the name of a topic. ### Administering Jobs diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md index e4cf905bbce..1d88c41b554 100644 --- a/doc/user/profile/index.md +++ b/doc/user/profile/index.md @@ -27,6 +27,12 @@ To access your user settings: ## Change your password +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23610) in GitLab 15.4 [with a flag](../../administration/feature_flags.md) named `block_weak_passwords`, weak passwords aren't accepted. Disabled by default. + +FLAG: +On self-managed GitLab, by default blocking weak passwords is not available. To make it available, ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `block_weak_passwords`. On GitLab.com, this feature is available but can be configured by GitLab.com administrators only. +The feature is not ready for production use. + To change your password: 1. On the top bar, in the top-right corner, select your avatar. diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md index 5c2118e02cf..b42694ff1de 100644 --- a/doc/user/project/settings/index.md +++ b/doc/user/project/settings/index.md @@ -40,6 +40,9 @@ To assign topics to a project: 1. In the **Topics** text box, enter the project topics. Popular topics are suggested as you type. 1. Select **Save changes**. +If you're an instance administrator, you can administer all project topics from the +[Admin Area's Topics page](../../admin_area/index.md#administering-topics). + ## Compliance frameworks **(PREMIUM)** > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276221) in GitLab 13.9. diff --git a/doc/user/project/working_with_projects.md b/doc/user/project/working_with_projects.md index e58bf5aa557..9b52652cea2 100644 --- a/doc/user/project/working_with_projects.md +++ b/doc/user/project/working_with_projects.md @@ -13,6 +13,11 @@ code are saved in projects, and most features are in the scope of projects. To view projects, on the top bar, select **Main menu > Projects > View all projects**. +NOTE: +The **Explore projects** tab is visible to unauthenticated users unless the +[**Public** visibility level](../admin_area/settings/visibility_and_access_controls.md#restrict-visibility-levels) +is restricted. Then the tab is visible only to signed-in users. + ### Who can view the Projects page When you select a project, the project landing page shows the project contents. @@ -46,11 +51,6 @@ To explore project topics: The **Explore topics** tab shows a list of topics sorted by the number of associated projects. -NOTE: -The **Explore projects** tab is visible to unauthenticated users unless the -[**Public** visibility level](../admin_area/settings/visibility_and_access_controls.md#restrict-visibility-levels) -is restricted. Then the tab is visible only to signed-in users. - You can assign topics to a project on the [Project Settings page](settings/index.md#assign-topics-to-a-project). If you're an instance administrator, you can administer all project topics from the diff --git a/jest.config.base.js b/jest.config.base.js index d90a3b9825e..b631c2009d9 100644 --- a/jest.config.base.js +++ b/jest.config.base.js @@ -144,6 +144,8 @@ module.exports = (path, options = {}) => { 'three', 'monaco-editor', 'monaco-yaml', + 'monaco-marker-data-provider', + 'monaco-worker-manager', 'fast-mersenne-twister', 'prosemirror-markdown', 'marked', diff --git a/lib/api/helpers/personal_access_tokens_helpers.rb b/lib/api/helpers/personal_access_tokens_helpers.rb index db28daa5396..4fd72d89f4c 100644 --- a/lib/api/helpers/personal_access_tokens_helpers.rb +++ b/lib/api/helpers/personal_access_tokens_helpers.rb @@ -4,11 +4,14 @@ module API module Helpers module PersonalAccessTokensHelpers def finder_params(current_user) - if current_user.can_admin_all_resources? - { user: user(params[:user_id]) } - else - { user: current_user, impersonation: false } - end + user_param = + if current_user.can_admin_all_resources? + { user: user(params[:user_id]) } + else + { user: current_user, impersonation: false } + end + + declared(params, include_missing: false).merge(user_param) end def user(user_id) diff --git a/lib/api/personal_access_tokens.rb b/lib/api/personal_access_tokens.rb index 1c00569bba2..a2903faa4ad 100644 --- a/lib/api/personal_access_tokens.rb +++ b/lib/api/personal_access_tokens.rb @@ -11,7 +11,15 @@ module API success Entities::PersonalAccessToken end params do - optional :user_id, type: Integer, desc: 'User ID' + optional :user_id, type: Integer, desc: 'Filter PATs by User ID' + optional :revoked, type: Boolean, desc: 'Filter PATs where revoked state matches parameter' + optional :state, type: String, desc: 'Filter PATs which are either active or not', + values: %w[active inactive] + optional :created_before, type: DateTime, desc: 'Filter PATs which were created before given datetime' + optional :created_after, type: DateTime, desc: 'Filter PATs which were created after given datetime' + optional :last_used_before, type: DateTime, desc: 'Filter PATs which were used before given datetime' + optional :last_used_after, type: DateTime, desc: 'Filter PATs which were used after given datetime' + optional :search, type: String, desc: 'Filters PATs by its name' use :pagination end diff --git a/lib/gitlab/usage/metrics/aggregates/aggregate.rb b/lib/gitlab/usage/metrics/aggregates/aggregate.rb index 11e2fd22638..d8d089c8575 100644 --- a/lib/gitlab/usage/metrics/aggregates/aggregate.rb +++ b/lib/gitlab/usage/metrics/aggregates/aggregate.rb @@ -13,62 +13,72 @@ module Gitlab end def all_time_data - aggregated_metrics_data(start_date: nil, end_date: nil, time_frame: Gitlab::Usage::TimeFrame::ALL_TIME_TIME_FRAME_NAME) + aggregated_metrics_data(Gitlab::Usage::TimeFrame::ALL_TIME_TIME_FRAME_NAME) end def monthly_data - aggregated_metrics_data(**monthly_time_range.merge(time_frame: Gitlab::Usage::TimeFrame::TWENTY_EIGHT_DAYS_TIME_FRAME_NAME)) + aggregated_metrics_data(Gitlab::Usage::TimeFrame::TWENTY_EIGHT_DAYS_TIME_FRAME_NAME) end def weekly_data - aggregated_metrics_data(**weekly_time_range.merge(time_frame: Gitlab::Usage::TimeFrame::SEVEN_DAYS_TIME_FRAME_NAME)) + aggregated_metrics_data(Gitlab::Usage::TimeFrame::SEVEN_DAYS_TIME_FRAME_NAME) end private attr_accessor :aggregated_metrics, :recorded_at - def aggregated_metrics_data(start_date:, end_date:, time_frame:) + def aggregated_metrics_data(time_frame) aggregated_metrics.each_with_object({}) do |aggregation, data| next if aggregation[:feature_flag] && Feature.disabled?(aggregation[:feature_flag], type: :development) next unless aggregation[:time_frame].include?(time_frame) - case aggregation[:source] - when REDIS_SOURCE - if time_frame == Gitlab::Usage::TimeFrame::ALL_TIME_TIME_FRAME_NAME - data[aggregation[:name]] = Gitlab::Utils::UsageData::FALLBACK - Gitlab::ErrorTracking - .track_and_raise_for_dev_exception( - DisallowedAggregationTimeFrame.new("Aggregation time frame: 'all' is not allowed for aggregation with source: '#{REDIS_SOURCE}'") - ) - else - data[aggregation[:name]] = calculate_count_for_aggregation(aggregation: aggregation, start_date: start_date, end_date: end_date) - end - when DATABASE_SOURCE - data[aggregation[:name]] = calculate_count_for_aggregation(aggregation: aggregation, start_date: start_date, end_date: end_date) - else - Gitlab::ErrorTracking - .track_and_raise_for_dev_exception(UnknownAggregationSource.new("Aggregation source: '#{aggregation[:source]}' must be included in #{SOURCES.keys}")) + data[aggregation[:name]] = calculate_count_for_aggregation(aggregation: aggregation, time_frame: time_frame) + end + end - data[aggregation[:name]] = Gitlab::Utils::UsageData::FALLBACK + def calculate_count_for_aggregation(aggregation:, time_frame:) + with_validate_configuration(aggregation, time_frame) do + source = SOURCES[aggregation[:source]] + + if aggregation[:operator] == UNION_OF_AGGREGATED_METRICS + source.calculate_metrics_union(**time_constraints(time_frame).merge(metric_names: aggregation[:events], recorded_at: recorded_at)) + else + source.calculate_metrics_intersections(**time_constraints(time_frame).merge(metric_names: aggregation[:events], recorded_at: recorded_at)) end end + rescue Gitlab::UsageDataCounters::HLLRedisCounter::EventError, AggregatedMetricError => error + failure(error) end - def calculate_count_for_aggregation(aggregation:, start_date:, end_date:) - source = SOURCES[aggregation[:source]] - - case aggregation[:operator] - when UNION_OF_AGGREGATED_METRICS - source.calculate_metrics_union(metric_names: aggregation[:events], start_date: start_date, end_date: end_date, recorded_at: recorded_at) - when INTERSECTION_OF_AGGREGATED_METRICS - source.calculate_metrics_intersections(metric_names: aggregation[:events], start_date: start_date, end_date: end_date, recorded_at: recorded_at) - else - Gitlab::ErrorTracking - .track_and_raise_for_dev_exception(UnknownAggregationOperator.new("Events should be aggregated with one of operators #{ALLOWED_METRICS_AGGREGATIONS}")) - Gitlab::Utils::UsageData::FALLBACK + def with_validate_configuration(aggregation, time_frame) + source = aggregation[:source] + + unless ALLOWED_METRICS_AGGREGATIONS.include?(aggregation[:operator]) + return failure( + UnknownAggregationOperator + .new("Events should be aggregated with one of operators #{ALLOWED_METRICS_AGGREGATIONS}") + ) end - rescue Gitlab::UsageDataCounters::HLLRedisCounter::EventError, AggregatedMetricError => error + + unless SOURCES[source] + return failure( + UnknownAggregationSource + .new("Aggregation source: '#{source}' must be included in #{SOURCES.keys}") + ) + end + + if time_frame == Gitlab::Usage::TimeFrame::ALL_TIME_TIME_FRAME_NAME && source == REDIS_SOURCE + return failure( + DisallowedAggregationTimeFrame + .new("Aggregation time frame: 'all' is not allowed for aggregation with source: '#{REDIS_SOURCE}'") + ) + end + + yield + end + + def failure(error) Gitlab::ErrorTracking.track_and_raise_for_dev_exception(error) Gitlab::Utils::UsageData::FALLBACK end @@ -82,6 +92,17 @@ module Gitlab def load_yaml_from_path(path) YAML.safe_load(File.read(path), aliases: true)&.map(&:with_indifferent_access) end + + def time_constraints(time_frame) + case time_frame + when Gitlab::Usage::TimeFrame::TWENTY_EIGHT_DAYS_TIME_FRAME_NAME + monthly_time_range + when Gitlab::Usage::TimeFrame::SEVEN_DAYS_TIME_FRAME_NAME + weekly_time_range + when Gitlab::Usage::TimeFrame::ALL_TIME_TIME_FRAME_NAME + { start_date: nil, end_date: nil } + end + end end end end diff --git a/lib/gitlab/usage_data_counters/hll_redis_counter.rb b/lib/gitlab/usage_data_counters/hll_redis_counter.rb index f0cb9bcbe94..81991df2d7d 100644 --- a/lib/gitlab/usage_data_counters/hll_redis_counter.rb +++ b/lib/gitlab/usage_data_counters/hll_redis_counter.rb @@ -25,7 +25,6 @@ module Gitlab error_tracking ide_edit incident_management - issues_edit pipeline_authoring quickactions ].freeze @@ -39,6 +38,7 @@ module Gitlab ide_edit importer incident_management_alerts + issues_edit kubernetes_agent pipeline_authoring search diff --git a/lib/gitlab/usage_data_counters/known_events/common.yml b/lib/gitlab/usage_data_counters/known_events/common.yml index 29b231f88f8..50033fec939 100644 --- a/lib/gitlab/usage_data_counters/known_events/common.yml +++ b/lib/gitlab/usage_data_counters/known_events/common.yml @@ -192,14 +192,6 @@ category: issues_edit redis_slot: project_management aggregation: daily -- name: g_project_management_issue_iteration_changed - category: issues_edit - redis_slot: project_management - aggregation: daily -- name: g_project_management_issue_weight_changed - category: issues_edit - redis_slot: project_management - aggregation: daily - name: g_project_management_issue_cross_referenced category: issues_edit redis_slot: project_management @@ -228,18 +220,6 @@ category: issues_edit redis_slot: project_management aggregation: daily -- name: g_project_management_issue_added_to_epic - category: issues_edit - redis_slot: project_management - aggregation: daily -- name: g_project_management_issue_removed_from_epic - category: issues_edit - redis_slot: project_management - aggregation: daily -- name: g_project_management_issue_changed_epic - category: issues_edit - redis_slot: project_management - aggregation: daily - name: g_project_management_issue_designs_added category: issues_edit redis_slot: project_management @@ -276,10 +256,6 @@ category: issues_edit redis_slot: project_management aggregation: daily -- name: g_project_management_issue_health_status_changed - category: issues_edit - redis_slot: project_management - aggregation: daily - name: g_project_management_issue_cloned category: issues_edit redis_slot: project_management diff --git a/package.json b/package.json index 1c081319831..6cad4af99d1 100644 --- a/package.json +++ b/package.json @@ -143,7 +143,7 @@ "minimatch": "^3.0.4", "monaco-editor": "^0.30.1", "monaco-editor-webpack-plugin": "^6.0.0", - "monaco-yaml": "3.0.0", + "monaco-yaml": "4.0.0", "mousetrap": "1.6.5", "papaparse": "^5.3.1", "patch-package": "^6.4.7", diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_commit_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_commit_spec.rb index a1d8b495129..d6e9c1a13df 100644 --- a/qa/qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_commit_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_commit_spec.rb @@ -18,9 +18,7 @@ module QA commit.branch = "development" commit.start_branch = project.default_branch commit.commit_message = 'Add new file' - commit.add_files([ - { file_path: file_name, content: 'pssst!' } - ]) + commit.add_files([{ file_path: file_name, content: 'pssst!' }]) end end diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb index 6ce4217f8ac..d975e18e962 100644 --- a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb @@ -20,12 +20,13 @@ module QA Resource::Repository::Commit.fabricate_via_api! do |commit| commit.project = template_project commit.commit_message = 'Add custom merge request template' - commit.add_files([ - { - file_path: ".gitlab/merge_request_templates/#{template_name}.md", - content: template_content - } - ]) + commit.add_files( + [ + { + file_path: ".gitlab/merge_request_templates/#{template_name}.md", + content: template_content + } + ]) end end diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb index 8885163b5e3..205ff12ff03 100644 --- a/qa/qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb @@ -16,9 +16,7 @@ module QA Resource::Repository::Commit.fabricate_via_api! do |commit| commit.project = project commit.commit_message = 'Add new file' - commit.add_files([ - { file_path: file_name, content: 'pssst!' } - ]) + commit.add_files([{ file_path: file_name, content: 'pssst!' }]) end end diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb index aa332a76c94..0503b1b3761 100644 --- a/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb @@ -22,9 +22,7 @@ module QA commit.branch = branch_name commit.start_branch = project.default_branch commit.commit_message = 'Add new file' - commit.add_files([ - { file_path: 'test-folder/test-file.md', content: 'new content' } - ]) + commit.add_files([{ file_path: 'test-folder/test-file.md', content: 'new content' }]) end project.visit! diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb index 9735aa7959a..561a5a2cc1c 100644 --- a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb @@ -22,12 +22,13 @@ module QA before do Resource::Repository::Commit.fabricate_via_api! do |commit| commit.project = project - commit.add_files([ - { - file_path: 'first_directory/test_file.txt', - content: "Test file content" - } - ]) + commit.add_files( + [ + { + file_path: 'first_directory/test_file.txt', + content: "Test file content" + } + ]) end project.visit! diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb index fc5754e2c7a..f03c651992c 100644 --- a/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/open_web_ide_from_diff_tab_spec.rb @@ -4,18 +4,18 @@ module QA RSpec.describe 'Create' do describe 'Open Web IDE from Diff Tab' do files = [ - { - file_path: 'file1', - content: 'test1' - }, - { - file_path: 'file2', - content: 'test2' - }, - { - file_path: 'file3', - content: 'test3' - } + { + file_path: 'file1', + content: 'test1' + }, + { + file_path: 'file2', + content: 'test2' + }, + { + file_path: 'file3', + content: 'test3' + } ] let(:project) do diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb index 078465770c6..222d1993bf4 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb @@ -51,16 +51,11 @@ module QA commit.project = package_project commit.commit_message = 'Add .gitlab-ci.yml' - commit.add_files([ - { - file_path: '.gitlab-ci.yml', - content: helm_upload_yaml - }, - { - file_path: 'Chart.yaml', - content: helm_chart_yaml - } - ]) + commit.add_files( + [ + { file_path: '.gitlab-ci.yml', content: helm_upload_yaml }, + { file_path: 'Chart.yaml', content: helm_chart_yaml } + ]) end end @@ -94,12 +89,7 @@ module QA commit.project = client_project commit.commit_message = 'Add .gitlab-ci.yml' - commit.add_files([ - { - file_path: '.gitlab-ci.yml', - content: helm_install_yaml - } - ]) + commit.add_files([{ file_path: '.gitlab-ci.yml', content: helm_install_yaml }]) end end diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb index 921b36b34af..e65607630f8 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb @@ -54,20 +54,12 @@ module QA commit.project = package_project commit.commit_message = 'Add files' - commit.add_files([ - { - file_path: '.gitlab-ci.yml', - content: maven_upload_package_yaml - }, - { - file_path: 'pom.xml', - content: package_pom_xml - }, - { - file_path: 'settings.xml', - content: settings_xml - } - ]) + commit.add_files( + [ + { file_path: '.gitlab-ci.yml', content: maven_upload_package_yaml }, + { file_path: 'pom.xml', content: package_pom_xml }, + { file_path: 'settings.xml', content: settings_xml } + ]) end end @@ -103,20 +95,12 @@ module QA commit.project = client_project commit.commit_message = 'Add files' - commit.add_files([ - { - file_path: '.gitlab-ci.yml', - content: maven_install_package_yaml - }, - { - file_path: 'pom.xml', - content: client_pom_xml - }, - { - file_path: 'settings.xml', - content: settings_xml - } - ]) + commit.add_files( + [ + { file_path: '.gitlab-ci.yml', content: maven_install_package_yaml }, + { file_path: 'pom.xml', content: client_pom_xml }, + { file_path: 'settings.xml', content: settings_xml } + ]) end end @@ -251,15 +235,15 @@ module QA package_pom_xml = ERB.new(read_fixture('package_managers/maven', 'package_pom.xml.erb')).result(binding) with_fixtures([ - { - file_path: 'pom.xml', - content: package_pom_xml - }, - { - file_path: 'settings.xml', - content: settings_xml_with_pat - } - ]) do |dir| + { + file_path: 'pom.xml', + content: package_pom_xml + }, + { + file_path: 'settings.xml', + content: settings_xml_with_pat + } + ]) do |dir| Service::DockerRun::Maven.new(dir).publish! end @@ -281,20 +265,12 @@ module QA commit.project = client_project commit.commit_message = 'Add .gitlab-ci.yml' - commit.add_files([ - { - file_path: '.gitlab-ci.yml', - content: maven_upload_package_yaml - }, - { - file_path: 'pom.xml', - content: package_pom_xml - }, - { - file_path: 'settings.xml', - content: settings_xml - } - ]) + commit.add_files( + [ + { file_path: '.gitlab-ci.yml', content: maven_upload_package_yaml }, + { file_path: 'pom.xml', content: package_pom_xml }, + { file_path: 'settings.xml', content: settings_xml } + ]) end end end diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb index 13607ba1b41..0af315af8a5 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb @@ -171,11 +171,12 @@ module QA Resource::Repository::Commit.fabricate_via_api! do |commit| commit.project = package_project commit.commit_message = 'Add .gitlab-ci.yml' - commit.add_files([ - gitlab_ci_file, - pom_file, - settings_xml - ]) + commit.add_files( + [ + gitlab_ci_file, + pom_file, + settings_xml + ]) end end diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb index 45693ecee41..3c8561615f9 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb @@ -39,15 +39,10 @@ module QA commit.project = package_project commit.commit_message = 'Add .gitlab-ci.yml' - commit.add_files([ - { - file_path: '.gitlab-ci.yml', - content: gradle_upload_yaml - }, - { - file_path: 'build.gradle', - content: build_upload_gradle - } + commit.add_files( + [ + { file_path: '.gitlab-ci.yml', content: gradle_upload_yaml }, + { file_path: 'build.gradle', content: build_upload_gradle } ]) end end @@ -83,16 +78,11 @@ module QA commit.project = client_project commit.commit_message = 'Add files' - commit.add_files([ - { - file_path: '.gitlab-ci.yml', - content: gradle_install_yaml - }, - { - file_path: 'build.gradle', - content: build_install_gradle - } - ]) + commit.add_files( + [ + { file_path: '.gitlab-ci.yml', content: gradle_install_yaml }, + { file_path: 'build.gradle', content: build_install_gradle } + ]) end end diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb index f229f30facc..e2a7006249d 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_group_level_spec.rb @@ -105,14 +105,7 @@ module QA nuget_upload_yaml = ERB.new(read_fixture('package_managers/nuget', 'nuget_upload_package.yaml.erb')).result(binding) commit.project = project commit.commit_message = 'Add .gitlab-ci.yml' - commit.update_files( - [ - { - file_path: '.gitlab-ci.yml', - content: nuget_upload_yaml - } - ] - ) + commit.update_files([{ file_path: '.gitlab-ci.yml', content: nuget_upload_yaml }]) end end @@ -137,9 +130,9 @@ module QA commit.commit_message = 'Add new csproj file' commit.add_files( [ - { - file_path: 'otherdotnet.csproj', - content: <<~EOF + { + file_path: 'otherdotnet.csproj', + content: <<~EOF <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> @@ -148,18 +141,11 @@ module QA </PropertyGroup> </Project> - EOF - } - ] - ) - commit.update_files( - [ - { - file_path: '.gitlab-ci.yml', - content: nuget_install_yaml - } + EOF + } ] ) + commit.update_files([{ file_path: '.gitlab-ci.yml', content: nuget_install_yaml }]) end end diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb index e70b95db1a5..620bb7e4988 100644 --- a/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/nuget/nuget_project_level_spec.rb @@ -101,9 +101,9 @@ module QA commit.commit_message = 'Add files' commit.update_files( [ - { - file_path: '.gitlab-ci.yml', - content: <<~YAML + { + file_path: '.gitlab-ci.yml', + content: <<~YAML stages: - deploy - install @@ -132,11 +132,11 @@ module QA - if: '$CI_COMMIT_BRANCH == "#{project.default_branch}"' tags: - "runner-for-#{project.name}" - YAML - }, - { - file_path: 'dotnetcore.csproj', - content: <<~EOF + YAML + }, + { + file_path: 'dotnetcore.csproj', + content: <<~EOF <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> @@ -145,8 +145,8 @@ module QA </PropertyGroup> </Project> - EOF - } + EOF + } ] ) end diff --git a/spec/finders/personal_access_tokens_finder_spec.rb b/spec/finders/personal_access_tokens_finder_spec.rb index f22bff62082..21380cb6632 100644 --- a/spec/finders/personal_access_tokens_finder_spec.rb +++ b/spec/finders/personal_access_tokens_finder_spec.rb @@ -7,6 +7,50 @@ RSpec.describe PersonalAccessTokensFinder do described_class.new(options, current_user) end + describe '# searches PATs' do + using RSpec::Parameterized::TableSyntax + + let_it_be(:time_token) do + create(:personal_access_token, created_at: DateTime.new(2022, 01, 02), + last_used_at: DateTime.new(2022, 01, 02)) + end + + let_it_be(:name_token) { create(:personal_access_token, name: 'test_1') } + + let_it_be(:impersonated_token) do + create(:personal_access_token, :impersonation, + created_at: DateTime.new(2022, 01, 02), + last_used_at: DateTime.new(2022, 01, 02), + name: 'imp_token' + ) + end + + shared_examples 'finding tokens by user and options' do + subject { finder(option, user).execute } + + it 'finds exactly' do + subject + + is_expected.to contain_exactly(*result) + end + end + + context 'by' do + where(:option, :user, :result) do + { created_before: DateTime.new(2022, 01, 03) } | create(:admin) | lazy { [time_token, impersonated_token] } + { created_after: DateTime.new(2022, 01, 01) } | create(:admin) | lazy { [time_token, name_token, impersonated_token] } + { last_used_before: DateTime.new(2022, 01, 03) } | create(:admin) | lazy { [time_token, impersonated_token] } + { last_used_before: DateTime.new(2022, 01, 03) } | create(:admin) | lazy { [time_token, impersonated_token] } + { impersonation: true } | create(:admin) | lazy { [impersonated_token] } + { search: 'test' } | create(:admin) | lazy { [name_token] } + end + + with_them do + it_behaves_like 'finding tokens by user and options' + end + end + end + describe '#execute' do let(:user) { create(:user) } let(:params) { {} } diff --git a/spec/frontend/__mocks__/monaco-editor/index.js b/spec/frontend/__mocks__/monaco-editor/index.js index 384f9993150..d09672a4ecf 100644 --- a/spec/frontend/__mocks__/monaco-editor/index.js +++ b/spec/frontend/__mocks__/monaco-editor/index.js @@ -8,10 +8,8 @@ import 'monaco-editor/esm/vs/language/css/monaco.contribution'; import 'monaco-editor/esm/vs/language/json/monaco.contribution'; import 'monaco-editor/esm/vs/language/html/monaco.contribution'; import 'monaco-editor/esm/vs/basic-languages/monaco.contribution'; -import 'monaco-yaml/lib/esm/monaco.contribution'; // This language starts trying to spin up web workers which obviously breaks in Jest environment jest.mock('monaco-editor/esm/vs/language/typescript/tsMode'); -jest.mock('monaco-yaml/lib/esm/yamlMode'); export * from 'monaco-editor/esm/vs/editor/editor.api'; diff --git a/spec/frontend/__mocks__/monaco-yaml/index.js b/spec/frontend/__mocks__/monaco-yaml/index.js new file mode 100644 index 00000000000..36681854d0b --- /dev/null +++ b/spec/frontend/__mocks__/monaco-yaml/index.js @@ -0,0 +1,4 @@ +const setDiagnosticsOptions = jest.fn(); +const yamlDefaults = {}; + +export { setDiagnosticsOptions, yamlDefaults }; diff --git a/spec/frontend/editor/source_editor_ci_schema_ext_spec.js b/spec/frontend/editor/source_editor_ci_schema_ext_spec.js index 9a14e1a55eb..21f8979f1a9 100644 --- a/spec/frontend/editor/source_editor_ci_schema_ext_spec.js +++ b/spec/frontend/editor/source_editor_ci_schema_ext_spec.js @@ -1,4 +1,4 @@ -import { languages } from 'monaco-editor'; +import { setDiagnosticsOptions } from 'monaco-yaml'; import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; import { TEST_HOST } from 'helpers/test_constants'; import { CiSchemaExtension } from '~/editor/extensions/source_editor_ci_schema_ext'; @@ -52,16 +52,12 @@ describe('~/editor/editor_ci_config_ext', () => { }); describe('registerCiSchema', () => { - beforeEach(() => { - jest.spyOn(languages.yaml.yamlDefaults, 'setDiagnosticsOptions'); - }); - describe('register validations options with monaco for yaml language', () => { const mockProjectNamespace = 'namespace1'; const mockProjectPath = 'project1'; const getConfiguredYmlSchema = () => { - return languages.yaml.yamlDefaults.setDiagnosticsOptions.mock.calls[0][0].schemas[0]; + return setDiagnosticsOptions.mock.calls[0][0].schemas[0]; }; it('with expected basic validation configuration', () => { @@ -77,8 +73,8 @@ describe('~/editor/editor_ci_config_ext', () => { completion: true, }; - expect(languages.yaml.yamlDefaults.setDiagnosticsOptions).toHaveBeenCalledTimes(1); - expect(languages.yaml.yamlDefaults.setDiagnosticsOptions).toHaveBeenCalledWith( + expect(setDiagnosticsOptions).toHaveBeenCalledTimes(1); + expect(setDiagnosticsOptions).toHaveBeenCalledWith( expect.objectContaining(expectedOptions), ); }); diff --git a/spec/frontend/ide/utils_spec.js b/spec/frontend/ide/utils_spec.js index fd9d481251d..4efc0ac6028 100644 --- a/spec/frontend/ide/utils_spec.js +++ b/spec/frontend/ide/utils_spec.js @@ -1,4 +1,5 @@ import { languages } from 'monaco-editor'; +import { setDiagnosticsOptions as yamlDiagnosticsOptions } from 'monaco-yaml'; import { isTextFile, registerLanguages, @@ -203,7 +204,6 @@ describe('WebIDE utils', () => { }; jest.spyOn(languages.json.jsonDefaults, 'setDiagnosticsOptions'); - jest.spyOn(languages.yaml.yamlDefaults, 'setDiagnosticsOptions'); }); it('registers the given schemas with monaco for both json and yaml languages', () => { @@ -212,7 +212,7 @@ describe('WebIDE utils', () => { expect(languages.json.jsonDefaults.setDiagnosticsOptions).toHaveBeenCalledWith( expect.objectContaining({ schemas: [schema] }), ); - expect(languages.yaml.yamlDefaults.setDiagnosticsOptions).toHaveBeenCalledWith( + expect(yamlDiagnosticsOptions).toHaveBeenCalledWith( expect.objectContaining({ schemas: [schema] }), ); }); diff --git a/spec/frontend/projects/settings/branch_rules/branch_dropdown_spec.js b/spec/frontend/projects/settings/branch_rules/components/edit/branch_dropdown_spec.js index 79bce5a4b3f..11f219c1f90 100644 --- a/spec/frontend/projects/settings/branch_rules/branch_dropdown_spec.js +++ b/spec/frontend/projects/settings/branch_rules/components/edit/branch_dropdown_spec.js @@ -4,7 +4,7 @@ import { GlDropdown, GlSearchBoxByType, GlDropdownItem, GlSprintf } from '@gitla import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import BranchDropdown, { i18n, -} from '~/projects/settings/branch_rules/components/branch_dropdown.vue'; +} from '~/projects/settings/branch_rules/components/edit/branch_dropdown.vue'; import createMockApollo from 'helpers/mock_apollo_helper'; import branchesQuery from '~/projects/settings/branch_rules/queries/branches.query.graphql'; import waitForPromises from 'helpers/wait_for_promises'; diff --git a/spec/frontend/projects/settings/branch_rules/rule_edit_spec.js b/spec/frontend/projects/settings/branch_rules/components/edit/index_spec.js index b0b2b9191d4..21e63fdb24d 100644 --- a/spec/frontend/projects/settings/branch_rules/rule_edit_spec.js +++ b/spec/frontend/projects/settings/branch_rules/components/edit/index_spec.js @@ -1,9 +1,9 @@ import { nextTick } from 'vue'; import { getParameterByName } from '~/lib/utils/url_utility'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; -import RuleEdit from '~/projects/settings/branch_rules/components/rule_edit.vue'; -import BranchDropdown from '~/projects/settings/branch_rules/components/branch_dropdown.vue'; -import Protections from '~/projects/settings/branch_rules/components/protections/index.vue'; +import RuleEdit from '~/projects/settings/branch_rules/components/edit/index.vue'; +import BranchDropdown from '~/projects/settings/branch_rules/components/edit/branch_dropdown.vue'; +import Protections from '~/projects/settings/branch_rules/components/edit/protections/index.vue'; jest.mock('~/lib/utils/url_utility', () => ({ getParameterByName: jest.fn().mockImplementation(() => 'main'), diff --git a/spec/frontend/projects/settings/branch_rules/components/protections/index_spec.js b/spec/frontend/projects/settings/branch_rules/components/edit/protections/index_spec.js index 3592fa50622..ee90ff8318f 100644 --- a/spec/frontend/projects/settings/branch_rules/components/protections/index_spec.js +++ b/spec/frontend/projects/settings/branch_rules/components/edit/protections/index_spec.js @@ -3,10 +3,10 @@ import { GlLink } from '@gitlab/ui'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import Protections, { i18n, -} from '~/projects/settings/branch_rules/components/protections/index.vue'; -import PushProtections from '~/projects/settings/branch_rules/components/protections/push_protections.vue'; -import MergeProtections from '~/projects/settings/branch_rules/components/protections/merge_protections.vue'; -import { protections } from '../../mock_data'; +} from '~/projects/settings/branch_rules/components/edit/protections/index.vue'; +import PushProtections from '~/projects/settings/branch_rules/components/edit/protections/push_protections.vue'; +import MergeProtections from '~/projects/settings/branch_rules/components/edit/protections/merge_protections.vue'; +import { protections } from '../../../mock_data'; describe('Branch Protections', () => { let wrapper; diff --git a/spec/frontend/projects/settings/branch_rules/components/protections/merge_protections_spec.js b/spec/frontend/projects/settings/branch_rules/components/edit/protections/merge_protections_spec.js index 0e168a2ad78..b5fdc46d600 100644 --- a/spec/frontend/projects/settings/branch_rules/components/protections/merge_protections_spec.js +++ b/spec/frontend/projects/settings/branch_rules/components/edit/protections/merge_protections_spec.js @@ -2,8 +2,8 @@ import { GlFormGroup, GlFormCheckbox } from '@gitlab/ui'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import MergeProtections, { i18n, -} from '~/projects/settings/branch_rules/components/protections/merge_protections.vue'; -import { membersAllowedToMerge, requireCodeOwnersApproval } from '../../mock_data'; +} from '~/projects/settings/branch_rules/components/edit/protections/merge_protections.vue'; +import { membersAllowedToMerge, requireCodeOwnersApproval } from '../../../mock_data'; describe('Merge Protections', () => { let wrapper; diff --git a/spec/frontend/projects/settings/branch_rules/components/protections/push_protections_spec.js b/spec/frontend/projects/settings/branch_rules/components/edit/protections/push_protections_spec.js index d54dad08338..60bb7a51dcb 100644 --- a/spec/frontend/projects/settings/branch_rules/components/protections/push_protections_spec.js +++ b/spec/frontend/projects/settings/branch_rules/components/edit/protections/push_protections_spec.js @@ -2,8 +2,8 @@ import { GlFormGroup, GlSprintf, GlFormCheckbox } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import PushProtections, { i18n, -} from '~/projects/settings/branch_rules/components/protections/push_protections.vue'; -import { membersAllowedToPush, allowForcePush } from '../../mock_data'; +} from '~/projects/settings/branch_rules/components/edit/protections/push_protections.vue'; +import { membersAllowedToPush, allowForcePush } from '../../../mock_data'; describe('Push Protections', () => { let wrapper; diff --git a/spec/models/personal_access_token_spec.rb b/spec/models/personal_access_token_spec.rb index 5bce6a2cc3f..67e7d444d25 100644 --- a/spec/models/personal_access_token_spec.rb +++ b/spec/models/personal_access_token_spec.rb @@ -105,6 +105,31 @@ RSpec.describe PersonalAccessToken do end end + describe '.last_used_before' do + context 'last_used_*' do + let_it_be(:date) { DateTime.new(2022, 01, 01) } + let_it_be(:token) { create(:personal_access_token, last_used_at: date ) } + # This token should never occur in the following tests and indicates that filtering was done correctly with it + let_it_be(:never_used_token) { create(:personal_access_token) } + + describe '.last_used_before' do + it 'returns personal access tokens used before the specified date only' do + expect(described_class.last_used_before(date + 1)).to contain_exactly(token) + end + end + + it 'does not return token that is last_used_at after given date' do + expect(described_class.last_used_before(date + 1)).not_to contain_exactly(never_used_token) + end + + describe '.last_used_after' do + it 'returns personal access tokens used after the specified date only' do + expect(described_class.last_used_after(date - 1)).to contain_exactly(token) + end + end + end + end + describe '.last_used_before_or_unused' do let(:last_used_at) { 1.month.ago.beginning_of_hour } let!(:unused_token) { create(:personal_access_token) } diff --git a/spec/models/preloaders/project_root_ancestor_preloader_spec.rb b/spec/models/preloaders/project_root_ancestor_preloader_spec.rb index 30036a6a033..bb0de24abe5 100644 --- a/spec/models/preloaders/project_root_ancestor_preloader_spec.rb +++ b/spec/models/preloaders/project_root_ancestor_preloader_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe Preloaders::ProjectRootAncestorPreloader do let_it_be(:root_parent1) { create(:group, :private, name: 'root-1', path: 'root-1') } - let_it_be(:root_parent2) { create(:group, :private, name: 'root-2', path: 'root-2') } + let_it_be(:root_parent2) { create(:group, name: 'root-2', path: 'root-2') } let_it_be(:guest_project) { create(:project, name: 'public guest', path: 'public-guest') } let_it_be(:private_maintainer_project) do create(:project, :private, name: 'b private maintainer', path: 'b-private-maintainer', namespace: root_parent1) @@ -15,7 +15,7 @@ RSpec.describe Preloaders::ProjectRootAncestorPreloader do end let_it_be(:public_maintainer_project) do - create(:project, :private, name: 'a public maintainer', path: 'a-public-maintainer', namespace: root_parent2) + create(:project, name: 'a public maintainer', path: 'a-public-maintainer', namespace: root_parent2) end let(:root_query_regex) { /\ASELECT.+FROM "namespaces" WHERE "namespaces"."id" = \d+/ } @@ -36,20 +36,20 @@ RSpec.describe Preloaders::ProjectRootAncestorPreloader do it 'strong_memoizes the correct root_ancestor' do pristine_projects.each do |project| - expected_parent_id = project.root_ancestor&.id + preloaded_parent_id = project.root_ancestor&.id - expect(project.parent_id).to eq(expected_parent_id) + expect(preloaded_parent_id).to eq(project.parent_id) end end end context 'when use_traversal_ids FF is enabled' do context 'when the preloader is used' do - before do - preload_ancestors - end - context 'when no additional preloads are provided' do + before do + preload_ancestors(:group) + end + it_behaves_like 'executes N matching DB queries', 0 end @@ -57,6 +57,10 @@ RSpec.describe Preloaders::ProjectRootAncestorPreloader do let(:additional_preloads) { [:route] } let(:root_query_regex) { /\ASELECT.+FROM "routes" WHERE "routes"."source_id" = \d+/ } + before do + preload_ancestors + end + it_behaves_like 'executes N matching DB queries', 0, :full_path end end @@ -64,6 +68,17 @@ RSpec.describe Preloaders::ProjectRootAncestorPreloader do context 'when the preloader is not used' do it_behaves_like 'executes N matching DB queries', 4 end + + context 'when using a :group sti name and passing projects in a user namespace' do + let(:projects) { [private_developer_project] } + let(:additional_preloads) { [:ip_restrictions, :saml_provider] } + + it 'does not load a nil value for root_ancestor' do + preload_ancestors(:group) + + expect(pristine_projects.first.root_ancestor).to eq(private_developer_project.root_ancestor) + end + end end context 'when use_traversal_ids FF is disabled' do @@ -91,9 +106,22 @@ RSpec.describe Preloaders::ProjectRootAncestorPreloader do context 'when the preloader is not used' do it_behaves_like 'executes N matching DB queries', 4 end + + context 'when using a :group sti name and passing projects in a user namespace' do + let(:projects) { [private_developer_project] } + let(:additional_preloads) { [:ip_restrictions, :saml_provider] } + + it 'does not load a nil value for root_ancestor' do + preload_ancestors(:group) + + expect(pristine_projects.first.root_ancestor).to eq(private_developer_project.root_ancestor) + end + end end - def preload_ancestors - described_class.new(pristine_projects, :namespace, additional_preloads).execute + private + + def preload_ancestors(namespace_sti_name = :namespace) + described_class.new(pristine_projects, namespace_sti_name, additional_preloads).execute end end diff --git a/spec/requests/api/personal_access_tokens_spec.rb b/spec/requests/api/personal_access_tokens_spec.rb index 37b5a594f2a..31c4e8803e3 100644 --- a/spec/requests/api/personal_access_tokens_spec.rb +++ b/spec/requests/api/personal_access_tokens_spec.rb @@ -4,14 +4,34 @@ require 'spec_helper' RSpec.describe API::PersonalAccessTokens do let_it_be(:path) { '/personal_access_tokens' } - let_it_be(:token1) { create(:personal_access_token) } - let_it_be(:token2) { create(:personal_access_token) } - let_it_be(:token_impersonated) { create(:personal_access_token, impersonation: true, user: token1.user) } - let_it_be(:current_user) { create(:user) } describe 'GET /personal_access_tokens' do + using RSpec::Parameterized::TableSyntax + + def map_id(json_resonse) + json_response.map { |pat| pat['id'] } + end + + shared_examples 'response as expected' do |params| + subject { get api(path, personal_access_token: current_users_token), params: params } + + it "status, count and result as expected" do + subject + + if status == :bad_request + expect(json_response).to eq(result) + elsif status == :ok + expect(map_id(json_response)).to a_collection_containing_exactly(*result) + end + + expect(response).to have_gitlab_http_status(status) + expect(json_response.count).to eq(result_count) + end + end + context 'logged in as an Administrator' do let_it_be(:current_user) { create(:admin) } + let_it_be(:current_users_token) { create(:personal_access_token, user: current_user) } it 'returns all PATs by default' do get api(path, current_user) @@ -21,60 +41,348 @@ RSpec.describe API::PersonalAccessTokens do end context 'filtered with user_id parameter' do + let_it_be(:token) { create(:personal_access_token) } + let_it_be(:token_impersonated) { create(:personal_access_token, impersonation: true, user: token.user) } + it 'returns only PATs belonging to that user' do - get api(path, current_user), params: { user_id: token1.user.id } + get api(path, current_user), params: { user_id: token.user.id } expect(response).to have_gitlab_http_status(:ok) expect(json_response.count).to eq(2) - expect(json_response.first['user_id']).to eq(token1.user.id) + expect(json_response.first['user_id']).to eq(token.user.id) expect(json_response.last['id']).to eq(token_impersonated.id) end end - context 'logged in as a non-Administrator' do - let_it_be(:current_user) { create(:user) } + context 'filter with revoked parameter' do + let_it_be(:revoked_token) { create(:personal_access_token, revoked: true) } + let_it_be(:not_revoked_token1) { create(:personal_access_token, revoked: false) } + let_it_be(:not_revoked_token2) { create(:personal_access_token, revoked: false) } + + where(:revoked, :status, :result_count, :result) do + true | :ok | 1 | lazy { [revoked_token.id] } + false | :ok | 3 | lazy { [not_revoked_token1.id, not_revoked_token2.id, current_users_token.id] } + 'asdf' | :bad_request | 1 | { "error" => "revoked is invalid" } + end + + with_them do + it_behaves_like 'response as expected', revoked: params[:revoked] + end + end + + context 'filter with active parameter' do + let_it_be(:inactive_token1) { create(:personal_access_token, revoked: true) } + let_it_be(:inactive_token2) { create(:personal_access_token, expires_at: Time.new(2022, 01, 01, 00, 00, 00)) } + let_it_be(:active_token) { create(:personal_access_token) } + + where(:state, :status, :result_count, :result) do + 'inactive' | :ok | 2 | lazy { [inactive_token1.id, inactive_token2.id] } + 'active' | :ok | 2 | lazy { [active_token.id, current_users_token.id] } + 'asdf' | :bad_request | 1 | { "error" => "state does not have a valid value" } + end + + with_them do + it_behaves_like 'response as expected', state: params[:state] + end + end + + context 'filter with created parameter' do + let_it_be(:token1) { create(:personal_access_token, created_at: DateTime.new(2022, 01, 01, 12, 30, 25) ) } + + context 'test created_before' do + where(:created_at, :status, :result_count, :result) do + '2022-01-02' | :ok | 1 | lazy { [token1.id] } + '2022-01-01' | :ok | 0 | lazy { [] } + '2022-01-01T12:30:24' | :ok | 0 | lazy { [] } + '2022-01-01T12:30:25' | :ok | 1 | lazy { [token1.id] } + '2022-01-01T:12:30:26' | :ok | 1 | lazy { [token1.id] } + 'asdf' | :bad_request | 1 | { "error" => "created_before is invalid" } + end + + with_them do + it_behaves_like 'response as expected', created_before: params[:created_at] + end + end + + context 'test created_after' do + where(:created_at, :status, :result_count, :result) do + '2022-01-03' | :ok | 1 | lazy { [current_users_token.id] } + '2022-01-01' | :ok | 2 | lazy { [token1.id, current_users_token.id] } + '2022-01-01T12:30:25' | :ok | 2 | lazy { [token1.id, current_users_token.id] } + '2022-01-01T12:30:26' | :ok | 1 | lazy { [current_users_token.id] } + (DateTime.now + 1).to_s | :ok | 0 | lazy { [] } + 'asdf' | :bad_request | 1 | { "error" => "created_after is invalid" } + end + + with_them do + it_behaves_like 'response as expected', created_after: params[:created_at] + end + end + end + + context 'filter with last_used parameter' do + let_it_be(:token1) { create(:personal_access_token, last_used_at: DateTime.new(2022, 01, 01, 12, 30, 25) ) } + let_it_be(:never_used_token) { create(:personal_access_token) } + + context 'test last_used_before' do + where(:last_used_at, :status, :result_count, :result) do + '2022-01-02' | :ok | 1 | lazy { [token1.id] } + '2022-01-01' | :ok | 0 | lazy { [] } + '2022-01-01T12:30:24' | :ok | 0 | lazy { [] } + '2022-01-01T12:30:25' | :ok | 1 | lazy { [token1.id] } + '2022-01-01T12:30:26' | :ok | 1 | lazy { [token1.id] } + 'asdf' | :bad_request | 1 | { "error" => "last_used_before is invalid" } + end + + with_them do + it_behaves_like 'response as expected', last_used_before: params[:last_used_at] + end + end + + context 'test last_used_after' do + where(:last_used_at, :status, :result_count, :result) do + '2022-01-03' | :ok | 1 | lazy { [current_users_token.id] } + '2022-01-01' | :ok | 2 | lazy { [token1.id, current_users_token.id] } + '2022-01-01T12:30:26' | :ok | 1 | lazy { [current_users_token.id] } + '2022-01-01T12:30:25' | :ok | 2 | lazy { [token1.id, current_users_token.id] } + (DateTime.now + 1).to_s | :ok | 0 | lazy { [] } + 'asdf' | :bad_request | 1 | { "error" => "last_used_after is invalid" } + end + + with_them do + it_behaves_like 'response as expected', last_used_after: params[:last_used_at] + end + end + end + + context 'filter with search parameter' do + let_it_be(:token1) { create(:personal_access_token, name: 'test_1') } + let_it_be(:token2) { create(:personal_access_token, name: 'test_2') } + let_it_be(:token3) { create(:personal_access_token, name: '') } + + where(:pattern, :status, :result_count, :result) do + 'test' | :ok | 2 | lazy { [token1.id, token2.id] } + '' | :ok | 4 | lazy { [token1.id, token2.id, token3.id, current_users_token.id] } + 'test_1' | :ok | 1 | lazy { [token1.id] } + 'asdf' | :ok | 0 | lazy { [] } + end + + with_them do + it_behaves_like 'response as expected', search: params[:pattern] + end + end + + context 'filter created_before/created_after combined with last_used_before/last_used_after' do + let_it_be(:date) { DateTime.new(2022, 01, 02) } + let_it_be(:token1) { create(:personal_access_token, created_at: date, last_used_at: date) } + + where(:date_before, :date_after, :status, :result_count, :result) do + '2022-01-03' | '2022-01-01' | :ok | 1 | lazy { [token1.id] } + '2022-01-01' | '2022-01-03' | :ok | 0 | lazy { [] } + '2022-01-03' | nil | :ok | 1 | lazy { [token1.id] } + nil | '2022-01-01' | :ok | 2 | lazy { [token1.id, current_users_token.id] } + end + + with_them do + it_behaves_like 'response as expected', { created_before: params[:date_before], + created_after: params[:date_after] } + it_behaves_like 'response as expected', { last_used_before: params[:date_before], + last_used_after: params[:date_after] } + end + end + + context 'filter created_before and created_after combined is valid' do + let_it_be(:token1) { create(:personal_access_token, created_at: DateTime.new(2022, 01, 02)) } + + where(:created_before, :created_after, :status, :result) do + '2022-01-02' | '2022-01-02' | :ok | lazy { [token1.id] } + '2022-01-03' | '2022-01-01' | :ok | lazy { [token1.id] } + '2022-01-01' | '2022-01-03' | :ok | lazy { [] } + '2022-01-03' | nil | :ok | lazy { [token1.id] } + nil | '2022-01-01' | :ok | lazy { [token1.id] } + end + + with_them do + it "returns all valid tokens" do + get api(path, personal_access_token: current_users_token), + params: { created_before: created_before, created_after: created_after } + + expect(response).to have_gitlab_http_status(status) + + expect(json_response.map { |pat| pat['id'] } ).to include(*result) if status == :ok + end + end + end + + context 'filter last_used_before and last_used_after combined is valid' do + let_it_be(:token1) { create(:personal_access_token, last_used_at: DateTime.new(2022, 01, 02) ) } + + where(:last_used_before, :last_used_after, :status, :result) do + '2022-01-02' | '2022-01-02' | :ok | lazy { [token1.id] } + '2022-01-03' | '2022-01-01' | :ok | lazy { [token1.id] } + '2022-01-01' | '2022-01-03' | :ok | lazy { [] } + '2022-01-03' | nil | :ok | lazy { [token1.id] } + nil | '2022-01-01' | :ok | lazy { [token1.id] } + end + + with_them do + it "returns all valid tokens" do + get api(path, personal_access_token: current_users_token), + params: { last_used_before: last_used_before, last_used_after: last_used_after } + + expect(response).to have_gitlab_http_status(status) + + expect(json_response.map { |pat| pat['id'] } ).to include(*result) if status == :ok + end + end + end + end + + context 'logged in as a non-Administrator' do + let_it_be(:current_user) { create(:user) } + let_it_be(:current_users_token) { create(:personal_access_token, user: current_user) } + + it 'returns all PATs belonging to the signed-in user' do + get api(path, personal_access_token: current_users_token) + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response.count).to eq(1) + expect(json_response.map { |r| r['id'] }.uniq).to contain_exactly(current_users_token.id) + expect(json_response.map { |r| r['user_id'] }.uniq).to contain_exactly(current_user.id) + end + + context 'filtered with user_id parameter' do let_it_be(:user) { create(:user) } - let_it_be(:token) { create(:personal_access_token, user: current_user) } - let_it_be(:other_token) { create(:personal_access_token, user: user) } - let_it_be(:token_impersonated) { create(:personal_access_token, impersonation: true, user: current_user) } - it 'returns all PATs belonging to the signed-in user' do - get api(path, current_user, personal_access_token: token) + it 'returns PATs belonging to the specific user' do + get api(path, current_user, personal_access_token: current_users_token), params: { user_id: current_user.id } expect(response).to have_gitlab_http_status(:ok) expect(json_response.count).to eq(1) + expect(json_response.map { |r| r['id'] }.uniq).to contain_exactly(current_users_token.id) expect(json_response.map { |r| r['user_id'] }.uniq).to contain_exactly(current_user.id) end - context 'filtered with user_id parameter' do - it 'returns PATs belonging to the specific user' do - get api(path, current_user, personal_access_token: token), params: { user_id: current_user.id } + it 'is unauthorized if filtered by a user other than current_user' do + get api(path, current_user, personal_access_token: current_users_token), params: { user_id: user.id } - expect(response).to have_gitlab_http_status(:ok) - expect(json_response.count).to eq(1) - expect(json_response.map { |r| r['user_id'] }.uniq).to contain_exactly(current_user.id) - end + expect(response).to have_gitlab_http_status(:unauthorized) + end + end - it 'is unauthorized if filtered by a user other than current_user' do - get api(path, current_user, personal_access_token: token), params: { user_id: user.id } + context 'filter with revoked parameter' do + let_it_be(:users_revoked_token) { create(:personal_access_token, revoked: true, user: current_user) } + let_it_be(:not_revoked_token) { create(:personal_access_token, revoked: false) } + let_it_be(:oter_revoked_token) { create(:personal_access_token, revoked: true) } - expect(response).to have_gitlab_http_status(:unauthorized) - end + where(:revoked, :status, :result_count, :result) do + true | :ok | 1 | lazy { [users_revoked_token.id] } + false | :ok | 1 | lazy { [current_users_token.id] } + end + + with_them do + it_behaves_like 'response as expected', revoked: params[:revoked] end end - context 'not authenticated' do - it 'is forbidden' do - get api(path) + context 'filter with active parameter' do + let_it_be(:users_inactive_token) { create(:personal_access_token, revoked: true, user: current_user) } + let_it_be(:inactive_token) { create(:personal_access_token, expires_at: Time.new(2022, 01, 01, 00, 00, 00)) } + let_it_be(:other_active_token) { create(:personal_access_token) } - expect(response).to have_gitlab_http_status(:unauthorized) + where(:state, :status, :result_count, :result) do + 'inactive' | :ok | 1 | lazy { [users_inactive_token.id] } + 'active' | :ok | 1 | lazy { [current_users_token.id] } + end + + with_them do + it_behaves_like 'response as expected', state: params[:state] + end + end + + # The created_before filter has been extensively tested in the 'logged in as administrator' section. + # Here it is only tested whether PATs to which the user has no access right are excluded from the filter function. + context 'filter with created parameter' do + let_it_be(:token1) do + create(:personal_access_token, created_at: DateTime.new(2022, 01, 02, 12, 30, 25), user: current_user ) + end + + let_it_be(:token2) { create(:personal_access_token, created_at: DateTime.new(2022, 01, 02, 12, 30, 25)) } + let_it_be(:status) { :ok } + + context 'created_before' do + let_it_be(:result_count) { 1 } + let_it_be(:result) { [token1.id] } + + it_behaves_like 'response as expected', created_before: '2022-01-03' + end + + context 'created_after' do + let_it_be(:result_count) { 2 } + let_it_be(:result) { [token1.id, current_users_token.id] } + + it_behaves_like 'response as expected', created_after: '2022-01-01' + end + end + + # The last_used_before filter has been extensively tested in the 'logged in as administrator' section. + # Here it is only tested whether PATs to which the user has no access right are excluded from the filter function. + context 'filter with last_used' do + let_it_be(:token1) do + create(:personal_access_token, last_used_at: DateTime.new(2022, 01, 01, 12, 30, 25), user: current_user) + end + + let_it_be(:token2) { create(:personal_access_token, last_used_at: DateTime.new(2022, 01, 01, 12, 30, 25) ) } + let_it_be(:never_used_token) { create(:personal_access_token) } + let_it_be(:status) { :ok } + + context 'last_used_before' do + let_it_be(:result_count) { 1 } + let_it_be(:result) { [token1.id] } + + it_behaves_like 'response as expected', last_used_before: '2022-01-02' + end + + context 'last_used_after' do + let_it_be(:result_count) { 2 } + let_it_be(:result) { [token1.id, current_users_token.id] } + + it_behaves_like 'response as expected', last_used_after: '2022-01-01' + end + end + + # The search filter has been extensively tested in the 'logged in as administrator' section. + # Here it is only tested whether PATs to which the user has no access right are excluded from the filter function. + context 'filter with search parameter' do + let_it_be(:token1) { create(:personal_access_token, name: 'test_1', user: current_user) } + let_it_be(:token2) { create(:personal_access_token, name: 'test_1') } + let_it_be(:token3) { create(:personal_access_token, name: '') } + + where(:pattern, :status, :result_count, :result) do + 'test' | :ok | 1 | lazy { [token1.id] } + '' | :ok | 2 | lazy { [token1.id, current_users_token.id] } + 'test_1' | :ok | 1 | lazy { [token1.id] } + end + + with_them do + it_behaves_like 'response as expected', search: params[:pattern] end end end + + context 'not authenticated' do + it 'is forbidden' do + get api(path) + + expect(response).to have_gitlab_http_status(:unauthorized) + end + end end describe 'GET /personal_access_tokens/:id' do + let_it_be(:current_user) { create(:user) } let_it_be(:user_token) { create(:personal_access_token, user: current_user) } + let_it_be(:token1) { create(:personal_access_token) } let_it_be(:user_read_only_token) { create(:personal_access_token, scopes: ['read_repository'], user: current_user) } let_it_be(:user_token_path) { "/personal_access_tokens/#{user_token.id}" } let_it_be(:invalid_path) { "/personal_access_tokens/#{non_existing_record_id}" } @@ -136,6 +444,9 @@ RSpec.describe API::PersonalAccessTokens do end describe 'DELETE /personal_access_tokens/:id' do + let_it_be(:current_user) { create(:user) } + let_it_be(:token1) { create(:personal_access_token) } + let(:path) { "/personal_access_tokens/#{token1.id}" } context 'when current_user is an administrator', :enable_admin_mode do diff --git a/yarn.lock b/yarn.lock index 8c8eadc8e49..30e8a148a7f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2047,10 +2047,10 @@ jest-matcher-utils "^27.0.0" pretty-format "^27.0.0" -"@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": - version "7.0.9" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" - integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== +"@types/json-schema@^7.0.0", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.11" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== "@types/json5@^0.0.29": version "0.0.29" @@ -7492,7 +7492,7 @@ js-sdsl@^4.1.4: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.13.1, js-yaml@^3.14.1: +js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -7587,7 +7587,7 @@ json5@^2.1.2, json5@^2.2.1: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== -jsonc-parser@~3.1.0: +jsonc-parser@^3.0.0, jsonc-parser@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.1.0.tgz#73b8f0e5c940b83d03476bc2e51a20ef0932615d" integrity sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg== @@ -8840,14 +8840,31 @@ monaco-editor@^0.30.1: resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.30.1.tgz#47f8d18a0aa2264fc5654581741ab8d7bec01689" integrity sha512-B/y4+b2O5G2gjuxIFtCE2EkM17R2NM7/3F8x0qcPsqy4V83bitJTIO4TIeZpYlzu/xy6INiY/+84BEm6+7Cmzg== -monaco-yaml@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/monaco-yaml/-/monaco-yaml-3.0.0.tgz#b3d59c3485bd5a161438072a8e5a8aaf74041dfd" - integrity sha512-WkgdfjCj0L2VPPwPoiwc4l8+B78FyVpSsMoufEWqe3ukm8+WygKUtmtCFOFnehmMih6tSqhy4DUtoAhfnicyZA== - dependencies: - "@types/json-schema" "^7.0.8" - js-yaml "^3.14.1" - yaml-ast-parser-custom-tags "^0.0.43" +monaco-marker-data-provider@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/monaco-marker-data-provider/-/monaco-marker-data-provider-1.1.1.tgz#0ca69f367152f5aa12cec2bda95f32b7403e876f" + integrity sha512-PGB7TJSZE5tmHzkxv/OEwK2RGNC2A7dcq4JRJnnj31CUAsfmw0Gl+1QTrH0W0deKhcQmQM0YVPaqgQ+0wCt8Mg== + +monaco-worker-manager@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/monaco-worker-manager/-/monaco-worker-manager-2.0.1.tgz#f67c54dfca34ed4b225d5de84e77b24b4e36de8a" + integrity sha512-kdPL0yvg5qjhKPNVjJoym331PY/5JC11aPJXtCZNwWRvBr6jhkIamvYAyiY5P1AWFmNOy0aRDRoMdZfa71h8kg== + +monaco-yaml@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/monaco-yaml/-/monaco-yaml-4.0.0.tgz#3bf8624f984665aba7e5f10c4987cecf2890e89b" + integrity sha512-7/CbvZIHE1iOQF6ny+uPclcsrBnkK54lcZugIB9SzqADljhb3TliJyYs7vouaGNDF73RWvrtZrMsWCC5N/GW+g== + dependencies: + "@types/json-schema" "^7.0.0" + jsonc-parser "^3.0.0" + monaco-marker-data-provider "^1.0.0" + monaco-worker-manager "^2.0.0" + path-browserify "^1.0.0" + prettier "^2.0.0" + vscode-languageserver-textdocument "^1.0.0" + vscode-languageserver-types "^3.0.0" + vscode-uri "^3.0.0" + yaml "^2.0.0" mousetrap@1.6.5: version "1.6.5" @@ -9437,6 +9454,11 @@ path-browserify@0.0.1: resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== +path-browserify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -9670,7 +9692,7 @@ pretender@^3.4.3: fake-xml-http-request "^2.1.1" route-recognizer "^0.3.3" -prettier@2.2.1, "prettier@^1.18.2 || ^2.0.0": +prettier@2.2.1, "prettier@^1.18.2 || ^2.0.0", prettier@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== @@ -11881,6 +11903,21 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.0.tgz#bd76d6a23323e2ca8ffa12028dc04559c75f9019" integrity sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw== +vscode-languageserver-textdocument@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.5.tgz#838769940ece626176ec5d5a2aa2d0aa69f5095c" + integrity sha512-1ah7zyQjKBudnMiHbZmxz5bYNM9KKZYz+5VQLj+yr8l+9w3g+WAhCkUkWbhMEdC5u0ub4Ndiye/fDyS8ghIKQg== + +vscode-languageserver-types@^3.0.0: + version "3.17.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz#b2c2e7de405ad3d73a883e91989b850170ffc4f2" + integrity sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA== + +vscode-uri@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.3.tgz#a95c1ce2e6f41b7549f86279d19f47951e4f4d84" + integrity sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA== + vue-apollo@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/vue-apollo/-/vue-apollo-3.0.7.tgz#97a031d45641faa4888a6d5a7f71c40834359704" @@ -12402,20 +12439,15 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml-ast-parser-custom-tags@^0.0.43: - version "0.0.43" - resolved "https://registry.yarnpkg.com/yaml-ast-parser-custom-tags/-/yaml-ast-parser-custom-tags-0.0.43.tgz#46968145ce4e24cb03c3312057f0f141b93a7d02" - integrity sha512-R5063FF/JSAN6qXCmylwjt9PcDH6M0ExEme/nJBzLspc6FJDmHHIqM7xh2WfEmsTJqClF79A9VkXjkAqmZw9SQ== - yaml@^1.10.0: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yaml@^2.0.0-10: - version "2.0.0-10" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.0.0-10.tgz#d5b59e2d14b8683313a534f2bbc648e211a2753e" - integrity sha512-FHV8s5ODFFQXX/enJEU2EkanNl1UDBUz8oa4k5Qo/sR+Iq7VmhCDkRMb0/mjJCNeAWQ31W8WV6PYStDE4d9EIw== +yaml@^2.0.0, yaml@^2.0.0-10: + version "2.1.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.1.tgz#1e06fb4ca46e60d9da07e4f786ea370ed3c3cfec" + integrity sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw== yargs-parser@^20.2.2, yargs-parser@^20.2.3: version "20.2.9" |