diff options
41 files changed, 832 insertions, 258 deletions
diff --git a/app/assets/javascripts/pages/admin/application_settings/general/components/signup_form.vue b/app/assets/javascripts/pages/admin/application_settings/general/components/signup_form.vue index 7e6654140a9..cde46c3da50 100644 --- a/app/assets/javascripts/pages/admin/application_settings/general/components/signup_form.vue +++ b/app/assets/javascripts/pages/admin/application_settings/general/components/signup_form.vue @@ -58,8 +58,6 @@ export default { 'emailRestrictions', 'afterSignUpText', 'pendingUserCount', - 'projectSharingHelpLink', - 'groupSharingHelpLink', ], data() { return { @@ -83,8 +81,6 @@ export default { supportedSyntaxLinkUrl: this.supportedSyntaxLinkUrl, emailRestrictions: this.emailRestrictions, afterSignUpText: this.afterSignUpText, - projectSharingHelpLink: this.projectSharingHelpLink, - groupSharingHelpLink: this.groupSharingHelpLink, }, }; }, @@ -224,7 +220,7 @@ export default { ), userCapLabel: s__('ApplicationSettings|User cap'), userCapDescription: s__( - 'ApplicationSettings|After the instance reaches the user cap, any user who is added or requests access must be approved by an administrator. Leave blank for an unlimited user cap. If you change the user cap to unlimited, you must re-enable %{projectSharingLinkStart}project sharing%{projectSharingLinkEnd} and %{groupSharingLinkStart}group sharing%{groupSharingLinkEnd}.', + 'ApplicationSettings|After the instance reaches the user cap, any user who is added or requests access must be approved by an administrator. Leave blank for unlimited.', ), domainDenyListGroupLabel: s__('ApplicationSettings|Domain denylist'), domainDenyListLabel: s__('ApplicationSettings|Enable domain denylist for sign-ups'), @@ -313,7 +309,6 @@ export default { <gl-form-group :label="$options.i18n.userCapLabel" :description="$options.i18n.userCapDescription" - data-testid="user-cap-form-group" > <gl-form-input v-model="form.userCap" @@ -321,17 +316,6 @@ export default { name="application_setting[new_user_signups_cap]" data-testid="user-cap-input" /> - - <template #description> - <gl-sprintf :message="$options.i18n.userCapDescription"> - <template #projectSharingLink="{ content }"> - <gl-link :href="projectSharingHelpLink" target="_blank">{{ content }}</gl-link> - </template> - <template #groupSharingLink="{ content }"> - <gl-link :href="groupSharingHelpLink" target="_blank">{{ content }}</gl-link> - </template> - </gl-sprintf> - </template> </gl-form-group> <gl-form-group :label="$options.i18n.minimumPasswordLengthLabel"> diff --git a/app/finders/clusters/agents/authorizations/user_access/finder.rb b/app/finders/clusters/agents/authorizations/user_access/finder.rb index 6bd0c226337..bde75fa46cb 100644 --- a/app/finders/clusters/agents/authorizations/user_access/finder.rb +++ b/app/finders/clusters/agents/authorizations/user_access/finder.rb @@ -5,9 +5,12 @@ module Clusters module Authorizations module UserAccess class Finder - def initialize(user, agent:) + def initialize(user, agent: nil, project: nil, preload: true, limit: nil) @user = user @agent = agent + @project = project + @limit = limit + @preload = preload end def execute @@ -16,19 +19,23 @@ module Clusters private - attr_reader :user, :agent + attr_reader :user, :agent, :project, :preload, :limit def project_authorizations authorizations = Clusters::Agents::Authorizations::UserAccess::ProjectAuthorization.for_user(user) authorizations = filter_by_agent(authorizations) - authorizations = preload(authorizations) + authorizations = filter_by_project(authorizations) + authorizations = apply_limit(authorizations) + authorizations = apply_preload(authorizations) authorizations.to_a end def group_authorizations authorizations = Clusters::Agents::Authorizations::UserAccess::GroupAuthorization.for_user(user) authorizations = filter_by_agent(authorizations) - authorizations = preload(authorizations) + authorizations = filter_by_project(authorizations) + authorizations = apply_limit(authorizations) + authorizations = apply_preload(authorizations) authorizations.to_a end @@ -38,7 +45,21 @@ module Clusters authorizations.for_agent(agent) end - def preload(authorizations) + def filter_by_project(authorizations) + return authorizations unless project.present? + + authorizations.for_project(project) + end + + def apply_limit(authorizations) + return authorizations unless limit.present? + + authorizations.limit(limit) + end + + def apply_preload(authorizations) + return authorizations unless preload + authorizations.preloaded end end diff --git a/app/graphql/resolvers/clusters/agents/authorizations/user_access_resolver.rb b/app/graphql/resolvers/clusters/agents/authorizations/user_access_resolver.rb new file mode 100644 index 00000000000..280db570aa6 --- /dev/null +++ b/app/graphql/resolvers/clusters/agents/authorizations/user_access_resolver.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Resolvers + module Clusters + module Agents + module Authorizations + class UserAccessResolver < BaseResolver + type Types::Clusters::Agents::Authorizations::UserAccessType, null: true + + alias_method :project, :object + + def resolve(*) + ::Clusters::Agents::Authorizations::UserAccess::Finder.new(current_user, project: project).execute + end + end + end + end + end +end diff --git a/app/graphql/types/clusters/agents/authorizations/user_access_type.rb b/app/graphql/types/clusters/agents/authorizations/user_access_type.rb new file mode 100644 index 00000000000..8c5a466cde2 --- /dev/null +++ b/app/graphql/types/clusters/agents/authorizations/user_access_type.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Types + module Clusters + module Agents + module Authorizations + class UserAccessType < BaseObject # rubocop:disable Graphql/AuthorizeTypes + graphql_name 'ClusterAgentAuthorizationUserAccess' + + field :agent, Types::Clusters::AgentType, + description: 'Authorized cluster agent.', + null: true + + field :config, GraphQL::Types::JSON, # rubocop:disable Graphql/JSONType + description: 'Configuration for the authorized project.', + null: true + end + end + end + end +end diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index a0f9864351c..b10b4621534 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -528,6 +528,12 @@ module Types resolver: ::Resolvers::Clusters::Agents::Authorizations::CiAccessResolver, authorize: :read_cluster_agent + field :user_access_authorized_agents, ::Types::Clusters::Agents::Authorizations::UserAccessType.connection_type, + null: true, + description: 'Authorized cluster agents for the project through user_access keyword.', + resolver: ::Resolvers::Clusters::Agents::Authorizations::UserAccessResolver, + authorize: :read_cluster_agent + field :merge_commit_template, GraphQL::Types::String, null: true, description: 'Template used to create merge commit message in merge requests.' diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 05f679df2c9..69a0e31d9b4 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -580,9 +580,7 @@ module ApplicationSettingsHelper supported_syntax_link_url: 'https://github.com/google/re2/wiki/Syntax', email_restrictions: @application_setting.email_restrictions.to_s, after_sign_up_text: @application_setting[:after_sign_up_text].to_s, - pending_user_count: pending_user_count, - project_sharing_help_link: help_page_path('user/group/access_and_permissions', anchor: 'prevent-a-project-from-being-shared-with-groups'), - group_sharing_help_link: help_page_path('user/group/access_and_permissions', anchor: 'prevent-group-sharing-outside-the-group-hierarchy') + pending_user_count: pending_user_count } end end diff --git a/app/models/clusters/agent.rb b/app/models/clusters/agent.rb index b5d634927e5..6980ec1c2d3 100644 --- a/app/models/clusters/agent.rb +++ b/app/models/clusters/agent.rb @@ -71,6 +71,14 @@ module Clusters ).exists? end + def user_access_authorized_for?(user) + return false unless user + return false unless ::Feature.enabled?(:expose_authorized_cluster_agents, project) + + Clusters::Agents::Authorizations::UserAccess::Finder + .new(user, agent: self, preload: false, limit: 1).execute.any? + end + # As of today, all config values of associated authorization rows have the same value. # See `UserAccess::RefreshService` for more information. def user_access_config diff --git a/app/models/clusters/agents/authorizations/user_access/group_authorization.rb b/app/models/clusters/agents/authorizations/user_access/group_authorization.rb index 486a6011d84..7027870855a 100644 --- a/app/models/clusters/agents/authorizations/user_access/group_authorization.rb +++ b/app/models/clusters/agents/authorizations/user_access/group_authorization.rb @@ -22,6 +22,10 @@ module Clusters .order('id, access_level DESC') } + scope :for_project, ->(project) { + where('all_groups_with_membership.traversal_ids @> ARRAY[?]', project.namespace_id) + } + validates :config, json_schema: { filename: 'clusters_agents_authorizations_user_access_config' } def config_project @@ -44,7 +48,9 @@ module Clusters def all_groups_with_membership ::Group.joins('INNER JOIN groups_with_direct_membership ON ' \ 'namespaces.traversal_ids @> ARRAY[groups_with_direct_membership.id]') - .select('namespaces.id AS id, groups_with_direct_membership.access_level AS access_level') + .select('namespaces.id AS id, ' \ + 'namespaces.traversal_ids AS traversal_ids, ' \ + 'groups_with_direct_membership.access_level AS access_level') end def groups_with_direct_membership_cte(user) diff --git a/app/models/clusters/agents/authorizations/user_access/project_authorization.rb b/app/models/clusters/agents/authorizations/user_access/project_authorization.rb index 3e2e6f6d35a..476666e3ad8 100644 --- a/app/models/clusters/agents/authorizations/user_access/project_authorization.rb +++ b/app/models/clusters/agents/authorizations/user_access/project_authorization.rb @@ -19,6 +19,8 @@ module Clusters .select('agent_user_access_project_authorizations.*, project_authorizations.access_level AS access_level') } + scope :for_project, ->(project) { where(project: project) } + validates :config, json_schema: { filename: 'clusters_agents_authorizations_user_access_config' } def config_project diff --git a/app/policies/clusters/agent_policy.rb b/app/policies/clusters/agent_policy.rb index afacf782a76..ecd83cceb8b 100644 --- a/app/policies/clusters/agent_policy.rb +++ b/app/policies/clusters/agent_policy.rb @@ -12,7 +12,11 @@ module Clusters @subject.ci_access_authorized_for?(@user) end - rule { ci_access_authorized_agent }.policy do + condition(:user_access_authorized_agent, score: 10) do + @subject.user_access_authorized_for?(@user) + end + + rule { ci_access_authorized_agent | user_access_authorized_agent }.policy do enable :read_cluster_agent end end diff --git a/data/removals/16-0/16-0-vulnerabilityFindingDismiss-mutation.yml b/data/removals/16-0/16-0-vulnerabilityFindingDismiss-mutation.yml new file mode 100644 index 00000000000..dd9de9e118b --- /dev/null +++ b/data/removals/16-0/16-0-vulnerabilityFindingDismiss-mutation.yml @@ -0,0 +1,11 @@ +- title: "Use of `id` field in vulnerabilityFindingDismiss mutation" + announcement_milestone: "15.3" + removal_milestone: "16.0" + breaking_change: true + reporter: abellucci + stage: Govern + issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367166 + body: | + You can use the vulnerabilityFindingDismiss GraphQL mutation to set the status of a vulnerability finding to `Dismissed`. Previously, this mutation used the `id` field to identify findings uniquely. However, this did not work for dismissing findings from the pipeline security tab. Therefore, using the `id` field as an identifier has been dropped in favor of the `uuid` field. Using the 'uuid' field as an identifier allows you to dismiss the finding from the pipeline security tab. + tiers: Ultimate + documentation_url: https://docs.gitlab.com/ee/api/graphql/reference/index.html#mutationvulnerabilityfindingdismiss diff --git a/data/removals/16_0/16-0-PipelineSecurityReportFinding-name-GraphQL-field.yml b/data/removals/16_0/16-0-PipelineSecurityReportFinding-name-GraphQL-field.yml new file mode 100644 index 00000000000..8d535040686 --- /dev/null +++ b/data/removals/16_0/16-0-PipelineSecurityReportFinding-name-GraphQL-field.yml @@ -0,0 +1,10 @@ +- title: "PipelineSecurityReportFinding name GraphQL field" + announcement_milestone: "15.1" + removal_milestone: "16.0" + breaking_change: true + reporter: abellucci + stage: Govern + issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/346335 + body: | + Previously, the [PipelineSecurityReportFinding GraphQL type was updated](https://gitlab.com/gitlab-org/gitlab/-/issues/335372) to include a new `title` field. This field is an alias for the current `name` field, making the less specific `name` field redundant. The `name` field is removed from the PipelineSecurityReportFinding type in GitLab 16.0. + tiers: [Ultimate] diff --git a/data/removals/16_0/16-0-vulnerabilityFindingDismiss-GraphQL-mutation.yml b/data/removals/16_0/16-0-vulnerabilityFindingDismiss-GraphQL-mutation.yml new file mode 100644 index 00000000000..cb5bb21ea74 --- /dev/null +++ b/data/removals/16_0/16-0-vulnerabilityFindingDismiss-GraphQL-mutation.yml @@ -0,0 +1,11 @@ +- title: "vulnerabilityFindingDismiss GraphQL mutation" + announcement_milestone: "15.5" + removal_milestone: "16.0" + breaking_change: true + reporter: abellucci + stage: Govern + issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/375645 + body: | + The `VulnerabilityFindingDismiss` GraphQL mutation has been removed. This mutation was not used often as the Vulnerability Finding ID was not available to users (this field was [deprecated in 15.3](https://docs.gitlab.com/ee/update/deprecations.html#use-of-id-field-in-vulnerabilityfindingdismiss-mutation)). Instead of `VulnerabilityFindingDismiss`, you should use `VulnerabilityDismiss` to dismiss vulnerabilities in the Vulnerability Report or `SecurityFindingDismiss` for security findings in the CI Pipeline Security tab. + tiers: [Ultimate] + documentation_url: https://docs.gitlab.com/ee/api/graphql/reference/index.html#mutationvulnerabilityfindingdismiss diff --git a/db/docs/batched_background_migrations/migrate_human_user_type.yml b/db/docs/batched_background_migrations/migrate_human_user_type.yml new file mode 100644 index 00000000000..869a4fe7351 --- /dev/null +++ b/db/docs/batched_background_migrations/migrate_human_user_type.yml @@ -0,0 +1,6 @@ +--- +migration_job_name: MigrateHumanUserType +description: Migrates human user type from old value (nil) to new value (0) for better indexing +feature_category: user_management +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/115849 +milestone: 16.0 diff --git a/db/docs/clusters_applications_knative.yml b/db/docs/deleted_tables/clusters_applications_knative.yml index 5cad3c342b0..666f126f022 100644 --- a/db/docs/clusters_applications_knative.yml +++ b/db/docs/deleted_tables/clusters_applications_knative.yml @@ -6,3 +6,5 @@ description: "(Deprecated) A GitLab managed Knative installation in a Kubernetes introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/b5155b90ee233e2824c168fbb06b3ce5d3aeb194 milestone: '11.5' gitlab_schema: gitlab_main +removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119515 +removed_in_milestone: '16.0' diff --git a/db/post_migrate/20230327103401_queue_migrate_human_user_type.rb b/db/post_migrate/20230327103401_queue_migrate_human_user_type.rb new file mode 100644 index 00000000000..d686c70cb5a --- /dev/null +++ b/db/post_migrate/20230327103401_queue_migrate_human_user_type.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class QueueMigrateHumanUserType < Gitlab::Database::Migration[2.1] + MIGRATION = "MigrateHumanUserType" + DELAY_INTERVAL = 2.minutes + BATCH_SIZE = 2000 + SUB_BATCH_SIZE = 10 + + restrict_gitlab_migration gitlab_schema: :gitlab_main + + def up + queue_batched_background_migration( + MIGRATION, + :users, + :id, + job_interval: DELAY_INTERVAL, + batch_size: BATCH_SIZE, + sub_batch_size: SUB_BATCH_SIZE + ) + end + + def down + delete_batched_background_migration(MIGRATION, :users, :id, []) + end +end diff --git a/db/post_migrate/20230503152349_drop_clusters_applications_knative.rb b/db/post_migrate/20230503152349_drop_clusters_applications_knative.rb new file mode 100644 index 00000000000..c94b9bba64b --- /dev/null +++ b/db/post_migrate/20230503152349_drop_clusters_applications_knative.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +# See https://docs.gitlab.com/ee/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class DropClustersApplicationsKnative < Gitlab::Database::Migration[2.1] + def up + drop_table :clusters_applications_knative + end + + # Based on init migration: + # https://gitlab.com/gitlab-org/gitlab/-/blob/b237f836df215a4ada92b9406733e6cd2483ca2d/db/migrate/20181228175414_init_schema.rb#L730-L740 + # rubocop:disable Migration/SchemaAdditionMethodsNoPost + def down + create_table "clusters_applications_knative", id: :serial, force: :cascade do |t| + t.integer "cluster_id", null: false + t.datetime_with_timezone "created_at", null: false + t.datetime_with_timezone "updated_at", null: false + t.integer "status", null: false + t.string "version", null: false + t.string "hostname" + t.text "status_reason" + t.string "external_hostname" + t.string "external_ip" + t.index ["cluster_id"], name: "index_clusters_applications_knative_on_cluster_id", unique: true + end + end + # rubocop:enable Migration/SchemaAdditionMethodsNoPost +end diff --git a/db/schema_migrations/20230327103401 b/db/schema_migrations/20230327103401 new file mode 100644 index 00000000000..4dc9095594a --- /dev/null +++ b/db/schema_migrations/20230327103401 @@ -0,0 +1 @@ +c5c1627079f081d241031e0ffb205b3088e83e97514e692d3093ab012d727365
\ No newline at end of file diff --git a/db/schema_migrations/20230503152349 b/db/schema_migrations/20230503152349 new file mode 100644 index 00000000000..b53f7e1da28 --- /dev/null +++ b/db/schema_migrations/20230503152349 @@ -0,0 +1 @@ +1cda8d54c964870fd0660dc864e39ef31c0a91994a275b7478d615141670b693
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 6aa2bd22823..1d7b24ebef3 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -11279,8 +11279,8 @@ CREATE TABLE appearances ( email_header_and_footer_enabled boolean DEFAULT false NOT NULL, profile_image_guidelines text, profile_image_guidelines_html text, - pwa_icon text, pwa_short_name text, + pwa_icon text, pwa_name text, pwa_description text, CONSTRAINT appearances_profile_image_guidelines CHECK ((char_length(profile_image_guidelines) <= 4096)), @@ -11694,6 +11694,10 @@ CREATE TABLE application_settings ( database_grafana_api_url text, database_grafana_tag text, public_runner_releases_url text DEFAULT 'https://gitlab.com/api/v4/projects/gitlab-org%2Fgitlab-runner/releases'::text NOT NULL, + password_uppercase_required boolean DEFAULT false NOT NULL, + password_lowercase_required boolean DEFAULT false NOT NULL, + password_number_required boolean DEFAULT false NOT NULL, + password_symbol_required boolean DEFAULT false NOT NULL, encrypted_arkose_labs_public_api_key bytea, encrypted_arkose_labs_public_api_key_iv bytea, encrypted_arkose_labs_private_api_key bytea, @@ -11704,14 +11708,14 @@ CREATE TABLE application_settings ( inactive_projects_min_size_mb integer DEFAULT 0 NOT NULL, inactive_projects_send_warning_email_after_months integer DEFAULT 1 NOT NULL, delayed_group_deletion boolean DEFAULT true NOT NULL, + maven_package_requests_forwarding boolean DEFAULT true NOT NULL, arkose_labs_namespace text DEFAULT 'client'::text NOT NULL, max_export_size integer DEFAULT 0, + encrypted_slack_app_signing_secret bytea, + encrypted_slack_app_signing_secret_iv bytea, container_registry_pre_import_timeout integer DEFAULT 1800 NOT NULL, container_registry_import_timeout integer DEFAULT 600 NOT NULL, pipeline_limit_per_project_user_sha integer DEFAULT 0 NOT NULL, - encrypted_slack_app_signing_secret bytea, - encrypted_slack_app_signing_secret_iv bytea, - globally_allowed_ips text DEFAULT ''::text NOT NULL, dingtalk_integration_enabled boolean DEFAULT false NOT NULL, encrypted_dingtalk_corpid bytea, encrypted_dingtalk_corpid_iv bytea, @@ -11719,11 +11723,8 @@ CREATE TABLE application_settings ( encrypted_dingtalk_app_key_iv bytea, encrypted_dingtalk_app_secret bytea, encrypted_dingtalk_app_secret_iv bytea, - password_uppercase_required boolean DEFAULT false NOT NULL, - password_lowercase_required boolean DEFAULT false NOT NULL, - password_number_required boolean DEFAULT false NOT NULL, - password_symbol_required boolean DEFAULT false NOT NULL, jira_connect_application_key text, + globally_allowed_ips text DEFAULT ''::text NOT NULL, container_registry_pre_import_tags_rate numeric(6,2) DEFAULT 0.5 NOT NULL, license_usage_data_exported boolean DEFAULT false NOT NULL, phone_verification_code_enabled boolean DEFAULT false NOT NULL, @@ -11738,34 +11739,33 @@ CREATE TABLE application_settings ( error_tracking_api_url text, git_rate_limit_users_allowlist text[] DEFAULT '{}'::text[] NOT NULL, error_tracking_access_token_encrypted text, + invitation_flow_enforcement boolean DEFAULT false NOT NULL, package_registry_cleanup_policies_worker_capacity integer DEFAULT 2 NOT NULL, deactivate_dormant_users_period integer DEFAULT 90 NOT NULL, auto_ban_user_on_excessive_projects_download boolean DEFAULT false NOT NULL, - invitation_flow_enforcement boolean DEFAULT false NOT NULL, max_pages_custom_domains_per_project integer DEFAULT 0 NOT NULL, cube_api_base_url text, encrypted_cube_api_key bytea, encrypted_cube_api_key_iv bytea, - maven_package_requests_forwarding boolean DEFAULT true NOT NULL, - dashboard_limit_enabled boolean DEFAULT false NOT NULL, - dashboard_limit integer DEFAULT 0 NOT NULL, - dashboard_notification_limit integer DEFAULT 0 NOT NULL, - dashboard_enforcement_limit integer DEFAULT 0 NOT NULL, - dashboard_limit_new_namespace_creation_enforcement_date date, jitsu_host text, jitsu_project_xid text, jitsu_administrator_email text, encrypted_jitsu_administrator_password bytea, encrypted_jitsu_administrator_password_iv bytea, + dashboard_limit_enabled boolean DEFAULT false NOT NULL, + dashboard_limit integer DEFAULT 0 NOT NULL, + dashboard_notification_limit integer DEFAULT 0 NOT NULL, + dashboard_enforcement_limit integer DEFAULT 0 NOT NULL, + dashboard_limit_new_namespace_creation_enforcement_date date, can_create_group boolean DEFAULT true NOT NULL, lock_maven_package_requests_forwarding boolean DEFAULT false NOT NULL, lock_pypi_package_requests_forwarding boolean DEFAULT false NOT NULL, lock_npm_package_requests_forwarding boolean DEFAULT false NOT NULL, + jira_connect_proxy_url text, password_expiration_enabled boolean DEFAULT false NOT NULL, password_expires_in_days integer DEFAULT 90 NOT NULL, password_expires_notice_before_days integer DEFAULT 7 NOT NULL, product_analytics_enabled boolean DEFAULT false NOT NULL, - jira_connect_proxy_url text, email_confirmation_setting smallint DEFAULT 0, disable_admin_oauth_scopes boolean DEFAULT false NOT NULL, default_preferred_language text DEFAULT 'en'::text NOT NULL, @@ -11774,37 +11774,37 @@ CREATE TABLE application_settings ( encrypted_telesign_customer_xid_iv bytea, encrypted_telesign_api_key bytea, encrypted_telesign_api_key_iv bytea, - max_terraform_state_size_bytes integer DEFAULT 0 NOT NULL, disable_personal_access_tokens boolean DEFAULT false NOT NULL, + max_terraform_state_size_bytes integer DEFAULT 0 NOT NULL, bulk_import_enabled boolean DEFAULT false NOT NULL, - user_defaults_to_private_profile boolean DEFAULT false NOT NULL, allow_runner_registration_token boolean DEFAULT true NOT NULL, + user_defaults_to_private_profile boolean DEFAULT false NOT NULL, + allow_possible_spam boolean DEFAULT false NOT NULL, + default_syntax_highlighting_theme integer DEFAULT 1 NOT NULL, encrypted_product_analytics_clickhouse_connection_string bytea, encrypted_product_analytics_clickhouse_connection_string_iv bytea, - allow_possible_spam boolean DEFAULT false NOT NULL, search_max_shard_size_gb integer DEFAULT 50 NOT NULL, search_max_docs_denominator integer DEFAULT 5000000 NOT NULL, search_min_docs_before_rollover integer DEFAULT 100000 NOT NULL, deactivation_email_additional_text text, - git_rate_limit_users_alertlist integer[] DEFAULT '{}'::integer[] NOT NULL, jira_connect_public_key_storage_enabled boolean DEFAULT false NOT NULL, - security_policy_global_group_approvers_enabled boolean DEFAULT true NOT NULL, - default_syntax_highlighting_theme integer DEFAULT 1 NOT NULL, + git_rate_limit_users_alertlist integer[] DEFAULT '{}'::integer[] NOT NULL, allow_deploy_tokens_and_keys_with_external_authn boolean DEFAULT false NOT NULL, + security_policy_global_group_approvers_enabled boolean DEFAULT true NOT NULL, projects_api_rate_limit_unauthenticated integer DEFAULT 400 NOT NULL, deny_all_requests_except_allowed boolean DEFAULT false NOT NULL, product_analytics_data_collector_host text, lock_memberships_to_saml boolean DEFAULT false NOT NULL, - update_runner_versions_enabled boolean DEFAULT true NOT NULL, gitlab_dedicated_instance boolean DEFAULT false NOT NULL, + update_runner_versions_enabled boolean DEFAULT true NOT NULL, database_apdex_settings jsonb, encrypted_openai_api_key bytea, encrypted_openai_api_key_iv bytea, database_max_running_batched_background_migrations integer DEFAULT 2 NOT NULL, - silent_mode_enabled boolean DEFAULT false NOT NULL, - package_metadata_purl_types smallint[] DEFAULT '{}'::smallint[], encrypted_product_analytics_configurator_connection_string bytea, encrypted_product_analytics_configurator_connection_string_iv bytea, + silent_mode_enabled boolean DEFAULT false NOT NULL, + package_metadata_purl_types smallint[] DEFAULT '{}'::smallint[], ci_max_includes integer DEFAULT 150 NOT NULL, encrypted_tofa_credentials bytea, encrypted_tofa_credentials_iv bytea, @@ -14387,28 +14387,6 @@ CREATE TABLE clusters ( helm_major_version integer DEFAULT 3 NOT NULL ); -CREATE TABLE clusters_applications_knative ( - id integer NOT NULL, - cluster_id integer NOT NULL, - created_at timestamp with time zone NOT NULL, - updated_at timestamp with time zone NOT NULL, - status integer NOT NULL, - version character varying NOT NULL, - hostname character varying, - status_reason text, - external_ip character varying, - external_hostname character varying -); - -CREATE SEQUENCE clusters_applications_knative_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - -ALTER SEQUENCE clusters_applications_knative_id_seq OWNED BY clusters_applications_knative.id; - CREATE TABLE clusters_applications_runners ( id integer NOT NULL, cluster_id integer NOT NULL, @@ -18702,13 +18680,13 @@ CREATE TABLE namespace_settings ( runner_token_expiration_interval integer, subgroup_runner_token_expiration_interval integer, project_runner_token_expiration_interval integer, + show_diff_preview_in_email boolean DEFAULT true NOT NULL, enabled_git_access_protocol smallint DEFAULT 0 NOT NULL, unique_project_download_limit smallint DEFAULT 0 NOT NULL, unique_project_download_limit_interval_in_seconds integer DEFAULT 0 NOT NULL, project_import_level smallint DEFAULT 50 NOT NULL, unique_project_download_limit_allowlist text[] DEFAULT '{}'::text[] NOT NULL, auto_ban_user_on_excessive_projects_download boolean DEFAULT false NOT NULL, - show_diff_preview_in_email boolean DEFAULT true NOT NULL, only_allow_merge_if_pipeline_succeeds boolean DEFAULT false NOT NULL, allow_merge_on_skipped_pipeline boolean DEFAULT false NOT NULL, only_allow_merge_if_all_discussions_are_resolved boolean DEFAULT false NOT NULL, @@ -20095,7 +20073,6 @@ CREATE TABLE plan_limits ( helm_max_file_size bigint DEFAULT 5242880 NOT NULL, ci_registered_group_runners integer DEFAULT 1000 NOT NULL, ci_registered_project_runners integer DEFAULT 1000 NOT NULL, - web_hook_calls integer DEFAULT 0 NOT NULL, ci_daily_pipeline_schedule_triggers integer DEFAULT 0 NOT NULL, ci_max_artifact_size_running_container_scanning integer DEFAULT 0 NOT NULL, ci_max_artifact_size_cluster_image_scanning integer DEFAULT 0 NOT NULL, @@ -20120,6 +20097,7 @@ CREATE TABLE plan_limits ( enforcement_limit integer DEFAULT 0 NOT NULL, notification_limit integer DEFAULT 0 NOT NULL, dashboard_limit_enabled_at timestamp with time zone, + web_hook_calls integer DEFAULT 0 NOT NULL, project_access_token_limit integer DEFAULT 0 NOT NULL ); @@ -21164,11 +21142,11 @@ CREATE TABLE project_settings ( target_platforms character varying[] DEFAULT '{}'::character varying[] NOT NULL, enforce_auth_checks_on_uploads boolean DEFAULT true NOT NULL, selective_code_owner_removals boolean DEFAULT false NOT NULL, + issue_branch_template text, show_diff_preview_in_email boolean DEFAULT true NOT NULL, - suggested_reviewers_enabled boolean DEFAULT false NOT NULL, jitsu_key text, + suggested_reviewers_enabled boolean DEFAULT false NOT NULL, only_allow_merge_if_all_status_checks_passed boolean DEFAULT false NOT NULL, - issue_branch_template text, mirror_branch_regex text, allow_pipeline_trigger_approve_deployment boolean DEFAULT false NOT NULL, emails_enabled boolean DEFAULT true NOT NULL, @@ -25031,8 +25009,6 @@ ALTER TABLE ONLY cluster_providers_gcp ALTER COLUMN id SET DEFAULT nextval('clus ALTER TABLE ONLY clusters ALTER COLUMN id SET DEFAULT nextval('clusters_id_seq'::regclass); -ALTER TABLE ONLY clusters_applications_knative ALTER COLUMN id SET DEFAULT nextval('clusters_applications_knative_id_seq'::regclass); - ALTER TABLE ONLY clusters_applications_runners ALTER COLUMN id SET DEFAULT nextval('clusters_applications_runners_id_seq'::regclass); ALTER TABLE ONLY clusters_kubernetes_namespaces ALTER COLUMN id SET DEFAULT nextval('clusters_kubernetes_namespaces_id_seq'::regclass); @@ -26934,9 +26910,6 @@ ALTER TABLE ONLY cluster_providers_aws ALTER TABLE ONLY cluster_providers_gcp ADD CONSTRAINT cluster_providers_gcp_pkey PRIMARY KEY (id); -ALTER TABLE ONLY clusters_applications_knative - ADD CONSTRAINT clusters_applications_knative_pkey PRIMARY KEY (id); - ALTER TABLE ONLY clusters_applications_runners ADD CONSTRAINT clusters_applications_runners_pkey PRIMARY KEY (id); @@ -30395,8 +30368,6 @@ CREATE INDEX index_cluster_providers_gcp_on_cloud_run ON cluster_providers_gcp U CREATE UNIQUE INDEX index_cluster_providers_gcp_on_cluster_id ON cluster_providers_gcp USING btree (cluster_id); -CREATE UNIQUE INDEX index_clusters_applications_knative_on_cluster_id ON clusters_applications_knative USING btree (cluster_id); - CREATE UNIQUE INDEX index_clusters_applications_runners_on_cluster_id ON clusters_applications_runners USING btree (cluster_id); CREATE INDEX index_clusters_applications_runners_on_runner_id ON clusters_applications_runners USING btree (runner_id); @@ -31389,8 +31360,6 @@ CREATE UNIQUE INDEX index_merge_request_reviewers_on_merge_request_id_and_user_i CREATE INDEX index_merge_request_reviewers_on_user_id ON merge_request_reviewers USING btree (user_id); -CREATE UNIQUE INDEX index_merge_request_user_mentions_note_id_convert_to_bigint ON merge_request_user_mentions USING btree (note_id_convert_to_bigint) WHERE (note_id_convert_to_bigint IS NOT NULL); - CREATE UNIQUE INDEX index_merge_request_user_mentions_on_note_id ON merge_request_user_mentions USING btree (note_id) WHERE (note_id IS NOT NULL); CREATE INDEX index_merge_requests_closing_issues_on_issue_id ON merge_requests_closing_issues USING btree (issue_id); @@ -35741,9 +35710,6 @@ ALTER TABLE ONLY issues ALTER TABLE ONLY geo_event_log ADD CONSTRAINT fk_geo_event_log_on_geo_event_id FOREIGN KEY (geo_event_id) REFERENCES geo_events(id) ON DELETE CASCADE; -ALTER TABLE ONLY merge_request_user_mentions - ADD CONSTRAINT fk_merge_request_user_mentions_note_id_convert_to_bigint FOREIGN KEY (note_id_convert_to_bigint) REFERENCES notes(id) ON DELETE CASCADE NOT VALID; - ALTER TABLE ONLY ml_candidate_metrics ADD CONSTRAINT fk_ml_candidate_metrics_on_candidate_id FOREIGN KEY (candidate_id) REFERENCES ml_candidates(id) ON DELETE CASCADE; diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index b76749c99f4..19961a034da 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -7770,6 +7770,29 @@ The edge type for [`ClusterAgentAuthorizationCiAccess`](#clusteragentauthorizati | <a id="clusteragentauthorizationciaccessedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. | | <a id="clusteragentauthorizationciaccessedgenode"></a>`node` | [`ClusterAgentAuthorizationCiAccess`](#clusteragentauthorizationciaccess) | The item at the end of the edge. | +#### `ClusterAgentAuthorizationUserAccessConnection` + +The connection type for [`ClusterAgentAuthorizationUserAccess`](#clusteragentauthorizationuseraccess). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="clusteragentauthorizationuseraccessconnectionedges"></a>`edges` | [`[ClusterAgentAuthorizationUserAccessEdge]`](#clusteragentauthorizationuseraccessedge) | A list of edges. | +| <a id="clusteragentauthorizationuseraccessconnectionnodes"></a>`nodes` | [`[ClusterAgentAuthorizationUserAccess]`](#clusteragentauthorizationuseraccess) | A list of nodes. | +| <a id="clusteragentauthorizationuseraccessconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | + +#### `ClusterAgentAuthorizationUserAccessEdge` + +The edge type for [`ClusterAgentAuthorizationUserAccess`](#clusteragentauthorizationuseraccess). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="clusteragentauthorizationuseraccessedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. | +| <a id="clusteragentauthorizationuseraccessedgenode"></a>`node` | [`ClusterAgentAuthorizationUserAccess`](#clusteragentauthorizationuseraccess) | The item at the end of the edge. | + #### `ClusterAgentConnection` The connection type for [`ClusterAgent`](#clusteragent). @@ -12506,6 +12529,15 @@ GitLab CI/CD configuration template. | <a id="clusteragentauthorizationciaccessagent"></a>`agent` | [`ClusterAgent`](#clusteragent) | Authorized cluster agent. | | <a id="clusteragentauthorizationciaccessconfig"></a>`config` | [`JSON`](#json) | Configuration for the authorized project. | +### `ClusterAgentAuthorizationUserAccess` + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="clusteragentauthorizationuseraccessagent"></a>`agent` | [`ClusterAgent`](#clusteragent) | Authorized cluster agent. | +| <a id="clusteragentauthorizationuseraccessconfig"></a>`config` | [`JSON`](#json) | Configuration for the authorized project. | + ### `ClusterAgentToken` #### Fields @@ -18870,6 +18902,7 @@ Represents a product analytics dashboard visualization. | <a id="projectterraformstates"></a>`terraformStates` | [`TerraformStateConnection`](#terraformstateconnection) | Terraform states associated with the project. (see [Connections](#connections)) | | <a id="projecttimelogcategories"></a>`timelogCategories` **{warning-solid}** | [`TimeTrackingTimelogCategoryConnection`](#timetrackingtimelogcategoryconnection) | **Introduced** in 15.3. This feature is an Experiment. It can be changed or removed at any time. Timelog categories for the project. | | <a id="projecttopics"></a>`topics` | [`[String!]`](#string) | List of project topics. | +| <a id="projectuseraccessauthorizedagents"></a>`userAccessAuthorizedAgents` | [`ClusterAgentAuthorizationUserAccessConnection`](#clusteragentauthorizationuseraccessconnection) | Authorized cluster agents for the project through user_access keyword. (see [Connections](#connections)) | | <a id="projectuserpermissions"></a>`userPermissions` | [`ProjectPermissions!`](#projectpermissions) | Permissions for the current user on the resource. | | <a id="projectvisibility"></a>`visibility` | [`String`](#string) | Visibility of the project. | | <a id="projectvulnerabilityimages"></a>`vulnerabilityImages` | [`VulnerabilityContainerImageConnection`](#vulnerabilitycontainerimageconnection) | Container images reported on the project vulnerabilities. (see [Connections](#connections)) | diff --git a/doc/update/removals.md b/doc/update/removals.md index caf185b4ffa..ce2e80b9850 100644 --- a/doc/update/removals.md +++ b/doc/update/removals.md @@ -78,6 +78,14 @@ Review the details carefully before upgrading. The Container Registry [pull-through cache](https://docs.docker.com/registry/recipes/mirror/) was deprecated in GitLab 15.8 and removed in GitLab 16.0. This feature is part of the upstream [Docker Distribution project](https://github.com/distribution/distribution) but we are removing that code in favor of the GitLab Dependency Proxy. Use the GitLab Dependency Proxy to proxy and cache container images from Docker Hub. +### PipelineSecurityReportFinding name GraphQL field + +WARNING: +This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/). +Review the details carefully before upgrading. + +Previously, the [PipelineSecurityReportFinding GraphQL type was updated](https://gitlab.com/gitlab-org/gitlab/-/issues/335372) to include a new `title` field. This field is an alias for the current `name` field, making the less specific `name` field redundant. The `name` field is removed from the PipelineSecurityReportFinding type in GitLab 16.0. + ### Project REST API field `operations_access_level` removed WARNING: @@ -104,6 +112,14 @@ In GitLab 13.9, we updated the Omnibus GitLab package and GitLab Helm chart 4.9 GitLab 16.0, we have removed support for Redis 5. If you are using your own Redis 5.0 instance, you must upgrade it to Redis 6.0 or later before upgrading to GitLab 16.0 or later. +### Use of `id` field in vulnerabilityFindingDismiss mutation + +WARNING: +This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/). +Review the details carefully before upgrading. + +You can use the vulnerabilityFindingDismiss GraphQL mutation to set the status of a vulnerability finding to `Dismissed`. Previously, this mutation used the `id` field to identify findings uniquely. However, this did not work for dismissing findings from the pipeline security tab. Therefore, using the `id` field as an identifier has been dropped in favor of the `uuid` field. Using the 'uuid' field as an identifier allows you to dismiss the finding from the pipeline security tab. + ### Vulnerability confidence field WARNING: @@ -114,6 +130,14 @@ In GitLab 15.3, [security report schemas below version 15 were deprecated](https The `confidence` attribute on vulnerability findings exists only in schema versions before `15-0-0` and in GitLab prior to 15.4. To maintain consistency between the reports and our public APIs, the `confidence` attribute on any vulnerability-related components of our GraphQL API is now removed. +### vulnerabilityFindingDismiss GraphQL mutation + +WARNING: +This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/). +Review the details carefully before upgrading. + +The `VulnerabilityFindingDismiss` GraphQL mutation has been removed. This mutation was not used often as the Vulnerability Finding ID was not available to users (this field was [deprecated in 15.3](https://docs.gitlab.com/ee/update/deprecations.html#use-of-id-field-in-vulnerabilityfindingdismiss-mutation)). Instead of `VulnerabilityFindingDismiss`, you should use `VulnerabilityDismiss` to dismiss vulnerabilities in the Vulnerability Report or `SecurityFindingDismiss` for security findings in the CI Pipeline Security tab. + ## Removed in 15.11 ### Exporting and importing projects in JSON format not supported diff --git a/doc/user/clusters/agent/ci_cd_workflow.md b/doc/user/clusters/agent/ci_cd_workflow.md index 1ca558adbc2..7a3f09f50ca 100644 --- a/doc/user/clusters/agent/ci_cd_workflow.md +++ b/doc/user/clusters/agent/ci_cd_workflow.md @@ -366,3 +366,15 @@ Unable to connect to the server: x509: certificate signed by unknown authority The error occurs because the job does not trust the certificate authority (CA) that signed the KAS certificate. To resolve the issue, [configure `kubectl` to trust the CA](#environments-with-kas-that-use-self-signed-certificates). + +### Validation errors + +If you use `kubectl` versions v1.27.0 or v.1.27.1, you might get the following error: + +```plaintext +error: error validating "file.yml": error validating data: the server responded with the status code 426 but did not return more information; if you choose to ignore these errors, turn validation off with --validate=false +``` + +This issue is caused by [a bug](https://github.com/kubernetes/kubernetes/issues/117463) with `kubectl` and other tools that use the shared Kubernetes libraries. + +To resolve the issue, use another version of `kubectl`. diff --git a/lib/gitlab/background_migration/migrate_human_user_type.rb b/lib/gitlab/background_migration/migrate_human_user_type.rb new file mode 100644 index 00000000000..2cb27225274 --- /dev/null +++ b/lib/gitlab/background_migration/migrate_human_user_type.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Gitlab + module BackgroundMigration + # Migrates all users with user_type = nil to user_type = 0 + class MigrateHumanUserType < BatchedMigrationJob + OLD_TYPE_VALUE = nil + NEW_TYPE_VALUE = 0 + + operation_name :migrate_human_user_type + scope_to ->(relation) { relation.where(user_type: OLD_TYPE_VALUE) } + feature_category :user_management + + def perform + cleanup_gin_indexes('users') + + each_sub_batch do |sub_batch| + sub_batch.update_all(user_type: NEW_TYPE_VALUE) + end + end + + private + + def cleanup_gin_indexes(table_name) + sql = <<-SQL + SELECT indexname::text FROM pg_indexes WHERE tablename = '#{table_name}' AND indexdef ILIKE '%using gin%' + SQL + + index_names = ApplicationRecord.connection.select_values(sql) + + index_names.each do |index_name| + ApplicationRecord.connection.execute("SELECT gin_clean_pending_list('#{index_name}')") + end + end + end + end +end diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake index 0d34cacf9f7..22e1d903c8d 100644 --- a/lib/tasks/gitlab/backup.rake +++ b/lib/tasks/gitlab/backup.rake @@ -5,227 +5,207 @@ module Tasks module Backup PID = Process.pid.freeze PID_FILE = "#{Rails.application.root}/tmp/backup_restore.pid" + + def self.create_backup + lock_backup do + ::Gitlab::TaskHelpers.warn_user_is_not_gitlab + + ::Backup::Manager.new(backup_progress).create + end + end + + def self.restore_backup + lock_backup do + ::Gitlab::TaskHelpers.warn_user_is_not_gitlab + + ::Backup::Manager.new(backup_progress).restore + end + end + + def self.create_task(task) + lock_backup do + ::Backup::Manager.new(backup_progress).run_create_task(task) + end + end + + def self.restore_task(task) + lock_backup do + ::Backup::Manager.new(backup_progress).run_restore_task(task) + end + end + + def self.backup_progress + if ENV['CRON'] + # We need an object we can say 'puts' and 'print' to; let's use a StringIO. + StringIO.new + else + $stdout + end + end + + def self.lock_backup + File.open(PID_FILE, File::RDWR | File::CREAT) do |f| + f.flock(File::LOCK_EX) + + file_content = f.read + + read_pid(file_content) unless file_content.blank? + + f.rewind + f.write(PID) + f.flush + ensure + f.flock(File::LOCK_UN) + end + + begin + yield + ensure + backup_progress.puts( + "#{Time.current} " + '-- Deleting backup and restore PID file ... '.color(:blue) + 'done'.color(:green) + ) + File.delete(PID_FILE) + end + end + + def self.read_pid(file_content) + Process.getpgid(file_content.to_i) + + backup_progress.puts(<<~MESSAGE.color(:red)) + Backup and restore in progress: + There is a backup and restore task in progress (PID #{file_content}). + Try to run the current task once the previous one ends. + MESSAGE + + exit 1 + rescue Errno::ESRCH + backup_progress.puts(<<~MESSAGE.color(:blue)) + The PID file #{PID_FILE} exists and contains #{file_content}, but the process is not running. + The PID file will be rewritten with the current process ID #{PID}. + MESSAGE + end + + private_class_method :backup_progress, :lock_backup, :read_pid end end end namespace :gitlab do require 'active_record/fixtures' + require 'stringio' namespace :backup do # Create backup of GitLab system desc 'GitLab | Backup | Create a backup of the GitLab system' task create: :gitlab_environment do - lock_backup do - warn_user_is_not_gitlab - - Backup::Manager.new(backup_progress).create - end + Tasks::Gitlab::Backup.create_backup end # Restore backup of GitLab system desc 'GitLab | Backup | Restore a previously created backup' task restore: :gitlab_environment do - lock_backup do - warn_user_is_not_gitlab - - Backup::Manager.new(backup_progress).restore - end + Tasks::Gitlab::Backup.restore_backup end namespace :repo do task create: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_create_task('repositories') - end + Tasks::Gitlab::Backup.create_task('repositories') end task restore: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_restore_task('repositories') - end + Tasks::Gitlab::Backup.restore_task('repositories') end end namespace :db do task create: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_create_task('db') - end + Tasks::Gitlab::Backup.create_task('db') end task restore: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_restore_task('db') - end + Tasks::Gitlab::Backup.restore_task('db') end end namespace :builds do task create: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_create_task('builds') - end + Tasks::Gitlab::Backup.create_task('builds') end task restore: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_restore_task('builds') - end + Tasks::Gitlab::Backup.restore_task('builds') end end namespace :uploads do task create: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_create_task('uploads') - end + Tasks::Gitlab::Backup.create_task('uploads') end task restore: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_restore_task('uploads') - end + Tasks::Gitlab::Backup.restore_task('uploads') end end namespace :artifacts do task create: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_create_task('artifacts') - end + Tasks::Gitlab::Backup.create_task('artifacts') end task restore: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_restore_task('artifacts') - end + Tasks::Gitlab::Backup.restore_task('artifacts') end end namespace :pages do task create: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_create_task('pages') - end + Tasks::Gitlab::Backup.create_task('pages') end task restore: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_restore_task('pages') - end + Tasks::Gitlab::Backup.restore_task('pages') end end namespace :lfs do task create: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_create_task('lfs') - end + Tasks::Gitlab::Backup.create_task('lfs') end task restore: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_restore_task('lfs') - end + Tasks::Gitlab::Backup.restore_task('lfs') end end namespace :terraform_state do task create: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_create_task('terraform_state') - end + Tasks::Gitlab::Backup.create_task('terraform_state') end task restore: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_restore_task('terraform_state') - end + Tasks::Gitlab::Backup.restore_task('terraform_state') end end namespace :registry do task create: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_create_task('registry') - end + Tasks::Gitlab::Backup.create_task('registry') end task restore: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_restore_task('registry') - end + Tasks::Gitlab::Backup.restore_task('registry') end end namespace :packages do task create: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_create_task('packages') - end + Tasks::Gitlab::Backup.create_task('packages') end task restore: :gitlab_environment do - lock_backup do - Backup::Manager.new(backup_progress).run_restore_task('packages') - end - end - end - - private - - def backup_progress - if ENV['CRON'] - # We need an object we can say 'puts' and 'print' to; let's use a - # StringIO. - require 'stringio' - StringIO.new - else - $stdout + Tasks::Gitlab::Backup.restore_task('packages') end end - - def lock_backup - File.open(Tasks::Gitlab::Backup::PID_FILE, File::RDWR | File::CREAT) do |f| - f.flock(File::LOCK_EX) - - file_content = f.read - - read_pid(file_content) unless file_content.blank? - - f.rewind - f.write(Tasks::Gitlab::Backup::PID) - f.flush - ensure - f.flock(File::LOCK_UN) - end - - begin - yield - ensure - backup_progress.puts( - "#{Time.current} " + '-- Deleting backup and restore PID file ... '.color(:blue) + 'done'.color(:green) - ) - File.delete(Tasks::Gitlab::Backup::PID_FILE) - end - end - - def read_pid(file_content) - Process.getpgid(file_content.to_i) - - backup_progress.puts(<<~MESSAGE.color(:red)) - Backup and restore in progress: - There is a backup and restore task in progress (PID #{file_content}). Try to run the current task once the previous one ends. - MESSAGE - - exit 1 - rescue Errno::ESRCH - backup_progress.puts(<<~MESSAGE.color(:blue)) - The PID file #{Tasks::Gitlab::Backup::PID_FILE} exists and contains #{file_content}, but the process is not running. - The PID file will be rewritten with the current process ID #{Tasks::Gitlab::Backup::PID}. - MESSAGE - end end # namespace end: backup end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 0257e2790db..92495b6eadb 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -5328,7 +5328,7 @@ msgstr "" msgid "ApplicationSettings|After sign-up text" msgstr "" -msgid "ApplicationSettings|After the instance reaches the user cap, any user who is added or requests access must be approved by an administrator. Leave blank for an unlimited user cap. If you change the user cap to unlimited, you must re-enable %{projectSharingLinkStart}project sharing%{projectSharingLinkEnd} and %{groupSharingLinkStart}group sharing%{groupSharingLinkEnd}." +msgid "ApplicationSettings|After the instance reaches the user cap, any user who is added or requests access must be approved by an administrator. Leave blank for unlimited." msgstr "" msgid "ApplicationSettings|Allowed domains for sign-ups" @@ -29116,6 +29116,9 @@ msgstr "" msgid "NamespaceLimits|Storage Phased Notification" msgstr "" +msgid "NamespaceLimits|These namespaces won't receive any notifications nor any degraded functionality while they remain on this list" +msgstr "" + msgid "NamespaceLimits|This will limit the amount of notifications your namespace receives, this can be removed in the future." msgstr "" diff --git a/package.json b/package.json index 0ad035b978a..ff92e222567 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "@gitlab/svgs": "3.45.0", "@gitlab/ui": "62.9.1", "@gitlab/visual-review-tools": "1.7.3", - "@gitlab/web-ide": "0.0.1-dev-20230425040132", + "@gitlab/web-ide": "0.0.1-dev-20230508202052", "@mattiasbuelens/web-streams-adapter": "^0.1.0", "@popperjs/core": "^2.11.2", "@rails/actioncable": "6.1.4-7", diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index 6da2bd81328..7a3f93db37d 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -22,7 +22,7 @@ RSpec.describe 'Database schema', feature_category: :database do # In the subsequent table removal MR, remove the entries. # See: https://docs.gitlab.com/ee/development/migration_style_guide.html#dropping-a-database-table REMOVED_FKS = { - clusters_applications_knative: %w[cluster_id], + # example_table: %w[example_column] clusters_applications_runners: %w[cluster_id] }.with_indifferent_access.freeze diff --git a/spec/finders/clusters/agents/authorizations/user_access/finder_spec.rb b/spec/finders/clusters/agents/authorizations/user_access/finder_spec.rb index 84112502d80..7e6897a723d 100644 --- a/spec/finders/clusters/agents/authorizations/user_access/finder_spec.rb +++ b/spec/finders/clusters/agents/authorizations/user_access/finder_spec.rb @@ -22,12 +22,12 @@ RSpec.describe Clusters::Agents::Authorizations::UserAccess::Finder, feature_cat end context 'with project authorizations' do - let!(:authorization) do + let!(:authorization_1) do create(:agent_user_access_project_authorization, agent: agent, project: deployment_project) end it 'returns authorization' do - is_expected.to eq([authorization]) + is_expected.to contain_exactly(authorization_1) expect(subject.first.access_level).to eq(Gitlab::Access::DEVELOPER) end @@ -36,7 +36,7 @@ RSpec.describe Clusters::Agents::Authorizations::UserAccess::Finder, feature_cat let(:user) { deployment_maintainer } it 'returns authorization' do - is_expected.to eq([authorization]) + is_expected.to contain_exactly(authorization_1) expect(subject.first.access_level).to eq(Gitlab::Access::MAINTAINER) end @@ -52,27 +52,53 @@ RSpec.describe Clusters::Agents::Authorizations::UserAccess::Finder, feature_cat context 'with multiple authorizations' do let_it_be(:agent_2) { create(:cluster_agent, project: agent_configuration_project) } + let_it_be(:agent_3) { create(:cluster_agent, project: agent_configuration_project) } + let_it_be(:deployment_project_2) { create(:project, namespace: organization) } let_it_be(:authorization_2) do create(:agent_user_access_project_authorization, agent: agent_2, project: deployment_project) end + let_it_be(:authorization_3) do + create(:agent_user_access_project_authorization, agent: agent_3, project: deployment_project_2) + end + + before_all do + deployment_project_2.add_developer(deployment_developer) + end + it 'returns authorizations' do - is_expected.to contain_exactly(authorization, authorization_2) + is_expected.to contain_exactly(authorization_1, authorization_2, authorization_3) end context 'with specific agent' do let(:params) { { agent: agent_2 } } it 'returns authorization' do - is_expected.to eq([authorization_2]) + is_expected.to contain_exactly(authorization_2) + end + end + + context 'with specific project' do + let(:params) { { project: deployment_project_2 } } + + it 'returns authorization' do + is_expected.to contain_exactly(authorization_3) + end + end + + context 'with limit' do + let(:params) { { limit: 1 } } + + it 'returns authorization' do + expect(subject.count).to eq(1) end end end end context 'with group authorizations' do - let!(:authorization) do + let!(:authorization_1) do create(:agent_user_access_group_authorization, agent: agent, group: organization) end @@ -83,7 +109,7 @@ RSpec.describe Clusters::Agents::Authorizations::UserAccess::Finder, feature_cat end it 'returns authorization' do - is_expected.to eq([authorization]) + is_expected.to contain_exactly(authorization_1) expect(subject.first.access_level).to eq(Gitlab::Access::DEVELOPER) end @@ -92,7 +118,7 @@ RSpec.describe Clusters::Agents::Authorizations::UserAccess::Finder, feature_cat let(:user) { deployment_maintainer } it 'returns authorization' do - is_expected.to eq([authorization]) + is_expected.to contain_exactly(authorization_1) expect(subject.first.access_level).to eq(Gitlab::Access::MAINTAINER) end @@ -113,8 +139,10 @@ RSpec.describe Clusters::Agents::Authorizations::UserAccess::Finder, feature_cat create(:agent_user_access_group_authorization, agent: agent_2, group: organization) end + let_it_be(:authorization_3) { create(:agent_user_access_group_authorization) } + it 'returns authorizations' do - is_expected.to contain_exactly(authorization, authorization_2) + is_expected.to contain_exactly(authorization_1, authorization_2) end context 'with specific agent' do @@ -124,20 +152,46 @@ RSpec.describe Clusters::Agents::Authorizations::UserAccess::Finder, feature_cat is_expected.to eq([authorization_2]) end end + + context 'with specific project' do + let(:params) { { project: deployment_project } } + + it 'returns authorization' do + is_expected.to contain_exactly(authorization_1, authorization_2) + end + end + + context 'with limit' do + let(:params) { { limit: 1 } } + + it 'returns authorization' do + expect(subject.count).to eq(1) + end + end end context 'when sub-group is authorized' do - let_it_be(:subgroup) { create(:group, parent: organization) } + let_it_be(:subgroup_1) { create(:group, parent: organization) } + let_it_be(:subgroup_2) { create(:group, parent: organization) } + let_it_be(:deployment_project_1) { create(:project, group: subgroup_1) } + let_it_be(:deployment_project_2) { create(:project, group: subgroup_2) } - let!(:authorization) do - create(:agent_user_access_group_authorization, agent: agent, group: subgroup) - end + let!(:authorization_1) { create(:agent_user_access_group_authorization, agent: agent, group: subgroup_1) } + let!(:authorization_2) { create(:agent_user_access_group_authorization, agent: agent, group: subgroup_2) } it 'returns authorization' do - is_expected.to eq([authorization]) + is_expected.to contain_exactly(authorization_1, authorization_2) expect(subject.first.access_level).to eq(Gitlab::Access::DEVELOPER) end + + context 'with specific deployment project' do + let(:params) { { project: deployment_project_1 } } + + it 'returns only the authorization connected to the parent group' do + is_expected.to contain_exactly(authorization_1) + end + end end end end diff --git a/spec/frontend/admin/signup_restrictions/components/signup_form_spec.js b/spec/frontend/admin/signup_restrictions/components/signup_form_spec.js index 9192fc12401..db8c33d01cb 100644 --- a/spec/frontend/admin/signup_restrictions/components/signup_form_spec.js +++ b/spec/frontend/admin/signup_restrictions/components/signup_form_spec.js @@ -1,4 +1,4 @@ -import { GlButton, GlModal, GlLink } from '@gitlab/ui'; +import { GlButton, GlModal } from '@gitlab/ui'; import { within } from '@testing-library/dom'; import { shallowMount, mount, createWrapper } from '@vue/test-utils'; import { stubComponent } from 'helpers/stub_component'; @@ -36,7 +36,6 @@ describe('Signup Form', () => { const findDenyListRawInputGroup = () => wrapper.findByTestId('domain-denylist-raw-input-group'); const findDenyListFileInputGroup = () => wrapper.findByTestId('domain-denylist-file-input-group'); const findUserCapInput = () => wrapper.findByTestId('user-cap-input'); - const findUserCapFormGroup = () => wrapper.findByTestId('user-cap-form-group'); const findModal = () => wrapper.findComponent(GlModal); afterEach(() => { @@ -212,19 +211,4 @@ describe('Signup Form', () => { }); }); }); - - describe('rendering help links within user cap description', () => { - beforeEach(() => { - mountComponent({ mountFn: mount }); - }); - - it('renders projectSharingHelpLink and groupSharingHelpLink', () => { - const [projectSharingLink, groupSharingLink] = findUserCapFormGroup().findAllComponents( - GlLink, - ).wrappers; - - expect(projectSharingLink.attributes('href')).toBe(mockData.projectSharingHelpLink); - expect(groupSharingLink.attributes('href')).toBe(mockData.groupSharingHelpLink); - }); - }); }); diff --git a/spec/frontend/admin/signup_restrictions/mock_data.js b/spec/frontend/admin/signup_restrictions/mock_data.js index ce5ec2248fe..3140d7be105 100644 --- a/spec/frontend/admin/signup_restrictions/mock_data.js +++ b/spec/frontend/admin/signup_restrictions/mock_data.js @@ -22,8 +22,6 @@ export const rawMockData = { passwordLowercaseRequired: 'true', passwordUppercaseRequired: 'true', passwordSymbolRequired: 'true', - projectSharingHelpLink: 'project-sharing/help/link', - groupSharingHelpLink: 'group-sharing/help/link', }; export const mockData = { @@ -50,6 +48,4 @@ export const mockData = { passwordLowercaseRequired: true, passwordUppercaseRequired: true, passwordSymbolRequired: true, - projectSharingHelpLink: 'project-sharing/help/link', - groupSharingHelpLink: 'group-sharing/help/link', }; diff --git a/spec/graphql/resolvers/clusters/agents/authorizations/user_access_resolver_spec.rb b/spec/graphql/resolvers/clusters/agents/authorizations/user_access_resolver_spec.rb new file mode 100644 index 00000000000..b7e2fef78eb --- /dev/null +++ b/spec/graphql/resolvers/clusters/agents/authorizations/user_access_resolver_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::Clusters::Agents::Authorizations::UserAccessResolver, + feature_category: :deployment_management do + include GraphqlHelpers + + it { expect(described_class.type).to eq(Types::Clusters::Agents::Authorizations::UserAccessType) } + it { expect(described_class.null).to be_truthy } + + describe '#resolve' do + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user, maintainer_projects: [project]) } + + let(:ctx) { { current_user: user } } + + subject { resolve(described_class, obj: project, ctx: ctx) } + + it 'calls the finder' do + expect_next_instance_of(::Clusters::Agents::Authorizations::UserAccess::Finder, + user, project: project) do |finder| + expect(finder).to receive(:execute) + end + + subject + end + end +end diff --git a/spec/graphql/types/clusters/agents/authorizations/user_access_type_spec.rb b/spec/graphql/types/clusters/agents/authorizations/user_access_type_spec.rb new file mode 100644 index 00000000000..0e798cd1b18 --- /dev/null +++ b/spec/graphql/types/clusters/agents/authorizations/user_access_type_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['ClusterAgentAuthorizationUserAccess'], + feature_category: :deployment_management do + let(:fields) { %i[agent config] } + + it { expect(described_class.graphql_name).to eq('ClusterAgentAuthorizationUserAccess') } + it { expect(described_class).to have_graphql_fields(fields) } +end diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb index 4cae970de64..5c2e67ca787 100644 --- a/spec/graphql/types/project_type_spec.rb +++ b/spec/graphql/types/project_type_spec.rb @@ -34,7 +34,7 @@ RSpec.describe GitlabSchema.types['Project'] do issue_status_counts terraform_states alert_management_integrations container_repositories container_repositories_count pipeline_analytics squash_read_only sast_ci_configuration - cluster_agent cluster_agents agent_configurations ci_access_authorized_agents + cluster_agent cluster_agents agent_configurations ci_access_authorized_agents user_access_authorized_agents ci_template timelogs merge_commit_template squash_commit_template work_item_types recent_issue_boards ci_config_path_or_default packages_cleanup_policy ci_variables timelog_categories fork_targets branch_rules ci_config_variables pipeline_schedules languages diff --git a/spec/lib/gitlab/background_migration/migrate_human_user_type_spec.rb b/spec/lib/gitlab/background_migration/migrate_human_user_type_spec.rb new file mode 100644 index 00000000000..7edeaed5794 --- /dev/null +++ b/spec/lib/gitlab/background_migration/migrate_human_user_type_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::BackgroundMigration::MigrateHumanUserType, schema: 20230327103401, feature_category: :user_management do # rubocop:disable Layout/LineLength + let!(:valid_users) do + # 13 is the max value we have at the moment. + (0..13).map do |type| + table(:users).create!(username: "user#{type}", email: "user#{type}@test.com", user_type: type, projects_limit: 0) + end + end + + let!(:user_to_update) do + table(:users).create!(username: "user_nil", email: "user_nil@test.com", user_type: nil, projects_limit: 0) + end + + let(:starting_id) { table(:users).pluck(:id).min } + let(:end_id) { table(:users).pluck(:id).max } + + let(:migration) do + described_class.new( + start_id: starting_id, + end_id: end_id, + batch_table: :users, + batch_column: :id, + sub_batch_size: 100, + pause_ms: 2, + connection: ::ApplicationRecord.connection + ) + end + + describe 'perform' do + it 'updates user with `nil` user type only' do + expect do + migration.perform + valid_users.map(&:reload) + user_to_update.reload + end.not_to change { valid_users.map(&:user_type) } + + expect(user_to_update.user_type).to eq(0) + end + end +end diff --git a/spec/migrations/20230327103401_queue_migrate_human_user_type_spec.rb b/spec/migrations/20230327103401_queue_migrate_human_user_type_spec.rb new file mode 100644 index 00000000000..e5852c93471 --- /dev/null +++ b/spec/migrations/20230327103401_queue_migrate_human_user_type_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe QueueMigrateHumanUserType, feature_category: :user_management do + let(:batched_migration) { described_class::MIGRATION } + + it 'schedules a new batched migration' do + reversible_migration do |migration| + migration.before -> { + expect(batched_migration).not_to have_scheduled_batched_migration + } + + migration.after -> { + expect(batched_migration).to have_scheduled_batched_migration( + table_name: :users, + column_name: :id, + interval: described_class::DELAY_INTERVAL, + batch_size: described_class::BATCH_SIZE, + sub_batch_size: described_class::SUB_BATCH_SIZE + ) + } + end + end +end diff --git a/spec/models/clusters/agent_spec.rb b/spec/models/clusters/agent_spec.rb index c2b8c5ea0d0..10081b955f4 100644 --- a/spec/models/clusters/agent_spec.rb +++ b/spec/models/clusters/agent_spec.rb @@ -236,6 +236,77 @@ RSpec.describe Clusters::Agent, feature_category: :deployment_management do end end + describe '#user_access_authorized_for?' do + using RSpec::Parameterized::TableSyntax + + let_it_be(:organization) { create(:group) } + let_it_be(:agent_management_project) { create(:project, group: organization) } + let_it_be(:agent) { create(:cluster_agent, project: agent_management_project) } + let_it_be(:deployment_project) { create(:project, group: organization) } + + let(:user) { create(:user) } + + subject { agent.user_access_authorized_for?(user) } + + it { is_expected.to eq(false) } + + context 'with project-level authorization' do + let!(:authorization) { create(:agent_user_access_project_authorization, agent: agent, project: deployment_project) } + + where(:user_role, :allowed) do + :guest | false + :reporter | false + :developer | true + :maintainer | true + :owner | true + end + + with_them do + before do + deployment_project.add_member(user, user_role) + end + + it { is_expected.to eq(allowed) } + end + + context 'when expose_authorized_cluster_agents feature flag is disabled' do + before do + stub_feature_flags(expose_authorized_cluster_agents: false) + end + + it { is_expected.to eq(false) } + end + end + + context 'with group-level authorization' do + let!(:authorization) { create(:agent_user_access_group_authorization, agent: agent, group: organization) } + + where(:user_role, :allowed) do + :guest | false + :reporter | false + :developer | true + :maintainer | true + :owner | true + end + + with_them do + before do + organization.add_member(user, user_role) + end + + it { is_expected.to eq(allowed) } + end + + context 'when expose_authorized_cluster_agents feature flag is disabled' do + before do + stub_feature_flags(expose_authorized_cluster_agents: false) + end + + it { is_expected.to eq(false) } + end + end + end + describe '#user_access_config' do let_it_be(:group) { create(:group) } let_it_be(:project) { create(:project) } diff --git a/spec/policies/clusters/agent_policy_spec.rb b/spec/policies/clusters/agent_policy_spec.rb index 200cb8ae99b..b3c43647a84 100644 --- a/spec/policies/clusters/agent_policy_spec.rb +++ b/spec/policies/clusters/agent_policy_spec.rb @@ -9,6 +9,8 @@ RSpec.describe Clusters::AgentPolicy do let(:project) { cluster_agent.project } describe 'rules' do + it { expect(policy).to be_disallowed :read_cluster_agent } + context 'when developer' do before do project.add_developer(user) @@ -32,5 +34,13 @@ RSpec.describe Clusters::AgentPolicy do it { expect(policy).to be_allowed :read_cluster_agent } end + + context 'when agent is user_access authorized for project members' do + before do + allow(cluster_agent).to receive(:user_access_authorized_for?).with(user).and_return(true) + end + + it { expect(policy).to be_allowed :read_cluster_agent } + end end end diff --git a/spec/requests/api/graphql/project/user_access_authorized_agents_spec.rb b/spec/requests/api/graphql/project/user_access_authorized_agents_spec.rb new file mode 100644 index 00000000000..b8017171fd1 --- /dev/null +++ b/spec/requests/api/graphql/project/user_access_authorized_agents_spec.rb @@ -0,0 +1,129 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Project.user_access_authorized_agents', feature_category: :deployment_management do + include GraphqlHelpers + + let_it_be(:organization) { create(:group) } + let_it_be(:agent_management_project) { create(:project, :private, group: organization) } + let_it_be(:agent) { create(:cluster_agent, project: agent_management_project) } + + let_it_be(:deployment_project) { create(:project, :private, group: organization) } + let_it_be(:deployment_developer) { create(:user).tap { |u| deployment_project.add_developer(u) } } + let_it_be(:deployment_reporter) { create(:user).tap { |u| deployment_project.add_reporter(u) } } + + let(:user) { deployment_developer } + + let(:query) do + %( + query { + project(fullPath: "#{deployment_project.full_path}") { + userAccessAuthorizedAgents { + nodes { + agent { + id + name + project { + name + } + } + config + } + } + } + } + ) + end + + subject { GitlabSchema.execute(query, context: { current_user: user }).as_json } + + context 'with project authorization' do + let!(:user_access) { create(:agent_user_access_project_authorization, agent: agent, project: deployment_project) } + + it 'returns the authorized agent' do + authorized_agents = subject.dig('data', 'project', 'userAccessAuthorizedAgents', 'nodes') + + expect(authorized_agents.count).to eq(1) + + authorized_agent = authorized_agents.first + + expect(authorized_agent['agent']['id']).to eq(agent.to_global_id.to_s) + expect(authorized_agent['agent']['name']).to eq(agent.name) + expect(authorized_agent['config']).to eq({}) + expect(authorized_agent['agent']['project']).to be_nil # User is not authorized to read other resources. + end + + context 'when user is developer in the agent management project' do + before do + agent_management_project.add_developer(deployment_developer) + end + + it 'returns the project information as well' do + authorized_agent = subject.dig('data', 'project', 'userAccessAuthorizedAgents', 'nodes').first + + expect(authorized_agent['agent']['project']['name']).to eq(agent_management_project.name) + end + end + + context 'when user is reporter' do + let(:user) { deployment_reporter } + + it 'returns nothing' do + expect(subject['data']['project']['userAccessAuthorizedAgents']).to be_nil + end + end + end + + context 'with group authorization' do + let_it_be(:deployment_group) { create(:group, :private, parent: organization) } + + let!(:user_access) { create(:agent_user_access_group_authorization, agent: agent, group: deployment_group) } + + before_all do + deployment_group.add_developer(deployment_developer) + deployment_group.add_reporter(deployment_reporter) + end + + it 'returns the authorized agent' do + authorized_agents = subject.dig('data', 'project', 'userAccessAuthorizedAgents', 'nodes') + + expect(authorized_agents.count).to eq(1) + + authorized_agent = authorized_agents.first + + expect(authorized_agent['agent']['id']).to eq(agent.to_global_id.to_s) + expect(authorized_agent['agent']['name']).to eq(agent.name) + expect(authorized_agent['config']).to eq({}) + expect(authorized_agent['agent']['project']).to be_nil # User is not authorized to read other resources. + end + + context 'when user is developer in the agent management project' do + before do + agent_management_project.add_developer(deployment_developer) + end + + it 'returns the project information as well' do + authorized_agent = subject.dig('data', 'project', 'userAccessAuthorizedAgents', 'nodes').first + + expect(authorized_agent['agent']['project']['name']).to eq(agent_management_project.name) + end + end + + context 'when user is reporter' do + let(:user) { deployment_reporter } + + it 'returns nothing' do + expect(subject['data']['project']['userAccessAuthorizedAgents']).to be_nil + end + end + end + + context 'when deployment project is not authorized to user_access to the agent' do + it 'returns empty' do + authorized_agents = subject.dig('data', 'project', 'userAccessAuthorizedAgents', 'nodes') + + expect(authorized_agents).to be_empty + end + end +end diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 3f8d5973759..7113818ed34 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -94,7 +94,8 @@ RSpec.describe 'gitlab:backup namespace rake tasks', :delete, feature_category: expect { run_rake_task('gitlab:backup:create') }.to raise_error(SystemExit).and output( <<~MESSAGE Backup and restore in progress: - There is a backup and restore task in progress (PID 123456). Try to run the current task once the previous one ends. + There is a backup and restore task in progress (PID 123456). + Try to run the current task once the previous one ends. MESSAGE ).to_stdout end diff --git a/yarn.lock b/yarn.lock index c19b1b53876..e26786a8895 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1134,10 +1134,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/visual-review-tools/-/visual-review-tools-1.7.3.tgz#9ea641146436da388ffbad25d7f2abe0df52c235" integrity sha512-NMV++7Ew1FSBDN1xiZaauU9tfeSfgDHcOLpn+8bGpP+O5orUPm2Eu66R5eC5gkjBPaXosNAxNWtriee+aFk4+g== -"@gitlab/web-ide@0.0.1-dev-20230425040132": - version "0.0.1-dev-20230425040132" - resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20230425040132.tgz#a3f5c7e0d86031b3887f6605901ae1671fef54e3" - integrity sha512-rgqbR63JosRVTTAhVI7sDoTNKNYNbA5zdDFCkLUkgn3t2Ot0H8Wd6BFP8Me0zYVuc+XxTYF5F+pIS7lI+Y6csw== +"@gitlab/web-ide@0.0.1-dev-20230508202052": + version "0.0.1-dev-20230508202052" + resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20230508202052.tgz#594259f161d4276653fc7f6028465cbd098e6e94" + integrity sha512-lTo7JrZqT8CGiNVh2WeJbVKeqT69zlse/0pZVsi5HlfQCFgkIi/M2Sj8cem+1uezBVo2UI2SAA4zXUsmCC4apw== "@graphql-eslint/eslint-plugin@3.18.0": version "3.18.0" |