Commit 2c4e0ce5 authored by Serena Fang's avatar Serena Fang

Shadow ban UI

Add shadow ban user state and UI

Add config admin route

Add shadow ban admin user path

Run rake translate

Add shadow banned user state

Add specs and UI touches

Remove line about hiding comments and issues

Add shadow banned users tab

Run bin/rake translate

Add specs for shadow ban user

Model and controller specs, factory

Add specs for unsuccessful shadow ban

Add specs for unsuccessful shadow ban

Add application log when user shadow banned

Changelog: added

Add changelog entry

Add shadow_ban_user_feature_flag

Add feature flag, flag yml,
modify code and tests to check for flag

Check if shadow_ban_user_feature_flag enabled

Add un shadow ban option

Add route for un shadow ban user

Add docs page for shadow banning

Add docs for shadow banning users

Change un shadow ban to unban

Change to unban

Move shadow ban docs to block docs

Combine banning and blocking docs

Move shadow ban docs to block docs

Combine banning and blocking docs

Move activate deactivate into user moderation

Combine block, ban, and deactivate into one file

Rename blocking and unblocking to user moderation

Now that this page has blocking, banning,
and deactivating, rename to user moderation

Remove activating deactivating users page

All included in user moderation page

Apply MR review suggestion

Apply MR review suggestion

Fix broken anchors

Fix anchor, run translation

Fix anchor links

Fix anchor links to removed pages

Apply MR review suggestions

Check feature available helpers

Rename shadow ban to ban

Modify references to shadow ban,
WIP, clicking shadow ban just blocks the user
rename shadow ban files to ban

Remove references to shadow ban

Remove shadow ban from docs and changelog

Link to ban user docs

Add link to banned users docs

Move confirm text to helper

