Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
8ed964b660
commit
b509bf0a57
|
@ -216,7 +216,7 @@ export default {
|
||||||
v-if="currentRequest"
|
v-if="currentRequest"
|
||||||
:current-request="currentRequest"
|
:current-request="currentRequest"
|
||||||
:requests="requests"
|
:requests="requests"
|
||||||
class="ml-auto"
|
class="gl-ml-auto"
|
||||||
@change-current-request="changeCurrentRequest"
|
@change-current-request="changeCurrentRequest"
|
||||||
/>
|
/>
|
||||||
<add-request v-on="$listeners" />
|
<add-request v-on="$listeners" />
|
||||||
|
|
|
@ -63,11 +63,11 @@ module AlertManagement
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_new_alert
|
def process_new_alert
|
||||||
|
return if resolving_alert?
|
||||||
|
|
||||||
if alert.save
|
if alert.save
|
||||||
alert.execute_integrations
|
alert.execute_integrations
|
||||||
SystemNoteService.create_new_alert(alert, alert_source)
|
SystemNoteService.create_new_alert(alert, alert_source)
|
||||||
|
|
||||||
process_resolved_alert if resolving_alert?
|
|
||||||
else
|
else
|
||||||
logger.warn(
|
logger.warn(
|
||||||
message: "Unable to create AlertManagement::Alert from #{alert_source}",
|
message: "Unable to create AlertManagement::Alert from #{alert_source}",
|
||||||
|
|
|
@ -25,14 +25,18 @@ class EventCreateService
|
||||||
def open_mr(merge_request, current_user)
|
def open_mr(merge_request, current_user)
|
||||||
create_record_event(merge_request, current_user, :created).tap do
|
create_record_event(merge_request, current_user, :created).tap do
|
||||||
track_event(event_action: :created, event_target: MergeRequest, author_id: current_user.id)
|
track_event(event_action: :created, event_target: MergeRequest, author_id: current_user.id)
|
||||||
track_mr_snowplow_event(merge_request, current_user, :create)
|
track_snowplow_event(merge_request, current_user,
|
||||||
|
Gitlab::UsageDataCounters::TrackUniqueEvents::MERGE_REQUEST_ACTION,
|
||||||
|
:create, 'merge_requests_users')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def close_mr(merge_request, current_user)
|
def close_mr(merge_request, current_user)
|
||||||
create_record_event(merge_request, current_user, :closed).tap do
|
create_record_event(merge_request, current_user, :closed).tap do
|
||||||
track_event(event_action: :closed, event_target: MergeRequest, author_id: current_user.id)
|
track_event(event_action: :closed, event_target: MergeRequest, author_id: current_user.id)
|
||||||
track_mr_snowplow_event(merge_request, current_user, :close)
|
track_snowplow_event(merge_request, current_user,
|
||||||
|
Gitlab::UsageDataCounters::TrackUniqueEvents::MERGE_REQUEST_ACTION,
|
||||||
|
:close, 'merge_requests_users')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -43,7 +47,9 @@ class EventCreateService
|
||||||
def merge_mr(merge_request, current_user)
|
def merge_mr(merge_request, current_user)
|
||||||
create_record_event(merge_request, current_user, :merged).tap do
|
create_record_event(merge_request, current_user, :merged).tap do
|
||||||
track_event(event_action: :merged, event_target: MergeRequest, author_id: current_user.id)
|
track_event(event_action: :merged, event_target: MergeRequest, author_id: current_user.id)
|
||||||
track_mr_snowplow_event(merge_request, current_user, :merge)
|
track_snowplow_event(merge_request, current_user,
|
||||||
|
Gitlab::UsageDataCounters::TrackUniqueEvents::MERGE_REQUEST_ACTION,
|
||||||
|
:merge, 'merge_requests_users')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -67,7 +73,9 @@ class EventCreateService
|
||||||
create_record_event(note, current_user, :commented).tap do
|
create_record_event(note, current_user, :commented).tap do
|
||||||
if note.is_a?(DiffNote) && note.for_merge_request?
|
if note.is_a?(DiffNote) && note.for_merge_request?
|
||||||
track_event(event_action: :commented, event_target: MergeRequest, author_id: current_user.id)
|
track_event(event_action: :commented, event_target: MergeRequest, author_id: current_user.id)
|
||||||
track_mr_snowplow_event(note, current_user, :comment)
|
track_snowplow_event(note, current_user,
|
||||||
|
Gitlab::UsageDataCounters::TrackUniqueEvents::MERGE_REQUEST_ACTION,
|
||||||
|
:comment, 'merge_requests_users')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -100,12 +108,27 @@ class EventCreateService
|
||||||
records = create.zip([:created].cycle) + update.zip([:updated].cycle)
|
records = create.zip([:created].cycle) + update.zip([:updated].cycle)
|
||||||
return [] if records.empty?
|
return [] if records.empty?
|
||||||
|
|
||||||
|
if create.any?
|
||||||
|
track_snowplow_event(create.first, current_user,
|
||||||
|
Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION,
|
||||||
|
:create, 'design_users')
|
||||||
|
end
|
||||||
|
|
||||||
|
if update.any?
|
||||||
|
track_snowplow_event(update.first, current_user,
|
||||||
|
Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION,
|
||||||
|
:update, 'design_users')
|
||||||
|
end
|
||||||
|
|
||||||
create_record_events(records, current_user)
|
create_record_events(records, current_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy_designs(designs, current_user)
|
def destroy_designs(designs, current_user)
|
||||||
return [] unless designs.present?
|
return [] unless designs.present?
|
||||||
|
|
||||||
|
track_snowplow_event(designs.first, current_user,
|
||||||
|
Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION,
|
||||||
|
:destroy, 'design_users')
|
||||||
create_record_events(designs.zip([:destroyed].cycle), current_user)
|
create_record_events(designs.zip([:destroyed].cycle), current_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -230,14 +253,14 @@ class EventCreateService
|
||||||
Gitlab::UsageDataCounters::TrackUniqueEvents.track_event(**params)
|
Gitlab::UsageDataCounters::TrackUniqueEvents.track_event(**params)
|
||||||
end
|
end
|
||||||
|
|
||||||
def track_mr_snowplow_event(record, current_user, action)
|
def track_snowplow_event(record, current_user, category, action, label)
|
||||||
return unless Feature.enabled?(:route_hll_to_snowplow_phase2)
|
return unless Feature.enabled?(:route_hll_to_snowplow_phase2)
|
||||||
|
|
||||||
project = record.project
|
project = record.project
|
||||||
Gitlab::Tracking.event(
|
Gitlab::Tracking.event(
|
||||||
Gitlab::UsageDataCounters::TrackUniqueEvents::MERGE_REQUEST_ACTION.to_s,
|
category.to_s,
|
||||||
action.to_s,
|
action.to_s,
|
||||||
label: 'merge_requests_users',
|
label: label,
|
||||||
project: project,
|
project: project,
|
||||||
namespace: project.namespace,
|
namespace: project.namespace,
|
||||||
user: current_user
|
user: current_user
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
description: Triggered from backend layer when design is created
|
||||||
|
category: design_actions
|
||||||
|
action: create
|
||||||
|
label_description: Constant string that match with ServicePing metric name of action_monthly_active_users_design_management
|
||||||
|
property_description:
|
||||||
|
value_description:
|
||||||
|
extra_properties:
|
||||||
|
identifiers:
|
||||||
|
- project
|
||||||
|
- user
|
||||||
|
- namespace
|
||||||
|
product_section: dev
|
||||||
|
product_stage: plan
|
||||||
|
product_group: group::product_planning
|
||||||
|
product_category: design_management
|
||||||
|
milestone: "15.1"
|
||||||
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90107
|
||||||
|
distributions:
|
||||||
|
- ce
|
||||||
|
- ee
|
||||||
|
tiers:
|
||||||
|
- free
|
||||||
|
- premium
|
||||||
|
- ultimate
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
description: Triggered from backend layer when design is updated
|
||||||
|
category: design_actions
|
||||||
|
action: update
|
||||||
|
label_description: Constant string that match with ServicePing metric name of action_monthly_active_users_design_management
|
||||||
|
property_description:
|
||||||
|
value_description:
|
||||||
|
extra_properties:
|
||||||
|
identifiers:
|
||||||
|
- project
|
||||||
|
- user
|
||||||
|
- namespace
|
||||||
|
product_section: dev
|
||||||
|
product_stage: plan
|
||||||
|
product_group: group::product_planning
|
||||||
|
product_category: design_management
|
||||||
|
milestone: "15.1"
|
||||||
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90107
|
||||||
|
distributions:
|
||||||
|
- ce
|
||||||
|
- ee
|
||||||
|
tiers:
|
||||||
|
- free
|
||||||
|
- premium
|
||||||
|
- ultimate
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
description: Triggered from backend layer when design is deleted
|
||||||
|
category: design_actions
|
||||||
|
action: destroy
|
||||||
|
label_description: Constant string that match with ServicePing metric name of action_monthly_active_users_design_management
|
||||||
|
property_description:
|
||||||
|
value_description:
|
||||||
|
extra_properties:
|
||||||
|
identifiers:
|
||||||
|
- project
|
||||||
|
- user
|
||||||
|
- namespace
|
||||||
|
product_section: dev
|
||||||
|
product_stage: plan
|
||||||
|
product_group: group::product_planning
|
||||||
|
product_category: design_management
|
||||||
|
milestone: "15.1"
|
||||||
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90107
|
||||||
|
distributions:
|
||||||
|
- ce
|
||||||
|
- ee
|
||||||
|
tiers:
|
||||||
|
- free
|
||||||
|
- premium
|
||||||
|
- ultimate
|
|
@ -2000,3 +2000,33 @@ Example response:
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Disable two factor authentication (administrator only)
|
||||||
|
|
||||||
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/295260) in GitLab 15.2.
|
||||||
|
|
||||||
|
Disables two factor authentication (2FA) for the specified user. Only administrators can disable 2FA for users.
|
||||||
|
|
||||||
|
Administrators cannot disable 2FA for their own user account or other administrators using the API. Instead, they can disable an
|
||||||
|
administrator's 2FA [using the Rails console](../security/two_factor_authentication.md#for-a-single-user).
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
PATCH /user/:id/disable_two_factor
|
||||||
|
```
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
| Attribute | Type | Required | Description |
|
||||||
|
| --------- | ------- | -------- | --------------------- |
|
||||||
|
| `id` | integer | yes | The ID of the user |
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl --request PATCH --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/user/1/disable_two_factor"
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
- `204 No content` on success.
|
||||||
|
- `400 Bad request` if two factor authentication is not enabled for the specified user.
|
||||||
|
- `403 Forbidden` if not authenticated as an administrator.
|
||||||
|
- `404 User Not Found` if user cannot be found.
|
||||||
|
|
|
@ -275,7 +275,7 @@ This saves reviewers time and helps authors catch mistakes earlier.
|
||||||
Verify that the merge request meets all [contribution acceptance criteria](contributing/merge_request_workflow.md#contribution-acceptance-criteria).
|
Verify that the merge request meets all [contribution acceptance criteria](contributing/merge_request_workflow.md#contribution-acceptance-criteria).
|
||||||
|
|
||||||
If a merge request is too large, fixes more than one issue, or implements more
|
If a merge request is too large, fixes more than one issue, or implements more
|
||||||
than one feature, you should guide the author towards spltting the merge request
|
than one feature, you should guide the author towards splitting the merge request
|
||||||
into smaller merge requests.
|
into smaller merge requests.
|
||||||
|
|
||||||
When you are confident
|
When you are confident
|
||||||
|
@ -300,11 +300,18 @@ Because a maintainer's job only depends on their knowledge of the overall GitLab
|
||||||
codebase, and not that of any specific domain, they can review, approve, and merge
|
codebase, and not that of any specific domain, they can review, approve, and merge
|
||||||
merge requests from any team and in any product area.
|
merge requests from any team and in any product area.
|
||||||
|
|
||||||
If a merge request is too large, fixes more than one issue, or implements more
|
A maintainer should ask the author to make a merge request smaller if it is:
|
||||||
than one feature, the maintainer can ask the author to make the merge request
|
|
||||||
smaller. Request the previous reviewer, or a merge request coach to help guide
|
- Too large.
|
||||||
the author on how to split the merge request, and to review the resulting
|
- Fixes more than one issue.
|
||||||
changes.
|
- Implements more than one feature.
|
||||||
|
- Has a high complexity resulting in additional risk.
|
||||||
|
|
||||||
|
The maintainer, any of the
|
||||||
|
reviewers, or a merge request coach can step up to help the author to divide work
|
||||||
|
into smaller iterations, and guide the author on how to split the merge request.
|
||||||
|
The author may choose to request that the current maintainers and reviewers review the split MRs
|
||||||
|
or request a new group of maintainers and reviewers.
|
||||||
|
|
||||||
Maintainers do their best to also review the specifics of the chosen solution
|
Maintainers do their best to also review the specifics of the chosen solution
|
||||||
before merging, but as they are not necessarily [domain experts](#domain-experts), they may be poorly
|
before merging, but as they are not necessarily [domain experts](#domain-experts), they may be poorly
|
||||||
|
|
|
@ -1754,7 +1754,7 @@ Be sure to create a full database backup before attempting any changes.
|
||||||
#### Disable user two-factor authentication (2FA)
|
#### Disable user two-factor authentication (2FA)
|
||||||
|
|
||||||
Users with 2FA enabled can't sign in to GitLab. In that case, you must
|
Users with 2FA enabled can't sign in to GitLab. In that case, you must
|
||||||
[disable 2FA for everyone](../security/two_factor_authentication.md#disable-2fa-for-everyone),
|
[disable 2FA for everyone](../security/two_factor_authentication.md#for-all-users),
|
||||||
after which users must reactivate 2FA.
|
after which users must reactivate 2FA.
|
||||||
|
|
||||||
#### Reset CI/CD variables
|
#### Reset CI/CD variables
|
||||||
|
|
|
@ -81,14 +81,33 @@ The following are important notes about 2FA:
|
||||||
|
|
||||||
This action causes all subgroups with 2FA requirements to stop requiring that from their members.
|
This action causes all subgroups with 2FA requirements to stop requiring that from their members.
|
||||||
|
|
||||||
## Disable 2FA for everyone
|
## Disable 2FA
|
||||||
|
|
||||||
WARNING:
|
WARNING:
|
||||||
Disabling 2FA for everyone does not disable the [enforce 2FA for all users](#enforce-2fa-for-all-users)
|
Disabling 2FA for users does not disable the [enforce 2FA for all users](#enforce-2fa-for-all-users)
|
||||||
or [enforce 2FA for all users in a group](#enforce-2fa-for-all-users-in-a-group)
|
or [enforce 2FA for all users in a group](#enforce-2fa-for-all-users-in-a-group)
|
||||||
settings. You must also disable any enforced 2FA settings so users aren't asked to set up 2FA again
|
settings. You must also disable any enforced 2FA settings so users aren't asked to set up 2FA again
|
||||||
when they next sign in to GitLab.
|
when they next sign in to GitLab.
|
||||||
|
|
||||||
|
WARNING:
|
||||||
|
This is a permanent and irreversible action. Users must reactivate 2FA to use it again.
|
||||||
|
|
||||||
|
### For a single user
|
||||||
|
|
||||||
|
To disable 2FA for non-administrator users, we recommend using the [API endpoint](../api/users.md#disable-two-factor-authentication-administrator-only)
|
||||||
|
instead of the Rails console.
|
||||||
|
Using the [Rails console](../administration/operations/rails_console.md), 2FA for a single user can be disabled.
|
||||||
|
Connect to the Rails console and run:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
admin = User.find_by_username('<USERNAME>')
|
||||||
|
user_to_disable = User.find_by_username('<USERNAME>')
|
||||||
|
|
||||||
|
TwoFactor::DestroyService.new(admin, user: user_to_disable).execute
|
||||||
|
```
|
||||||
|
|
||||||
|
### For all users
|
||||||
|
|
||||||
There may be some special situations where you want to disable 2FA for everyone
|
There may be some special situations where you want to disable 2FA for everyone
|
||||||
even when forced 2FA is disabled. There is a Rake task for that:
|
even when forced 2FA is disabled. There is a Rake task for that:
|
||||||
|
|
||||||
|
@ -100,10 +119,6 @@ sudo gitlab-rake gitlab:two_factor:disable_for_all_users
|
||||||
sudo -u git -H bundle exec rake gitlab:two_factor:disable_for_all_users RAILS_ENV=production
|
sudo -u git -H bundle exec rake gitlab:two_factor:disable_for_all_users RAILS_ENV=production
|
||||||
```
|
```
|
||||||
|
|
||||||
WARNING:
|
|
||||||
This is a permanent and irreversible action. Users have to
|
|
||||||
reactivate 2FA from scratch if they want to use it again.
|
|
||||||
|
|
||||||
## 2FA for Git over SSH operations **(PREMIUM)**
|
## 2FA for Git over SSH operations **(PREMIUM)**
|
||||||
|
|
||||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/270554) in GitLab 13.7.
|
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/270554) in GitLab 13.7.
|
||||||
|
|
|
@ -325,6 +325,30 @@ module API
|
||||||
end
|
end
|
||||||
# rubocop: enable CodeReuse/ActiveRecord
|
# rubocop: enable CodeReuse/ActiveRecord
|
||||||
|
|
||||||
|
desc "Disable two factor authentication for a user. Available only for admins" do
|
||||||
|
detail 'This feature was added in GitLab 15.2'
|
||||||
|
success Entities::UserWithAdmin
|
||||||
|
end
|
||||||
|
params do
|
||||||
|
requires :id, type: Integer, desc: 'The ID of the user'
|
||||||
|
end
|
||||||
|
patch ":id/disable_two_factor", feature_category: :authentication_and_authorization do
|
||||||
|
authenticated_as_admin!
|
||||||
|
|
||||||
|
user = User.find_by_id(params[:id])
|
||||||
|
not_found!('User') unless user
|
||||||
|
|
||||||
|
forbidden!('Two-factor authentication for admins cannot be disabled via the API. Use the Rails console') if user.admin?
|
||||||
|
|
||||||
|
result = TwoFactor::DestroyService.new(current_user, user: user).execute
|
||||||
|
|
||||||
|
if result[:status] == :success
|
||||||
|
no_content!
|
||||||
|
else
|
||||||
|
bad_request!(result[:message])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
desc "Delete a user's identity. Available only for admins" do
|
desc "Delete a user's identity. Available only for admins" do
|
||||||
success Entities::UserWithAdmin
|
success Entities::UserWithAdmin
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,7 +10,6 @@ module Sidebars
|
||||||
|
|
||||||
add_item(metrics_dashboard_menu_item)
|
add_item(metrics_dashboard_menu_item)
|
||||||
add_item(logs_menu_item)
|
add_item(logs_menu_item)
|
||||||
add_item(tracing_menu_item)
|
|
||||||
add_item(error_tracking_menu_item)
|
add_item(error_tracking_menu_item)
|
||||||
add_item(alert_management_menu_item)
|
add_item(alert_management_menu_item)
|
||||||
add_item(incidents_menu_item)
|
add_item(incidents_menu_item)
|
||||||
|
@ -72,21 +71,6 @@ module Sidebars
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def tracing_menu_item
|
|
||||||
if !Feature.enabled?(:monitor_tracing, context.project) ||
|
|
||||||
!can?(context.current_user, :read_environment, context.project) ||
|
|
||||||
!can?(context.current_user, :admin_project, context.project)
|
|
||||||
return ::Sidebars::NilMenuItem.new(item_id: :tracing)
|
|
||||||
end
|
|
||||||
|
|
||||||
::Sidebars::MenuItem.new(
|
|
||||||
title: _('Tracing'),
|
|
||||||
link: project_tracing_path(context.project),
|
|
||||||
active_routes: { path: 'tracings#show' },
|
|
||||||
item_id: :tracing
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def error_tracking_menu_item
|
def error_tracking_menu_item
|
||||||
unless can?(context.current_user, :read_sentry_issue, context.project)
|
unless can?(context.current_user, :read_sentry_issue, context.project)
|
||||||
return ::Sidebars::NilMenuItem.new(item_id: :error_tracking)
|
return ::Sidebars::NilMenuItem.new(item_id: :error_tracking)
|
||||||
|
|
|
@ -146,7 +146,7 @@ module QA
|
||||||
issue_comments: issues.sum { |_k, v| v[:comments].length }
|
issue_comments: issues.sum { |_k, v| v[:comments].length }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
not_imported: {
|
diff: {
|
||||||
mrs: @mr_diff,
|
mrs: @mr_diff,
|
||||||
issues: @issue_diff
|
issues: @issue_diff
|
||||||
}
|
}
|
||||||
|
@ -256,11 +256,12 @@ module QA
|
||||||
count_msg = "Expected to contain same amount of #{type}s. Source: #{expected.length}, Target: #{actual.length}"
|
count_msg = "Expected to contain same amount of #{type}s. Source: #{expected.length}, Target: #{actual.length}"
|
||||||
expect(actual.length).to eq(expected.length), count_msg
|
expect(actual.length).to eq(expected.length), count_msg
|
||||||
|
|
||||||
missing_comments = verify_comments(type, actual, expected)
|
comment_diff = verify_comments(type, actual, expected)
|
||||||
|
|
||||||
{
|
{
|
||||||
"#{type}s": (expected.keys - actual.keys).map { |it| actual[it]&.slice(:title, :url) }.compact,
|
"missing_#{type}s": (expected.keys - actual.keys).map { |it| actual[it]&.slice(:title, :url) }.compact,
|
||||||
"#{type}_comments": missing_comments
|
"extra_#{type}s": (actual.keys - expected.keys).map { |it| expected[it]&.slice(:title, :url) }.compact,
|
||||||
|
"#{type}_comments": comment_diff
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -271,7 +272,7 @@ module QA
|
||||||
# @param [Hash] expected
|
# @param [Hash] expected
|
||||||
# @return [Hash]
|
# @return [Hash]
|
||||||
def verify_comments(type, actual, expected)
|
def verify_comments(type, actual, expected)
|
||||||
actual.each_with_object([]) do |(key, actual_item), missing_comments|
|
actual.each_with_object([]) do |(key, actual_item), diff|
|
||||||
expected_item = expected[key]
|
expected_item = expected[key]
|
||||||
title = actual_item[:title]
|
title = actual_item[:title]
|
||||||
msg = "expected #{type} with title '#{title}' to have"
|
msg = "expected #{type} with title '#{title}' to have"
|
||||||
|
@ -305,16 +306,18 @@ module QA
|
||||||
expect(actual_comments.length).to eq(expected_comments.length), comment_count_msg
|
expect(actual_comments.length).to eq(expected_comments.length), comment_count_msg
|
||||||
expect(actual_comments).to match_array(expected_comments)
|
expect(actual_comments).to match_array(expected_comments)
|
||||||
|
|
||||||
# Save missing comments
|
# Save comment diff
|
||||||
#
|
#
|
||||||
comment_diff = expected_comments - actual_comments
|
missing_comments = expected_comments - actual_comments
|
||||||
next if comment_diff.empty?
|
extra_comments = actual_comments - expected_comments
|
||||||
|
next if missing_comments.empty? && extra_comments.empty?
|
||||||
|
|
||||||
missing_comments << {
|
diff << {
|
||||||
title: title,
|
title: title,
|
||||||
target_url: actual_item[:url],
|
target_url: actual_item[:url],
|
||||||
source_url: expected_item[:url],
|
source_url: expected_item[:url],
|
||||||
missing_comments: comment_diff
|
missing_comments: missing_comments,
|
||||||
|
extra_comments: extra_comments
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -86,9 +86,11 @@ RSpec.describe Mutations::DesignManagement::Delete do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'runs no more than 29 queries' do
|
it 'runs no more than 30 queries' do
|
||||||
|
allow(Gitlab::Tracking).to receive(:event) # rubocop:disable RSpec/ExpectGitlabTracking
|
||||||
|
|
||||||
filenames.each(&:present?) # ignore setup
|
filenames.each(&:present?) # ignore setup
|
||||||
# Queries: as of 2021-07-22
|
# Queries: as of 2022-06-15
|
||||||
# -------------
|
# -------------
|
||||||
# 01. routing query
|
# 01. routing query
|
||||||
# 02. find project by id
|
# 02. find project by id
|
||||||
|
@ -106,20 +108,21 @@ RSpec.describe Mutations::DesignManagement::Delete do
|
||||||
# 15. current designs by filename and issue
|
# 15. current designs by filename and issue
|
||||||
# 16, 17 project.authorizations for user (same query as 5)
|
# 16, 17 project.authorizations for user (same query as 5)
|
||||||
# 18. find route by id and source_type
|
# 18. find route by id and source_type
|
||||||
|
# 19. find plan for standard context
|
||||||
# ------------- our queries are below:
|
# ------------- our queries are below:
|
||||||
# 19. start transaction 1
|
# 20. start transaction 1
|
||||||
# 20. start transaction 2
|
# 21. start transaction 2
|
||||||
# 21. find version by sha and issue
|
# 22. find version by sha and issue
|
||||||
# 22. exists version with sha and issue?
|
# 23. exists version with sha and issue?
|
||||||
# 23. leave transaction 2
|
# 24. leave transaction 2
|
||||||
# 24. create version with sha and issue
|
# 25. create version with sha and issue
|
||||||
# 25. create design-version links
|
# 26. create design-version links
|
||||||
# 26. validate version.actions.present?
|
# 27. validate version.actions.present?
|
||||||
# 27. validate version.issue.present?
|
# 28. validate version.issue.present?
|
||||||
# 28. validate version.sha is unique
|
# 29. validate version.sha is unique
|
||||||
# 29. leave transaction 1
|
# 30. leave transaction 1
|
||||||
#
|
#
|
||||||
expect { run_mutation }.not_to exceed_query_limit(29)
|
expect { run_mutation }.not_to exceed_query_limit(30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -82,20 +82,6 @@ RSpec.describe Sidebars::Projects::Menus::MonitorMenu do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'Tracing' do
|
|
||||||
let(:item_id) { :tracing }
|
|
||||||
|
|
||||||
it_behaves_like 'access rights checks'
|
|
||||||
|
|
||||||
context 'when feature disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(monitor_tracing: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
specify { is_expected.to be_nil }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'Error Tracking' do
|
describe 'Error Tracking' do
|
||||||
let(:item_id) { :error_tracking }
|
let(:item_id) { :error_tracking }
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@ RSpec.describe API::Users do
|
||||||
let(:deactivated_user) { create(:user, state: 'deactivated') }
|
let(:deactivated_user) { create(:user, state: 'deactivated') }
|
||||||
let(:banned_user) { create(:user, :banned) }
|
let(:banned_user) { create(:user, :banned) }
|
||||||
let(:internal_user) { create(:user, :bot) }
|
let(:internal_user) { create(:user, :bot) }
|
||||||
|
let(:user_with_2fa) { create(:user, :two_factor_via_otp) }
|
||||||
|
let(:admin_with_2fa) { create(:admin, :two_factor_via_otp) }
|
||||||
|
|
||||||
context 'admin notes' do
|
context 'admin notes' do
|
||||||
let_it_be(:admin) { create(:admin, note: '2019-10-06 | 2FA added | user requested | www.gitlab.com') }
|
let_it_be(:admin) { create(:admin, note: '2019-10-06 | 2FA added | user requested | www.gitlab.com') }
|
||||||
|
@ -81,6 +83,79 @@ RSpec.describe API::Users do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "PATCH /users/:id/disable_two_factor" do
|
||||||
|
context "when current user is an admin" do
|
||||||
|
it "returns a 204 when 2FA is disabled for the target user" do
|
||||||
|
expect do
|
||||||
|
patch api("/users/#{user_with_2fa.id}/disable_two_factor", admin)
|
||||||
|
end.to change { user_with_2fa.reload.two_factor_enabled? }
|
||||||
|
.from(true)
|
||||||
|
.to(false)
|
||||||
|
expect(response).to have_gitlab_http_status(:no_content)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "uses TwoFactor Destroy Service" do
|
||||||
|
destroy_service = instance_double(TwoFactor::DestroyService, execute: nil)
|
||||||
|
expect(TwoFactor::DestroyService).to receive(:new)
|
||||||
|
.with(admin, user: user_with_2fa)
|
||||||
|
.and_return(destroy_service)
|
||||||
|
expect(destroy_service).to receive(:execute)
|
||||||
|
|
||||||
|
patch api("/users/#{user_with_2fa.id}/disable_two_factor", admin)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a 400 if 2FA is not enabled for the target user" do
|
||||||
|
expect(TwoFactor::DestroyService).to receive(:new).and_call_original
|
||||||
|
|
||||||
|
expect do
|
||||||
|
patch api("/users/#{user.id}/disable_two_factor", admin)
|
||||||
|
end.not_to change { user.reload.two_factor_enabled? }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
|
expect(json_response['message']).to eq("400 Bad request - Two-factor authentication is not enabled for this user")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a 403 if the target user is an admin" do
|
||||||
|
expect(TwoFactor::DestroyService).to receive(:new).never
|
||||||
|
|
||||||
|
expect do
|
||||||
|
patch api("/users/#{admin_with_2fa.id}/disable_two_factor", admin)
|
||||||
|
end.not_to change { admin_with_2fa.reload.two_factor_enabled? }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:forbidden)
|
||||||
|
expect(json_response['message']).to eq("403 Forbidden - Two-factor authentication for admins cannot be disabled via the API. Use the Rails console")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns a 404 if the target user cannot be found" do
|
||||||
|
expect(TwoFactor::DestroyService).to receive(:new).never
|
||||||
|
|
||||||
|
patch api("/users/#{non_existing_record_id}/disable_two_factor", admin)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:not_found)
|
||||||
|
expect(json_response['message']).to eq("404 User Not Found")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when current user is not an admin" do
|
||||||
|
it "returns a 403" do
|
||||||
|
expect do
|
||||||
|
patch api("/users/#{user_with_2fa.id}/disable_two_factor", user)
|
||||||
|
end.not_to change { user_with_2fa.reload.two_factor_enabled? }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:forbidden)
|
||||||
|
expect(json_response['message']).to eq("403 Forbidden")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when unauthenticated" do
|
||||||
|
it "returns a 401" do
|
||||||
|
patch api("/users/#{user_with_2fa.id}/disable_two_factor")
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'GET /users/' do
|
describe 'GET /users/' do
|
||||||
context 'when unauthenticated' do
|
context 'when unauthenticated' do
|
||||||
it "does not contain certain fields" do
|
it "does not contain certain fields" do
|
||||||
|
|
|
@ -379,10 +379,14 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'design events' do
|
describe 'design events', :snowplow do
|
||||||
let_it_be(:design) { create(:design, project: project) }
|
let_it_be(:design) { create(:design, project: project) }
|
||||||
let_it_be(:author) { user }
|
let_it_be(:author) { user }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(Gitlab::Tracking).to receive(:event) # rubocop:disable RSpec/ExpectGitlabTracking
|
||||||
|
end
|
||||||
|
|
||||||
describe '#save_designs' do
|
describe '#save_designs' do
|
||||||
let_it_be(:updated) { create_list(:design, 5) }
|
let_it_be(:updated) { create_list(:design, 5) }
|
||||||
let_it_be(:created) { create_list(:design, 3) }
|
let_it_be(:created) { create_list(:design, 3) }
|
||||||
|
@ -411,6 +415,44 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
|
||||||
it_behaves_like "it records the event in the event counter" do
|
it_behaves_like "it records the event in the event counter" do
|
||||||
let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION }
|
let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'records correct create payload with Snowplow event' do
|
||||||
|
service.save_designs(author, create: [design])
|
||||||
|
|
||||||
|
expect_snowplow_event(
|
||||||
|
category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
|
||||||
|
action: 'create',
|
||||||
|
namespace: design.project.namespace,
|
||||||
|
user: author,
|
||||||
|
project: design.project,
|
||||||
|
label: 'design_users'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'records correct update payload with Snowplow event' do
|
||||||
|
service.save_designs(author, update: [design])
|
||||||
|
|
||||||
|
expect_snowplow_event(
|
||||||
|
category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
|
||||||
|
action: 'update',
|
||||||
|
namespace: design.project.namespace,
|
||||||
|
user: author,
|
||||||
|
project: design.project,
|
||||||
|
label: 'design_users'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when FF is disabled' do
|
||||||
|
before do
|
||||||
|
stub_feature_flags(route_hll_to_snowplow_phase2: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'doesnt emit snowwplow events', :snowplow do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect_no_snowplow_event
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#destroy_designs' do
|
describe '#destroy_designs' do
|
||||||
|
@ -434,6 +476,31 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
|
||||||
it_behaves_like "it records the event in the event counter" do
|
it_behaves_like "it records the event in the event counter" do
|
||||||
let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION }
|
let(:event_action) { Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'records correct payload with Snowplow event' do
|
||||||
|
service.destroy_designs([design], author)
|
||||||
|
|
||||||
|
expect_snowplow_event(
|
||||||
|
category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
|
||||||
|
action: 'destroy',
|
||||||
|
namespace: design.project.namespace,
|
||||||
|
user: author,
|
||||||
|
project: design.project,
|
||||||
|
label: 'design_users'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when FF is disabled' do
|
||||||
|
before do
|
||||||
|
stub_feature_flags(route_hll_to_snowplow_phase2: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'doesnt emit snowwplow events', :snowplow do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect_no_snowplow_event
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -228,12 +228,14 @@ RSpec.describe Projects::Prometheus::Alerts::NotifyService do
|
||||||
context 'when payload exceeds max amount of processable alerts' do
|
context 'when payload exceeds max amount of processable alerts' do
|
||||||
# We are defining 2 alerts in payload_raw above
|
# We are defining 2 alerts in payload_raw above
|
||||||
let(:max_alerts) { 1 }
|
let(:max_alerts) { 1 }
|
||||||
|
let(:fingerprint) { prometheus_alert_payload_fingerprint(alert_resolved) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_const("#{described_class}::PROCESS_MAX_ALERTS", max_alerts)
|
stub_const("#{described_class}::PROCESS_MAX_ALERTS", max_alerts)
|
||||||
|
|
||||||
create(:prometheus_integration, project: project)
|
create(:prometheus_integration, project: project)
|
||||||
create(:project_alerting_setting, project: project, token: token)
|
create(:project_alerting_setting, project: project, token: token)
|
||||||
|
create(:alert_management_alert, project: project, fingerprint: fingerprint)
|
||||||
|
|
||||||
allow(Gitlab::AppLogger).to receive(:warn)
|
allow(Gitlab::AppLogger).to receive(:warn)
|
||||||
end
|
end
|
||||||
|
|
|
@ -267,6 +267,13 @@ module PrometheusHelpers
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def prometheus_alert_payload_fingerprint(prometheus_alert)
|
||||||
|
# timestamp is hard-coded in #prometheus_map_alert_payload
|
||||||
|
fingerprint = "#{prometheus_alert.id}/2018-09-24T08:57:31.095725221Z"
|
||||||
|
|
||||||
|
Gitlab::AlertManagement::Fingerprint.generate(fingerprint)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def prometheus_map_alert_payload(status, alert)
|
def prometheus_map_alert_payload(status, alert)
|
||||||
|
|
|
@ -84,7 +84,6 @@ RSpec.shared_context 'project navbar structure' do
|
||||||
nav_sub_items: [
|
nav_sub_items: [
|
||||||
_('Metrics'),
|
_('Metrics'),
|
||||||
_('Logs'),
|
_('Logs'),
|
||||||
_('Tracing'),
|
|
||||||
_('Error Tracking'),
|
_('Error Tracking'),
|
||||||
_('Alerts'),
|
_('Alerts'),
|
||||||
_('Incidents'),
|
_('Incidents'),
|
||||||
|
|
|
@ -56,7 +56,7 @@ RSpec.shared_examples 'processes recovery alert' do
|
||||||
context 'seen for the first time' do
|
context 'seen for the first time' do
|
||||||
let(:alert) { AlertManagement::Alert.last }
|
let(:alert) { AlertManagement::Alert.last }
|
||||||
|
|
||||||
include_examples 'processes never-before-seen recovery alert'
|
it_behaves_like 'alerts service responds with an error and takes no actions', :bad_request
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'for an existing alert with the same fingerprint' do
|
context 'for an existing alert with the same fingerprint' do
|
||||||
|
@ -107,7 +107,7 @@ RSpec.shared_examples 'processes recovery alert' do
|
||||||
context 'which is resolved' do
|
context 'which is resolved' do
|
||||||
let_it_be(:alert) { create(:alert_management_alert, :resolved, project: project, fingerprint: gitlab_fingerprint, monitoring_tool: source) }
|
let_it_be(:alert) { create(:alert_management_alert, :resolved, project: project, fingerprint: gitlab_fingerprint, monitoring_tool: source) }
|
||||||
|
|
||||||
include_examples 'processes never-before-seen recovery alert'
|
it_behaves_like 'alerts service responds with an error and takes no actions', :bad_request
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -68,14 +68,14 @@ RSpec.shared_examples 'processes one firing and one resolved prometheus alerts'
|
||||||
expect(Gitlab::AppLogger).not_to receive(:warn)
|
expect(Gitlab::AppLogger).not_to receive(:warn)
|
||||||
|
|
||||||
expect { subject }
|
expect { subject }
|
||||||
.to change(AlertManagement::Alert, :count).by(2)
|
.to change(AlertManagement::Alert, :count).by(1)
|
||||||
.and change(Note, :count).by(4)
|
.and change(Note, :count).by(1)
|
||||||
|
|
||||||
expect(subject).to be_success
|
expect(subject).to be_success
|
||||||
expect(subject.payload[:alerts]).to all(be_a_kind_of(AlertManagement::Alert))
|
expect(subject.payload[:alerts]).to all(be_a_kind_of(AlertManagement::Alert))
|
||||||
expect(subject.payload[:alerts].size).to eq(2)
|
expect(subject.payload[:alerts].size).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'processes incident issues'
|
it_behaves_like 'processes incident issues'
|
||||||
it_behaves_like 'sends alert notification emails', count: 2
|
it_behaves_like 'sends alert notification emails'
|
||||||
end
|
end
|
||||||
|
|
|
@ -437,32 +437,6 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'Tracing' do
|
|
||||||
it 'has a link to the tracing page' do
|
|
||||||
render
|
|
||||||
|
|
||||||
expect(rendered).to have_link('Tracing', href: project_tracing_path(project))
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'without project.tracing_external_url' do
|
|
||||||
it 'has a link to the tracing page' do
|
|
||||||
render
|
|
||||||
|
|
||||||
expect(rendered).to have_link('Tracing', href: project_tracing_path(project))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'when the user does not have access' do
|
|
||||||
let(:user) { nil }
|
|
||||||
|
|
||||||
it 'does not have a link to the tracing page' do
|
|
||||||
render
|
|
||||||
|
|
||||||
expect(rendered).not_to have_text 'Tracing'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'Error Tracking' do
|
describe 'Error Tracking' do
|
||||||
it 'has a link to the error tracking page' do
|
it 'has a link to the error tracking page' do
|
||||||
render
|
render
|
||||||
|
|
Loading…
Reference in New Issue