diff options
103 files changed, 1384 insertions, 350 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index 069b0a225c6..516a61edb62 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -800,29 +800,245 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab /doc/user/workspace/index.md @fneill [Authentication and Authorization] -/app/**/*password* @gitlab-org/manage/authentication-and-authorization -/ee/app/**/*password* @gitlab-org/manage/authentication-and-authorization -/config/**/*password* @gitlab-org/manage/authentication-and-authorization -/ee/config/**/*password* @gitlab-org/manage/authentication-and-authorization -/lib/**/*password* @gitlab-org/manage/authentication-and-authorization -/ee/lib/**/*password* @gitlab-org/manage/authentication-and-authorization -/app/controllers/**/*password* @gitlab-org/manage/authentication-and-authorization -/ee/app/controllers/**/*password* @gitlab-org/manage/authentication-and-authorization - -/app/**/*auth* @gitlab-org/manage/authentication-and-authorization -/ee/app/**/*auth* @gitlab-org/manage/authentication-and-authorization -/config/**/*auth* @gitlab-org/manage/authentication-and-authorization -/ee/config/**/*auth* @gitlab-org/manage/authentication-and-authorization -/lib/**/*auth* @gitlab-org/manage/authentication-and-authorization -/ee/lib/**/*auth* @gitlab-org/manage/authentication-and-authorization -/app/controllers/**/*auth* @gitlab-org/manage/authentication-and-authorization -/ee/app/controllers/**/*auth* @gitlab-org/manage/authentication-and-authorization - -/app/**/*token* @gitlab-org/manage/authentication-and-authorization -/ee/app/**/*token* @gitlab-org/manage/authentication-and-authorization -/config/**/*token* @gitlab-org/manage/authentication-and-authorization -/ee/config/**/*token* @gitlab-org/manage/authentication-and-authorization -/lib/**/*token* @gitlab-org/manage/authentication-and-authorization -/ee/lib/**/*token* @gitlab-org/manage/authentication-and-authorization -/app/controllers/**/*token* @gitlab-org/manage/authentication-and-authorization -/ee/app/controllers/**/*token* @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/access_tokens @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/authentication @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/ide/components/shared/tokened_input.vue @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/invite_members/components/members_token_select.vue @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/logs/components/tokens @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/packages_and_registries/package_registry/components/list/tokens @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/pages/admin/impersonation_tokens @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/pages/groups/settings/access_tokens @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/pages/ldap @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/pages/oauth @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/pages/omniauth_callbacks @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/pages/profiles/password_prompt @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/pages/profiles/personal_access_tokens @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/pages/profiles/two_factor_auths @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/pages/projects/settings/access_tokens @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/pages/sessions/new/oauth_remember_me.js @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/pipelines/components/pipelines_list/tokens/constants.js @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_branch_name_token.vue @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_source_token.vue @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_status_token.vue @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_tag_name_token.vue @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/projects/settings/topics/components @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/related_issues/components/issue_token.vue @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/runner/components/registration/registration_token.vue @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/runner/components/registration/registration_token_reset_dropdown_item.vue @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/runner/components/search_tokens @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/static_site_editor/rich_content_editor/services/renderers/build_uneditable_token.js @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/token_access/components @gitlab-org/manage/authentication-and-authorization +/app/assets/javascripts/token_access/index.js @gitlab-org/manage/authentication-and-authorization +/app/assets/stylesheets/page_bundles/profile_two_factor_auth.scss @gitlab-org/manage/authentication-and-authorization +/app/controllers/admin/impersonation_tokens_controller.rb @gitlab-org/manage/authentication-and-authorization +/app/controllers/concerns/access_tokens_actions.rb @gitlab-org/manage/authentication-and-authorization +/app/controllers/concerns/authenticates_with_two_factor.rb @gitlab-org/manage/authentication-and-authorization +/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb @gitlab-org/manage/authentication-and-authorization +/app/controllers/concerns/enforces_admin_authentication.rb @gitlab-org/manage/authentication-and-authorization +/app/controllers/concerns/enforces_two_factor_authentication.rb @gitlab-org/manage/authentication-and-authorization +/app/controllers/concerns/oauth_applications.rb @gitlab-org/manage/authentication-and-authorization +/app/controllers/concerns/project_unauthorized.rb @gitlab-org/manage/authentication-and-authorization +/app/controllers/concerns/sessionless_authentication.rb @gitlab-org/manage/authentication-and-authorization +/app/controllers/concerns/snippet_authorizations.rb @gitlab-org/manage/authentication-and-authorization +/app/controllers/concerns/workhorse_authorization.rb @gitlab-org/manage/authentication-and-authorization +/app/controllers/groups/settings/access_tokens_controller.rb @gitlab-org/manage/authentication-and-authorization +/app/controllers/ldap @gitlab-org/manage/authentication-and-authorization +/app/controllers/oauth @gitlab-org/manage/authentication-and-authorization +/app/controllers/omniauth_callbacks_controller.rb @gitlab-org/manage/authentication-and-authorization +/app/controllers/passwords_controller.rb @gitlab-org/manage/authentication-and-authorization +/app/controllers/profiles/passwords_controller.rb @gitlab-org/manage/authentication-and-authorization +/app/controllers/profiles/personal_access_tokens_controller.rb @gitlab-org/manage/authentication-and-authorization +/app/controllers/profiles/two_factor_auths_controller.rb @gitlab-org/manage/authentication-and-authorization +/app/controllers/profiles/webauthn_registrations_controller.rb @gitlab-org/manage/authentication-and-authorization +/app/controllers/projects/settings/access_tokens_controller.rb @gitlab-org/manage/authentication-and-authorization +/app/finders/groups/projects_requiring_authorizations_refresh @gitlab-org/manage/authentication-and-authorization +/app/finders/personal_access_tokens_finder.rb @gitlab-org/manage/authentication-and-authorization +/app/helpers/access_tokens_helper.rb @gitlab-org/manage/authentication-and-authorization +/app/helpers/auth_helper.rb @gitlab-org/manage/authentication-and-authorization +/app/models/authentication_event.rb @gitlab-org/manage/authentication-and-authorization +/app/models/concerns/admin_changed_password_notifier.rb @gitlab-org/manage/authentication-and-authorization +/app/models/concerns/mirror_authentication.rb @gitlab-org/manage/authentication-and-authorization +/app/models/concerns/select_for_project_authorization.rb @gitlab-org/manage/authentication-and-authorization +/app/models/concerns/token_authenticatable.rb @gitlab-org/manage/authentication-and-authorization +/app/models/concerns/token_authenticatable_strategies @gitlab-org/manage/authentication-and-authorization +/app/models/oauth_access_grant.rb @gitlab-org/manage/authentication-and-authorization +/app/models/oauth_access_token.rb @gitlab-org/manage/authentication-and-authorization +/app/models/personal_access_token.rb @gitlab-org/manage/authentication-and-authorization +/app/models/project_authorization.rb @gitlab-org/manage/authentication-and-authorization +/app/models/token_with_iv.rb @gitlab-org/manage/authentication-and-authorization +/app/models/webauthn_registration.rb @gitlab-org/manage/authentication-and-authorization +/app/policies/personal_access_token_policy.rb @gitlab-org/manage/authentication-and-authorization +/app/services/access_token_validation_service.rb @gitlab-org/manage/authentication-and-authorization +/app/services/auth @gitlab-org/manage/authentication-and-authorization +/app/services/authorized_project_update @gitlab-org/manage/authentication-and-authorization +/app/services/chat_names/authorize_user_service.rb @gitlab-org/manage/authentication-and-authorization +/app/services/personal_access_tokens @gitlab-org/manage/authentication-and-authorization +/app/services/projects/move_project_authorizations_service.rb @gitlab-org/manage/authentication-and-authorization +/app/services/resource_access_tokens @gitlab-org/manage/authentication-and-authorization +/app/services/todos/destroy/unauthorized_features_service.rb @gitlab-org/manage/authentication-and-authorization +/app/services/users/authorized_build_service.rb @gitlab-org/manage/authentication-and-authorization +/app/services/users/authorized_create_service.rb @gitlab-org/manage/authentication-and-authorization +/app/services/users/refresh_authorized_projects_service.rb @gitlab-org/manage/authentication-and-authorization +/app/services/webauthn @gitlab-org/manage/authentication-and-authorization +/app/validators/json_schemas/cluster_agent_authorization_configuration.json @gitlab-org/manage/authentication-and-authorization +/app/views/admin/application_settings/_external_authorization_service_form.html.haml @gitlab-org/manage/authentication-and-authorization +/app/views/admin/impersonation_tokens @gitlab-org/manage/authentication-and-authorization +/app/views/authentication @gitlab-org/manage/authentication-and-authorization +/app/views/ci/token_access @gitlab-org/manage/authentication-and-authorization +/app/views/dashboard/projects/_zero_authorized_projects.html.haml @gitlab-org/manage/authentication-and-authorization +/app/views/devise/mailer/password_change.html.haml @gitlab-org/manage/authentication-and-authorization +/app/views/devise/mailer/password_change.text.erb @gitlab-org/manage/authentication-and-authorization +/app/views/devise/mailer/password_change_by_admin.html.haml @gitlab-org/manage/authentication-and-authorization +/app/views/devise/mailer/password_change_by_admin.text.erb @gitlab-org/manage/authentication-and-authorization +/app/views/devise/mailer/reset_password_instructions.html.haml @gitlab-org/manage/authentication-and-authorization +/app/views/devise/mailer/reset_password_instructions.text.erb @gitlab-org/manage/authentication-and-authorization +/app/views/devise/passwords @gitlab-org/manage/authentication-and-authorization +/app/views/devise/shared/_omniauth_box.html.haml @gitlab-org/manage/authentication-and-authorization +/app/views/devise/shared/_signup_omniauth_provider_list.haml @gitlab-org/manage/authentication-and-authorization +/app/views/devise/shared/_signup_omniauth_providers.haml @gitlab-org/manage/authentication-and-authorization +/app/views/devise/shared/_signup_omniauth_providers_top.haml @gitlab-org/manage/authentication-and-authorization +/app/views/doorkeeper/authorizations @gitlab-org/manage/authentication-and-authorization +/app/views/doorkeeper/authorized_applications @gitlab-org/manage/authentication-and-authorization +/app/views/errors/omniauth_error.html.haml @gitlab-org/manage/authentication-and-authorization +/app/views/groups/settings/_resource_access_token_creation.html.haml @gitlab-org/manage/authentication-and-authorization +/app/views/groups/settings/_two_factor_auth.html.haml @gitlab-org/manage/authentication-and-authorization +/app/views/groups/settings/access_tokens @gitlab-org/manage/authentication-and-authorization +/app/views/layouts/oauth_error.html.haml @gitlab-org/manage/authentication-and-authorization +/app/views/notify/access_token_about_to_expire_email.html.haml @gitlab-org/manage/authentication-and-authorization +/app/views/notify/access_token_about_to_expire_email.text.erb @gitlab-org/manage/authentication-and-authorization +/app/views/notify/access_token_created_email.html.haml @gitlab-org/manage/authentication-and-authorization +/app/views/notify/access_token_created_email.text.erb @gitlab-org/manage/authentication-and-authorization +/app/views/notify/access_token_expired_email.html.haml @gitlab-org/manage/authentication-and-authorization +/app/views/notify/access_token_expired_email.text.erb @gitlab-org/manage/authentication-and-authorization +/app/views/profiles/passwords @gitlab-org/manage/authentication-and-authorization +/app/views/profiles/personal_access_tokens @gitlab-org/manage/authentication-and-authorization +/app/views/profiles/two_factor_auths @gitlab-org/manage/authentication-and-authorization +/app/views/projects/mirrors/_authentication_method.html.haml @gitlab-org/manage/authentication-and-authorization +/app/views/projects/settings/access_tokens @gitlab-org/manage/authentication-and-authorization +/app/views/shared/_no_password.html.haml @gitlab-org/manage/authentication-and-authorization +/app/views/shared/_two_factor_auth_recovery_settings_check.html.haml @gitlab-org/manage/authentication-and-authorization +/app/views/shared/access_tokens @gitlab-org/manage/authentication-and-authorization +/app/views/shared/members/_two_factor_auth_badge.html.haml @gitlab-org/manage/authentication-and-authorization +/app/views/shared/tokens @gitlab-org/manage/authentication-and-authorization +/app/workers/authorized_keys_worker.rb @gitlab-org/manage/authentication-and-authorization +/app/workers/authorized_project_update @gitlab-org/manage/authentication-and-authorization +/app/workers/authorized_projects_worker.rb @gitlab-org/manage/authentication-and-authorization +/app/workers/personal_access_tokens @gitlab-org/manage/authentication-and-authorization +/config/feature_flags/development/application_settings_tokens_optional_encryption.yml @gitlab-org/manage/authentication-and-authorization +/config/feature_flags/development/enforce_auth_checks_on_uploads.yml @gitlab-org/manage/authentication-and-authorization +/config/feature_flags/development/forti_authenticator.yml @gitlab-org/manage/authentication-and-authorization +/config/feature_flags/development/forti_token_cloud.yml @gitlab-org/manage/authentication-and-authorization +/config/feature_flags/development/groups_tokens_optional_encryption.yml @gitlab-org/manage/authentication-and-authorization +/config/feature_flags/development/omniauth_initializer_fullhost_proc.yml @gitlab-org/manage/authentication-and-authorization +/config/feature_flags/development/omniauth_login_minimal_scopes.yml @gitlab-org/manage/authentication-and-authorization +/config/feature_flags/development/personal_access_tokens_scoped_to_projects.yml @gitlab-org/manage/authentication-and-authorization +/config/feature_flags/development/projects_tokens_optional_encryption.yml @gitlab-org/manage/authentication-and-authorization +/config/feature_flags/development/specialized_worker_for_group_lock_update_auth_recalculation.yml @gitlab-org/manage/authentication-and-authorization +/config/feature_flags/development/webauthn.yml @gitlab-org/manage/authentication-and-authorization +/config/feature_flags/ops/block_password_auth_for_saml_users.yml @gitlab-org/manage/authentication-and-authorization +/config/initializers/01_secret_token.rb @gitlab-org/manage/authentication-and-authorization +/config/initializers/devise_dynamic_password_length_validation.rb @gitlab-org/manage/authentication-and-authorization +/config/initializers/devise_password_length.rb.example @gitlab-org/manage/authentication-and-authorization +/config/initializers/gitlab_shell_secret_token.rb @gitlab-org/manage/authentication-and-authorization +/config/initializers/omniauth.rb @gitlab-org/manage/authentication-and-authorization +/config/initializers/rails_host_authorization.rb @gitlab-org/manage/authentication-and-authorization +/config/initializers/rails_host_authorization_gitpod.rb @gitlab-org/manage/authentication-and-authorization +/config/initializers/webauthn.rb @gitlab-org/manage/authentication-and-authorization +/config/initializers_before_autoloader/100_patch_omniauth_oauth2.rb @gitlab-org/manage/authentication-and-authorization +/config/initializers_before_autoloader/100_patch_omniauth_saml.rb @gitlab-org/manage/authentication-and-authorization +/ee/app/assets/javascripts/access_tokens @gitlab-org/manage/authentication-and-authorization +/ee/app/assets/javascripts/audit_events/components/tokens @gitlab-org/manage/authentication-and-authorization +/ee/app/assets/javascripts/audit_events/token_utils.js @gitlab-org/manage/authentication-and-authorization +/ee/app/assets/javascripts/groups/settings/components @gitlab-org/manage/authentication-and-authorization +/ee/app/assets/javascripts/pages/groups/omniauth_callbacks @gitlab-org/manage/authentication-and-authorization +/ee/app/assets/javascripts/pipelines/components/pipelines_list @gitlab-org/manage/authentication-and-authorization +/ee/app/assets/javascripts/requirements/components/tokens @gitlab-org/manage/authentication-and-authorization +/ee/app/assets/javascripts/saml_providers/scim_token_service.js @gitlab-org/manage/authentication-and-authorization +/ee/app/assets/javascripts/saml_sso/components @gitlab-org/manage/authentication-and-authorization +/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_auth.vue @gitlab-org/manage/authentication-and-authorization +/ee/app/controllers/concerns/ee/authenticates_with_two_factor.rb @gitlab-org/manage/authentication-and-authorization +/ee/app/controllers/concerns/ee/enforces_two_factor_authentication.rb @gitlab-org/manage/authentication-and-authorization +/ee/app/controllers/concerns/saml_authorization.rb @gitlab-org/manage/authentication-and-authorization +/ee/app/controllers/ee/ldap @gitlab-org/manage/authentication-and-authorization +/ee/app/controllers/ee/omniauth_callbacks_controller.rb @gitlab-org/manage/authentication-and-authorization +/ee/app/controllers/ee/passwords_controller.rb @gitlab-org/manage/authentication-and-authorization +/ee/app/controllers/groups/omniauth_callbacks_controller.rb @gitlab-org/manage/authentication-and-authorization +/ee/app/controllers/groups/scim_oauth_controller.rb @gitlab-org/manage/authentication-and-authorization +/ee/app/controllers/oauth @gitlab-org/manage/authentication-and-authorization +/ee/app/controllers/omniauth_kerberos_spnego_controller.rb @gitlab-org/manage/authentication-and-authorization +/ee/app/finders/auth @gitlab-org/manage/authentication-and-authorization +/ee/app/helpers/ee/access_tokens_helper.rb @gitlab-org/manage/authentication-and-authorization +/ee/app/helpers/ee/auth_helper.rb @gitlab-org/manage/authentication-and-authorization +/ee/app/helpers/ee/personal_access_tokens_helper.rb @gitlab-org/manage/authentication-and-authorization +/ee/app/models/ee/personal_access_token.rb @gitlab-org/manage/authentication-and-authorization +/ee/app/models/ee/project_authorization.rb @gitlab-org/manage/authentication-and-authorization +/ee/app/models/scim_oauth_access_token.rb @gitlab-org/manage/authentication-and-authorization +/ee/app/serializers/scim_oauth_access_token_entity.rb @gitlab-org/manage/authentication-and-authorization +/ee/app/services/ee/auth @gitlab-org/manage/authentication-and-authorization +/ee/app/services/ee/personal_access_tokens @gitlab-org/manage/authentication-and-authorization +/ee/app/services/ee/resource_access_tokens @gitlab-org/manage/authentication-and-authorization +/ee/app/services/personal_access_tokens @gitlab-org/manage/authentication-and-authorization +/ee/app/services/security/token_revocation_service.rb @gitlab-org/manage/authentication-and-authorization +/ee/app/views/admin/application_settings/_personal_access_token_expiration_policy.html.haml @gitlab-org/manage/authentication-and-authorization +/ee/app/views/credentials_inventory_mailer/personal_access_token_revoked_email.html.haml @gitlab-org/manage/authentication-and-authorization +/ee/app/views/credentials_inventory_mailer/personal_access_token_revoked_email.text.haml @gitlab-org/manage/authentication-and-authorization +/ee/app/views/groups/_personal_access_token_expiration_policy.html.haml @gitlab-org/manage/authentication-and-authorization +/ee/app/views/groups/sso/_authorize_pane.html.haml @gitlab-org/manage/authentication-and-authorization +/ee/app/views/notify/policy_revoked_personal_access_tokens_email.html.haml @gitlab-org/manage/authentication-and-authorization +/ee/app/views/notify/policy_revoked_personal_access_tokens_email.text.erb @gitlab-org/manage/authentication-and-authorization +/ee/app/views/oauth @gitlab-org/manage/authentication-and-authorization +/ee/app/views/shared/credentials_inventory/_personal_access_tokens.html.haml @gitlab-org/manage/authentication-and-authorization +/ee/app/views/shared/credentials_inventory/_project_access_tokens.html.haml @gitlab-org/manage/authentication-and-authorization +/ee/app/views/shared/credentials_inventory/personal_access_tokens @gitlab-org/manage/authentication-and-authorization +/ee/app/views/shared/credentials_inventory/project_access_tokens @gitlab-org/manage/authentication-and-authorization +/ee/app/workers/personal_access_tokens @gitlab-org/manage/authentication-and-authorization +/ee/config/routes/oauth.rb @gitlab-org/manage/authentication-and-authorization +/ee/lib/ee/gitlab/auth @gitlab-org/manage/authentication-and-authorization +/ee/lib/ee/gitlab/auth.rb @gitlab-org/manage/authentication-and-authorization +/ee/lib/ee/gitlab/omniauth_initializer.rb @gitlab-org/manage/authentication-and-authorization +/ee/lib/gitlab/auth @gitlab-org/manage/authentication-and-authorization +/ee/lib/gitlab/auth_logger.rb @gitlab-org/manage/authentication-and-authorization +/ee/lib/gitlab/authority_analyzer.rb @gitlab-org/manage/authentication-and-authorization +/ee/lib/gitlab/geo/oauth @gitlab-org/manage/authentication-and-authorization +/ee/lib/gitlab/kerberos @gitlab-org/manage/authentication-and-authorization +/ee/lib/omni_auth @gitlab-org/manage/authentication-and-authorization +/ee/lib/system_check/geo/authorized_keys_check.rb @gitlab-org/manage/authentication-and-authorization +/ee/lib/system_check/geo/authorized_keys_flag_check.rb @gitlab-org/manage/authentication-and-authorization +/lib/api/entities/ci/reset_token_result.rb @gitlab-org/manage/authentication-and-authorization +/lib/api/entities/impersonation_token.rb @gitlab-org/manage/authentication-and-authorization +/lib/api/entities/impersonation_token_with_token.rb @gitlab-org/manage/authentication-and-authorization +/lib/api/entities/personal_access_token.rb @gitlab-org/manage/authentication-and-authorization +/lib/api/entities/personal_access_token_with_token.rb @gitlab-org/manage/authentication-and-authorization +/lib/api/entities/resource_access_token.rb @gitlab-org/manage/authentication-and-authorization +/lib/api/entities/resource_access_token_with_token.rb @gitlab-org/manage/authentication-and-authorization +/lib/api/helpers/authentication.rb @gitlab-org/manage/authentication-and-authorization +/lib/api/helpers/packages/basic_auth_helpers.rb @gitlab-org/manage/authentication-and-authorization +/lib/api/personal_access_tokens.rb @gitlab-org/manage/authentication-and-authorization +/lib/api/resource_access_tokens.rb @gitlab-org/manage/authentication-and-authorization +/lib/api/support/token_with_expiration.rb @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/api_authentication @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/auth @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/auth.rb @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/auth_logger.rb @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/authorized_keys.rb @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/background_migration/encrypt_static_object_token.rb @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/background_migration/migrate_u2f_webauthn.rb @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/background_migration/update_users_where_two_factor_auth_required_from_group.rb @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/chat_name_token.rb @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/ci/pipeline/expression/token.rb @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/external_authorization @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/external_authorization.rb @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/graphql/authorize @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/jwt_authenticatable.rb @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/jwt_token.rb @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/lfs_token.rb @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/mail_room @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/omniauth_initializer.rb @gitlab-org/manage/authentication-and-authorization +/lib/gitlab/project_authorizations.rb @gitlab-org/manage/authentication-and-authorization +/lib/json_web_token @gitlab-org/manage/authentication-and-authorization +/lib/omni_auth @gitlab-org/manage/authentication-and-authorization +/lib/system_check/app/authorized_keys_permission_check.rb @gitlab-org/manage/authentication-and-authorization +/lib/system_check/incoming_email/imap_authentication_check.rb @gitlab-org/manage/authentication-and-authorization +/lib/tasks/gitlab/password.rake @gitlab-org/manage/authentication-and-authorization +/lib/tasks/tokens.rake @gitlab-org/manage/authentication-and-authorization diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index ccc33aa1d18..ee505dae81b 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -1497,13 +1497,13 @@ .reports:rules:gemnasium-dependency_scanning: rules: - - if: '$DEPENDENCY_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\bdependency_scanning\b/ || $DS_EXCLUDED_ANALYZERS =~ /gemnasium([^-]|$)/ || $DS_DEFAULT_ANALYZERS !~ /gemnasium([^-]|$)/' + - if: '$DEPENDENCY_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\bdependency_scanning\b/ || $DS_EXCLUDED_ANALYZERS =~ /gemnasium([^-]|$)/' when: never - changes: *dependency-patterns .reports:rules:gemnasium-python-dependency_scanning: rules: - - if: '$DEPENDENCY_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\bdependency_scanning\b/ || $DS_EXCLUDED_ANALYZERS =~ /gemnasium-python/ || $DS_DEFAULT_ANALYZERS !~ /gemnasium-python/' + - if: '$DEPENDENCY_SCANNING_DISABLED || $GITLAB_FEATURES !~ /\bdependency_scanning\b/ || $DS_EXCLUDED_ANALYZERS =~ /gemnasium-python/' when: never - changes: *python-patterns diff --git a/app/assets/javascripts/content_editor/services/markdown_serializer.js b/app/assets/javascripts/content_editor/services/markdown_serializer.js index 6c8c562af8e..d665f24bba1 100644 --- a/app/assets/javascripts/content_editor/services/markdown_serializer.js +++ b/app/assets/javascripts/content_editor/services/markdown_serializer.js @@ -48,7 +48,6 @@ import Text from '../extensions/text'; import Video from '../extensions/video'; import WordBreak from '../extensions/word_break'; import { - isPlainURL, renderCodeBlock, renderHardBreak, renderTable, @@ -62,36 +61,29 @@ import { renderHTMLNode, renderContent, preserveUnchanged, + bold, + italic, + link, + code, } from './serialization_helpers'; const defaultSerializerConfig = { marks: { - [Bold.name]: defaultMarkdownSerializer.marks.strong, - [Italic.name]: { open: '_', close: '_', mixable: true, expelEnclosingWhitespace: true }, - [Code.name]: defaultMarkdownSerializer.marks.code, + [Bold.name]: bold, + [Italic.name]: italic, + [Code.name]: code, [Subscript.name]: { open: '<sub>', close: '</sub>', mixable: true }, [Superscript.name]: { open: '<sup>', close: '</sup>', mixable: true }, [InlineDiff.name]: { mixable: true, - open(state, mark) { + open(_, mark) { return mark.attrs.type === 'addition' ? '{+' : '{-'; }, - close(state, mark) { + close(_, mark) { return mark.attrs.type === 'addition' ? '+}' : '-}'; }, }, - [Link.name]: { - open(state, mark, parent, index) { - return isPlainURL(mark, parent, index, 1) ? '<' : '['; - }, - close(state, mark, parent, index) { - const href = mark.attrs.canonicalSrc || mark.attrs.href; - - return isPlainURL(mark, parent, index, -1) - ? '>' - : `](${state.esc(href)}${mark.attrs.title ? ` ${state.quote(mark.attrs.title)}` : ''})`; - }, - }, + [Link.name]: link, [MathInline.name]: { open: (...args) => `$${defaultMarkdownSerializer.marks.code.open(...args)}`, close: (...args) => `${defaultMarkdownSerializer.marks.code.close(...args)}$`, diff --git a/app/assets/javascripts/content_editor/services/serialization_helpers.js b/app/assets/javascripts/content_editor/services/serialization_helpers.js index 99aaee8f312..089d30edec7 100644 --- a/app/assets/javascripts/content_editor/services/serialization_helpers.js +++ b/app/assets/javascripts/content_editor/services/serialization_helpers.js @@ -363,3 +363,120 @@ export function preserveUnchanged(render) { } }; } + +const generateBoldTags = (open = true) => { + return (_, mark) => { + const type = /^(\*\*|__|<strong|<b).*/.exec(mark.attrs.sourceMarkdown)?.[1]; + + switch (type) { + case '**': + case '__': + return type; + // eslint-disable-next-line @gitlab/require-i18n-strings + case '<strong': + case '<b': + return (open ? openTag : closeTag)(type.substring(1)); + default: + return '**'; + } + }; +}; + +export const bold = { + open: generateBoldTags(), + close: generateBoldTags(false), + mixable: true, + expelEnclosingWhitespace: true, +}; + +const generateItalicTag = (open = true) => { + return (_, mark) => { + const type = /^(\*|_|<em|<i).*/.exec(mark.attrs.sourceMarkdown)?.[1]; + + switch (type) { + case '*': + case '_': + return type; + // eslint-disable-next-line @gitlab/require-i18n-strings + case '<em': + case '<i': + return (open ? openTag : closeTag)(type.substring(1)); + default: + return '_'; + } + }; +}; + +export const italic = { + open: generateItalicTag(), + close: generateItalicTag(false), + mixable: true, + expelEnclosingWhitespace: true, +}; + +const generateCodeTag = (open = true) => { + return (_, mark) => { + const type = /^(`|<code).*/.exec(mark.attrs.sourceMarkdown)?.[1]; + + if (type === '<code') { + return (open ? openTag : closeTag)(type.substring(1)); + } + + return '`'; + }; +}; + +export const code = { + open: generateCodeTag(), + close: generateCodeTag(false), + mixable: true, + expelEnclosingWhitespace: true, +}; + +const LINK_HTML = 'linkHtml'; +const LINK_MARKDOWN = 'linkMarkdown'; + +const linkType = (sourceMarkdown) => { + const expression = /^(\[|<a).*/.exec(sourceMarkdown)?.[1]; + + if (!expression || expression === '[') { + return LINK_MARKDOWN; + } + + return LINK_HTML; +}; + +export const link = { + open(state, mark, parent, index) { + if (isPlainURL(mark, parent, index, 1)) { + return '<'; + } + + const { canonicalSrc, href, title, sourceMarkdown } = mark.attrs; + + if (linkType(sourceMarkdown) === LINK_MARKDOWN) { + return '['; + } + + const attrs = { href: state.esc(href || canonicalSrc) }; + + if (title) { + attrs.title = title; + } + + return openTag('a', attrs); + }, + close(state, mark, parent, index) { + if (isPlainURL(mark, parent, index, -1)) { + return '>'; + } + + const { canonicalSrc, href, title, sourceMarkdown } = mark.attrs; + + if (linkType(sourceMarkdown) === LINK_HTML) { + return closeTag('a'); + } + + return `](${state.esc(canonicalSrc || href)}${title ? ` ${state.quote(title)}` : ''})`; + }, +}; diff --git a/app/assets/javascripts/issues/show/components/description.vue b/app/assets/javascripts/issues/show/components/description.vue index 6d310ed973c..831cef66836 100644 --- a/app/assets/javascripts/issues/show/components/description.vue +++ b/app/assets/javascripts/issues/show/components/description.vue @@ -8,7 +8,7 @@ import { } from '@gitlab/ui'; import $ from 'jquery'; import Vue from 'vue'; -import { convertToGraphQLId } from '~/graphql_shared/utils'; +import { getIdFromGraphQLId, convertToGraphQLId } from '~/graphql_shared/utils'; import { TYPE_WORK_ITEM } from '~/graphql_shared/constants'; import createFlash from '~/flash'; import { isPositiveInteger } from '~/lib/utils/number_utils'; @@ -140,7 +140,10 @@ export default { } if (this.workItemId) { - this.$refs.detailsModal.show(); + const taskLink = this.$el.querySelector( + `.gfm-issue[data-issue="${getIdFromGraphQLId(this.workItemId)}"]`, + ); + this.openWorkItemDetailModal(taskLink); } }, methods: { @@ -216,7 +219,7 @@ export default { this.addHoverListeners(taskLink, workItemId); taskLink.addEventListener('click', (e) => { e.preventDefault(); - this.$refs.detailsModal.show(); + this.openWorkItemDetailModal(taskLink); this.workItemId = workItemId; this.updateWorkItemIdUrlQuery(issue); this.track('viewed_work_item_from_modal', { @@ -248,7 +251,7 @@ export default { </svg> `; button.setAttribute('aria-label', s__('WorkItem|Convert to work item')); - button.addEventListener('click', () => this.openCreateTaskModal(button.id)); + button.addEventListener('click', () => this.openCreateTaskModal(button)); item.prepend(button); }); }, @@ -265,20 +268,29 @@ export default { } }); }, - openCreateTaskModal(id) { - const { parentElement } = this.$el.querySelector(`#${id}`); + setActiveTask(el) { + const { parentElement } = el; const lineNumbers = parentElement.getAttribute('data-sourcepos').match(/\b\d+(?=:)/g); this.activeTask = { - id, title: parentElement.innerText, lineNumberStart: lineNumbers[0], lineNumberEnd: lineNumbers[1], }; + }, + openCreateTaskModal(el) { + this.setActiveTask(el); this.$refs.modal.show(); }, closeCreateTaskModal() { this.$refs.modal.hide(); }, + openWorkItemDetailModal(el) { + if (!el) { + return; + } + this.setActiveTask(el); + this.$refs.detailsModal.show(); + }, closeWorkItemDetailModal() { this.workItemId = undefined; this.updateWorkItemIdUrlQuery(undefined); @@ -287,7 +299,8 @@ export default { this.$emit('updateDescription', description); this.closeCreateTaskModal(); }, - handleDeleteTask() { + handleDeleteTask(description) { + this.$emit('updateDescription', description); this.$toast.show(s__('WorkItem|Work item deleted')); }, updateWorkItemIdUrlQuery(workItemId) { @@ -353,6 +366,10 @@ export default { ref="detailsModal" :can-update="canUpdate" :work-item-id="workItemId" + :issue-gid="issueGid" + :lock-version="lockVersion" + :line-number-start="activeTask.lineNumberStart" + :line-number-end="activeTask.lineNumberEnd" @workItemDeleted="handleDeleteTask" @close="closeWorkItemDetailModal" /> diff --git a/app/assets/javascripts/jobs/components/sidebar.vue b/app/assets/javascripts/jobs/components/sidebar.vue index 1b4c9ebdf7d..cc099dba72f 100644 --- a/app/assets/javascripts/jobs/components/sidebar.vue +++ b/app/assets/javascripts/jobs/components/sidebar.vue @@ -125,6 +125,7 @@ export default { :title="$options.i18n.cancelJobButtonLabel" :aria-label="$options.i18n.cancelJobButtonLabel" :href="job.cancel_path" + variant="danger" icon="cancel" data-method="post" data-testid="cancel-button" diff --git a/app/assets/javascripts/work_items/components/work_item_actions.vue b/app/assets/javascripts/work_items/components/work_item_actions.vue index 701cb84df59..31e4a932c5a 100644 --- a/app/assets/javascripts/work_items/components/work_item_actions.vue +++ b/app/assets/javascripts/work_items/components/work_item_actions.vue @@ -1,7 +1,7 @@ <script> import { GlDropdown, GlDropdownItem, GlModal, GlModalDirective } from '@gitlab/ui'; import { s__ } from '~/locale'; -import deleteWorkItemMutation from '../graphql/delete_work_item.mutation.graphql'; +import Tracking from '~/tracking'; export default { i18n: { @@ -15,6 +15,7 @@ export default { directives: { GlModal: GlModalDirective, }, + mixins: [Tracking.mixin({ label: 'actions_menu' })], props: { workItemId: { type: String, @@ -27,36 +28,16 @@ export default { default: false, }, }, - emits: ['workItemDeleted', 'error'], + emits: ['deleteWorkItem'], methods: { - deleteWorkItem() { - this.$apollo - .mutate({ - mutation: deleteWorkItemMutation, - variables: { - input: { - id: this.workItemId, - }, - }, - }) - .then(({ data: { workItemDelete, errors } }) => { - if (errors?.length) { - throw new Error(errors[0].message); - } - - if (workItemDelete?.errors.length) { - throw new Error(workItemDelete.errors[0]); - } - - this.$emit('workItemDeleted'); - }) - .catch((e) => { - this.$emit( - 'error', - e.message || - s__('WorkItem|Something went wrong when deleting the work item. Please try again.'), - ); - }); + handleDeleteWorkItem() { + this.track('click_delete_work_item'); + this.$emit('deleteWorkItem'); + }, + handleCancelDeleteWorkItem({ trigger }) { + if (trigger !== 'ok') { + this.track('cancel_delete_work_item'); + } }, }, }; @@ -81,7 +62,8 @@ export default { :title="$options.i18n.deleteWorkItem" :ok-title="$options.i18n.deleteWorkItem" ok-variant="danger" - @ok="deleteWorkItem" + @ok="handleDeleteWorkItem" + @hide="handleCancelDeleteWorkItem" > {{ s__( diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue index 33d49583b04..4222ffe42fe 100644 --- a/app/assets/javascripts/work_items/components/work_item_detail.vue +++ b/app/assets/javascripts/work_items/components/work_item_detail.vue @@ -67,11 +67,6 @@ export default { return this.workItem?.userPermissions?.deleteWorkItem; }, }, - methods: { - handleWorkItemDeleted() { - this.$emit('workItemDeleted'); - }, - }, }; </script> @@ -101,7 +96,7 @@ export default { :work-item-id="workItem.id" :can-delete="canDelete" class="gl-ml-auto gl-mt-5" - @workItemDeleted="handleWorkItemDeleted" + @deleteWorkItem="$emit('deleteWorkItem')" @error="error = $event" /> </div> diff --git a/app/assets/javascripts/work_items/components/work_item_detail_modal.vue b/app/assets/javascripts/work_items/components/work_item_detail_modal.vue index 693a7649508..172a40a6e56 100644 --- a/app/assets/javascripts/work_items/components/work_item_detail_modal.vue +++ b/app/assets/javascripts/work_items/components/work_item_detail_modal.vue @@ -1,5 +1,7 @@ <script> import { GlAlert, GlModal } from '@gitlab/ui'; +import { s__ } from '~/locale'; +import deleteWorkItemFromTaskMutation from '../graphql/delete_task_from_work_item.mutation.graphql'; import WorkItemDetail from './work_item_detail.vue'; export default { @@ -14,17 +16,72 @@ export default { required: false, default: null, }, + issueGid: { + type: String, + required: false, + default: '', + }, + lockVersion: { + type: Number, + required: false, + default: null, + }, + lineNumberStart: { + type: String, + required: false, + default: null, + }, + lineNumberEnd: { + type: String, + required: false, + default: null, + }, }, - emits: ['workItemDeleted', 'close'], + emits: ['workItemDeleted', 'workItemUpdated', 'close'], data() { return { error: undefined, }; }, methods: { - handleWorkItemDeleted() { - this.$emit('workItemDeleted'); - this.closeModal(); + deleteWorkItem() { + this.$apollo + .mutate({ + mutation: deleteWorkItemFromTaskMutation, + variables: { + input: { + id: this.issueGid, + lockVersion: this.lockVersion, + taskData: { + id: this.workItemId, + lineNumberStart: Number(this.lineNumberStart), + lineNumberEnd: Number(this.lineNumberEnd), + }, + }, + }, + }) + .then( + ({ + data: { + workItemDeleteTask: { + workItem: { descriptionHtml }, + errors, + }, + }, + }) => { + if (errors?.length) { + throw new Error(errors[0].message); + } + + this.$emit('workItemDeleted', descriptionHtml); + this.$refs.modal.hide(); + }, + ) + .catch((e) => { + this.error = + e.message || + s__('WorkItem|Something went wrong when deleting the work item. Please try again.'); + }); }, closeModal() { this.error = ''; @@ -46,7 +103,11 @@ export default { {{ error }} </gl-alert> - <work-item-detail :work-item-id="workItemId" @workItemDeleted="handleWorkItemDeleted" /> + <work-item-detail + :work-item-id="workItemId" + @deleteWorkItem="deleteWorkItem" + @workItemUpdated="$emit('workItemUpdated')" + /> </gl-modal> </template> diff --git a/app/assets/javascripts/work_items/components/work_item_state.vue b/app/assets/javascripts/work_items/components/work_item_state.vue index 7d4b48f847f..51db4c804eb 100644 --- a/app/assets/javascripts/work_items/components/work_item_state.vue +++ b/app/assets/javascripts/work_items/components/work_item_state.vue @@ -75,6 +75,8 @@ export default { if (workItemUpdate?.errors?.length) { throw new Error(workItemUpdate.errors[0]); } + + this.$emit('updated'); } catch (error) { this.$emit('error', i18n.updateError); Sentry.captureException(error); diff --git a/app/assets/javascripts/work_items/components/work_item_title.vue b/app/assets/javascripts/work_items/components/work_item_title.vue index 73b46bb06d2..d2e6d3c0bbf 100644 --- a/app/assets/javascripts/work_items/components/work_item_title.vue +++ b/app/assets/javascripts/work_items/components/work_item_title.vue @@ -52,6 +52,7 @@ export default { }, }); this.track('updated_title'); + this.$emit('updated'); } catch { this.$emit('error', i18n.updateError); } diff --git a/app/assets/javascripts/work_items/graphql/delete_task_from_work_item.mutation.graphql b/app/assets/javascripts/work_items/graphql/delete_task_from_work_item.mutation.graphql new file mode 100644 index 00000000000..32c07ed48c7 --- /dev/null +++ b/app/assets/javascripts/work_items/graphql/delete_task_from_work_item.mutation.graphql @@ -0,0 +1,9 @@ +mutation workItemDeleteTask($input: WorkItemDeleteTaskInput!) { + workItemDeleteTask(input: $input) { + workItem { + id + descriptionHtml + } + errors + } +} diff --git a/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql b/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql index ca5ba7a7d8e..e25fd102699 100644 --- a/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql +++ b/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql @@ -2,6 +2,7 @@ fragment WorkItem on WorkItem { id title state + description workItemType { id name diff --git a/app/assets/javascripts/work_items/index.js b/app/assets/javascripts/work_items/index.js index 10fae9b9cc0..e39b0d6a353 100644 --- a/app/assets/javascripts/work_items/index.js +++ b/app/assets/javascripts/work_items/index.js @@ -5,7 +5,7 @@ import { createApolloProvider } from './graphql/provider'; export const initWorkItemsRoot = () => { const el = document.querySelector('#js-work-items'); - const { fullPath } = el.dataset; + const { fullPath, issuesListPath } = el.dataset; return new Vue({ el, @@ -13,6 +13,7 @@ export const initWorkItemsRoot = () => { apolloProvider: createApolloProvider(), provide: { fullPath, + issuesListPath, }, render(createElement) { return createElement(App); diff --git a/app/assets/javascripts/work_items/pages/work_item_root.vue b/app/assets/javascripts/work_items/pages/work_item_root.vue index b8ce6d641a9..6dc3dc3b3c9 100644 --- a/app/assets/javascripts/work_items/pages/work_item_root.vue +++ b/app/assets/javascripts/work_items/pages/work_item_root.vue @@ -1,33 +1,70 @@ <script> +import { GlAlert } from '@gitlab/ui'; import { TYPE_WORK_ITEM } from '~/graphql_shared/constants'; import { convertToGraphQLId } from '~/graphql_shared/utils'; +import { visitUrl } from '~/lib/utils/url_utility'; import { s__ } from '~/locale'; import WorkItemDetail from '../components/work_item_detail.vue'; +import deleteWorkItemMutation from '../graphql/delete_work_item.mutation.graphql'; export default { components: { + GlAlert, WorkItemDetail, }, + inject: ['issuesListPath'], props: { id: { type: String, required: true, }, }, + data() { + return { + error: '', + }; + }, computed: { gid() { return convertToGraphQLId(TYPE_WORK_ITEM, this.id); }, }, methods: { - handleWorkItemDeleted() { - this.$root.$toast.show(s__('WorkItem|Work item deleted')); - this.$router.push('/'); + deleteWorkItem() { + this.$apollo + .mutate({ + mutation: deleteWorkItemMutation, + variables: { + input: { + id: this.gid, + }, + }, + }) + .then(({ data: { workItemDelete, errors } }) => { + if (errors?.length) { + throw new Error(errors[0].message); + } + + if (workItemDelete?.errors.length) { + throw new Error(workItemDelete.errors[0]); + } + + this.$toast.show(s__('WorkItem|Work item deleted')); + visitUrl(this.issuesListPath); + }) + .catch((e) => { + this.error = + e.message || + s__('WorkItem|Something went wrong when deleting the work item. Please try again.'); + }); }, }, }; </script> <template> - <work-item-detail :work-item-id="gid" @workItemDeleted="handleWorkItemDeleted" /> + <div> + <gl-alert v-if="error" variant="danger" @dismiss="error = ''">{{ error }}</gl-alert> + <work-item-detail :work-item-id="gid" @deleteWorkItem="deleteWorkItem" /> + </div> </template> diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb index e714cbb5b70..0817813f967 100644 --- a/app/controllers/oauth/authorizations_controller.rb +++ b/app/controllers/oauth/authorizations_controller.rb @@ -5,7 +5,7 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController include InitializesCurrentUserMode include Gitlab::Utils::StrongMemoize - before_action :verify_confirmed_email!, :verify_confidential_application! + before_action :verify_confirmed_email! layout 'profile' @@ -77,18 +77,6 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController doorkeeper_application&.includes_scope?(*::Gitlab::Auth::API_SCOPES) end - # Confidential apps require the client_secret to be sent with the request. - # Doorkeeper allows implicit grant flow requests (response_type=token) to - # work without client_secret regardless of the confidential setting. - # This leads to security vulnerabilities and we want to block it. - def verify_confidential_application! - render 'doorkeeper/authorizations/error' if authorizable_confidential? - end - - def authorizable_confidential? - pre_auth.authorizable? && pre_auth.response_type == 'token' && pre_auth.client.application.confidential - end - def verify_confirmed_email! return if current_user&.confirmed? diff --git a/app/graphql/types/permission_types/timelog.rb b/app/graphql/types/permission_types/timelog.rb new file mode 100644 index 00000000000..c35f3101e39 --- /dev/null +++ b/app/graphql/types/permission_types/timelog.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Types + module PermissionTypes + class Timelog < BasePermissionType + graphql_name 'TimelogPermissions' + + abilities :admin_timelog + end + end +end diff --git a/app/graphql/types/timelog_type.rb b/app/graphql/types/timelog_type.rb index fd9943780d7..c3fb9b77927 100644 --- a/app/graphql/types/timelog_type.rb +++ b/app/graphql/types/timelog_type.rb @@ -6,6 +6,8 @@ module Types authorize :read_issue + expose_permissions Types::PermissionTypes::Timelog + field :id, GraphQL::Types::ID, null: false, diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb index 0f4cac7f5b4..e1ed9f95601 100644 --- a/app/models/ci/job_artifact.rb +++ b/app/models/ci/job_artifact.rb @@ -187,7 +187,6 @@ module Ci scope :downloadable, -> { where(file_type: DOWNLOADABLE_TYPES) } scope :unlocked, -> { joins(job: :pipeline).merge(::Ci::Pipeline.unlocked) } scope :order_expired_asc, -> { order(expire_at: :asc) } - scope :order_expired_desc, -> { order(expire_at: :desc) } scope :with_destroy_preloads, -> { includes(project: [:route, :statistics]) } scope :for_project, ->(project) { where(project_id: project) } diff --git a/app/services/service_ping/submit_service.rb b/app/services/service_ping/submit_service.rb index 909c4e860e9..66069ef3e35 100644 --- a/app/services/service_ping/submit_service.rb +++ b/app/services/service_ping/submit_service.rb @@ -101,3 +101,5 @@ module ServicePing end end end + +ServicePing::SubmitService.prepend_mod diff --git a/app/views/projects/work_items/index.html.haml b/app/views/projects/work_items/index.html.haml index 0efd7a740d3..356f93c6ed5 100644 --- a/app/views/projects/work_items/index.html.haml +++ b/app/views/projects/work_items/index.html.haml @@ -1,3 +1,3 @@ - page_title s_('WorkItem|Work Items') -#js-work-items{ data: { full_path: @project.full_path } } +#js-work-items{ data: { full_path: @project.full_path, issues_list_path: project_issues_path(@project) } } diff --git a/config/database.yml.env b/config/database.yml.env deleted file mode 100644 index 1e35651c9a6..00000000000 --- a/config/database.yml.env +++ /dev/null @@ -1,17 +0,0 @@ -<%= ENV['RAILS_ENV'] %>: - ## Connection information - # Please be aware that the DATABASE_URL environment variable will take - # precedence over the following 6 parameters. For more information, see - # doc/administration/environment_variables.md - adapter: <%= ENV['GITLAB_DATABASE_ADAPTER'] || 'postgresql' %> - database: <%= ENV['GITLAB_DATABASE_DATABASE'] || "gitlab_#{ENV['RAILS_ENV']}" %> - username: <%= ENV['GITLAB_DATABASE_USERNAME'] || 'root' %> - password: <%= ENV['GITLAB_DATABASE_PASSWORD'] || '' %> - host: <%= ENV['GITLAB_DATABASE_HOST'] || 'localhost' %> - port: <%= ENV['GITLAB_DATABASE_PORT'] || '5432' %> - - ## Behavior information - # The following parameters will be used even if you're using the DATABASE_URL - # environment variable. - encoding: <%= ENV['GITLAB_DATABASE_ENCODING'] || 'unicode' %> - pool: <%= ENV['GITLAB_DATABASE_POOL'] || '10' %> diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index 58dd19a8270..7827ad8f168 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -90,11 +90,10 @@ Doorkeeper.configure do # strings and the flows they enable are: # # "authorization_code" => Authorization Code Grant Flow - # "implicit" => Implicit Grant Flow # "password" => Resource Owner Password Credentials Grant Flow # "client_credentials" => Client Credentials Grant Flow # - grant_flows %w(authorization_code implicit password client_credentials) + grant_flows %w(authorization_code password client_credentials) # Under some circumstances you might want to have applications auto-approved, # so that the user skips the authorization step. diff --git a/data/removals/15_0/15-0-database-deprecate-legacy-database-conf.yml b/data/removals/15_0/15-0-database-deprecate-legacy-database-conf.yml new file mode 100644 index 00000000000..c9c64de7aff --- /dev/null +++ b/data/removals/15_0/15-0-database-deprecate-legacy-database-conf.yml @@ -0,0 +1,17 @@ +- name: "Support for legacy format of `config/database.yml` removed" + announcement_milestone: "14.3" + announcement_date: "2021-09-22" + removal_milestone: "15.0" + removal_date: "2022-05-22" # the date of the milestone release when this feature is planned to be removed + breaking_change: true + body: | + The syntax of [GitLab's database](https://docs.gitlab.com/omnibus/settings/database.html) + configuration located in `database.yml` has changed and the legacy format has been removed. + The legacy format supported a single PostgreSQL adapter, whereas the new format supports multiple databases. + The `main:` database needs to be defined as a first configuration item. + + This change only impacts users compiling GitLab from source, all the other installation methods handle this configuration automatically. + Instructions are available [in the source update documentation](https://docs.gitlab.com/ee/update/upgrading_from_source.html#new-configuration-options-for-databaseyml). + stage: Enablement + tiers: [Core, Premium, Ultimate] + issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/338182 diff --git a/data/removals/15_0/15-0-geo-remove-db-rake-tasks.yml b/data/removals/15_0/15-0-geo-remove-db-rake-tasks.yml new file mode 100644 index 00000000000..571217f9c92 --- /dev/null +++ b/data/removals/15_0/15-0-geo-remove-db-rake-tasks.yml @@ -0,0 +1,31 @@ +- name: "Custom `geo:db:*` Rake tasks are no longer available" + announcement_milestone: "14.8" + announcement_date: "2022-02-22" + removal_milestone: "15.0" + removal_date: "2022-05-22" + breaking_change: false + reporter: sranasinghe + stage: enablement + issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351945 + body: | + In GitLab 14.8, we [deprecated the `geo:db:*` Rake tasks and replaced them with built-in tasks](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77269/diffs) after [switching the Geo tracking database to use Rails' 6 support of multiple databases](https://gitlab.com/groups/gitlab-org/-/epics/6458). + The following `geo:db:*` tasks have been removed from GitLab 15.0 and have been replaced with their corresponding `db:*:geo` tasks: + + - `geo:db:drop` -> `db:drop:geo` + - `geo:db:create` -> `db:create:geo` + - `geo:db:setup` -> `db:setup:geo` + - `geo:db:migrate` -> `db:migrate:geo` + - `geo:db:rollback` -> `db:rollback:geo` + - `geo:db:version` -> `db:version:geo` + - `geo:db:reset` -> `db:reset:geo` + - `geo:db:seed` -> `db:seed:geo` + - `geo:schema:load:geo` -> `db:schema:load:geo` + - `geo:db:schema:dump` -> `db:schema:dump:geo` + - `geo:db:migrate:up` -> `db:migrate:up:geo` + - `geo:db:migrate:down` -> `db:migrate:down:geo` + - `geo:db:migrate:redo` -> `db:migrate:redo:geo` + - `geo:db:migrate:status` -> `db:migrate:status:geo` + - `geo:db:test:prepare` -> `db:test:prepare:geo` + - `geo:db:test:load` -> `db:test:load:geo` + - `geo:db:test:purge` -> `db:test:purge:geo` + tiers: ["Premium", "Ultimate"] diff --git a/data/removals/15_0/15-0-geo-remove-promote-db.yml b/data/removals/15_0/15-0-geo-remove-promote-db.yml new file mode 100644 index 00000000000..65fa8fdff8e --- /dev/null +++ b/data/removals/15_0/15-0-geo-remove-promote-db.yml @@ -0,0 +1,13 @@ +- name: "The `promote-db` command is no longer available from `gitlab-ctl`" + announcement_milestone: "14.5" + announcement_date: "2021-11-22" + removal_milestone: "15.0" + removal_date: "2022-05-22" + breaking_change: true + reporter: sranasinghe + stage: enablement + issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345207 + body: | + In GitLab 14.5, we introduced the command `gitlab-ctl promote` to promote any Geo secondary node to a primary during a failover. This command replaces `gitlab-ctl promote-db` which is used to promote database nodes in multi-node Geo secondary sites. The `gitlab-ctl promote-db` command has been removed in GitLab 15.0. + tiers: [Premium, Ultimate] + documentation_url: https://docs.gitlab.com/ee/administration/geo/disaster_recovery/planned_failover.html diff --git a/data/removals/15_0/15-0-remove-replicaiton-detail-routes.yml b/data/removals/15_0/15-0-remove-replicaiton-detail-routes.yml new file mode 100644 index 00000000000..133a7464a91 --- /dev/null +++ b/data/removals/15_0/15-0-remove-replicaiton-detail-routes.yml @@ -0,0 +1,12 @@ +- name: "Legacy Geo Admin UI routes" + announcement_milestone: "14.8" + announcement_date: "2022-02-22" + removal_milestone: "15.0" + removal_date: "2022-05-22" + breaking_change: false + reporter: sranasinghe + stage: enablement + issue_url: "https://gitlab.com/gitlab-org/gitlab/-/issues/351345" + body: | + In GitLab 13.0, we introduced new project and design replication details routes in the Geo Admin UI. These routes are `/admin/geo/replication/projects` and `/admin/geo/replication/designs`. We kept the legacy routes and redirected them to the new routes. These legacy routes `/admin/geo/projects` and `/admin/geo/designs` have been removed in GitLab 15.0. Please update any bookmarks or scripts that may use the legacy routes. + tiers: ["Premium", "Ultimate"] diff --git a/doc/.vale/gitlab/Admin.yml b/doc/.vale/gitlab/Admin.yml index 987dbfdbd09..f6b0a988499 100644 --- a/doc/.vale/gitlab/Admin.yml +++ b/doc/.vale/gitlab/Admin.yml @@ -10,4 +10,4 @@ link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html level: suggestion ignorecase: false swap: - '[Aa]dmin ?\w*': '(?:Admin( Area| Mode)?|[Aa]dminist(ration|rator|rators|er|rative))' + '[Aa]dmin ?\w*': '(?:Admin( Area| Mode)?|[Aa]dminist(ration|rator|rators|er|rative|ering|ered))' diff --git a/doc/administration/environment_variables.md b/doc/administration/environment_variables.md index 22159b6e9db..1fe5b223f8b 100644 --- a/doc/administration/environment_variables.md +++ b/doc/administration/environment_variables.md @@ -37,31 +37,6 @@ You can use the following environment variables to override certain values: | `RAILS_ENV` | string | The Rails environment; can be one of `production`, `development`, `staging`, or `test`. | | `UNSTRUCTURED_RAILS_LOG` | string | Enables the unstructured log in addition to JSON logs (defaults to `true`). | -## Complete database variables - -The recommended method for specifying your database connection information is -to set the `DATABASE_URL` environment variable. This variable contains -connection information (`adapter`, `database`, `username`, `password`, `host`, -and `port`), but no behavior information (`encoding` or `pool`). If you don't -want to use `DATABASE_URL`, or want to set database behavior information, -either: - -- Copy the template file, `cp config/database.yml.env config/database.yml`. -- Set a value for some `GITLAB_DATABASE_XXX` variables. - -The list of `GITLAB_DATABASE_XXX` variables that you can set is: - -| Variable | Default value | Overridden by `DATABASE_URL`? | -|-----------------------------|--------------------------------|-------------------------------| -| `GITLAB_DATABASE_ADAPTER` | `postgresql` | **{check-circle}** Yes | -| `GITLAB_DATABASE_DATABASE` | `gitlab_#{ENV['RAILS_ENV']` | **{check-circle}** Yes | -| `GITLAB_DATABASE_ENCODING` | `unicode` | **{dotted-circle}** No | -| `GITLAB_DATABASE_HOST` | `localhost` | **{check-circle}** Yes | -| `GITLAB_DATABASE_PASSWORD` | _none_ | **{check-circle}** Yes | -| `GITLAB_DATABASE_POOL` | `10` | **{dotted-circle}** No | -| `GITLAB_DATABASE_PORT` | `5432` | **{check-circle}** Yes | -| `GITLAB_DATABASE_USERNAME` | `root` | **{check-circle}** Yes | - ## Adding more variables We welcome merge requests to make more settings configurable by using variables. diff --git a/doc/administration/maintenance_mode/index.md b/doc/administration/maintenance_mode/index.md index 3513ff1a274..41eed500594 100644 --- a/doc/administration/maintenance_mode/index.md +++ b/doc/administration/maintenance_mode/index.md @@ -116,11 +116,11 @@ For most JSON requests, POST, PUT, PATCH, and DELETE are blocked, and the API re | POST | `/users/sign_in` | To allow users to log in. | | POST | `/users/sign_out`| To allow users to log out. | | POST | `/oauth/token` | To allow users to log in to a Geo secondary for the first time. | -| POST | `/admin/session`, `/admin/session/destroy` | To allow [Administrator mode for GitLab administrators](https://gitlab.com/groups/gitlab-org/-/epics/2158) | +| POST | `/admin/session`, `/admin/session/destroy` | To allow [Admin Mode for GitLab administrators](https://gitlab.com/groups/gitlab-org/-/epics/2158) | | POST | Paths ending with `/compare`| Git revision routes. | | POST | `.git/git-upload-pack` | To allow Git pull/clone. | | POST | `/api/v4/internal` | [internal API routes](../../development/internal_api/index.md) | -| POST | `/admin/sidekiq` | To allow management of background jobs in the Admin UI | +| POST | `/admin/sidekiq` | To allow management of background jobs in the Admin Area | | POST | `/admin/geo` | To allow updating Geo Nodes in the administrator UI | | POST | `/api/v4/geo_replication`| To allow certain Geo-specific administrator UI actions on secondary sites | diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md index b59b823a00b..0edad83cc18 100644 --- a/doc/administration/packages/container_registry.md +++ b/doc/administration/packages/container_registry.md @@ -158,7 +158,7 @@ If your certificate provider provides the CA Bundle certificates, append them to An administrator may want the container registry listening on an arbitrary port such as `5678`. However, the registry and application server are behind an AWS application load balancer that only -listens on ports `80` and `443`. The admin may simply remove the port number for +listens on ports `80` and `443`. The administrator may simply remove the port number for `registry_external_url`, so HTTP or HTTPS is assumed. Then, the rules apply that map the load balancer to the registry from ports `80` or `443` to the arbitrary port. This is important if users rely on the `docker login` example in the container registry. Here's an example: @@ -1246,7 +1246,7 @@ GitLab has a default token expiration of 5 minutes for the registry. When pushin larger images, or images that take longer than 5 minutes to push, users may encounter this error. On GitLab.com, the expiration time is 15 minutes. -Administrators can increase the token duration in **Admin area > Settings > +Administrators can increase the token duration in **Admin Area > Settings > CI/CD > Container Registry > Authorization token duration (minutes)**. ### Docker login attempt fails with: 'token signed by untrusted key' diff --git a/doc/administration/raketasks/storage.md b/doc/administration/raketasks/storage.md index 0b7bef5b9a0..689ed5c5824 100644 --- a/doc/administration/raketasks/storage.md +++ b/doc/administration/raketasks/storage.md @@ -79,7 +79,7 @@ In GitLab 13.0, [hashed storage](../repository_storage_types.md#hashed-storage) is enabled by default and the legacy storage is deprecated. GitLab 14.0 eliminates support for legacy storage. If you're on GitLab 13.0 and later, switching new projects to legacy storage is not possible. -The option to choose between hashed and legacy storage in the admin area has +The option to choose between hashed and legacy storage in the Admin Area has been disabled. This task must be run on any machine that has Rails/Sidekiq configured, and the task @@ -132,7 +132,7 @@ In GitLab 13.0, [hashed storage](../repository_storage_types.md#hashed-storage) is enabled by default and the legacy storage is deprecated. GitLab 14.0 eliminates support for legacy storage. If you're on GitLab 13.0 and later, switching new projects to legacy storage is not possible. -The option to choose between hashed and legacy storage in the admin area has +The option to choose between hashed and legacy storage in the Admin Area has been disabled. This task schedules all your existing projects and associated attachments to be rolled back to the @@ -213,7 +213,7 @@ they might fail to migrate. ### Projects pending deletion -Check the project details in the admin area. If deleting the project failed +Check the project details in the Admin Area. If deleting the project failed it will show as `Marked For Deletion At ..`, `Scheduled Deletion At ..` and `pending removal`, but the dates will not be recent. @@ -229,7 +229,7 @@ Delete the project using the Rails console: puts "\nproject selected for deletion is:\nID: #{project.id}\nPATH: #{project.full_path}\nNAME: #{project.name}\n\n" ``` - - Replace `janedoe/testproject` with your project path from the Rake take output or from the admin area. + - Replace `janedoe/testproject` with your project path from the Rake take output or from the Admin Area. - Replace `admin_handle` with the handle of an instance administrator or with `root`. - Verify the output before proceeding. **There are no other checks performed**. diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md index f33d494f638..ff02361171d 100644 --- a/doc/administration/repository_storage_types.md +++ b/doc/administration/repository_storage_types.md @@ -75,7 +75,7 @@ translate between the human-readable project name and the hashed storage path. Y Administrators can look up a project's hashed path from its name or ID using: -- The [Admin area](../user/admin_area/index.md#administering-projects). +- The [Admin Area](../user/admin_area/index.md#administering-projects). - A Rails console. To look up a project's hash path in the Admin Area: diff --git a/doc/api/container_registry.md b/doc/api/container_registry.md index 3ba249099c2..0dd2106b4d1 100644 --- a/doc/api/container_registry.md +++ b/doc/api/container_registry.md @@ -420,7 +420,7 @@ These are different from project or personal access tokens in the GitLab applica GET /v2/_catalog ``` -To list all container repositories on your GitLab instance, admin credentials are required: +To list all container repositories on your GitLab instance, administrator credentials are required: ```shell $ curl --request GET --user "<admin-username>:<admin-password>" "https://gitlab.example.com/jwt/auth?service=container_registry&scope=registry:catalog:*" diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index ac7b2433596..bc8feb29e8e 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -9463,7 +9463,7 @@ Represents the total number of issues and their weights for a particular day. | <a id="ciminutesnamespacemonthlyusagemonth"></a>`month` | [`String`](#string) | Month related to the usage data. | | <a id="ciminutesnamespacemonthlyusagemonthiso8601"></a>`monthIso8601` | [`ISO8601Date`](#iso8601date) | Month related to the usage data in ISO 8601 date format. | | <a id="ciminutesnamespacemonthlyusageprojects"></a>`projects` | [`CiMinutesProjectMonthlyUsageConnection`](#ciminutesprojectmonthlyusageconnection) | CI minutes usage data for projects in the namespace. (see [Connections](#connections)) | -| <a id="ciminutesnamespacemonthlyusagesharedrunnersduration"></a>`sharedRunnersDuration` | [`Int`](#int) | Total numbers of minutes used by the shared runners in the namespace. | +| <a id="ciminutesnamespacemonthlyusagesharedrunnersduration"></a>`sharedRunnersDuration` | [`Int`](#int) | Total duration (in seconds) of shared runners use by the namespace for the month. | ### `CiMinutesProjectMonthlyUsage` @@ -9473,6 +9473,7 @@ Represents the total number of issues and their weights for a particular day. | ---- | ---- | ----------- | | <a id="ciminutesprojectmonthlyusageminutes"></a>`minutes` | [`Int`](#int) | Number of CI minutes used by the project in the month. | | <a id="ciminutesprojectmonthlyusagename"></a>`name` | [`String`](#string) | Name of the project. | +| <a id="ciminutesprojectmonthlyusagesharedrunnersduration"></a>`sharedRunnersDuration` | [`Int`](#int) | Total duration (in seconds) of shared runners use by the project for the month. | ### `CiRunner` @@ -16747,6 +16748,15 @@ Describes an incident management timeline event. | <a id="timelogsummary"></a>`summary` | [`String`](#string) | Summary of how the time was spent. | | <a id="timelogtimespent"></a>`timeSpent` | [`Int!`](#int) | Time spent displayed in seconds. | | <a id="timeloguser"></a>`user` | [`UserCore!`](#usercore) | User that logged the time. | +| <a id="timeloguserpermissions"></a>`userPermissions` | [`TimelogPermissions!`](#timelogpermissions) | Permissions for the current user on the resource. | + +### `TimelogPermissions` + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="timelogpermissionsadmintimelog"></a>`adminTimelog` | [`Boolean!`](#boolean) | Indicates the user can perform `admin_timelog` on this resource. | ### `Todo` diff --git a/doc/api/oauth2.md b/doc/api/oauth2.md index ad93d8033d0..aa9a86f33d5 100644 --- a/doc/api/oauth2.md +++ b/doc/api/oauth2.md @@ -33,7 +33,7 @@ Implicit grant and Resource Owner Password Credentials flows. Refer to the [OAuth RFC](https://tools.ietf.org/html/rfc6749) to find out how all those flows work and pick the right one for your use case. -Both **authorization code** (with or without PKCE) and **implicit grant** flows require `application` to be +Authorization code (with or without PKCE) flow requires `application` to be registered first via the `/profile/applications` page in your user's account. During registration, by enabling proper scopes, you can limit the range of resources which the `application` can access. Upon creation, you obtain the @@ -59,8 +59,6 @@ For development, GitLab allows insecure HTTP redirect URIs. As OAuth 2.0 bases its security entirely on the transport layer, you should not use unprotected URIs. For more information, see the [OAuth 2.0 RFC](https://tools.ietf.org/html/rfc6749#section-3.1.2.1) and the [OAuth 2.0 Threat Model RFC](https://tools.ietf.org/html/rfc6819#section-4.4.2.1). -These factors are particularly important when using the -[Implicit grant flow](#implicit-grant-flow-deprecated), where actual credentials are included in the `redirect_uri`. In the following sections you can find detailed instructions on how to obtain authorization with each flow. @@ -319,12 +317,13 @@ access_token = client.password.get_token('user@example.com', 'secret') puts access_token.token ``` -### Implicit grant flow (DEPRECATED) +<!--- start_remove The following content will be removed on remove_date: '2022-08-22' --> + +### Implicit grant flow (removed) -WARNING: Implicit grant flow is inherently insecure and the IETF has removed it in [OAuth 2.1](https://oauth.net/2.1/). -It is [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/288516) in GitLab 14.0, and is planned for -[removal](https://gitlab.com/gitlab-org/gitlab/-/issues/344609) in GitLab 15.0. +It is [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/288516) in GitLab 14.0 and is +[removed](https://gitlab.com/gitlab-org/gitlab/-/issues/344609) in GitLab 15.0. We recommend that you use [Authorization code with PKCE](#authorization-code-with-proof-key-for-code-exchange-pkce) instead. @@ -353,6 +352,8 @@ parameters, for example: https://example.com/oauth/redirect#access_token=ABCDExyz123&state=YOUR_UNIQUE_STATE_HASH&token_type=bearer&expires_in=3600 ``` +<!--- end_remove --> + ## Access GitLab API with `access token` The `access token` allows you to make requests to the API on behalf of a user. diff --git a/doc/api/project_import_export.md b/doc/api/project_import_export.md index b9ebfa0fc5c..3b35a43398e 100644 --- a/doc/api/project_import_export.md +++ b/doc/api/project_import_export.md @@ -184,7 +184,7 @@ requests.post(url, headers=headers, data=data, files=files) NOTE: The maximum import file size can be set by the Administrator, default is `0` (unlimited).. -As an administrator, you can modify the maximum import file size. To do so, use the `max_import_size` option in the [Application settings API](settings.md#change-application-settings) or the [Admin UI](../user/admin_area/settings/account_and_limit_settings.md). Default [modified](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50MB to 0 in GitLab 13.8. +As an administrator, you can modify the maximum import file size. To do so, use the `max_import_size` option in the [Application settings API](settings.md#change-application-settings) or the [Admin Area](../user/admin_area/settings/account_and_limit_settings.md). Default [modified](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50MB to 0 in GitLab 13.8. ## Import a file from a remote object storage diff --git a/doc/api/runners.md b/doc/api/runners.md index 304f2494f70..54f7df4be4c 100644 --- a/doc/api/runners.md +++ b/doc/api/runners.md @@ -651,7 +651,7 @@ POST /runners |--------------------|--------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------| | `token` | string | yes | [Registration token](#registration-and-authentication-tokens) | | `description` | string | no | Runner's description | -| `info` | hash | no | Runner's metadata. You can include `name`, `version`, `revision`, `platform`, and `architecture`, but only `version` is displayed in the Admin area of the UI | +| `info` | hash | no | Runner's metadata. You can include `name`, `version`, `revision`, `platform`, and `architecture`, but only `version` is displayed in the Admin Area of the UI | | `active` | boolean | no | Deprecated: Use `:paused` instead. Whether the runner is allowed to receive jobs | | `paused` | boolean | no | Whether the runner should ignore new jobs | | `locked` | boolean | no | Whether the runner should be locked for current project | diff --git a/doc/api/users.md b/doc/api/users.md index e58b28fa84c..5e41a0f6258 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -105,7 +105,7 @@ parameter `without_project_bots=true`. GET /users?without_project_bots=true ``` -### For admins +### For administrators > The `namespace_id` field in the response was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82045) in GitLab 14.10. @@ -314,7 +314,7 @@ Parameters: } ``` -### For admin +### For administrator > The `namespace_id` field in the response was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82045) in GitLab 14.10. @@ -453,7 +453,7 @@ Parameters: | Attribute | Required | Description | | :----------------------------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `admin` | No | User is admin - true or false (default) | +| `admin` | No | User is an administrator - true or false (default) | | `avatar` | No | Image file for user's avatar | | `bio` | No | User's biography | | `can_create_group` | No | User can create groups - true or false | @@ -467,7 +467,7 @@ Parameters: | `linkedin` | No | LinkedIn | | `location` | No | User's location | | `name` | Yes | Name | -| `note` | No | Admin notes for this user | +| `note` | No | Administrator notes for this user | | `organization` | No | Organization name | | `password` | No | Password | | `private_profile` | No | User's profile is private - true, false (default), or null (is converted to false) | @@ -497,7 +497,7 @@ Parameters: | Attribute | Required | Description | | :----------------------------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `admin` | No | User is admin - true or false (default) | +| `admin` | No | User is an administrator - true or false (default) | | `avatar` | No | Image file for user's avatar | | `bio` | No | User's biography | | `can_create_group` | No | User can create groups - true or false | @@ -511,7 +511,7 @@ Parameters: | `linkedin` | No | LinkedIn | | `location` | No | User's location | | `name` | No | Name | -| `note` | No | Admin notes for this user | +| `note` | No | Administration notes for this user | | `organization` | No | Organization name | | `password` | No | Password | | `private_profile` | No | User's profile is private - true, false (default), or null (is converted to false) | @@ -619,7 +619,7 @@ GET /user Users on [GitLab Premium or higher](https://about.gitlab.com/pricing/) also see the `shared_runners_minutes_limit`, `extra_shared_runners_minutes_limit` parameters. -## List current user (for admins) +## List current user (for administrators) > The `namespace_id` field in the response was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82045) in GitLab 14.10. @@ -1895,7 +1895,7 @@ Example response: } ``` -## Get user activities (admin only) +## Get user activities (administrator only) NOTE: This API endpoint is only available on 8.15 (EE) and 9.1 (CE) and above. @@ -1951,7 +1951,7 @@ Example response: `last_activity_at` is deprecated. Use `last_activity_on` instead. -## User memberships (admin only) +## User memberships (administrator only) > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/20532) in GitLab 12.8. diff --git a/doc/architecture/blueprints/consolidating_groups_and_projects/index.md b/doc/architecture/blueprints/consolidating_groups_and_projects/index.md index 52e9b48419c..4ff26f41fdd 100644 --- a/doc/architecture/blueprints/consolidating_groups_and_projects/index.md +++ b/doc/architecture/blueprints/consolidating_groups_and_projects/index.md @@ -132,7 +132,7 @@ epic. The initial iteration will provide a framework to house features under `Namespaces`. Stage groups will eventually need to migrate their own features and functionality over to `Namespaces`. This may impact these features in unexpected ways. Therefore, to minimize UX debt and maintain product consistency, stage groups will have to consider a number of factors when migrating their features over to `Namespaces`: 1. **Conceptual model**: What are the current and future state conceptual models of these features ([see object modeling for designers](https://hpadkisson.medium.com/object-modeling-for-designers-an-introduction-7871bdcf8baf))? These should be documented in Pajamas (example: [merge requests](https://design.gitlab.com/objects/merge-request)). -1. **Merge conflicts**: What inconsistencies are there across project, group, and admin levels? How might these be addressed? For an example of how we rationalized this for labels, please see [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/338820). +1. **Merge conflicts**: What inconsistencies are there across project, group, and administrator levels? How might these be addressed? For an example of how we rationalized this for labels, please see [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/338820). 1. **Inheritance & information flow**: How is information inherited across our container hierarchy currently? How might this be impacted if complying with the new [inheritance behavior](https://gitlab.com/gitlab-org/gitlab/-/issues/343316) framework? 1. **Settings**: Where can settings for this feature be found currently? How will these be impacted by `Namespaces`? 1. **Access**: Who can access this feature and is that impacted by the new container structure? Are there any role or privacy considerations? diff --git a/doc/architecture/blueprints/container_registry_metadata_database/index.md b/doc/architecture/blueprints/container_registry_metadata_database/index.md index 963977be549..5105f2e08e2 100644 --- a/doc/architecture/blueprints/container_registry_metadata_database/index.md +++ b/doc/architecture/blueprints/container_registry_metadata_database/index.md @@ -339,7 +339,7 @@ A more detailed list of all tasks, as well as periodic progress updates can be f ## Relevant Links -- [Allow admin to run garbage collection with zero downtime](https://gitlab.com/groups/gitlab-org/-/epics/2313) +- [Allow administrators to run garbage collection with zero downtime](https://gitlab.com/groups/gitlab-org/-/epics/2313) - [Proposal for continuous, on-demand online garbage collection](https://gitlab.com/gitlab-org/container-registry/-/issues/199) - [Gradual migration proposal for the GitLab.com container registry](https://gitlab.com/gitlab-org/container-registry/-/issues/191) - [Create a self-serve registry deployment](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/316) diff --git a/doc/ci/runners/configure_runners.md b/doc/ci/runners/configure_runners.md index fa0a3309040..e7165339ea0 100644 --- a/doc/ci/runners/configure_runners.md +++ b/doc/ci/runners/configure_runners.md @@ -148,7 +148,7 @@ different places. ### Determine the IP address of a shared runner -To view the IP address of a shared runner you must have admin access to +To view the IP address of a shared runner you must have administrator access to the GitLab instance. To determine this: 1. On the top bar, select **Menu > Admin**. diff --git a/doc/ci/variables/index.md b/doc/ci/variables/index.md index b630c6ebe02..90403351b11 100644 --- a/doc/ci/variables/index.md +++ b/doc/ci/variables/index.md @@ -381,7 +381,7 @@ these variables if the source branch is a protected branch. To mark a variable as protected: -1. Go to **Settings > CI/CD** in the project, group or instance admin area. +1. Go to **Settings > CI/CD** in the project, group or instance Admin Area. 1. Expand the **Variables** section. 1. Next to the variable you want to protect, select **Edit**. 1. Select the **Protect variable** checkbox. diff --git a/doc/development/code_review.md b/doc/development/code_review.md index a296717151e..afb8eb5d25a 100644 --- a/doc/development/code_review.md +++ b/doc/development/code_review.md @@ -154,7 +154,7 @@ with [domain expertise](#domain-experts). #### Acceptance checklist -This checklist encourages the authors, reviewers, and maintainers of merge requests (MRs) to confirm changes were analyzed for high-impact risks to quality, performance, reliability, security, and maintainability. +This checklist encourages the authors, reviewers, and maintainers of merge requests (MRs) to confirm changes were analyzed for high-impact risks to quality, performance, reliability, security, observability, and maintainability. Using checklists improves quality in software engineering. This checklist is a straightforward tool to support and bolster the skills of contributors to the GitLab codebase. @@ -182,6 +182,10 @@ See the [test engineering process](https://about.gitlab.com/handbook/engineering 1. I have considered the scalability risk based on future predicted growth. 1. I have considered the performance, reliability, and availability impacts of this change on large customers who may have significantly more data than the average customer. +##### Observability instrumentation + +1. I have included enough instrumentation to facilitate debugging and proactive performance improvements through observability. + ##### Documentation 1. I have included changelog trailers, or I have decided that they are not needed. diff --git a/doc/development/contributing/merge_request_workflow.md b/doc/development/contributing/merge_request_workflow.md index 5ed0885eed9..6fe15e51ba8 100644 --- a/doc/development/contributing/merge_request_workflow.md +++ b/doc/development/contributing/merge_request_workflow.md @@ -81,6 +81,7 @@ request is as follows: 1. If your MR touches code that executes shell commands, reads or opens files, or handles paths to files on disk, make sure it adheres to the [shell command guidelines](../shell_commands.md) +1. [Code changes should include observability instrumentation](../code_review.md#observability-instrumentation). 1. If your code needs to handle file storage, see the [uploads documentation](../uploads/index.md). 1. If your merge request adds one or more migrations, make sure to execute all migrations on a fresh database before the MR is reviewed. If the review leads diff --git a/doc/development/dangerbot.md b/doc/development/dangerbot.md index b12f00009c8..003df4fe078 100644 --- a/doc/development/dangerbot.md +++ b/doc/development/dangerbot.md @@ -196,11 +196,11 @@ is not shared to forks. Contributors can configure Danger for their forks with the following steps: -1. Create a [personal API token](https://gitlab.com/-/profile/personal_access_tokens?name=GitLab+Dangerbot&scopes=api). -that has the `api` scope set (don't forget to copy it to the clipboard). -1. Add a [project CI/CD variable](../ci/variables/index.md#add-a-cicd-variable-to-a-project) -called `DANGER_GITLAB_API_TOKEN` with the token copied in the previous step. +1. Create a [personal API token](https://gitlab.com/-/profile/personal_access_tokens?name=GitLab+Dangerbot&scopes=api) + that has the `api` scope set (don't forget to copy it to the clipboard). +1. In your fork, add a [project CI/CD variable](../ci/variables/index.md#add-a-cicd-variable-to-a-project) + called `DANGER_GITLAB_API_TOKEN` with the token copied in the previous step. 1. Make the variable [masked](../ci/variables/index.md#mask-a-cicd-variable) so it -doesn't show up in the job logs. The variable cannot be -[protected](../ci/variables/index.md#protected-cicd-variables), because it needs -to be present for all branches. + doesn't show up in the job logs. The variable cannot be + [protected](../ci/variables/index.md#protected-cicd-variables), because it needs + to be present for all branches. diff --git a/doc/development/database/pagination_guidelines.md b/doc/development/database/pagination_guidelines.md index 3a772b10a6d..08840124535 100644 --- a/doc/development/database/pagination_guidelines.md +++ b/doc/development/database/pagination_guidelines.md @@ -172,7 +172,7 @@ From the user point of view, this might not be always noticeable. As the user pa When requesting a large page number, the database needs to read `PAGE * PAGE_SIZE` rows. This makes offset pagination **unsuitable for large database tables**. -Example: listing users on the Admin page +Example: listing users on the Admin Area Listing users with a very simple SQL query: diff --git a/doc/development/documentation/feature_flags.md b/doc/development/documentation/feature_flags.md index dd1cd61ddb1..c5ea1985fc7 100644 --- a/doc/development/documentation/feature_flags.md +++ b/doc/development/documentation/feature_flags.md @@ -72,11 +72,11 @@ FLAG: ### GitLab.com availability information -| If the feature is... | Use this text | -|-------------------------------------|---------------| -| Available | `On GitLab.com, this feature is available.` | -| Available to GitLab.com admins only | `On GitLab.com, this feature is available but can be configured by GitLab.com administrators only.` -| Unavailable | `On GitLab.com, this feature is not available.`| +| If the feature is... | Use this text | +|---------------------------------------------|---------------| +| Available | `On GitLab.com, this feature is available.` | +| Available to GitLab.com administrators only | `On GitLab.com, this feature is available but can be configured by GitLab.com administrators only.` +| Unavailable | `On GitLab.com, this feature is not available.`| ### Optional information diff --git a/doc/development/geo.md b/doc/development/geo.md index a7725c34680..60d7b101fcd 100644 --- a/doc/development/geo.md +++ b/doc/development/geo.md @@ -97,7 +97,7 @@ projects that need updating. Those projects can be: timestamp that is more recent than the `last_repository_successful_sync_at` timestamp in the `Geo::ProjectRegistry` model. - Manual: The administrator can manually flag a repository to resync in the - [Geo admin panel](../user/admin_area/geo_nodes.md). + [Geo Admin Area](../user/admin_area/geo_nodes.md). When we fail to fetch a repository on the secondary `RETRIES_BEFORE_REDOWNLOAD` times, Geo does a so-called _re-download_. It will do a clean clone @@ -259,7 +259,7 @@ basically hashes all Git refs together and stores that hash in the The **secondary** site does the same to calculate the hash of its clone, and compares the hash with the value the **primary** site calculated. If there is a mismatch, Geo will mark this as a mismatch -and the administrator can see this in the [Geo admin panel](../user/admin_area/geo_nodes.md). +and the administrator can see this in the [Geo Admin Area](../user/admin_area/geo_nodes.md). ## Glossary diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md index 086e061452b..6dfd8202ac9 100644 --- a/doc/development/migration_style_guide.md +++ b/doc/development/migration_style_guide.md @@ -127,6 +127,24 @@ scripts/regenerate-schema TARGET=12-9-stable-ee scripts/regenerate-schema ``` +There may be times when the `scripts/regenerate-schema` script creates +additional differences. In this case, a manual procedure can be used, +where <migration ID> is the DATETIME part of the migration file. + +```shell +# Rebase against master +git rebase master + +# Rollback changes +VERSION=<migration ID> bundle exec rails db:rollback:main + +# Checkout db/structure.sql from master +git checkout origin/master db/structure.sql + +# Migrate changes +VERSION=<migration ID> bundle exec rails db:migrate:main +``` + ## Avoiding downtime The document ["Avoiding downtime in migrations"](database/avoiding_downtime_in_migrations.md) specifies diff --git a/doc/development/testing_guide/end_to_end/feature_flags.md b/doc/development/testing_guide/end_to_end/feature_flags.md index bd3fe498dd9..b4ec9e8ccd3 100644 --- a/doc/development/testing_guide/end_to_end/feature_flags.md +++ b/doc/development/testing_guide/end_to_end/feature_flags.md @@ -31,7 +31,7 @@ feature flag is under test. - Format: `feature_flag: { name: 'feature_flag_name', scope: :project }` - When `scope` is set to `:global`, the test will be **skipped on all live .com environments**. This is to avoid issues with feature flag changes affecting other tests or users on that environment. - When `scope` is set to any other value (such as `:project`, `:group` or `:user`), or if no `scope` is specified, the test will only be **skipped on canary and production**. -This is due to the fact that admin access is not available there. +This is due to the fact that administrator access is not available there. **WARNING:** You are strongly advised to first try and [enable feature flags only for a group, project, user](../../feature_flags/index.md#feature-actors), or [feature group](../../feature_flags/index.md#feature-groups). @@ -39,9 +39,9 @@ or [feature group](../../feature_flags/index.md#feature-groups). - If a global feature flag must be used, it is strongly recommended to apply `scope: :global` to the `feature_flag` metadata. This is, however, left up to the SET's discretion to determine the level of risk. - For example, a test uses a global feature flag that only affects a small area of the application and is also needed to check for critical issues on live environments. In such a scenario, it would be riskier to skip running the test. For cases like this, `scope` can be left out of the metadata so that it can still run in live environments - with admin access, such as staging. + with administrator access, such as staging. -**Note on `requires_admin`:** This tag should still be applied if there are other actions within the test that require admin access that are unrelated to updating a +**Note on `requires_admin`:** This tag should still be applied if there are other actions within the test that require administrator access that are unrelated to updating a feature flag (ex: creating a user via the API). The code below would enable a feature flag named `:feature_flag_name` for the project diff --git a/doc/development/testing_guide/end_to_end/rspec_metadata_tests.md b/doc/development/testing_guide/end_to_end/rspec_metadata_tests.md index 938504161ba..0163f2e648c 100644 --- a/doc/development/testing_guide/end_to_end/rspec_metadata_tests.md +++ b/doc/development/testing_guide/end_to_end/rspec_metadata_tests.md @@ -22,7 +22,7 @@ This is a partial list of the [RSpec metadata](https://relishapp.com/rspec/rspec | `:group_saml` | The test requires a GitLab instance that has SAML SSO enabled at the group level. Interacts with an external SAML identity provider. Paired with the `:orchestrated` tag. | | `:instance_saml` | The test requires a GitLab instance that has SAML SSO enabled at the instance level. Interacts with an external SAML identity provider. Paired with the `:orchestrated` tag. | | `:integrations` | This aims to test the available [integrations](../../../user/project/integrations/index.md#available-integrations). The test requires Docker to be installed in the run context. It will provision the containers and can be run against a local instance or using the `gitlab-qa` scenario `Test::Integration::Integrations` | -| `:service_ping_disabled` | The test interacts with the GitLab configuration service ping at the instance level to turn admin setting service ping checkbox on or off. This tag will have the test run only in the `service_ping_disabled` job and must be paired with the `:orchestrated` and `:requires_admin` tags. | +| `:service_ping_disabled` | The test interacts with the GitLab configuration service ping at the instance level to turn Admin Area setting service ping checkbox on or off. This tag will have the test run only in the `service_ping_disabled` job and must be paired with the `:orchestrated` and `:requires_admin` tags. | | `:jira` | The test requires a Jira Server. [GitLab-QA](https://gitlab.com/gitlab-org/gitlab-qa) provisions the Jira Server in a Docker container when the `Test::Integration::Jira` test scenario is run. | | `:kubernetes` | The test includes a GitLab instance that is configured to be run behind an SSH tunnel, allowing a TLS-accessible GitLab. This test also includes provisioning of at least one Kubernetes cluster to test against. _This tag is often be paired with `:orchestrated`._ | | `:ldap_no_server` | The test requires a GitLab instance to be configured to use LDAP. To be used with the `:orchestrated` tag. It does not spin up an LDAP server at orchestration time. Instead, it creates the LDAP server at runtime. | diff --git a/doc/install/docker.md b/doc/install/docker.md index a25ed629681..b2d8500596a 100644 --- a/doc/install/docker.md +++ b/doc/install/docker.md @@ -664,7 +664,7 @@ writing value to /dev/shm/gitlab/sidekiq/histogram_sidekiq_0-0.db failed with un writing value to /dev/shm/gitlab/sidekiq/histogram_sidekiq_0-0.db failed with unmapped file ``` -Other than disabling the Prometheus Metrics from the Admin page, the recommended +Other than disabling the Prometheus Metrics from the Admin Area, the recommended solution to fix this problem is to increase the size of shared memory to at least 256MB. If using `docker run`, this can be done by passing the flag `--shm-size 256m`. If using a `docker-compose.yml` file, the `shm_size` key can be used for this diff --git a/doc/integration/akismet.md b/doc/integration/akismet.md index 5541c5914d5..9a85511836f 100644 --- a/doc/integration/akismet.md +++ b/doc/integration/akismet.md @@ -12,7 +12,7 @@ Akismet for review, and instance administrators can [mark snippets as spam](../user/snippets.md#mark-snippet-as-spam). Detected spam is rejected, and an entry is added in the **Spam Log** section of the -Admin page. +Admin Area. Privacy note: GitLab submits the user's IP and user agent to Akismet. diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md index 2fc80aa1769..6b5ead6c2da 100644 --- a/doc/integration/bitbucket.md +++ b/doc/integration/bitbucket.md @@ -124,4 +124,4 @@ After the above configuration is set up, you can use Bitbucket to sign into GitLab and [start importing your projects](../user/project/import/bitbucket.md). If you want to import projects from Bitbucket, but don't want to enable signing in, -you can [disable Sign-Ins in the admin panel](omniauth.md#enable-or-disable-sign-in-with-an-omniauth-provider-without-disabling-import-sources). +you can [disable Sign-Ins in the Admin Area](omniauth.md#enable-or-disable-sign-in-with-an-omniauth-provider-without-disabling-import-sources). diff --git a/doc/integration/elasticsearch.md b/doc/integration/elasticsearch.md index 8a3d23e5d84..9ad709bea27 100644 --- a/doc/integration/elasticsearch.md +++ b/doc/integration/elasticsearch.md @@ -872,7 +872,7 @@ There are a couple of ways to achieve that: This is always correctly identifying whether the current project/namespace being searched is using Elasticsearch. -- From the admin area under **Settings > Advanced Search** check that the +- From the Admin Area under **Settings > Advanced Search** check that the Advanced Search settings are checked. Those same settings there can be obtained from the Rails console if necessary: diff --git a/doc/integration/saml.md b/doc/integration/saml.md index c5383f9e34b..5884429c8ca 100644 --- a/doc/integration/saml.md +++ b/doc/integration/saml.md @@ -853,7 +853,7 @@ When configuring the Google Workspace SAML app, be sure to record the following | SSO URL | Depends | Google Identity Provider details. Set to the GitLab `idp_sso_target_url` setting. | | Certificate | Downloadable | Run `openssl x509 -in <your_certificate.crt> -noout -fingerprint` to generate the SHA1 fingerprint that can be used in the `idp_cert_fingerprint` setting. | -While the Google Workspace Admin provides IdP metadata, Entity ID, and SHA-256 +While the Google Workspace Administrator provides IdP metadata, Entity ID, and SHA-256 fingerprint, they are not required. GitLab does not need that information to connect to the Google Workspace SAML app. diff --git a/doc/integration/sourcegraph.md b/doc/integration/sourcegraph.md index b2e5f7b4b7d..72ad0bcc32d 100644 --- a/doc/integration/sourcegraph.md +++ b/doc/integration/sourcegraph.md @@ -41,7 +41,7 @@ If you are using an HTTPS connection to GitLab, you must [configure HTTPS](https ### Connect your Sourcegraph instance to your GitLab instance -1. Navigate to the site admin area in Sourcegraph. +1. Navigate to the site Admin Area in Sourcegraph. 1. [Configure your GitLab external service](https://docs.sourcegraph.com/admin/external_service/gitlab). You can skip this step if you already have your GitLab repositories searchable in Sourcegraph. 1. Validate that you can search your repositories from GitLab in your Sourcegraph instance by running a test query. diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 0f9bf17d386..4befb9f62d4 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -987,7 +987,7 @@ This procedure assumes that: First ensure your backup tar file is in the backup directory described in the `gitlab.rb` configuration `gitlab_rails['backup_path']`. The default is -`/var/opt/gitlab/backups`. It needs to be owned by the `git` user. +`/var/opt/gitlab/backups`. The backup file needs to be owned by the `git` user. ```shell sudo cp 11493107454_2018_04_25_10.6.4-ce_gitlab_backup.tar /var/opt/gitlab/backups/ diff --git a/doc/security/ssh_keys_restrictions.md b/doc/security/ssh_keys_restrictions.md index 03bc0207cf5..272d840ef13 100644 --- a/doc/security/ssh_keys_restrictions.md +++ b/doc/security/ssh_keys_restrictions.md @@ -24,7 +24,7 @@ the minimum key length for each technology: 1. On the left sidebar, select **Settings > General** (`/admin/application_settings/general`). 1. Expand the **Visibility and access controls** section: - ![SSH keys restriction admin settings](img/ssh_keys_restrictions_settings.png) + ![SSH keys restriction Admin Area settings](img/ssh_keys_restrictions_settings.png) If a restriction is imposed on any key type, users cannot upload new SSH keys that don't meet the requirement. Any existing keys that don't meet it are disabled but not removed and users cannot diff --git a/doc/security/unlock_user.md b/doc/security/unlock_user.md index f2ad6696b9a..efe9c5784ad 100644 --- a/doc/security/unlock_user.md +++ b/doc/security/unlock_user.md @@ -10,7 +10,7 @@ type: howto Users are locked after ten failed sign-in attempts. These users remain locked: - For 10 minutes, after which time they are automatically unlocked. -- Until an admin unlocks them from the [Admin Area](../user/admin_area/index.md) or the command line in under 10 minutes. +- Until an administrator unlocks them from the [Admin Area](../user/admin_area/index.md) or the command line in under 10 minutes. ## Unlock a user from the Admin Area diff --git a/doc/security/webhooks.md b/doc/security/webhooks.md index 07b35ccebe8..c3d445103c4 100644 --- a/doc/security/webhooks.md +++ b/doc/security/webhooks.md @@ -49,7 +49,7 @@ This behavior can be overridden: 1. On the top bar, select **Menu > Admin**. 1. On the left sidebar, select **Settings > Network**. 1. Expand the **Outbound requests** section: - ![Outbound requests admin settings](img/outbound_requests_section_v12_2.png) + ![Outbound requests Admin Area settings](img/outbound_requests_section_v12_2.png) 1. Select **Allow requests to the local network from web hooks and services**. NOTE: diff --git a/doc/subscriptions/quarterly_reconciliation.md b/doc/subscriptions/quarterly_reconciliation.md index 3398902da1b..78c844b897c 100644 --- a/doc/subscriptions/quarterly_reconciliation.md +++ b/doc/subscriptions/quarterly_reconciliation.md @@ -70,7 +70,7 @@ sent and subject to your terms. ### Self-managed instances -Admins receive an email **six days after the reconciliation date**. +Administrators receive an email **six days after the reconciliation date**. This email communicates the [overage seat quantity](self_managed/index.md#users-over-license) and expected invoice amount. diff --git a/doc/topics/autodevops/stages.md b/doc/topics/autodevops/stages.md index 038d4daed65..a677787b980 100644 --- a/doc/topics/autodevops/stages.md +++ b/doc/topics/autodevops/stages.md @@ -240,7 +240,7 @@ To learn more about [License Compliance](../../user/compliance/license_compliance/index.md), see the documentation. -## Auto Container Scanning **(ULTIMATE)** +## Auto Container Scanning Vulnerability static analysis for containers uses [Trivy](https://aquasecurity.github.io/trivy/latest/) to check for potential security issues in Docker images. The Auto Container Scanning stage is diff --git a/doc/update/index.md b/doc/update/index.md index dcda9150059..19fe0b1b84e 100644 --- a/doc/update/index.md +++ b/doc/update/index.md @@ -935,7 +935,7 @@ for more information. When [Maintenance mode](../administration/maintenance_mode/index.md) is enabled, users cannot sign in with SSO, SAML, or LDAP. -Users who were signed in before Maintenance mode was enabled will continue to be signed in. If the admin who enabled Maintenance mode loses their session, then they will not be able to disable Maintenance mode via the UI. In that case, you can [disable Maintenance mode via the API or Rails console](../administration/maintenance_mode/#disable-maintenance-mode). +Users who were signed in before Maintenance mode was enabled will continue to be signed in. If the administrator who enabled Maintenance mode loses their session, then they will not be able to disable Maintenance mode via the UI. In that case, you can [disable Maintenance mode via the API or Rails console](../administration/maintenance_mode/#disable-maintenance-mode). [This bug](https://gitlab.com/gitlab-org/gitlab/-/issues/329261) was fixed in GitLab 14.5.0 and backported into 14.4.3 and 14.3.5. diff --git a/doc/update/package/convert_to_ee.md b/doc/update/package/convert_to_ee.md index d5a71ba3e80..8a0d55e34af 100644 --- a/doc/update/package/convert_to_ee.md +++ b/doc/update/package/convert_to_ee.md @@ -91,7 +91,7 @@ The steps can be summed up to: sudo gitlab-ctl reconfigure ``` -1. Now go to the GitLab admin panel of your server (`/admin/subscription`) and +1. Now go to the GitLab Admin Area of your server (`/admin/subscription`) and [add your license](../../user/admin_area/license.md). 1. After you confirm that GitLab is working as expected, you may remove the old diff --git a/doc/update/removals.md b/doc/update/removals.md index 5353664deda..06e2be2ab44 100644 --- a/doc/update/removals.md +++ b/doc/update/removals.md @@ -78,6 +78,29 @@ The Container Registry supports [authentication](https://gitlab.com/gitlab-org/c Since it isn't used in the context of GitLab (the product), `htpasswd` authentication will be deprecated in GitLab 14.9 and removed in GitLab 15.0. +### Custom `geo:db:*` Rake tasks are no longer available + +In GitLab 14.8, we [deprecated the `geo:db:*` Rake tasks and replaced them with built-in tasks](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77269/diffs) after [switching the Geo tracking database to use Rails' 6 support of multiple databases](https://gitlab.com/groups/gitlab-org/-/epics/6458). +The following `geo:db:*` tasks have been removed from GitLab 15.0 and have been replaced with their corresponding `db:*:geo` tasks: + +- `geo:db:drop` -> `db:drop:geo` +- `geo:db:create` -> `db:create:geo` +- `geo:db:setup` -> `db:setup:geo` +- `geo:db:migrate` -> `db:migrate:geo` +- `geo:db:rollback` -> `db:rollback:geo` +- `geo:db:version` -> `db:version:geo` +- `geo:db:reset` -> `db:reset:geo` +- `geo:db:seed` -> `db:seed:geo` +- `geo:schema:load:geo` -> `db:schema:load:geo` +- `geo:db:schema:dump` -> `db:schema:dump:geo` +- `geo:db:migrate:up` -> `db:migrate:up:geo` +- `geo:db:migrate:down` -> `db:migrate:down:geo` +- `geo:db:migrate:redo` -> `db:migrate:redo:geo` +- `geo:db:migrate:status` -> `db:migrate:status:geo` +- `geo:db:test:prepare` -> `db:test:prepare:geo` +- `geo:db:test:load` -> `db:test:load:geo` +- `geo:db:test:purge` -> `db:test:purge:geo` + ### GitLab Serverless WARNING: @@ -112,6 +135,10 @@ The permissions model for GraphQL is being updated. After 15.0, users with the G The issue for this removal is [GitLab-#350682](https://gitlab.com/gitlab-org/gitlab/-/issues/350682) +### Legacy Geo Admin UI routes + +In GitLab 13.0, we introduced new project and design replication details routes in the Geo Admin UI. These routes are `/admin/geo/replication/projects` and `/admin/geo/replication/designs`. We kept the legacy routes and redirected them to the new routes. These legacy routes `/admin/geo/projects` and `/admin/geo/designs` have been removed in GitLab 15.0. Please update any bookmarks or scripts that may use the legacy routes. + ### OAuth tokens without an expiration WARNING: @@ -225,6 +252,22 @@ If you installed GitLab from source, verify manually that both servers are confi The Static Site Editor was deprecated in GitLab 14.7 and the feature is being removed in GitLab 15.0. Incoming requests to the Static Site Editor will be redirected and open the target file to edit in the Web IDE. Current users of the Static Site Editor can view the [documentation](https://docs.gitlab.com/ee/user/project/static_site_editor/) for more information, including how to remove the configuration files from existing projects. We will continue investing in improvements to the Markdown editing experience by [maturing the Content Editor](https://gitlab.com/groups/gitlab-org/-/epics/5401) and making it available as a way to edit content across GitLab. +### Support for legacy format of `config/database.yml` removed + +WARNING: +This feature was changed or removed in 15.0 +as a [breaking change](https://docs.gitlab.com/ee/development/contributing/#breaking-changes). +Before updating GitLab, review the details carefully to determine if you need to make any +changes to your code, settings, or workflow. + +The syntax of [GitLab's database](https://docs.gitlab.com/omnibus/settings/database.html) +configuration located in `database.yml` has changed and the legacy format has been removed. +The legacy format supported a single PostgreSQL adapter, whereas the new format supports multiple databases. +The `main:` database needs to be defined as a first configuration item. + +This change only impacts users compiling GitLab from source, all the other installation methods handle this configuration automatically. +Instructions are available [in the source update documentation](https://docs.gitlab.com/ee/update/upgrading_from_source.html#new-configuration-options-for-databaseyml). + ### Test coverage project CI/CD setting WARNING: @@ -240,6 +283,16 @@ has been removed. To set test coverage parsing, use the project’s `.gitlab-ci.yml` file by providing a regular expression with the [`coverage` keyword](https://docs.gitlab.com/ee/ci/yaml/index.html#coverage). +### The `promote-db` command is no longer available from `gitlab-ctl` + +WARNING: +This feature was changed or removed in 15.0 +as a [breaking change](https://docs.gitlab.com/ee/development/contributing/#breaking-changes). +Before updating GitLab, review the details carefully to determine if you need to make any +changes to your code, settings, or workflow. + +In GitLab 14.5, we introduced the command `gitlab-ctl promote` to promote any Geo secondary node to a primary during a failover. This command replaces `gitlab-ctl promote-db` which is used to promote database nodes in multi-node Geo secondary sites. The `gitlab-ctl promote-db` command has been removed in GitLab 15.0. + ### Update to the Container Registry group-level API WARNING: diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md index dc78229787c..262bb2cc931 100644 --- a/doc/user/admin_area/index.md +++ b/doc/user/admin_area/index.md @@ -184,7 +184,7 @@ The following data is included in the export: - Type - Path - Access level ([Project](../permissions.md#project-members-permissions) and [Group](../permissions.md#group-members-permissions)) -- Date of last activity ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345388) in GitLab 14.6). For a list of activities that populate this column, see the [Users API documentation](../../api/users.md#get-user-activities-admin-only). +- Date of last activity ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345388) in GitLab 14.6). For a list of activities that populate this column, see the [Users API documentation](../../api/users.md#get-user-activities-administrator-only). Only the first 100,000 user accounts are exported. diff --git a/doc/user/admin_area/moderate_users.md b/doc/user/admin_area/moderate_users.md index e8db319df77..53c08d8cbc1 100644 --- a/doc/user/admin_area/moderate_users.md +++ b/doc/user/admin_area/moderate_users.md @@ -15,7 +15,7 @@ users. A user in _pending approval_ state requires action by an administrator. A user sign up can be in a pending approval state because an administrator has enabled any of the following options: -- [Require admin approval for new sign-ups](settings/sign_up_restrictions.md#require-administrator-approval-for-new-sign-ups) setting. +- [Require administrator approval for new sign-ups](settings/sign_up_restrictions.md#require-administrator-approval-for-new-sign-ups) setting. - [User cap](settings/sign_up_restrictions.md#user-cap). - [Block auto-created users (OmniAuth)](../../integration/omniauth.md#configure-initial-settings) - [Block auto-created users (LDAP)](../../administration/auth/ldap/index.md#basic-configuration-settings) diff --git a/doc/user/admin_area/reporting/spamcheck.md b/doc/user/admin_area/reporting/spamcheck.md index 559235fe322..b1ec203cffc 100644 --- a/doc/user/admin_area/reporting/spamcheck.md +++ b/doc/user/admin_area/reporting/spamcheck.md @@ -65,4 +65,4 @@ Spamcheck service on its own can not communicate directly over TLS with GitLab. However, Spamcheck can be deployed behind a reverse proxy which performs TLS termination. In such a scenario, GitLab can be made to communicate with Spamcheck over TLS by specifying `tls://` scheme for the external Spamcheck URL -instead of `grpc://` in the Admin settings. +instead of `grpc://` in the Admin Area settings. diff --git a/doc/user/admin_area/settings/account_and_limit_settings.md b/doc/user/admin_area/settings/account_and_limit_settings.md index 471b48dfec4..9b1a32e9083 100644 --- a/doc/user/admin_area/settings/account_and_limit_settings.md +++ b/doc/user/admin_area/settings/account_and_limit_settings.md @@ -311,5 +311,5 @@ To do this: NOTE: When this ability is disabled, GitLab administrators can still use the -[Admin UI](../index.md#administering-users) or the +[Admin Area](../index.md#administering-users) or the [API](../../../api/users.md#user-modification) to update usernames. diff --git a/doc/user/admin_area/settings/sign_in_restrictions.md b/doc/user/admin_area/settings/sign_in_restrictions.md index c63cd88eeb4..7316b1bdbb8 100644 --- a/doc/user/admin_area/settings/sign_in_restrictions.md +++ b/doc/user/admin_area/settings/sign_in_restrictions.md @@ -66,7 +66,7 @@ Git clients, and access RESTful API endpoints as administrators, without additio authentication steps. We may address these limitations in the future. For more information see the following epic: -[Admin mode for GitLab Administrators](https://gitlab.com/groups/gitlab-org/-/epics/2158). +[Admin Mode for GitLab Administrators](https://gitlab.com/groups/gitlab-org/-/epics/2158). ### Troubleshooting Admin Mode diff --git a/doc/user/admin_area/settings/user_and_ip_rate_limits.md b/doc/user/admin_area/settings/user_and_ip_rate_limits.md index 56e240a8d39..bb3ee64abac 100644 --- a/doc/user/admin_area/settings/user_and_ip_rate_limits.md +++ b/doc/user/admin_area/settings/user_and_ip_rate_limits.md @@ -104,7 +104,7 @@ attached into the response headers. | Header | Example | Description | |:----------------------|:--------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `RateLimit-Limit` | `60` | The request quota for the client **each minute**. If the rate limit period set in the admin area is different from 1 minute, the value of this header is adjusted to approximately the nearest 60-minute period. | +| `RateLimit-Limit` | `60` | The request quota for the client **each minute**. If the rate limit period set in the Admin Area is different from 1 minute, the value of this header is adjusted to approximately the nearest 60-minute period. | | `RateLimit-Name` | `throttle_authenticated_web` | Name of the throttle blocking the requests. | | `RateLimit-Observed` | `67` | Number of requests associated to the client in the time window. | | `RateLimit-Remaining` | `0` | Remaining quota in the time window. The result of `RateLimit-Limit` - `RateLimit-Observed`. | diff --git a/doc/user/application_security/container_scanning/index.md b/doc/user/application_security/container_scanning/index.md index 006c9902859..f4659435dad 100644 --- a/doc/user/application_security/container_scanning/index.md +++ b/doc/user/application_security/container_scanning/index.md @@ -5,7 +5,7 @@ group: Container Security info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- -# Container Scanning **(ULTIMATE)** +# Container Scanning **(FREE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/3672) in GitLab 10.4. @@ -44,6 +44,26 @@ information directly in the merge request. ![Container Scanning Widget](img/container_scanning_v13_2.png) +### Capabilities + +| Capability | In Free | In Ultimate | +| --- | ------ | ------ | +| [Configure Scanners](#configuration) | Yes | Yes | +| Customize Settings ([Variables](#available-cicd-variables), [Overriding](#overriding-the-container-scanning-template), [offline environment support](#running-container-scanning-in-an-offline-environment), etc) | Yes | Yes | +| [View JSON Report](#reports-json-format) as a CI job artifact | Yes | Yes | +| Generation of a JSON report of [dependencies](#dependency-list) as a CI job artifact | Yes | Yes | +| Ability to enable container scanning via an MR in the GitLab UI | Yes | Yes | +| [UBI Image Support](#fips-enabled-images) | Yes | Yes | +| Support for Trivy | Yes | Yes | +| Support for Grype | Yes | Yes | +| Inclusion of GitLab Advisory Database | Limited to the time-delayed content from GitLab [advisories-communities](https://gitlab.com/gitlab-org/advisories-community/) project | Yes - all the latest content from [Gemnasium DB](https://gitlab.com/gitlab-org/security-products/gemnasium-db) | +| Presentation of Report data in Merge Request and Security tab of the CI pipeline job | No | Yes | +| [Interaction with Vulnerabilities](#interacting-with-the-vulnerabilities) such as merge request approvals | No | Yes | +| [Solutions for vulnerabilities (auto-remediation)](#solutions-for-vulnerabilities-auto-remediation) | No | Yes | +| Support for the [vulnerability allow list](#vulnerability-allowlisting) | No | Yes | +| [Access to Security Dashboard page](#security-dashboard) | No | Yes | +| [Access to Dependency List page](../dependency_list/) | No | Yes | + ## Requirements To enable container scanning in your pipeline, you need the following: @@ -405,7 +425,7 @@ container_scanning: The `ADDITIONAL_CA_CERT_BUNDLE` value can also be configured as a [custom variable in the UI](../../../ci/variables/index.md#custom-cicd-variables), either as a `file`, which requires the path to the certificate, or as a variable, which requires the text representation of the certificate. -### Vulnerability allowlisting +### Vulnerability allowlisting **(ULTIMATE)** To allowlist specific vulnerabilities, follow these steps: @@ -770,7 +790,7 @@ Database update information for other analyzers is available in the After a vulnerability is found, you can [address it](../vulnerabilities/index.md). -## Solutions for vulnerabilities (auto-remediation) +## Solutions for vulnerabilities (auto-remediation) **(ULTIMATE)** Some vulnerabilities can be fixed by applying the solution that GitLab automatically generates. diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md index 3960c97142e..bc1799c2e54 100644 --- a/doc/user/group/saml_sso/scim_setup.md +++ b/doc/user/group/saml_sso/scim_setup.md @@ -110,7 +110,7 @@ Before you start this section: After the above steps are complete: 1. Sign in to Okta. -1. Ensure you are in the Admin section by selecting the **Admin** button located in the top right. The admin button is not visible from the admin page. +1. Ensure you are in the Admin Area by selecting the **Admin** button located in the top right. The button is not visible from the Admin Area. 1. In the **Application** tab, select **Browse App Catalog**. 1. Search for **GitLab**, find and select on the 'GitLab' application. 1. On the GitLab application overview page, select **Add**. diff --git a/doc/user/group/settings/import_export.md b/doc/user/group/settings/import_export.md index 7b63466656d..23a638fb98c 100644 --- a/doc/user/group/settings/import_export.md +++ b/doc/user/group/settings/import_export.md @@ -86,7 +86,7 @@ To export the contents of a group: NOTE: The maximum import file size can be set by the Administrator, default is `0` (unlimited). -As an administrator, you can modify the maximum import file size. To do so, use the `max_import_size` option in the [Application settings API](../../../api/settings.md#change-application-settings) or the [Admin UI](../../admin_area/settings/account_and_limit_settings.md). Default [modified](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50MB to 0 in GitLab 13.8. +As an administrator, you can modify the maximum import file size. To do so, use the `max_import_size` option in the [Application settings API](../../../api/settings.md#change-application-settings) or the [Admin Area](../../admin_area/settings/account_and_limit_settings.md). Default [modified](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50MB to 0 in GitLab 13.8. You can also use the [group import/export API](../../../api/group_import_export.md). diff --git a/doc/user/project/clusters/add_existing_cluster.md b/doc/user/project/clusters/add_existing_cluster.md index 554d772b5ec..c55c11151ce 100644 --- a/doc/user/project/clusters/add_existing_cluster.md +++ b/doc/user/project/clusters/add_existing_cluster.md @@ -27,7 +27,7 @@ To add any cluster to GitLab, you need: - Either a GitLab.com account or an account for a self-managed installation running GitLab 12.5 or later. - The Maintainer role for group-level and project-level clusters. -- Access to the Admin area for instance-level clusters. +- Access to the Admin Area for instance-level clusters. - A Kubernetes cluster. - Cluster administration access to the cluster with `kubectl`. diff --git a/lib/api/usage_data_queries.rb b/lib/api/usage_data_queries.rb index e991f914a92..fe972942111 100644 --- a/lib/api/usage_data_queries.rb +++ b/lib/api/usage_data_queries.rb @@ -5,6 +5,7 @@ module API before { authenticated_as_admin! } feature_category :service_ping + urgency :low namespace 'usage_data' do before do diff --git a/spec/controllers/oauth/authorizations_controller_spec.rb b/spec/controllers/oauth/authorizations_controller_spec.rb index e6553c027d6..7489f506674 100644 --- a/spec/controllers/oauth/authorizations_controller_spec.rb +++ b/spec/controllers/oauth/authorizations_controller_spec.rb @@ -56,27 +56,9 @@ RSpec.describe Oauth::AuthorizationsController do end end - shared_examples "Implicit grant can't be used in confidential application" do - context 'when application is confidential' do - before do - application.update!(confidential: true) - params[:response_type] = 'token' - end - - it 'does not allow the implicit flow' do - subject - - expect(response).to have_gitlab_http_status(:ok) - expect(response).to render_template('doorkeeper/authorizations/error') - end - end - end - describe 'GET #new' do subject { get :new, params: params } - include_examples "Implicit grant can't be used in confidential application" - context 'when the user is confirmed' do context 'when there is already an access token for the application with a matching scope' do before do @@ -219,14 +201,12 @@ RSpec.describe Oauth::AuthorizationsController do subject { post :create, params: params } include_examples 'OAuth Authorizations require confirmed user' - include_examples "Implicit grant can't be used in confidential application" end describe 'DELETE #destroy' do subject { delete :destroy, params: params } include_examples 'OAuth Authorizations require confirmed user' - include_examples "Implicit grant can't be used in confidential application" end it 'includes Two-factor enforcement concern' do diff --git a/spec/features/oauth_login_spec.rb b/spec/features/oauth_login_spec.rb index 99e4c680548..fca8972b56c 100644 --- a/spec/features/oauth_login_spec.rb +++ b/spec/features/oauth_login_spec.rb @@ -166,16 +166,6 @@ RSpec.describe 'OAuth Login', :allow_forgery_protection do expect(page).to have_current_path(Gitlab::Routing.url_helpers.root_url, ignore_query: true) end - - it 'does not include the fragment for an implicit grant' do - implicit_grant_params = params.merge(response_type: 'token') - escaped_url = Regexp.escape(Gitlab::Routing.url_helpers.root_url) - auth_params_fragment = '#[a-zA-Z0-9&=_]+' - - visit "#{Gitlab::Routing.url_helpers.oauth_authorization_url(implicit_grant_params)}#a_test-hash" - - expect(page).to have_current_path(%r{\A#{escaped_url}#{auth_params_fragment}\z}, ignore_query: true, url: true) - end end context 'when JS is disabled' do diff --git a/spec/frontend/__helpers__/flush_promises.js b/spec/frontend/__helpers__/flush_promises.js deleted file mode 100644 index eefc2ed7c17..00000000000 --- a/spec/frontend/__helpers__/flush_promises.js +++ /dev/null @@ -1,4 +0,0 @@ -export default function flushPromises() { - // eslint-disable-next-line no-restricted-syntax - return new Promise(setImmediate); -} diff --git a/spec/frontend/content_editor/services/markdown_serializer_spec.js b/spec/frontend/content_editor/services/markdown_serializer_spec.js index 6fa42ddbd2d..25b7483f234 100644 --- a/spec/frontend/content_editor/services/markdown_serializer_spec.js +++ b/spec/frontend/content_editor/services/markdown_serializer_spec.js @@ -24,6 +24,7 @@ import Link from '~/content_editor/extensions/link'; import ListItem from '~/content_editor/extensions/list_item'; import OrderedList from '~/content_editor/extensions/ordered_list'; import Paragraph from '~/content_editor/extensions/paragraph'; +import Sourcemap from '~/content_editor/extensions/sourcemap'; import Strike from '~/content_editor/extensions/strike'; import Table from '~/content_editor/extensions/table'; import TableCell from '~/content_editor/extensions/table_cell'; @@ -32,6 +33,7 @@ import TableRow from '~/content_editor/extensions/table_row'; import TaskItem from '~/content_editor/extensions/task_item'; import TaskList from '~/content_editor/extensions/task_list'; import markdownSerializer from '~/content_editor/services/markdown_serializer'; +import remarkMarkdownDeserializer from '~/content_editor/services/remark_markdown_deserializer'; import { createTestEditor, createDocBuilder } from '../test_utils'; jest.mock('~/emoji'); @@ -63,6 +65,7 @@ const tiptapEditor = createTestEditor({ Link, ListItem, OrderedList, + Sourcemap, Strike, Table, TableCell, @@ -1158,4 +1161,42 @@ Oranges are orange [^1] `.trim(), ); }); + + it.each` + mark | content | modifiedContent + ${'bold'} | ${'**bold**'} | ${'**bold modified**'} + ${'bold'} | ${'__bold__'} | ${'__bold modified__'} + ${'bold'} | ${'<strong>bold</strong>'} | ${'<strong>bold modified</strong>'} + ${'bold'} | ${'<b>bold</b>'} | ${'<b>bold modified</b>'} + ${'italic'} | ${'_italic_'} | ${'_italic modified_'} + ${'italic'} | ${'*italic*'} | ${'*italic modified*'} + ${'italic'} | ${'<em>italic</em>'} | ${'<em>italic modified</em>'} + ${'italic'} | ${'<i>italic</i>'} | ${'<i>italic modified</i>'} + ${'link'} | ${'[gitlab](https://gitlab.com)'} | ${'[gitlab modified](https://gitlab.com)'} + ${'link'} | ${'<a href="https://gitlab.com">link</a>'} | ${'<a href="https://gitlab.com">link modified</a>'} + ${'code'} | ${'`code`'} | ${'`code modified`'} + ${'code'} | ${'<code>code</code>'} | ${'<code>code modified</code>'} + `( + 'preserves original $mark syntax when sourceMarkdown is available', + async ({ content, modifiedContent }) => { + const { document } = await remarkMarkdownDeserializer().deserialize({ + schema: tiptapEditor.schema, + content, + }); + + tiptapEditor + .chain() + .setContent(document.toJSON()) + // changing the document ensures that block preservation doesn’t yield false positives + .insertContent(' modified') + .run(); + + const serialized = markdownSerializer({}).serialize({ + pristineDoc: document, + doc: tiptapEditor.state.doc, + }); + + expect(serialized).toEqual(modifiedContent); + }, + ); }); diff --git a/spec/frontend/issues/show/components/description_spec.js b/spec/frontend/issues/show/components/description_spec.js index c08453530e5..1ae04531a6b 100644 --- a/spec/frontend/issues/show/components/description_spec.js +++ b/spec/frontend/issues/show/components/description_spec.js @@ -37,6 +37,7 @@ const showDetailsModal = jest.fn(); const $toast = { show: jest.fn(), }; + const workItemQueryResponse = { data: { workItem: null, @@ -319,8 +320,10 @@ describe('Description component', () => { }); it('shows toast after delete success', async () => { - findWorkItemDetailModal().vm.$emit('workItemDeleted'); + const newDesc = 'description'; + findWorkItemDetailModal().vm.$emit('workItemDeleted', newDesc); + expect(wrapper.emitted('updateDescription')).toEqual([[newDesc]]); expect($toast.show).toHaveBeenCalledWith('Work item deleted'); }); }); @@ -381,7 +384,8 @@ describe('Description component', () => { describe('when url query `work_item_id` exists', () => { it.each` behavior | workItemId | modalOpened - ${'opens'} | ${'123'} | ${1} + ${'opens'} | ${'2'} | ${1} + ${'does not open'} | ${'123'} | ${0} ${'does not open'} | ${'123e'} | ${0} ${'does not open'} | ${'12e3'} | ${0} ${'does not open'} | ${'1e23'} | ${0} diff --git a/spec/frontend/pipeline_wizard/components/commit_spec.js b/spec/frontend/pipeline_wizard/components/commit_spec.js index 6496850b028..c987accbb0d 100644 --- a/spec/frontend/pipeline_wizard/components/commit_spec.js +++ b/spec/frontend/pipeline_wizard/components/commit_spec.js @@ -8,7 +8,7 @@ import createMockApollo from 'helpers/mock_apollo_helper'; import createCommitMutation from '~/pipeline_wizard/queries/create_commit.graphql'; import getFileMetadataQuery from '~/pipeline_wizard/queries/get_file_meta.graphql'; import RefSelector from '~/ref/components/ref_selector.vue'; -import flushPromises from 'helpers/flush_promises'; +import waitForPromises from 'helpers/wait_for_promises'; import { createCommitMutationErrorResult, createCommitMutationResult, @@ -107,7 +107,7 @@ describe('Pipeline Wizard - Commit Page', () => { it('does not show a load error if call is successful', async () => { createComponent({ projectPath, filename }); - await flushPromises(); + await waitForPromises(); expect(wrapper.findByTestId('load-error').exists()).not.toBe(true); }); @@ -117,7 +117,7 @@ describe('Pipeline Wizard - Commit Page', () => { { defaultBranch: branch, projectPath, filename }, createMockApollo([[getFileMetadataQuery, () => fileQueryErrorResult]]), ); - await flushPromises(); + await waitForPromises(); expect(wrapper.findByTestId('load-error').exists()).toBe(true); expect(wrapper.findByTestId('load-error').text()).toBe(i18n.errors.loadError); }); @@ -131,9 +131,9 @@ describe('Pipeline Wizard - Commit Page', () => { describe('successful commit', () => { beforeEach(async () => { createComponent(); - await flushPromises(); + await waitForPromises(); await getButtonWithLabel(__('Commit')).trigger('click'); - await flushPromises(); + await waitForPromises(); }); it('will not show an error', async () => { @@ -159,9 +159,9 @@ describe('Pipeline Wizard - Commit Page', () => { describe('failed commit', () => { beforeEach(async () => { createComponent({}, getMockApollo({ commitHasError: true })); - await flushPromises(); + await waitForPromises(); await getButtonWithLabel(__('Commit')).trigger('click'); - await flushPromises(); + await waitForPromises(); }); it('will show an error', async () => { @@ -229,7 +229,7 @@ describe('Pipeline Wizard - Commit Page', () => { }), ); - await flushPromises(); + await waitForPromises(); consoleSpy = jest.spyOn(console, 'error'); @@ -243,7 +243,7 @@ describe('Pipeline Wizard - Commit Page', () => { } await Vue.nextTick(); - await flushPromises(); + await waitForPromises(); }); afterAll(() => { diff --git a/spec/frontend/vue_shared/components/gitlab_version_check_spec.js b/spec/frontend/vue_shared/components/gitlab_version_check_spec.js index b673e5407d4..b180e8c12dd 100644 --- a/spec/frontend/vue_shared/components/gitlab_version_check_spec.js +++ b/spec/frontend/vue_shared/components/gitlab_version_check_spec.js @@ -1,7 +1,7 @@ import { GlBadge } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; -import flushPromises from 'helpers/flush_promises'; +import waitForPromises from 'helpers/wait_for_promises'; import axios from '~/lib/utils/axios_utils'; import GitlabVersionCheck from '~/vue_shared/components/gitlab_version_check.vue'; @@ -43,7 +43,7 @@ describe('GitlabVersionCheck', () => { describe(`is ${description}`, () => { beforeEach(async () => { createComponent(mockResponse); - await flushPromises(); // Ensure we wrap up the axios call + await waitForPromises(); // Ensure we wrap up the axios call }); it(`does${renders ? '' : ' not'} render GlBadge`, () => { @@ -61,7 +61,7 @@ describe('GitlabVersionCheck', () => { describe(`when response is ${mockResponse.res.severity}`, () => { beforeEach(async () => { createComponent(mockResponse); - await flushPromises(); // Ensure we wrap up the axios call + await waitForPromises(); // Ensure we wrap up the axios call }); it(`title is ${expectedUI.title}`, () => { diff --git a/spec/frontend/work_items/components/work_item_actions_spec.js b/spec/frontend/work_items/components/work_item_actions_spec.js index 286c8180e16..137a0a7326d 100644 --- a/spec/frontend/work_items/components/work_item_actions_spec.js +++ b/spec/frontend/work_items/components/work_item_actions_spec.js @@ -1,29 +1,17 @@ import { GlDropdownItem, GlModal } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; -import Vue from 'vue'; -import VueApollo from 'vue-apollo'; -import waitForPromises from 'helpers/wait_for_promises'; -import createMockApollo from 'helpers/mock_apollo_helper'; import WorkItemActions from '~/work_items/components/work_item_actions.vue'; -import deleteWorkItem from '~/work_items/graphql/delete_work_item.mutation.graphql'; -import { deleteWorkItemResponse, deleteWorkItemFailureResponse } from '../mock_data'; describe('WorkItemActions component', () => { let wrapper; let glModalDirective; - Vue.use(VueApollo); - const findModal = () => wrapper.findComponent(GlModal); const findDeleteButton = () => wrapper.findComponent(GlDropdownItem); - const createComponent = ({ - canDelete = true, - deleteWorkItemHandler = jest.fn().mockResolvedValue(deleteWorkItemResponse), - } = {}) => { + const createComponent = ({ canDelete = true } = {}) => { glModalDirective = jest.fn(); wrapper = shallowMount(WorkItemActions, { - apolloProvider: createMockApollo([[deleteWorkItem, deleteWorkItemHandler]]), propsData: { workItemId: '123', canDelete }, directives: { glModal: { @@ -54,43 +42,12 @@ describe('WorkItemActions component', () => { expect(glModalDirective).toHaveBeenCalled(); }); - it('calls delete mutation when clicking OK button', () => { - const deleteWorkItemHandler = jest.fn().mockResolvedValue(deleteWorkItemResponse); - - createComponent({ - deleteWorkItemHandler, - }); - - findModal().vm.$emit('ok'); - - expect(deleteWorkItemHandler).toHaveBeenCalled(); - expect(wrapper.emitted('error')).toBeUndefined(); - }); - - it('emits event after delete success', async () => { + it('emits event when clicking OK button', () => { createComponent(); findModal().vm.$emit('ok'); - await waitForPromises(); - - expect(wrapper.emitted('workItemDeleted')).not.toBeUndefined(); - expect(wrapper.emitted('error')).toBeUndefined(); - }); - - it('emits error event after delete failure', async () => { - createComponent({ - deleteWorkItemHandler: jest.fn().mockResolvedValue(deleteWorkItemFailureResponse), - }); - - findModal().vm.$emit('ok'); - - await waitForPromises(); - - expect(wrapper.emitted('error')[0]).toEqual([ - "The resource that you are attempting to access does not exist or you don't have permission to perform this action", - ]); - expect(wrapper.emitted('workItemDeleted')).toBeUndefined(); + expect(wrapper.emitted('deleteWorkItem')).toEqual([[]]); }); it('does not render when canDelete is false', () => { diff --git a/spec/frontend/work_items/components/work_item_detail_modal_spec.js b/spec/frontend/work_items/components/work_item_detail_modal_spec.js index 67d794519b6..aaabdbc82d9 100644 --- a/spec/frontend/work_items/components/work_item_detail_modal_spec.js +++ b/spec/frontend/work_items/components/work_item_detail_modal_spec.js @@ -1,21 +1,51 @@ -import { GlModal, GlAlert } from '@gitlab/ui'; +import { GlAlert } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; +import waitForPromises from 'helpers/wait_for_promises'; +import createMockApollo from 'helpers/mock_apollo_helper'; import WorkItemDetail from '~/work_items/components/work_item_detail.vue'; import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue'; +import deleteWorkItemFromTaskMutation from '~/work_items/graphql/delete_task_from_work_item.mutation.graphql'; describe('WorkItemDetailModal component', () => { let wrapper; Vue.use(VueApollo); + const hideModal = jest.fn(); + const GlModal = { + template: ` + <div> + <slot></slot> + </div> + `, + methods: { + hide: hideModal, + }, + }; + const findModal = () => wrapper.findComponent(GlModal); const findAlert = () => wrapper.findComponent(GlAlert); const findWorkItemDetail = () => wrapper.findComponent(WorkItemDetail); const createComponent = ({ workItemId = '1', error = false } = {}) => { + const apolloProvider = createMockApollo([ + [ + deleteWorkItemFromTaskMutation, + jest.fn().mockResolvedValue({ + data: { + workItemDeleteTask: { + workItem: { id: 123, descriptionHtml: 'updated work item desc' }, + errors: [], + }, + }, + }), + ], + ]); + wrapper = shallowMount(WorkItemDetailModal, { + apolloProvider, propsData: { workItemId }, data() { return { @@ -35,7 +65,9 @@ describe('WorkItemDetailModal component', () => { it('renders WorkItemDetail', () => { createComponent(); - expect(findWorkItemDetail().props()).toEqual({ workItemId: '1' }); + expect(findWorkItemDetail().props()).toEqual({ + workItemId: '1', + }); }); it('renders alert if there is an error', () => { @@ -65,10 +97,24 @@ describe('WorkItemDetailModal component', () => { expect(wrapper.emitted('close')).toBeTruthy(); }); - it('emits `workItemDeleted` event on deleting work item', () => { + it('emits `workItemUpdated` event on updating work item', () => { createComponent(); - findWorkItemDetail().vm.$emit('workItemDeleted'); + findWorkItemDetail().vm.$emit('workItemUpdated'); + + expect(wrapper.emitted('workItemUpdated')).toBeTruthy(); + }); + + describe('delete work item', () => { + it('emits workItemDeleted and closes modal', async () => { + createComponent(); + const newDesc = 'updated work item desc'; + + findWorkItemDetail().vm.$emit('deleteWorkItem'); - expect(wrapper.emitted('workItemDeleted')).toBeTruthy(); + await waitForPromises(); + + expect(wrapper.emitted('workItemDeleted')).toEqual([[newDesc]]); + expect(hideModal).toHaveBeenCalled(); + }); }); }); diff --git a/spec/frontend/work_items/components/work_item_state_spec.js b/spec/frontend/work_items/components/work_item_state_spec.js index 6584d197206..9e48f56d9e9 100644 --- a/spec/frontend/work_items/components/work_item_state_spec.js +++ b/spec/frontend/work_items/components/work_item_state_spec.js @@ -81,6 +81,15 @@ describe('WorkItemState component', () => { }); }); + it('emits updated event', async () => { + createComponent(); + + findItemState().vm.$emit('changed', STATE_CLOSED); + await waitForPromises(); + + expect(wrapper.emitted('updated')).toEqual([[]]); + }); + it('emits an error message when the mutation was unsuccessful', async () => { createComponent({ mutationHandler: jest.fn().mockRejectedValue('Error!') }); diff --git a/spec/frontend/work_items/components/work_item_title_spec.js b/spec/frontend/work_items/components/work_item_title_spec.js index afde0d9ec45..19b56362ac0 100644 --- a/spec/frontend/work_items/components/work_item_title_spec.js +++ b/spec/frontend/work_items/components/work_item_title_spec.js @@ -57,6 +57,15 @@ describe('WorkItemTitle component', () => { }); }); + it('emits updated event', async () => { + createComponent(); + + findItemTitle().vm.$emit('title-changed', 'new title'); + await waitForPromises(); + + expect(wrapper.emitted('updated')).toEqual([[]]); + }); + it('does not call a mutation when the title has not changed', () => { createComponent(); diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js index 0c50a3aa50a..f3483550013 100644 --- a/spec/frontend/work_items/mock_data.js +++ b/spec/frontend/work_items/mock_data.js @@ -5,6 +5,7 @@ export const workItemQueryResponse = { id: 'gid://gitlab/WorkItem/1', title: 'Test', state: 'OPEN', + description: 'description', workItemType: { __typename: 'WorkItemType', id: 'gid://gitlab/WorkItems::Type/5', @@ -27,6 +28,7 @@ export const updateWorkItemMutationResponse = { id: 'gid://gitlab/WorkItem/1', title: 'Updated title', state: 'OPEN', + description: 'description', workItemType: { __typename: 'WorkItemType', id: 'gid://gitlab/WorkItems::Type/5', @@ -65,6 +67,7 @@ export const createWorkItemMutationResponse = { id: 'gid://gitlab/WorkItem/1', title: 'Updated title', state: 'OPEN', + description: 'description', workItemType: { __typename: 'WorkItemType', id: 'gid://gitlab/WorkItems::Type/5', diff --git a/spec/frontend/work_items/pages/work_item_detail_spec.js b/spec/frontend/work_items/pages/work_item_detail_spec.js index 39fe7aed0ea..9f87655175c 100644 --- a/spec/frontend/work_items/pages/work_item_detail_spec.js +++ b/spec/frontend/work_items/pages/work_item_detail_spec.js @@ -104,4 +104,18 @@ describe('WorkItemDetail component', () => { issuableId: workItemQueryResponse.data.workItem.id, }); }); + + it('emits workItemUpdated event when fields updated', async () => { + createComponent(); + + await waitForPromises(); + + findWorkItemState().vm.$emit('updated'); + + expect(wrapper.emitted('workItemUpdated')).toEqual([[]]); + + findWorkItemTitle().vm.$emit('updated'); + + expect(wrapper.emitted('workItemUpdated')).toEqual([[], []]); + }); }); diff --git a/spec/frontend/work_items/pages/work_item_root_spec.js b/spec/frontend/work_items/pages/work_item_root_spec.js index 81d01a0cb45..85096392e84 100644 --- a/spec/frontend/work_items/pages/work_item_root_spec.js +++ b/spec/frontend/work_items/pages/work_item_root_spec.js @@ -1,21 +1,45 @@ +import { GlAlert } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; +import waitForPromises from 'helpers/wait_for_promises'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import { visitUrl } from '~/lib/utils/url_utility'; import WorkItemDetail from '~/work_items/components/work_item_detail.vue'; import WorkItemsRoot from '~/work_items/pages/work_item_root.vue'; +import deleteWorkItem from '~/work_items/graphql/delete_work_item.mutation.graphql'; +import { deleteWorkItemResponse, deleteWorkItemFailureResponse } from '../mock_data'; + +jest.mock('~/lib/utils/url_utility', () => ({ + visitUrl: jest.fn(), +})); Vue.use(VueApollo); describe('Work items root component', () => { let wrapper; + const issuesListPath = '/-/issues'; + const mockToastShow = jest.fn(); const findWorkItemDetail = () => wrapper.findComponent(WorkItemDetail); + const findAlert = () => wrapper.findComponent(GlAlert); - const createComponent = () => { + const createComponent = ({ + deleteWorkItemHandler = jest.fn().mockResolvedValue(deleteWorkItemResponse), + } = {}) => { wrapper = shallowMount(WorkItemsRoot, { + apolloProvider: createMockApollo([[deleteWorkItem, deleteWorkItemHandler]]), + provide: { + issuesListPath, + }, propsData: { id: '1', }, + mocks: { + $toast: { + show: mockToastShow, + }, + }, }); }; @@ -30,4 +54,34 @@ describe('Work items root component', () => { workItemId: 'gid://gitlab/WorkItem/1', }); }); + + it('deletes work item when deleteWorkItem event emitted', async () => { + const deleteWorkItemHandler = jest.fn().mockResolvedValue(deleteWorkItemResponse); + + createComponent({ + deleteWorkItemHandler, + }); + + findWorkItemDetail().vm.$emit('deleteWorkItem'); + + await waitForPromises(); + + expect(deleteWorkItemHandler).toHaveBeenCalled(); + expect(mockToastShow).toHaveBeenCalled(); + expect(visitUrl).toHaveBeenCalledWith(issuesListPath); + }); + + it('shows alert if delete fails', async () => { + const deleteWorkItemHandler = jest.fn().mockRejectedValue(deleteWorkItemFailureResponse); + + createComponent({ + deleteWorkItemHandler, + }); + + findWorkItemDetail().vm.$emit('deleteWorkItem'); + + await waitForPromises(); + + expect(findAlert().exists()).toBe(true); + }); }); diff --git a/spec/frontend/work_items/router_spec.js b/spec/frontend/work_items/router_spec.js index 7e68c5e4f0e..99dcd886f7b 100644 --- a/spec/frontend/work_items/router_spec.js +++ b/spec/frontend/work_items/router_spec.js @@ -17,6 +17,7 @@ describe('Work items router', () => { router, provide: { fullPath: 'full-path', + issuesListPath: 'full-path/-/issues', }, mocks: { $apollo: { diff --git a/spec/graphql/types/timelog_type_spec.rb b/spec/graphql/types/timelog_type_spec.rb index 56303e8c1ab..c897a25d10d 100644 --- a/spec/graphql/types/timelog_type_spec.rb +++ b/spec/graphql/types/timelog_type_spec.rb @@ -3,11 +3,12 @@ require 'spec_helper' RSpec.describe GitlabSchema.types['Timelog'] do - let(:fields) { %i[id spent_at time_spent user issue merge_request note summary] } + let_it_be(:fields) { %i[id spent_at time_spent user issue merge_request note summary userPermissions] } it { expect(described_class.graphql_name).to eq('Timelog') } it { expect(described_class).to have_graphql_fields(fields) } it { expect(described_class).to require_graphql_authorizations(:read_issue) } + it { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Timelog) } describe 'user field' do subject { described_class.fields['user'] } diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb index 24c318d0218..a5e24e8e288 100644 --- a/spec/models/ci/job_artifact_spec.rb +++ b/spec/models/ci/job_artifact_spec.rb @@ -270,15 +270,6 @@ RSpec.describe Ci::JobArtifact do end end - describe '.order_expired_desc' do - let_it_be(:first_artifact) { create(:ci_job_artifact, expire_at: 2.days.ago) } - let_it_be(:second_artifact) { create(:ci_job_artifact, expire_at: 1.day.ago) } - - it 'returns ordered artifacts' do - expect(described_class.order_expired_desc).to eq([second_artifact, first_artifact]) - end - end - describe '.order_expired_asc' do let_it_be(:first_artifact) { create(:ci_job_artifact, expire_at: 2.days.ago) } let_it_be(:second_artifact) { create(:ci_job_artifact, expire_at: 1.day.ago) } diff --git a/spec/tooling/fixtures/find_codeowners/dir0/dir1/dir2/file2 b/spec/tooling/fixtures/find_codeowners/dir0/dir1/dir2/file2 new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/spec/tooling/fixtures/find_codeowners/dir0/dir1/dir2/file2 diff --git a/spec/tooling/fixtures/find_codeowners/dir0/dir1/file1 b/spec/tooling/fixtures/find_codeowners/dir0/dir1/file1 new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/spec/tooling/fixtures/find_codeowners/dir0/dir1/file1 diff --git a/spec/tooling/fixtures/find_codeowners/dir0/file0 b/spec/tooling/fixtures/find_codeowners/dir0/file0 new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/spec/tooling/fixtures/find_codeowners/dir0/file0 diff --git a/spec/tooling/fixtures/find_codeowners/file b/spec/tooling/fixtures/find_codeowners/file new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/spec/tooling/fixtures/find_codeowners/file diff --git a/spec/tooling/lib/tooling/find_codeowners_spec.rb b/spec/tooling/lib/tooling/find_codeowners_spec.rb new file mode 100644 index 00000000000..b29c5f35ec9 --- /dev/null +++ b/spec/tooling/lib/tooling/find_codeowners_spec.rb @@ -0,0 +1,199 @@ +# frozen_string_literal: true + +require_relative '../../../../tooling/lib/tooling/find_codeowners' + +RSpec.describe Tooling::FindCodeowners do + let(:subject) { described_class.new } + let(:root) { File.expand_path('../../fixtures/find_codeowners', __dir__) } + + describe '#execute' do + before do + allow(subject).to receive(:load_config).and_return( + '[Section name]': { + '@group': { + allow: { + keywords: %w[dir0 file], + patterns: ['/%{keyword}/**/*', '/%{keyword}'] + }, + deny: { + keywords: %w[file0], + patterns: ['**/%{keyword}'] + } + } + } + ) + end + + it 'prints CODEOWNERS as configured' do + expect do + Dir.chdir(root) do + subject.execute + end + end.to output(<<~CODEOWNERS).to_stdout + [Section name] + /dir0/dir1 @group + /file @group + CODEOWNERS + end + end + + describe '#load_definitions' do + it 'expands the allow and deny list with keywords and patterns' do + subject.load_definitions.each do |section, group_defintions| + group_defintions.each do |group, definitions| + expect(definitions[:allow]).to be_an(Array) + expect(definitions[:deny]).to be_an(Array) + end + end + end + + it 'expands the auth group' do + auth = subject.load_definitions.dig( + :'[Authentication and Authorization]', + :'@gitlab-org/manage/authentication-and-authorization') + + expect(auth).to eq( + allow: %w[ + /{,ee/}app/**/*password*{/**/*,} + /{,ee/}config/**/*password*{/**/*,} + /{,ee/}lib/**/*password*{/**/*,} + /{,ee/}app/**/*auth*{/**/*,} + /{,ee/}config/**/*auth*{/**/*,} + /{,ee/}lib/**/*auth*{/**/*,} + /{,ee/}app/**/*token*{/**/*,} + /{,ee/}config/**/*token*{/**/*,} + /{,ee/}lib/**/*token*{/**/*,} + ], + deny: %w[ + **/*author.*{/**/*,} + **/*author_*{/**/*,} + **/*authored*{/**/*,} + **/*authoring*{/**/*,} + **/*.png*{/**/*,} + **/*.svg*{/**/*,} + **/*deploy_token*{/**/*,} + **/*runner{,s}_token*{/**/*,} + **/*job_token*{/**/*,} + **/*autocomplete_tokens*{/**/*,} + **/*dast_site_token*{/**/*,} + **/*reset_prometheus_token*{/**/*,} + **/*reset_registration_token*{/**/*,} + **/*runners_registration_token*{/**/*,} + **/*terraform_registry_token*{/**/*,} + **/*tokenizer*{/**/*,} + **/*filtered_search*{/**/*,} + **/*/alert_management/*{/**/*,} + **/*/analytics/*{/**/*,} + **/*/bitbucket/*{/**/*,} + **/*/clusters/*{/**/*,} + **/*/clusters_list/*{/**/*,} + **/*/dast/*{/**/*,} + **/*/dast_profiles/*{/**/*,} + **/*/dast_site_tokens/*{/**/*,} + **/*/dast_site_validation/*{/**/*,} + **/*/dependency_proxy/*{/**/*,} + **/*/error_tracking/*{/**/*,} + **/*/google_api/*{/**/*,} + **/*/google_cloud/*{/**/*,} + **/*/jira_connect/*{/**/*,} + **/*/kubernetes/*{/**/*,} + **/*/protected_environments/*{/**/*,} + **/*/config/feature_flags/development/jira_connect_*{/**/*,} + **/*/config/metrics/*{/**/*,} + **/*/app/controllers/groups/dependency_proxy_auth_controller.rb*{/**/*,} + **/*/app/finders/ci/auth_job_finder.rb*{/**/*,} + **/*/ee/config/metrics/*{/**/*,} + **/*/lib/gitlab/conan_token.rb*{/**/*,} + ] + ) + end + end + + describe '#load_config' do + it 'loads the config with symbolized keys' do + config = subject.load_config + + expect_hash_keys_to_be_symbols(config) + end + + context 'when YAML has safe_load_file' do + before do + allow(YAML).to receive(:respond_to?).with(:safe_load_file).and_return(true) + end + + it 'calls safe_load_file' do + expect(YAML).to receive(:safe_load_file) + + subject.load_config + end + end + + context 'when YAML does not have safe_load_file' do + before do + allow(YAML).to receive(:respond_to?).with(:safe_load_file).and_return(false) + end + + it 'calls load_file' do + expect(YAML).to receive(:safe_load) + + subject.load_config + end + end + + def expect_hash_keys_to_be_symbols(object) + if object.is_a?(Hash) + object.each do |key, value| + expect(key).to be_a(Symbol) + + expect_hash_keys_to_be_symbols(value) + end + end + end + end + + describe '#path_matches?' do + let(:pattern) { 'pattern' } + let(:path) { 'path' } + + it 'passes flags we are expecting to File.fnmatch?' do + expected_flags = + ::File::FNM_DOTMATCH | ::File::FNM_PATHNAME | ::File::FNM_EXTGLOB + + expect(File).to receive(:fnmatch?).with(pattern, path, expected_flags) + + subject.path_matches?(pattern, path) + end + end + + describe '#consolidate_paths' do + before do + allow(subject).to receive(:find_dir_maxdepth_1).and_return(<<~LINES) + dir + dir/0 + dir/2 + dir/3 + dir/1 + LINES + end + + context 'when the directory has the same number of entries' do + let(:input_paths) { %W[dir/0\n dir/1\n dir/2\n dir/3\n] } + + it 'consolidates into the directory' do + paths = subject.consolidate_paths(input_paths) + + expect(paths).to eq(["dir\n"]) + end + end + + context 'when the directory has different number of entries' do + let(:input_paths) { %W[dir/0\n dir/1\n dir/2\n] } + + it 'returns the original paths' do + paths = subject.consolidate_paths(input_paths) + + expect(paths).to eq(input_paths) + end + end + end +end diff --git a/tooling/bin/find_codeowners b/tooling/bin/find_codeowners new file mode 100755 index 00000000000..2c028b3162e --- /dev/null +++ b/tooling/bin/find_codeowners @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require_relative '../lib/tooling/find_codeowners' + +Tooling::FindCodeowners.new.execute diff --git a/tooling/config/CODEOWNERS.yml b/tooling/config/CODEOWNERS.yml new file mode 100644 index 00000000000..d867c8c22fc --- /dev/null +++ b/tooling/config/CODEOWNERS.yml @@ -0,0 +1,58 @@ +# This is supposed to be used with: +# tooling/bin/find_codeowners tooling/config/CODEOWNERS.yml +# And paste the contents into .gitlab/CODEOWNERS + +'[Authentication and Authorization]': + '@gitlab-org/manage/authentication-and-authorization': + allow: + keywords: + - password + - auth + - token + patterns: + - '/{,ee/}app/**/*%{keyword}*{/**/*,}' + - '/{,ee/}config/**/*%{keyword}*{/**/*,}' + - '/{,ee/}lib/**/*%{keyword}*{/**/*,}' + deny: + keywords: + - author. + - author_ + - authored + - authoring + - .png + - .svg + - deploy_token + - runner{,s}_token + - job_token + - autocomplete_tokens + - dast_site_token + - reset_prometheus_token + - reset_registration_token + - runners_registration_token + - terraform_registry_token + - tokenizer + - filtered_search + - /alert_management/ + - /analytics/ + - /bitbucket/ + - /clusters/ + - /clusters_list/ + - /dast/ + - /dast_profiles/ + - /dast_site_tokens/ + - /dast_site_validation/ + - /dependency_proxy/ + - /error_tracking/ + - /google_api/ + - /google_cloud/ + - /jira_connect/ + - /kubernetes/ + - /protected_environments/ + - /config/feature_flags/development/jira_connect_ + - /config/metrics/ + - /app/controllers/groups/dependency_proxy_auth_controller.rb + - /app/finders/ci/auth_job_finder.rb + - /ee/config/metrics/ + - /lib/gitlab/conan_token.rb + patterns: + - '**/*%{keyword}*{/**/*,}' diff --git a/tooling/lib/tooling/find_codeowners.rb b/tooling/lib/tooling/find_codeowners.rb new file mode 100644 index 00000000000..35d8a9d7461 --- /dev/null +++ b/tooling/lib/tooling/find_codeowners.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +require 'yaml' + +module Tooling + class FindCodeowners + def execute + load_definitions.each do |section, group_defintions| + puts section + + group_defintions.each do |group, list| + matched_files = git_ls_files.each_line.select do |line| + list[:allow].find do |pattern| + path = "/#{line.chomp}" + + path_matches?(pattern, path) && + list[:deny].none? { |pattern| path_matches?(pattern, path) } + end + end + + consolidated = consolidate_paths(matched_files) + consolidated_again = consolidate_paths(consolidated) + + # Consider the directory structure is a tree structure: + # https://en.wikipedia.org/wiki/Tree_(data_structure) + # After we consolidated the leaf entries, it could be possible that + # we can consolidate further for the new leaves. Repeat this + # process until we see no improvements. + while consolidated_again.size < consolidated.size + consolidated = consolidated_again + consolidated_again = consolidate_paths(consolidated) + end + + consolidated.each do |file| + puts "/#{file.chomp} #{group}" + end + end + end + end + + def load_definitions + result = load_config + + result.each do |section, group_defintions| + group_defintions.each do |group, definitions| + definitions.transform_values! do |rules| + rules[:keywords].flat_map do |keyword| + rules[:patterns].map do |pattern| + pattern % { keyword: keyword } + end + end + end + end + end + + result + end + + def load_config + config_path = "#{__dir__}/../../config/CODEOWNERS.yml" + + if YAML.respond_to?(:safe_load_file) # Ruby 3.0+ + YAML.safe_load_file(config_path, symbolize_names: true) + else + YAML.safe_load(File.read(config_path), symbolize_names: true) + end + end + + # Copied and modified from ee/lib/gitlab/code_owners/file.rb + def path_matches?(pattern, path) + # `FNM_DOTMATCH` makes sure we also match files starting with a `.` + # `FNM_PATHNAME` makes sure ** matches path separators + flags = ::File::FNM_DOTMATCH | ::File::FNM_PATHNAME + + # BEGIN extension + flags |= ::File::FNM_EXTGLOB + # END extension + + ::File.fnmatch?(pattern, path, flags) + end + + def consolidate_paths(matched_files) + matched_files.group_by(&File.method(:dirname)).flat_map do |dir, files| + # First line is the dir itself + if find_dir_maxdepth_1(dir).lines.drop(1).sort == files.sort + "#{dir}\n" + else + files + end + end.sort + end + + private + + def find_dir_maxdepth_1(dir) + `find #{dir} -maxdepth 1` + end + + def git_ls_files + @git_ls_files ||= `git ls-files` + end + end +end |