Move confirm text to helper
parent cbd76096
...@@ -7,6 +7,7 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -7,6 +7,7 @@ class Admin::UsersController < Admin::ApplicationController
before_action :user, except: [:index, :cohorts, :new, :create] before_action :user, except: [:index, :cohorts, :new, :create]
before_action :check_impersonation_availability, only: :impersonate before_action :check_impersonation_availability, only: :impersonate
before_action :ensure_destroy_prerequisites_met, only: [:destroy] before_action :ensure_destroy_prerequisites_met, only: [:destroy]
before_action :check_ban_user_feature_flag, only: [:ban]
feature_category :users feature_category :users
...@@ -130,6 +131,24 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -130,6 +131,24 @@ class Admin::UsersController < Admin::ApplicationController
end end
end end
def ban
result = Users::BanService.new(current_user).execute(user)
if result[:status] == :success
redirect_back_or_admin_user(notice: _("Successfully banned"))
else
redirect_back_or_admin_user(alert: _("Error occurred. User was not banned"))
end
end
def unban
if update_user { |user| user.activate }
redirect_back_or_admin_user(notice: _("Successfully unbanned"))
else
redirect_back_or_admin_user(alert: _("Error occurred. User was not unbanned"))
end
end
def unlock def unlock
if update_user { |user| user.unlock_access! } if update_user { |user| user.unlock_access! }
redirect_back_or_admin_user(alert: _("Successfully unlocked")) redirect_back_or_admin_user(alert: _("Successfully unlocked"))
...@@ -325,6 +344,10 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -325,6 +344,10 @@ class Admin::UsersController < Admin::ApplicationController
access_denied! unless Gitlab.config.gitlab.impersonation_enabled access_denied! unless Gitlab.config.gitlab.impersonation_enabled
end end
def check_ban_user_feature_flag
access_denied! unless Feature.enabled?(:ban_user_feature_flag)
end
def log_impersonation_event def log_impersonation_event
Gitlab::AppLogger.info(_("User %{current_user_username} has started impersonating %{username}") % { current_user_username: current_user.username, username: user.username }) Gitlab::AppLogger.info(_("User %{current_user_username} has started impersonating %{username}") % { current_user_username: current_user.username, username: user.username })
end end
......
...@@ -162,6 +162,49 @@ module UsersHelper ...@@ -162,6 +162,49 @@ module UsersHelper
header + list header + list
end end
def user_ban_data(user)
{
path: ban_admin_user_path(user),
method: 'put',
modal_attributes: {
title: s_('AdminUsers|Ban user %{username}?') % { username: sanitize_name(user.name) },
message: s_('AdminUsers|You can always unban their account. Their data remains intact.'),
okVariant: 'warning',
okTitle: s_('AdminUsers|Ban')
}.to_json
}
end
def user_unban_data(user)
{
path: unban_admin_user_path(user),
method: 'put',
modal_attributes: {
title: s_('AdminUsers|Unban %{username}?') % { username: sanitize_name(user.name) },
message: s_('AdminUsers|You can always ban their account again if needed.'),
okVariant: 'info',
okTitle: s_('AdminUsers|Unban')
}.to_json
}
end
def user_ban_effects
header = tag.p s_('AdminUsers|Banning the user has the following effects:')
list = tag.ul do
concat tag.li s_('AdminUsers|User will be blocked')
end
link_start = '<a href="%{url}" target="_blank">'.html_safe % { url: help_page_path("user/admin_area/user_moderation", anchor: "banning-a-user") }
info = tag.p s_('AdminUsers|Learn more about %{link_start}banned users.%{link_end}').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
header + list + info
end
def ban_feature_available?
Feature.enabled?(:ban_user_feature_flag)
end
def user_deactivation_data(user, message) def user_deactivation_data(user, message)
{ {
path: deactivate_admin_user_path(user), path: deactivate_admin_user_path(user),
...@@ -235,6 +278,9 @@ module UsersHelper ...@@ -235,6 +278,9 @@ module UsersHelper
pending_approval_badge = { text: s_('AdminUsers|Pending approval'), variant: 'info' } pending_approval_badge = { text: s_('AdminUsers|Pending approval'), variant: 'info' }
return pending_approval_badge if user.blocked_pending_approval? return pending_approval_badge if user.blocked_pending_approval?
banned_badge = { text: s_('AdminUsers|Banned'), variant: 'danger' }
return banned_badge if user.banned?
{ text: s_('AdminUsers|Blocked'), variant: 'danger' } { text: s_('AdminUsers|Blocked'), variant: 'danger' }
end end
......
...@@ -326,6 +326,7 @@ class User < ApplicationRecord ...@@ -326,6 +326,7 @@ class User < ApplicationRecord
transition deactivated: :blocked transition deactivated: :blocked
transition ldap_blocked: :blocked transition ldap_blocked: :blocked
transition blocked_pending_approval: :blocked transition blocked_pending_approval: :blocked
transition banned: :blocked
end end
event :ldap_block do event :ldap_block do
...@@ -338,19 +339,24 @@ class User < ApplicationRecord ...@@ -338,19 +339,24 @@ class User < ApplicationRecord
transition blocked: :active transition blocked: :active
transition ldap_blocked: :active transition ldap_blocked: :active
transition blocked_pending_approval: :active transition blocked_pending_approval: :active
transition banned: :active
end end
event :block_pending_approval do event :block_pending_approval do
transition active: :blocked_pending_approval transition active: :blocked_pending_approval
end end
event :ban do
transition active: :banned
end
event :deactivate do event :deactivate do
# Any additional changes to this event should be also # Any additional changes to this event should be also
# reflected in app/workers/users/deactivate_dormant_users_worker.rb # reflected in app/workers/users/deactivate_dormant_users_worker.rb
transition active: :deactivated transition active: :deactivated
end end
state :blocked, :ldap_blocked, :blocked_pending_approval do state :blocked, :ldap_blocked, :blocked_pending_approval, :banned do
def blocked? def blocked?
true true
end end
...@@ -375,8 +381,9 @@ class User < ApplicationRecord ...@@ -375,8 +381,9 @@ class User < ApplicationRecord
# Scopes # Scopes
scope :admins, -> { where(admin: true) } scope :admins, -> { where(admin: true) }
scope :instance_access_request_approvers_to_be_notified, -> { admins.active.order_recent_sign_in.limit(INSTANCE_ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT) } scope :instance_access_request_approvers_to_be_notified, -> { admins.active.order_recent_sign_in.limit(INSTANCE_ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT) }
scope :blocked, -> { with_states(:blocked, :ldap_blocked) } scope :blocked, -> { with_states(:blocked, :ldap_blocked, :banned) }
scope :blocked_pending_approval, -> { with_states(:blocked_pending_approval) } scope :blocked_pending_approval, -> { with_states(:blocked_pending_approval) }
scope :banned, -> { with_states(:banned) }
scope :external, -> { where(external: true) } scope :external, -> { where(external: true) }
scope :non_external, -> { where(external: false) } scope :non_external, -> { where(external: false) }
scope :confirmed, -> { where.not(confirmed_at: nil) } scope :confirmed, -> { where.not(confirmed_at: nil) }
...@@ -598,6 +605,8 @@ class User < ApplicationRecord ...@@ -598,6 +605,8 @@ class User < ApplicationRecord
blocked blocked
when 'blocked_pending_approval' when 'blocked_pending_approval'
blocked_pending_approval blocked_pending_approval
when 'banned'
banned
when 'two_factor_disabled' when 'two_factor_disabled'
without_two_factor without_two_factor
when 'two_factor_enabled' when 'two_factor_enabled'
......
# frozen_string_literal: true
module Users
class BanService < BaseService
def initialize(current_user)
@current_user = current_user
end
def execute(user)
if user.ban
log_event(user)
success
else
messages = user.errors.full_messages
error(messages.uniq.join('. '))
end
end
def log_event(user)
Gitlab::AppLogger.info(message: "User banned", user: "#{user.username}", email: "#{user.email}", banned_by: "#{current_user.username}", ip_address: "#{current_user.current_sign_in_ip}")
end
end
end
- if ban_feature_available?
.card.border-warning
.card-header.bg-warning.text-white
= s_('AdminUsers|Ban user')
.card-body
= user_ban_effects
%br
%button.btn.gl-button.btn-warning.js-confirm-modal-button{ data: user_ban_data(user) }
= s_('AdminUsers|Ban user')
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
- if @user.blocked_pending_approval? - if @user.blocked_pending_approval?
%span.cred %span.cred
= s_('AdminUsers|(Pending approval)') = s_('AdminUsers|(Pending approval)')
- elsif @user.banned?
%span.cred
= s_('AdminUsers|(Banned)')
- elsif @user.blocked? - elsif @user.blocked?
%span.cred %span.cred
= s_('AdminUsers|(Blocked)') = s_('AdminUsers|(Blocked)')
......
...@@ -28,6 +28,11 @@ ...@@ -28,6 +28,11 @@
= link_to admin_users_path(filter: "blocked") do = link_to admin_users_path(filter: "blocked") do
= s_('AdminUsers|Blocked') = s_('AdminUsers|Blocked')
%small.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm= limited_counter_with_delimiter(User.blocked) %small.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm= limited_counter_with_delimiter(User.blocked)
- if ban_feature_available?
= nav_link(html_options: { class: active_when(params[:filter] == 'banned') }) do
= link_to admin_users_path(filter: "banned") do
= s_('AdminUsers|Banned')
%small.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm= limited_counter_with_delimiter(User.banned)
= nav_link(html_options: { class: "#{active_when(params[:filter] == 'blocked_pending_approval')} filter-blocked-pending-approval" }) do = nav_link(html_options: { class: "#{active_when(params[:filter] == 'blocked_pending_approval')} filter-blocked-pending-approval" }) do
= link_to admin_users_path(filter: "blocked_pending_approval"), data: { qa_selector: 'pending_approval_tab' } do = link_to admin_users_path(filter: "blocked_pending_approval"), data: { qa_selector: 'pending_approval_tab' } do
= s_('AdminUsers|Pending approval') = s_('AdminUsers|Pending approval')
......
...@@ -176,6 +176,20 @@ ...@@ -176,6 +176,20 @@
- if @user.blocked_pending_approval? - if @user.blocked_pending_approval?
= render 'admin/users/approve_user', user: @user = render 'admin/users/approve_user', user: @user
= render 'admin/users/reject_pending_user', user: @user = render 'admin/users/reject_pending_user', user: @user
- elsif @user.banned?
.gl-card.border-info.gl-mb-5
.gl-card-header.gl-bg-blue-500.gl-text-white
= _('This user is banned')
.gl-card-body
%p= _('A banned user cannot:')
%ul
%li= _('Log in')
%li= _('Access Git repositories')
- link_start = '<a href="%{url}" target="_blank">'.html_safe % { url: help_page_path("user/admin_area/user_moderation", anchor: "banning-a-user") }
= s_('AdminUsers|Learn more about %{link_start}banned users.%{link_end}').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
%p
%button.btn.gl-button.btn-info.js-confirm-modal-button{ data: user_unban_data(@user) }
= s_('AdminUsers|Unban user')
- else - else
.gl-card.border-info.gl-mb-5 .gl-card.border-info.gl-mb-5
.gl-card-header.gl-bg-blue-500.gl-text-white .gl-card-header.gl-bg-blue-500.gl-text-white
...@@ -190,6 +204,7 @@ ...@@ -190,6 +204,7 @@
= s_('AdminUsers|Unblock user') = s_('AdminUsers|Unblock user')
- elsif !@user.internal? - elsif !@user.internal?
= render 'admin/users/block_user', user: @user = render 'admin/users/block_user', user: @user
= render 'admin/users/ban_user', user: @user
- if @user.access_locked? - if @user.access_locked?
.card.border-info.gl-mb-5 .card.border-info.gl-mb-5
......
---
title: Ban user state and UI
merge_request: 61292
author:
type: added
---
name: ban_user_feature_flag
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61292
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/330667
milestone: '13.12'
type: development
group: group::access
default_enabled: false
...@@ -21,6 +21,8 @@ namespace :admin do ...@@ -21,6 +21,8 @@ namespace :admin do
get :keys get :keys
put :block put :block
put :unblock put :unblock
put :ban
put :unban
put :deactivate put :deactivate
put :activate put :activate
put :unlock put :unlock
......
...@@ -48,8 +48,8 @@ using [Seat Link](#seat-link). ...@@ -48,8 +48,8 @@ using [Seat Link](#seat-link).
A _billable user_ counts against the number of subscription seats. Every user is considered a A _billable user_ counts against the number of subscription seats. Every user is considered a
billable user, with the following exceptions: billable user, with the following exceptions:
- [Deactivated users](../../user/admin_area/activating_deactivating_users.md#deactivating-a-user) and - [Deactivated users](../../user/admin_area/user_moderation.md#deactivating-a-user) and
[blocked users](../../user/admin_area/blocking_unblocking_users.md) don't count as billable users in the current subscription. When they are either deactivated or blocked they release a _billable user_ seat. However, they may [blocked users](../../user/admin_area/user_moderation.md#blocking-a-user) don't count as billable users in the current subscription. When they are either deactivated or blocked they release a _billable user_ seat. However, they may
count toward overages in the subscribed seat count. count toward overages in the subscribed seat count.
- Users who are [pending approval](../../user/admin_area/approving_users.md). - Users who are [pending approval](../../user/admin_area/approving_users.md).
- Members with Guest permissions on an Ultimate subscription. - Members with Guest permissions on an Ultimate subscription.
...@@ -183,7 +183,7 @@ Starting 30 days before a subscription expires, GitLab notifies administrators o ...@@ -183,7 +183,7 @@ Starting 30 days before a subscription expires, GitLab notifies administrators o
We recommend following these steps during renewal: We recommend following these steps during renewal:
1. Prune any inactive or unwanted users by [blocking them](../../user/admin_area/blocking_unblocking_users.md#blocking-a-user). 1. Prune any inactive or unwanted users by [blocking them](../../user/admin_area/user_moderation.md#blocking-a-user).
1. Determine if you have a need for user growth in the upcoming subscription. 1. Determine if you have a need for user growth in the upcoming subscription.
1. Log in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in) and select the **Renew** button beneath your existing subscription. 1. Log in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in) and select the **Renew** button beneath your existing subscription.
......
...@@ -21,7 +21,7 @@ When a user registers for an account while this setting is enabled: ...@@ -21,7 +21,7 @@ When a user registers for an account while this setting is enabled:
A user pending approval: A user pending approval:
- Is functionally identical to a [blocked](blocking_unblocking_users.md) user. - Is functionally identical to a [blocked](user_moderation.md#blocking-a-user) user.
- Cannot sign in. - Cannot sign in.
- Cannot access Git repositories or the GitLab API. - Cannot access Git repositories or the GitLab API.
- Does not receive any notifications from GitLab. - Does not receive any notifications from GitLab.
......
---
stage: Manage
group: Access
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
type: howto
---
# Blocking and unblocking users
GitLab administrators block and unblock users.
## Blocking a user
In order to completely prevent access of a user to the GitLab instance, administrators can choose to
block the user.
Users can be blocked [via an abuse report](review_abuse_reports.md#blocking-users),
or directly from the Admin Area. To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Select a user.
1. Under the **Account** tab, click **Block user**.
A blocked user:
- Cannot log in.
- Cannot access Git repositories or the API.
- Does not receive any notifications from GitLab.
- Cannot use [slash commands](../../integration/slash_commands.md).
Personal projects, and group and user history of the blocked user are left intact.
Users can also be blocked using the [GitLab API](../../api/users.md#block-user).
NOTE:
A blocked user does not consume a [seat](../../subscriptions/self_managed/index.md#billable-users).
## Unblocking a user
A blocked user can be unblocked from the Admin Area. To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Click on the **Blocked** tab.
1. Select a user.
1. Under the **Account** tab, click **Unblock user**.
Users can also be unblocked using the [GitLab API](../../api/users.md#unblock-user).
NOTE:
Unblocking a user changes the user's state to active and consumes a
[seat](../../subscriptions/self_managed/index.md#billable-users).
...@@ -117,8 +117,8 @@ To list users matching a specific criteria, click on one of the following tabs o ...@@ -117,8 +117,8 @@ To list users matching a specific criteria, click on one of the following tabs o
- **2FA Enabled** - **2FA Enabled**
- **2FA Disabled** - **2FA Disabled**
- **External** - **External**
- **[Blocked](blocking_unblocking_users.md)** - **[Blocked](user_moderation.md#blocking-a-user)**
- **[Deactivated](activating_deactivating_users.md)** - **[Deactivated](user_moderation.md#deactivating-a-user)**
- **Without projects** - **Without projects**
For each user, the following are listed: For each user, the following are listed:
......
...@@ -5,18 +5,67 @@ info: To determine the technical writer assigned to the Stage/Group associated w ...@@ -5,18 +5,67 @@ info: To determine the technical writer assigned to the Stage/Group associated w
type: howto type: howto
--- ---
# Activating and deactivating users # User moderation
GitLab administrators can moderate user access by blocking, banning, or deactivating users.
## Blocking and unblocking users
GitLab administrators can block and unblock users.
### Blocking a user
In order to completely prevent access of a user to the GitLab instance,
administrators can choose to block the user.
Users can be blocked [via an abuse report](review_abuse_reports.md#blocking-users),
or directly from the Admin Area. To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Select a user.
1. Under the **Account** tab, click **Block user**.
A blocked user:
- Cannot log in.
- Cannot access Git repositories or the API.
- Does not receive any notifications from GitLab.
- Cannot use [slash commands](../../integration/slash_commands.md).
Personal projects, and group and user history of the blocked user are left intact.
Users can also be blocked using the [GitLab API](../../api/users.md#block-user).
NOTE:
A blocked user does not consume a [seat](../../subscriptions/self_managed/index.md#billable-users).
### Unblocking a user
A blocked user can be unblocked from the Admin Area. To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Click on the **Blocked** tab.
1. Select a user.
1. Under the **Account** tab, click **Unblock user**.
Users can also be unblocked using the [GitLab API](../../api/users.md#unblock-user).
NOTE:
Unblocking a user changes the user's state to active and consumes a
[seat](../../subscriptions/self_managed/index.md#billable-users).
## Activating and deactivating users
GitLab administrators can deactivate and activate users. GitLab administrators can deactivate and activate users.
## Deactivating a user ### Deactivating a user
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22257) in GitLab 12.4. > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22257) in GitLab 12.4.
In order to temporarily prevent access by a GitLab user that has no recent activity, administrators In order to temporarily prevent access by a GitLab user that has no recent activity,
can choose to deactivate the user. administrators can choose to deactivate the user.
Deactivating a user is functionally identical to [blocking a user](blocking_unblocking_users.md), Deactivating a user is functionally identical to [blocking a user](#blocking-and-unblocking-users),
with the following differences: with the following differences:
- It does not prohibit the user from logging back in via the UI. - It does not prohibit the user from logging back in via the UI.
...@@ -28,7 +77,7 @@ A deactivated user: ...@@ -28,7 +77,7 @@ A deactivated user:
- Will not receive any notifications from GitLab. - Will not receive any notifications from GitLab.
- Will not be able to use [slash commands](../../integration/slash_commands.md). - Will not be able to use [slash commands](../../integration/slash_commands.md).
Personal projects, and group and user history of the deactivated user will be left intact. Personal projects, and group and user history of the deactivated user are left intact.
A user can be deactivated from the Admin Area. To do this: A user can be deactivated from the Admin Area. To do this:
...@@ -46,7 +95,7 @@ Users can also be deactivated using the [GitLab API](../../api/users.md#deactiva ...@@ -46,7 +95,7 @@ Users can also be deactivated using the [GitLab API](../../api/users.md#deactiva
NOTE: NOTE:
A deactivated user does not consume a [seat](../../subscriptions/self_managed/index.md#billable-users). A deactivated user does not consume a [seat](../../subscriptions/self_managed/index.md#billable-users).
## Activating a user ### Activating a user
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22257) in GitLab 12.4. > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22257) in GitLab 12.4.
...@@ -67,3 +116,49 @@ Activating a user changes the user's state to active and consumes a ...@@ -67,3 +116,49 @@ Activating a user changes the user's state to active and consumes a
NOTE: NOTE:
A deactivated user can also activate their account themselves by logging back in via the UI. A deactivated user can also activate their account themselves by logging back in via the UI.
## Banning and unbanning users
GitLab administrators can ban users.
NOTE:
This feature is behind a feature flag that is disabled by default. GitLab administrators
with access to the GitLab Rails console can [enable](../../administration/feature_flags.md)
this feature for your GitLab instance.
### Banning a user
In order to completely block a user and hide the user's comments and issues from other users,
administrators can choose to ban the user.
Users can be banned from the Admin Area. To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Select a user.
1. Under the **Account** tab, click **Ban user**.
Banning a user is functionally identical to [blocking a user](#blocking-and-unblocking-users),
with the following differences:
- Comments made by the banned user will be hidden from other users.
- Issues created by the banned user will be hidden from other users.
Personal projects, and group and user history of the blocked user are left intact.
NOTE:
This feature is a work in progress. Currently, banning a user
only blocks them and does not hide their comments or issues.
This functionality will be implemented in follow up issues.
### Unbanning a user
A banned user can be unbanned from the Admin Area. To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Click on the **Banned** tab.
1. Select a user.
1. Under the **Account** tab, click **Unban user**.
NOTE:
Unbanning a user changes the user's state to active and consumes a
[seat](../../subscriptions/self_managed/index.md#billable-users).
...@@ -69,7 +69,7 @@ username of the original user. ...@@ -69,7 +69,7 @@ username of the original user.
When using the **Delete user and contributions** option, **all** associated records When using the **Delete user and contributions** option, **all** associated records
are removed. This includes all of the items mentioned above including issues, are removed. This includes all of the items mentioned above including issues,
merge requests, notes/comments, and more. Consider merge requests, notes/comments, and more. Consider
[blocking a user](../../admin_area/blocking_unblocking_users.md) [blocking a user](../../admin_area/user_moderation.md#blocking-a-user)
or using the **Delete user** option instead. or using the **Delete user** option instead.
When a user is deleted from an [abuse report](../../admin_area/review_abuse_reports.md) When a user is deleted from an [abuse report](../../admin_area/review_abuse_reports.md)
......
...@@ -73,7 +73,7 @@ Your account has been blocked. Fatal: Could not read from remote repository ...@@ -73,7 +73,7 @@ Your account has been blocked. Fatal: Could not read from remote repository
Your primary email address is not confirmed. Your primary email address is not confirmed.
``` ```
You can assure your users that they have not been [Blocked](admin_area/blocking_unblocking_users.md) by an administrator. You can assure your users that they have not been [Blocked](admin_area/user_moderation.md#blocking-and-unblocking-users) by an administrator.
When affected users see this message, they must confirm their email address before they can commit code. When affected users see this message, they must confirm their email address before they can commit code.
## What do I need to know as an administrator of a GitLab self-managed Instance? ## What do I need to know as an administrator of a GitLab self-managed Instance?
......
...@@ -1373,6 +1373,9 @@ msgstr "" ...@@ -1373,6 +1373,9 @@ msgstr ""
msgid "A Let's Encrypt account will be configured for this GitLab installation using your email address. You will receive emails to warn of expiring certificates." msgid "A Let's Encrypt account will be configured for this GitLab installation using your email address. You will receive emails to warn of expiring certificates."
msgstr "" msgstr ""
msgid "A banned user cannot:"
msgstr ""
msgid "A basic page and serverless function that uses AWS Lambda, AWS API Gateway, and GitLab Pages" msgid "A basic page and serverless function that uses AWS Lambda, AWS API Gateway, and GitLab Pages"
msgstr "" msgstr ""
...@@ -2435,6 +2438,9 @@ msgstr "" ...@@ -2435,6 +2438,9 @@ msgstr ""
msgid "AdminUsers|(Admin)" msgid "AdminUsers|(Admin)"
msgstr "" msgstr ""
msgid "AdminUsers|(Banned)"
msgstr ""
msgid "AdminUsers|(Blocked)" msgid "AdminUsers|(Blocked)"
msgstr "" msgstr ""
...@@ -2501,6 +2507,21 @@ msgstr "" ...@@ -2501,6 +2507,21 @@ msgstr ""
msgid "AdminUsers|Automatically marked as default internal user" msgid "AdminUsers|Automatically marked as default internal user"
msgstr "" msgstr ""
msgid "AdminUsers|Ban"
msgstr ""
msgid "AdminUsers|Ban user"
msgstr ""
msgid "AdminUsers|Ban user %{username}?"
msgstr ""
msgid "AdminUsers|Banned"
msgstr ""
msgid "AdminUsers|Banning the user has the following effects:"
msgstr ""
msgid "AdminUsers|Be added to groups and projects" msgid "AdminUsers|Be added to groups and projects"
msgstr "" msgstr ""
...@@ -2588,6 +2609,9 @@ msgstr "" ...@@ -2588,6 +2609,9 @@ msgstr ""
msgid "AdminUsers|It's you!" msgid "AdminUsers|It's you!"
msgstr "" msgstr ""
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
msgid "AdminUsers|Log in" msgid "AdminUsers|Log in"
msgstr "" msgstr ""
...@@ -2669,6 +2693,15 @@ msgstr "" ...@@ -2669,6 +2693,15 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}" msgid "AdminUsers|To confirm, type %{username}"
msgstr "" msgstr ""
msgid "AdminUsers|Unban"
msgstr ""
msgid "AdminUsers|Unban %{username}?"
msgstr ""
msgid "AdminUsers|Unban user"
msgstr ""
msgid "AdminUsers|Unblock" msgid "AdminUsers|Unblock"
msgstr "" msgstr ""
...@@ -2684,6 +2717,9 @@ msgstr "" ...@@ -2684,6 +2717,9 @@ msgstr ""
msgid "AdminUsers|Unlock user %{username}?" msgid "AdminUsers|Unlock user %{username}?"
msgstr "" msgstr ""
msgid "AdminUsers|User will be blocked"
msgstr ""
msgid "AdminUsers|User will not be able to access git repositories" msgid "AdminUsers|User will not be able to access git repositories"
msgstr "" msgstr ""
...@@ -2720,6 +2756,9 @@ msgstr "" ...@@ -2720,6 +2756,9 @@ msgstr ""
msgid "AdminUsers|You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strongStart}block user%{strongEnd} feature instead. Once you %{strongStart}Delete user%{strongEnd}, it cannot be undone or recovered." msgid "AdminUsers|You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strongStart}block user%{strongEnd} feature instead. Once you %{strongStart}Delete user%{strongEnd}, it cannot be undone or recovered."
msgstr "" msgstr ""
msgid "AdminUsers|You can always ban their account again if needed."
msgstr ""
msgid "AdminUsers|You can always block their account again if needed." msgid "AdminUsers|You can always block their account again if needed."
msgstr "" msgstr ""
...@@ -2729,6 +2768,9 @@ msgstr "" ...@@ -2729,6 +2768,9 @@ msgstr ""
msgid "AdminUsers|You can always re-activate their account, their data will remain intact." msgid "AdminUsers|You can always re-activate their account, their data will remain intact."
msgstr "" msgstr ""
msgid "AdminUsers|You can always unban their account. Their data remains intact."
msgstr ""
msgid "AdminUsers|You can always unblock their account, their data will remain intact." msgid "AdminUsers|You can always unblock their account, their data will remain intact."
msgstr "" msgstr ""
...@@ -12905,12 +12947,18 @@ msgstr "" ...@@ -12905,12 +12947,18 @@ msgstr ""
msgid "Error occurred. A blocked user must be unblocked to be activated" msgid "Error occurred. A blocked user must be unblocked to be activated"
msgstr "" msgstr ""
msgid "Error occurred. User was not banned"
msgstr ""
msgid "Error occurred. User was not blocked" msgid "Error occurred. User was not blocked"
msgstr "" msgstr ""
msgid "Error occurred. User was not confirmed" msgid "Error occurred. User was not confirmed"
msgstr "" msgstr ""
msgid "Error occurred. User was not unbanned"
msgstr ""
msgid "Error occurred. User was not unblocked" msgid "Error occurred. User was not unblocked"
msgstr "" msgstr ""
...@@ -31247,6 +31295,9 @@ msgstr "" ...@@ -31247,6 +31295,9 @@ msgstr ""
msgid "Successfully approved" msgid "Successfully approved"
msgstr "" msgstr ""
msgid "Successfully banned"
msgstr ""
msgid "Successfully blocked" msgid "Successfully blocked"
msgstr "" msgstr ""
...@@ -31271,6 +31322,9 @@ msgstr "" ...@@ -31271,6 +31322,9 @@ msgstr ""
msgid "Successfully synced %{synced_timeago}." msgid "Successfully synced %{synced_timeago}."
msgstr "" msgstr ""
msgid "Successfully unbanned"
msgstr ""
msgid "Successfully unblocked" msgid "Successfully unblocked"
msgstr "" msgstr ""
...@@ -33377,6 +33431,9 @@ msgstr "" ...@@ -33377,6 +33431,9 @@ msgstr ""
msgid "This user has the %{access} role in the %{name} project." msgid "This user has the %{access} role in the %{name} project."
msgstr "" msgstr ""
msgid "This user is banned"
msgstr ""
msgid "This user is blocked" msgid "This user is blocked"
msgstr "" msgstr ""
......
...@@ -365,6 +365,28 @@ RSpec.describe Admin::UsersController do ...@@ -365,6 +365,28 @@ RSpec.describe Admin::UsersController do
end end
end end
describe 'PUT ban/:id' do
it 'bans user' do
put :ban, params: { id: user.username }
user.reload
expect(user.banned?).to be_truthy
expect(flash[:notice]).to eq _('Successfully banned')
end
context 'when unsuccessful' do
let(:user) { create(:user, :blocked) }
it 'does not ban user' do
put :ban, params: { id: user.username }
user.reload
expect(user.banned?).to be_falsey
expect(flash[:alert]).to eq _('Error occurred. User was not banned')
end
end
end
describe 'PUT unlock/:id' do describe 'PUT unlock/:id' do
before do before do
request.env["HTTP_REFERER"] = "/" request.env["HTTP_REFERER"] = "/"
......
...@@ -27,6 +27,10 @@ FactoryBot.define do ...@@ -27,6 +27,10 @@ FactoryBot.define do
after(:build) { |user, _| user.block_pending_approval! } after(:build) { |user, _| user.block_pending_approval! }
end end
trait :banned do
after(:build) { |user, _| user.ban! }
end
trait :ldap_blocked do trait :ldap_blocked do
after(:build) { |user, _| user.ldap_block! } after(:build) { |user, _| user.ldap_block! }
end end
......
...@@ -85,6 +85,7 @@ RSpec.describe 'Admin::Users' do ...@@ -85,6 +85,7 @@ RSpec.describe 'Admin::Users' do
expect(page).to have_link('2FA Disabled', href: admin_users_path(filter: 'two_factor_disabled')) expect(page).to have_link('2FA Disabled', href: admin_users_path(filter: 'two_factor_disabled'))
expect(page).to have_link('External', href: admin_users_path(filter: 'external')) expect(page).to have_link('External', href: admin_users_path(filter: 'external'))
expect(page).to have_link('Blocked', href: admin_users_path(filter: 'blocked')) expect(page).to have_link('Blocked', href: admin_users_path(filter: 'blocked'))
expect(page).to have_link('Banned', href: admin_users_path(filter: 'banned'))
expect(page).to have_link('Deactivated', href: admin_users_path(filter: 'deactivated')) expect(page).to have_link('Deactivated', href: admin_users_path(filter: 'deactivated'))
expect(page).to have_link('Without projects', href: admin_users_path(filter: 'wop')) expect(page).to have_link('Without projects', href: admin_users_path(filter: 'wop'))
end end
......
...@@ -136,6 +136,16 @@ RSpec.describe UsersHelper do ...@@ -136,6 +136,16 @@ RSpec.describe UsersHelper do
end end
end end
context 'with a banned user' do
it 'returns the banned badge' do
banned_user = create(:user, :banned)
badges = helper.user_badges_in_admin_section(banned_user)
expect(filter_ee_badges(badges)).to eq([text: 'Banned', variant: 'danger'])
end
end
context 'with an admin user' do context 'with an admin user' do
it "returns the admin badge" do it "returns the admin badge" do
admin_user = create(:admin) admin_user = create(:admin)
......
...@@ -728,6 +728,7 @@ RSpec.describe User do ...@@ -728,6 +728,7 @@ RSpec.describe User do
let_it_be(:blocked_user) { create(:user, :blocked) } let_it_be(:blocked_user) { create(:user, :blocked) }
let_it_be(:ldap_blocked_user) { create(:omniauth_user, :ldap_blocked) } let_it_be(:ldap_blocked_user) { create(:omniauth_user, :ldap_blocked) }
let_it_be(:blocked_pending_approval_user) { create(:user, :blocked_pending_approval) } let_it_be(:blocked_pending_approval_user) { create(:user, :blocked_pending_approval) }
let_it_be(:banned_user) { create(:user, :banned) }
describe '.blocked' do describe '.blocked' do
subject { described_class.blocked } subject { described_class.blocked }
...@@ -735,6 +736,7 @@ RSpec.describe User do ...@@ -735,6 +736,7 @@ RSpec.describe User do
it 'returns only blocked users' do it 'returns only blocked users' do
expect(subject).to include( expect(subject).to include(
blocked_user, blocked_user,
banned_user,
ldap_blocked_user ldap_blocked_user
) )
...@@ -749,6 +751,14 @@ RSpec.describe User do ...@@ -749,6 +751,14 @@ RSpec.describe User do
expect(subject).to contain_exactly(blocked_pending_approval_user) expect(subject).to contain_exactly(blocked_pending_approval_user)
end end
end end
describe '.banned' do
subject { described_class.banned }
it 'returns only banned users' do
expect(subject).to contain_exactly(banned_user)
end
end
end end
describe ".with_two_factor" do describe ".with_two_factor" do
...@@ -1934,6 +1944,12 @@ RSpec.describe User do ...@@ -1934,6 +1944,12 @@ RSpec.describe User do
expect(described_class.filter_items('blocked')).to include user expect(described_class.filter_items('blocked')).to include user
end end
it 'filters by banned' do
expect(described_class).to receive(:banned).and_return([user])
expect(described_class.filter_items('banned')).to include user
end
it 'filters by blocked pending approval' do it 'filters by blocked pending approval' do
expect(described_class).to receive(:blocked_pending_approval).and_return([user]) expect(described_class).to receive(:blocked_pending_approval).and_return([user])
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Users::BanService do
let(:current_user) { create(:admin) }
subject(:service) { described_class.new(current_user) }
describe '#execute' do
subject(:operation) { service.execute(user) }
context 'when successful' do
let(:user) { create(:user) }
it { is_expected.to eq(status: :success) }
it "bans the user" do
expect { operation }.to change { user.state }.to('banned')
end
it "blocks the user" do
expect { operation }.to change { user.blocked? }.from(false).to(true)
end
it 'logs ban in application logs' do
allow(Gitlab::AppLogger).to receive(:info)
operation
expect(Gitlab::AppLogger).to have_received(:info).with(message: "User banned", user: "#{user.username}", email: "#{user.email}", banned_by: "#{current_user.username}", ip_address: "#{current_user.current_sign_in_ip}")
end
end
context 'when failed' do
let(:user) { create(:user, :blocked) }
it 'returns error result' do
aggregate_failures 'error result' do
expect(operation[:status]).to eq(:error)
expect(operation[:message]).to match(/State cannot transition/)
end
end
it "does not change the user's state" do
expect { operation }.not_to change { user.state }
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment