diff options
| author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-17 06:08:15 +0000 |
|---|---|---|
| committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-17 06:08:15 +0000 |
| commit | e4476c4a182e5af930799342f681405dc98d6a1c (patch) | |
| tree | ff66eed0b3b797eb7605c1c1b2cf7e4415c21c7a | |
| parent | 80816914ff75f9e0708bc7747200a9cfc5fc24e5 (diff) | |
| download | gitlab-ce-e4476c4a182e5af930799342f681405dc98d6a1c.tar.gz | |
Add latest changes from gitlab-org/gitlab@master
20 files changed, 288 insertions, 63 deletions
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list.vue b/app/assets/javascripts/ide/components/commit_sidebar/list.vue index 86b0666e7b0..91d78a7c28c 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/list.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/list.vue @@ -80,10 +80,10 @@ export default { <template> <div class="ide-commit-list-container"> - <header class="multi-file-commit-panel-header d-flex mb-0"> - <div class="d-flex align-items-center flex-fill"> + <header class="multi-file-commit-panel-header gl-display-flex gl-mb-0"> + <div class="gl-display-flex gl-align-items-center flex-fill"> <strong> {{ titleText }} </strong> - <div class="d-flex ml-auto"> + <div class="gl-display-flex gl-ml-auto"> <gl-button v-if="!stagedList" v-gl-tooltip @@ -114,7 +114,7 @@ export default { /> </li> </ul> - <p v-else class="multi-file-commit-list form-text text-muted text-center"> + <p v-else class="multi-file-commit-list form-text gl-text-gray-600 gl-text-center"> {{ emptyStateText }} </p> <gl-modal diff --git a/app/assets/javascripts/work_items/components/work_item_assignees.vue b/app/assets/javascripts/work_items/components/work_item_assignees.vue index 1c89476ea34..4d1c171772e 100644 --- a/app/assets/javascripts/work_items/components/work_item_assignees.vue +++ b/app/assets/javascripts/work_items/components/work_item_assignees.vue @@ -75,25 +75,25 @@ export default { <span class="gl-font-weight-bold gl-w-15 gl-pt-2" data-testid="assignees-title">{{ __('Assignee(s)') }}</span> - <!-- TODO: Remove this div when https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/2872 is merged --> - <div - v-if="assigneeListEmpty && !isEditing" - class="add-assignees gl-min-w-fit-content gl-absolute gl-display-flex gl-align-items-center gl-text-gray-300 gl-pr-4 gl-top-2 gl-z-index-0" - data-testid="empty-state" - > - <gl-icon name="profile" /> - <span class="gl-ml-2">{{ __('Add assignees') }}</span> - </div> <gl-token-selector ref="tokenSelector" v-model="localAssignees" hide-dropdown-with-no-items :container-class="containerClass" - class="gl-w-full gl-border gl-border-white gl-hover-border-gray-200 gl-rounded-base gl-z-index-1 gl-bg-transparent!" + class="gl-w-full gl-border gl-border-white gl-hover-border-gray-200 gl-rounded-base" @token-remove="focusTokenSelector" @focus="isEditing = true" @blur="setAssignees" > + <template #empty-placeholder> + <div + class="add-assignees gl-min-w-fit-content gl-display-flex gl-align-items-center gl-text-gray-300 gl-pr-4 gl-top-2" + data-testid="empty-state" + > + <gl-icon name="profile" /> + <span class="gl-ml-2">{{ __('Add assignees') }}</span> + </div> + </template> <template #token-content="{ token }"> <gl-link :href="token.webUrl" @@ -109,10 +109,3 @@ export default { </gl-token-selector> </div> </template> - -<style lang="scss"> -/* TODO: Remove style block when https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/2872 is merged */ -.work-item-assignees .add-assignees { - left: 7.5rem; -} -</style> diff --git a/app/controllers/projects/settings/packages_and_registries_controller.rb b/app/controllers/projects/settings/packages_and_registries_controller.rb index 0cd2bfa9695..d3c08bef808 100644 --- a/app/controllers/projects/settings/packages_and_registries_controller.rb +++ b/app/controllers/projects/settings/packages_and_registries_controller.rb @@ -17,12 +17,7 @@ module Projects private def packages_and_registries_settings_enabled! - render_404 unless can_destroy_container_registry_image?(project) - end - - def can_destroy_container_registry_image?(project) - Gitlab.config.registry.enabled && - can?(current_user, :destroy_container_image, project) + render_404 unless can?(current_user, :view_package_registry_project_settings, project) end end end diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb index f25fc56a588..b20a671179b 100644 --- a/app/graphql/types/ci/job_type.rb +++ b/app/graphql/types/ci/job_type.rb @@ -7,7 +7,7 @@ module Types class JobType < BaseObject graphql_name 'CiJob' - connection_type_class(Types::CountableConnectionType) + connection_type_class(Types::LimitedCountableConnectionType) expose_permissions Types::PermissionTypes::Ci::Job diff --git a/app/graphql/types/limited_countable_connection_type.rb b/app/graphql/types/limited_countable_connection_type.rb new file mode 100644 index 00000000000..f0698222ea3 --- /dev/null +++ b/app/graphql/types/limited_countable_connection_type.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Types + # rubocop: disable Graphql/AuthorizeTypes + class LimitedCountableConnectionType < GraphQL::Types::Relay::BaseConnection + COUNT_LIMIT = 1000 + COUNT_DESCRIPTION = "Limited count of collection. Returns limit + 1 for counts greater than the limit." + + field :count, GraphQL::Types::Int, null: false, description: COUNT_DESCRIPTION do + argument :limit, GraphQL::Types::Int, + required: false, default_value: COUNT_LIMIT, + validates: { numericality: { greater_than: 0, less_than_or_equal_to: COUNT_LIMIT } }, + description: "Limit value to be applied to the count query. Default is 1000." + end + + def count(limit:) + relation = object.items + + if relation.respond_to?(:page) + relation.page.total_count_with_limit(:all, limit: limit) + else + [relation.size, limit.next].min + end + end + end +end diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index fc4ff02934d..3bce26be756 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -203,6 +203,10 @@ class ProjectPolicy < BasePolicy Feature.disabled?(:runner_registration_control) || Gitlab::CurrentSettings.valid_runner_registrars.include?('project') end + condition :registry_enabled do + Gitlab.config.registry.enabled + end + # `:read_project` may be prevented in EE, but `:read_project_for_iids` should # not. rule { guest | admin }.enable :read_project_for_iids @@ -760,6 +764,10 @@ class ProjectPolicy < BasePolicy enable :import_project_members_from_another_project end + rule { registry_enabled & can?(:admin_container_image) }.policy do + enable :view_package_registry_project_settings + end + private def user_is_user? diff --git a/app/services/two_factor/destroy_service.rb b/app/services/two_factor/destroy_service.rb index b8bbe215d6e..859012c2153 100644 --- a/app/services/two_factor/destroy_service.rb +++ b/app/services/two_factor/destroy_service.rb @@ -8,7 +8,7 @@ module TwoFactor result = disable_two_factor - notification_service.disabled_two_factor(user) if result[:status] == :success + notify_on_success(user) if result[:status] == :success result end @@ -20,5 +20,11 @@ module TwoFactor user.disable_two_factor! end end + + def notify_on_success(user) + notification_service.disabled_two_factor(user) + end end end + +TwoFactor::DestroyService.prepend_mod_with('TwoFactor::DestroyService') diff --git a/app/views/admin/broadcast_messages/index.html.haml b/app/views/admin/broadcast_messages/index.html.haml index b6b762c64e8..46924393a27 100644 --- a/app/views/admin/broadcast_messages/index.html.haml +++ b/app/views/admin/broadcast_messages/index.html.haml @@ -43,7 +43,7 @@ = message.target_path %td = message.broadcast_type.capitalize - %td.gl-white-space-nowrap + %td.gl-white-space-nowrap< = link_to sprite_icon('pencil', css_class: 'gl-icon'), edit_admin_broadcast_message_path(message), title: _('Edit'), class: 'btn btn-icon gl-button' - = link_to sprite_icon('remove', css_class: 'gl-icon'), admin_broadcast_message_path(message), method: :delete, remote: true, title: _('Remove'), class: 'js-remove-tr btn btn-icon gl-button btn-danger ml-2' + = link_to sprite_icon('remove', css_class: 'gl-icon'), admin_broadcast_message_path(message), method: :delete, remote: true, title: _('Remove'), class: 'js-remove-tr btn btn-icon gl-button btn-danger gl-ml-3' = paginate @broadcast_messages, theme: 'gitlab' diff --git a/config/feature_flags/development/omit_epic_subscribed.yml b/config/feature_flags/development/omit_epic_subscribed.yml deleted file mode 100644 index 885636d6626..00000000000 --- a/config/feature_flags/development/omit_epic_subscribed.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: omit_epic_subscribed -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86016 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/360663 -milestone: '15.0' -type: development -group: group::product planning -default_enabled: true diff --git a/doc/administration/audit_event_streaming.md b/doc/administration/audit_event_streaming.md index c7b2406174d..ad235ead992 100644 --- a/doc/administration/audit_event_streaming.md +++ b/doc/administration/audit_event_streaming.md @@ -166,6 +166,18 @@ mutation { } ``` +### Delete with the API + +Group owners can remove a HTTP header using the GraphQL `auditEventsStreamingHeadersDestroy` mutation. + +```graphql +mutation { + auditEventsStreamingHeadersDestroy(input: { headerId: "gid://gitlab/AuditEvents::ExternalAuditEventDestination/24601" }) { + errors + } +} +``` + The header is created if the returned `errors` object is empty. ## Verify event authenticity diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md index 442f743f2c7..a329adbed22 100644 --- a/doc/administration/audit_events.md +++ b/doc/administration/audit_events.md @@ -234,6 +234,7 @@ The following user actions are recorded: - Administrator added or removed ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/323905) in GitLab 14.1) - Removed SSH key ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220127) in GitLab 14.1) - Added or removed GPG key ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220127) in GitLab 14.1) +- A user's two-factor authentication was disabled ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/238177) in GitLab 15.1) Instance events can also be accessed via the [Instance Audit Events API](../api/audit_events.md#instance-audit-events). diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 750006950fd..9d5b9a8971f 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -738,6 +738,24 @@ Input type: `AuditEventsStreamingHeadersCreateInput` | <a id="mutationauditeventsstreamingheaderscreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | | <a id="mutationauditeventsstreamingheaderscreateheader"></a>`header` | [`AuditEventStreamingHeader`](#auditeventstreamingheader) | Created header. | +### `Mutation.auditEventsStreamingHeadersDestroy` + +Input type: `AuditEventsStreamingHeadersDestroyInput` + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mutationauditeventsstreamingheadersdestroyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| <a id="mutationauditeventsstreamingheadersdestroyheaderid"></a>`headerId` | [`AuditEventsStreamingHeaderID!`](#auditeventsstreamingheaderid) | Header to delete. | + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mutationauditeventsstreamingheadersdestroyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| <a id="mutationauditeventsstreamingheadersdestroyerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | + ### `Mutation.awardEmojiAdd` Input type: `AwardEmojiAddInput` @@ -6033,11 +6051,24 @@ The connection type for [`CiJob`](#cijob). | Name | Type | Description | | ---- | ---- | ----------- | -| <a id="cijobconnectioncount"></a>`count` | [`Int!`](#int) | Total count of collection. | | <a id="cijobconnectionedges"></a>`edges` | [`[CiJobEdge]`](#cijobedge) | A list of edges. | | <a id="cijobconnectionnodes"></a>`nodes` | [`[CiJob]`](#cijob) | A list of nodes. | | <a id="cijobconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | +##### Fields with arguments + +###### `CiJobConnection.count` + +Limited count of collection. Returns limit + 1 for counts greater than the limit. + +Returns [`Int!`](#int). + +####### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="cijobconnectioncountlimit"></a>`limit` | [`Int`](#int) | Limit value to be applied to the count query. Default is 1000. | + #### `CiJobEdge` The edge type for [`CiJob`](#cijob). @@ -20079,6 +20110,12 @@ A `AuditEventsExternalAuditEventDestinationID` is a global ID. It is encoded as An example `AuditEventsExternalAuditEventDestinationID` is: `"gid://gitlab/AuditEvents::ExternalAuditEventDestination/1"`. +### `AuditEventsStreamingHeaderID` + +A `AuditEventsStreamingHeaderID` is a global ID. It is encoded as a string. + +An example `AuditEventsStreamingHeaderID` is: `"gid://gitlab/AuditEvents::Streaming::Header/1"`. + ### `AwardableID` A `AwardableID` is a global ID. It is encoded as a string. diff --git a/doc/ci/cloud_deployment/ecs/deploy_to_aws_ecs.md b/doc/ci/cloud_deployment/ecs/deploy_to_aws_ecs.md index 9837722046b..9af5218e058 100644 --- a/doc/ci/cloud_deployment/ecs/deploy_to_aws_ecs.md +++ b/doc/ci/cloud_deployment/ecs/deploy_to_aws_ecs.md @@ -242,6 +242,10 @@ Change a file in the project and see if it's reflected in the demo application o Congratulations! You successfully set up continuous deployment to ECS. +NOTE: +ECS deploy jobs wait for the rollout to complete before exiting. To disable this behavior, +set `CI_AWS_ECS_WAIT_FOR_ROLLOUT_COMPLETE_DISABLED` to a non-empty value. + ## Further reading - If you're interested in more of the continuous deployments to clouds, see [cloud deployments](../index.md). diff --git a/doc/ci/cloud_deployment/index.md b/doc/ci/cloud_deployment/index.md index dc9bd07f713..c5be2328264 100644 --- a/doc/ci/cloud_deployment/index.md +++ b/doc/ci/cloud_deployment/index.md @@ -124,6 +124,10 @@ Finally, your AWS ECS service is updated with the new revision of the task definition, making the cluster pull the newest version of your application. +NOTE: +ECS deploy jobs wait for the rollout to complete before exiting. To disable this behavior, +set `CI_AWS_ECS_WAIT_FOR_ROLLOUT_COMPLETE_DISABLED` to a non-empty value. + WARNING: The [`AWS/Deploy-ECS.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/AWS/Deploy-ECS.gitlab-ci.yml) template includes two templates: [`Jobs/Build.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml) diff --git a/doc/user/admin_area/settings/sign_up_restrictions.md b/doc/user/admin_area/settings/sign_up_restrictions.md index 534450c1871..9d08c5898ac 100644 --- a/doc/user/admin_area/settings/sign_up_restrictions.md +++ b/doc/user/admin_area/settings/sign_up_restrictions.md @@ -118,6 +118,25 @@ create or update pipelines until their email address is confirmed. You can [change](../../../security/password_length_limits.md#modify-minimum-password-length-using-gitlab-ui) the minimum number of characters a user must have in their password using the GitLab UI. +### Password complexity requirements **(PREMIUM SELF)** + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/354965) in GitLab 15.1. + +By default, the only requirement for user passwords is [minimum password length](#minimum-password-length-limit). +You can add additional complexity requirements. Changes to password complexity requirements apply to new passwords: + +- For new users that sign up. +- For existing users that reset their password. + +Existing passwords are unaffected. To change password complexity requirements: + +1. On the top bar, select **Menu > Admin**. +1. On the left sidebar, select **Settings > General**. +1. Expand **Sign-up restrictions**. +1. Under **Minimum password length (number of characters)**, select additional password complexity requirements. You can require numbers, uppercase letters, lowercase letters, + and symbols. +1. Select **Save changes**. + ## Allow or deny sign ups using specific email domains You can specify an inclusive or exclusive list of email domains which can be used for user sign up. diff --git a/lib/sidebars/projects/menus/settings_menu.rb b/lib/sidebars/projects/menus/settings_menu.rb index d2d5b5881ca..85931e63ebc 100644 --- a/lib/sidebars/projects/menus/settings_menu.rb +++ b/lib/sidebars/projects/menus/settings_menu.rb @@ -104,8 +104,7 @@ module Sidebars end def packages_and_registries_menu_item - if !Gitlab.config.registry.enabled || - !can?(context.current_user, :destroy_container_image, context.project) + unless can?(context.current_user, :view_package_registry_project_settings, context.project) return ::Sidebars::NilMenuItem.new(item_id: :packages_and_registries) end diff --git a/spec/frontend/work_items/components/work_item_assignees_spec.js b/spec/frontend/work_items/components/work_item_assignees_spec.js index c73b433c0bb..0552fe5050e 100644 --- a/spec/frontend/work_items/components/work_item_assignees_spec.js +++ b/spec/frontend/work_items/components/work_item_assignees_spec.js @@ -60,32 +60,11 @@ describe('WorkItemAssignees component', () => { expect(findAssigneeLinks().at(0).attributes('data-user-id')).toBe('1'); }); - describe('when there are no assignees', () => { - beforeEach(() => { - createComponent({ assignees: [] }); - }); - - it('should render empty state placeholder', () => { - expect(findEmptyState().exists()).toBe(true); - }); - - it('should hide empty state placeholder on focusing token selector', async () => { - findTokenSelector().vm.$emit('focus'); - await nextTick(); - - expect(findEmptyState().exists()).toBe(false); - }); - }); - describe('when there are assignees', () => { beforeEach(() => { createComponent(); }); - it('should not render empty state placeholder', () => { - expect(findEmptyState().exists()).toBe(false); - }); - it('should focus token selector on token removal', async () => { findTokenSelector().vm.$emit('token-remove', mockAssignees[0].id); await nextTick(); diff --git a/spec/graphql/types/limited_countable_connection_type_spec.rb b/spec/graphql/types/limited_countable_connection_type_spec.rb new file mode 100644 index 00000000000..30af26cdb83 --- /dev/null +++ b/spec/graphql/types/limited_countable_connection_type_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::LimitedCountableConnectionType do + it 'has the expected fields' do + expected_fields = %i[count page_info] + + expect(described_class).to have_graphql_fields(*expected_fields) + end +end diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index 3050cf6000e..7b3d1abadc1 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -1369,6 +1369,68 @@ RSpec.describe ProjectPolicy do end end + describe 'view_package_registry_project_settings' do + context 'with registry enabled' do + before do + stub_config(registry: { enabled: true }) + end + + context 'with an admin user' do + let(:current_user) { admin } + + context 'when admin mode enabled', :enable_admin_mode do + it { is_expected.to be_allowed(:view_package_registry_project_settings) } + end + + context 'when admin mode disabled' do + it { is_expected.to be_disallowed(:view_package_registry_project_settings) } + end + end + + %i[owner maintainer].each do |role| + context "with #{role}" do + let(:current_user) { public_send(role) } + + it { is_expected.to be_allowed(:view_package_registry_project_settings) } + end + end + + %i[developer reporter guest non_member anonymous].each do |role| + context "with #{role}" do + let(:current_user) { public_send(role) } + + it { is_expected.to be_disallowed(:view_package_registry_project_settings) } + end + end + end + + context 'with registry disabled' do + before do + stub_config(registry: { enabled: false }) + end + + context 'with admin user' do + let(:current_user) { admin } + + context 'when admin mode enabled', :enable_admin_mode do + it { is_expected.to be_disallowed(:view_package_registry_project_settings) } + end + + context 'when admin mode disabled' do + it { is_expected.to be_disallowed(:view_package_registry_project_settings) } + end + end + + %i[owner maintainer developer reporter guest non_member anonymous].each do |role| + context "with #{role}" do + let(:current_user) { public_send(role) } + + it { is_expected.to be_disallowed(:view_package_registry_project_settings) } + end + end + end + end + describe 'read_feature_flag' do subject { described_class.new(current_user, project) } diff --git a/spec/requests/api/graphql/ci/jobs_spec.rb b/spec/requests/api/graphql/ci/jobs_spec.rb index 2d1bb45390b..d1737fc22ae 100644 --- a/spec/requests/api/graphql/ci/jobs_spec.rb +++ b/spec/requests/api/graphql/ci/jobs_spec.rb @@ -258,4 +258,81 @@ RSpec.describe 'Query.project.pipeline' do end end end + + describe '.jobs.count' do + let_it_be(:pipeline) { create(:ci_pipeline, project: project) } + let_it_be(:successful_job) { create(:ci_build, :success, pipeline: pipeline) } + let_it_be(:pending_job) { create(:ci_build, :pending, pipeline: pipeline) } + let_it_be(:failed_job) { create(:ci_build, :failed, pipeline: pipeline) } + + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + pipeline(iid: "#{pipeline.iid}") { + jobs { + count + } + } + } + } + ) + end + + before do + post_graphql(query, current_user: user) + end + + it 'returns the number of jobs' do + expect(graphql_data_at(:project, :pipeline, :jobs, :count)).to eq(3) + end + + context 'with limit value' do + let(:limit) { 1 } + + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + pipeline(iid: "#{pipeline.iid}") { + jobs { + count(limit: #{limit}) + } + } + } + } + ) + end + + it 'returns a limited number of jobs' do + expect(graphql_data_at(:project, :pipeline, :jobs, :count)).to eq(2) + end + + context 'with invalid value' do + let(:limit) { 1500 } + + it 'returns a validation error' do + expect(graphql_errors).to include(a_hash_including('message' => 'limit must be less than or equal to 1000')) + end + end + end + + context 'with jobs filter' do + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + jobs(statuses: FAILED) { + count + } + } + } + ) + end + + it 'returns the number of failed jobs' do + expect(graphql_data_at(:project, :jobs, :count)).to eq(1) + end + end + end end |
