Commit c26630ac authored by Imre Farkas's avatar Imre Farkas

Merge branch 'shadowban-user-state-ui' into 'master'

Ban user state and UI [RUN ALL RSPEC] [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!61292
parents b5be8cd0 19d410d5
...@@ -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 unban their account in the future. 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 ban their account in the future if necessary.'),
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/moderate_users", anchor: "ban-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
...@@ -377,6 +383,7 @@ class User < ApplicationRecord ...@@ -377,6 +383,7 @@ class User < ApplicationRecord
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) }
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
private
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.gl-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/moderate_users", anchor: "ban-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/moderate_users.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/moderate_users.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/moderate_users.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.
......
--- ---
stage: Manage redirect_to: 'moderate_users.md'
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
--- ---
# Activating and deactivating users This document was moved to [another location](moderate_users.md).
GitLab administrators can deactivate and activate users. <!-- This redirect file can be deleted after <2021-08-12>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
## Deactivating a user
> [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
can choose to deactivate the user.
Deactivating a user is functionally identical to [blocking a user](blocking_unblocking_users.md),
with the following differences:
- It does not prohibit the user from logging back in via the UI.
- Once a deactivated user logs back into the GitLab UI, their account is set to active.
A deactivated user:
- Cannot access Git repositories or the API.
- Will not receive any notifications from GitLab.
- 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.
A user can be deactivated from the Admin Area. To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Select a user.
1. Under the **Account** tab, click **Deactivate user**.
Please note that for the deactivation option to be visible to an admin, the user:
- Must be currently active.
- Must not have signed in, or have any activity, in the last 90 days.
Users can also be deactivated using the [GitLab API](../../api/users.md#deactivate-user).
NOTE:
A deactivated user does not consume a [seat](../../subscriptions/self_managed/index.md#billable-users).
## Activating a user
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22257) in GitLab 12.4.
A deactivated user can be activated from the Admin Area.
To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Click on the **Deactivated** tab.
1. Select a user.
1. Under the **Account** tab, click **Activate user**.
Users can also be activated using the [GitLab API](../../api/users.md#activate-user).
NOTE:
Activating a user changes the user's state to active and consumes a
[seat](../../subscriptions/self_managed/index.md#billable-users).
NOTE:
A deactivated user can also activate their account themselves by logging back in via the UI.
...@@ -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](moderate_users.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 redirect_to: 'moderate_users.md'
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 This document was moved to [another location](moderate_users.md).
GitLab administrators block and unblock users. <!-- This redirect file can be deleted after <2021-08-12>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
## 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](moderate_users.md#blocking-a-user)**
- **[Deactivated](activating_deactivating_users.md)** - **[Deactivated](moderate_users.md#deactivating-a-user)**
- **Without projects** - **Without projects**
For each user, the following are listed: For each user, the following are listed:
......
---
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
---
# Moderate users
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.
### Deactivating a user
> [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 can choose to deactivate the user.
Deactivating a user is functionally identical to [blocking a user](#blocking-and-unblocking-users),
with the following differences:
- It does not prohibit the user from logging back in via the UI.
- Once a deactivated user logs back into the GitLab UI, their account is set to active.
A deactivated user:
- Cannot access Git repositories or the API.
- Will not receive any notifications from GitLab.
- Will not be able to use [slash commands](../../integration/slash_commands.md).
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:
1. Navigate to **Admin Area > Overview > Users**.
1. Select a user.
1. Under the **Account** tab, click **Deactivate user**.
Please note that for the deactivation option to be visible to an admin, the user:
- Must be currently active.
- Must not have signed in, or have any activity, in the last 90 days.
Users can also be deactivated using the [GitLab API](../../api/users.md#deactivate-user).
NOTE:
A deactivated user does not consume a [seat](../../subscriptions/self_managed/index.md#billable-users).
### Activating a user
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22257) in GitLab 12.4.
A deactivated user can be activated from the Admin Area.
To do this:
1. Navigate to **Admin Area > Overview > Users**.
1. Click on the **Deactivated** tab.
1. Select a user.
1. Under the **Account** tab, click **Activate user**.
Users can also be activated using the [GitLab API](../../api/users.md#activate-user).
NOTE:
Activating a user changes the user's state to active and consumes a
[seat](../../subscriptions/self_managed/index.md#billable-users).
NOTE:
A deactivated user can also activate their account themselves by logging back in via the UI.
## Ban and unban users
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/327353) in GitLab 13.12.
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.
### Ban a user
To completely block a user, administrators can choose to ban the user.
Users can be banned using 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**.
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.
### Unban a user
A banned user can be unbanned using 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/moderate_users.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/moderate_users.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?
......
...@@ -1375,6 +1375,9 @@ msgstr "" ...@@ -1375,6 +1375,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 ""
...@@ -2437,6 +2440,9 @@ msgstr "" ...@@ -2437,6 +2440,9 @@ msgstr ""
msgid "AdminUsers|(Admin)" msgid "AdminUsers|(Admin)"
msgstr "" msgstr ""
msgid "AdminUsers|(Banned)"
msgstr ""
msgid "AdminUsers|(Blocked)" msgid "AdminUsers|(Blocked)"
msgstr "" msgstr ""
...@@ -2503,6 +2509,21 @@ msgstr "" ...@@ -2503,6 +2509,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 ""
...@@ -2590,6 +2611,9 @@ msgstr "" ...@@ -2590,6 +2611,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 ""
...@@ -2671,6 +2695,15 @@ msgstr "" ...@@ -2671,6 +2695,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 ""
...@@ -2686,6 +2719,9 @@ msgstr "" ...@@ -2686,6 +2719,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 ""
...@@ -2722,6 +2758,9 @@ msgstr "" ...@@ -2722,6 +2758,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 ban their account in the future if necessary."
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 ""
...@@ -2734,6 +2773,9 @@ msgstr "" ...@@ -2734,6 +2773,9 @@ 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 ""
msgid "AdminUsers|You can unban their account in the future. Their data remains intact."
msgstr ""
msgid "AdminUsers|You cannot remove your own admin rights." msgid "AdminUsers|You cannot remove your own admin rights."
msgstr "" msgstr ""
...@@ -12898,12 +12940,18 @@ msgstr "" ...@@ -12898,12 +12940,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 ""
...@@ -31249,6 +31297,9 @@ msgstr "" ...@@ -31249,6 +31297,9 @@ msgstr ""
msgid "Successfully approved" msgid "Successfully approved"
msgstr "" msgstr ""
msgid "Successfully banned"
msgstr ""
msgid "Successfully blocked" msgid "Successfully blocked"
msgstr "" msgstr ""
...@@ -31273,6 +31324,9 @@ msgstr "" ...@@ -31273,6 +31324,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 ""
...@@ -33382,6 +33436,9 @@ msgstr "" ...@@ -33382,6 +33436,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,56 @@ RSpec.describe Admin::UsersController do ...@@ -365,6 +365,56 @@ RSpec.describe Admin::UsersController do
end end
end end
describe 'PUT ban/:id' do
context 'when ban_user_feature_flag is enabled' 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
context 'when ban_user_feature_flag is not enabled' do
before do
stub_feature_flags(ban_user_feature_flag: false)
end
it 'does not ban user, renders 404' do
put :ban, params: { id: user.username }
user.reload
expect(user.banned?).to be_falsey
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
describe 'PUT unban/:id' do
let(:banned_user) { create(:user, :banned) }
it 'unbans user' do
put :unban, params: { id: banned_user.username }
banned_user.reload
expect(banned_user.banned?).to be_falsey
expect(flash[:notice]).to eq _('Successfully unbanned')
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 }
...@@ -738,7 +739,7 @@ RSpec.describe User do ...@@ -738,7 +739,7 @@ RSpec.describe User do
ldap_blocked_user ldap_blocked_user
) )
expect(subject).not_to include(active_user, blocked_pending_approval_user) expect(subject).not_to include(active_user, blocked_pending_approval_user, banned_user)
end end
end end
...@@ -749,6 +750,14 @@ RSpec.describe User do ...@@ -749,6 +750,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 +1943,12 @@ RSpec.describe User do ...@@ -1934,6 +1943,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