Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
f3bcf3c911
commit
023c409645
20 changed files with 352 additions and 185 deletions
|
@ -29,7 +29,7 @@ run-dev-fixtures-ee:
|
||||||
extends:
|
extends:
|
||||||
- .run-dev-fixtures
|
- .run-dev-fixtures
|
||||||
- .dev-fixtures:rules:ee-only
|
- .dev-fixtures:rules:ee-only
|
||||||
- .use-pg12-ee
|
- .use-pg12-es7-ee
|
||||||
script:
|
script:
|
||||||
- cp ee/db/fixtures/development/* $FIXTURE_PATH
|
- cp ee/db/fixtures/development/* $FIXTURE_PATH
|
||||||
- *run-dev-fixtures-script
|
- *run-dev-fixtures-script
|
||||||
|
|
|
@ -275,34 +275,34 @@
|
||||||
POSTGRES_HOST_AUTH_METHOD: trust
|
POSTGRES_HOST_AUTH_METHOD: trust
|
||||||
PG_VERSION: "13"
|
PG_VERSION: "13"
|
||||||
|
|
||||||
.use-pg11-ee:
|
.use-pg11-es7-ee:
|
||||||
services:
|
services:
|
||||||
- name: postgres:11.6
|
- name: postgres:11.6
|
||||||
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||||
- name: redis:5.0-alpine
|
- name: redis:5.0-alpine
|
||||||
- name: elasticsearch:7.17.0
|
- name: elasticsearch:7.17.6
|
||||||
command: ["elasticsearch", "-E", "discovery.type=single-node", "-E", "xpack.security.enabled=false"]
|
command: ["elasticsearch", "-E", "discovery.type=single-node", "-E", "xpack.security.enabled=false"]
|
||||||
variables:
|
variables:
|
||||||
POSTGRES_HOST_AUTH_METHOD: trust
|
POSTGRES_HOST_AUTH_METHOD: trust
|
||||||
PG_VERSION: "11"
|
PG_VERSION: "11"
|
||||||
|
|
||||||
.use-pg12-ee:
|
.use-pg12-es7-ee:
|
||||||
services:
|
services:
|
||||||
- name: postgres:12
|
- name: postgres:12
|
||||||
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||||
- name: redis:6.0-alpine
|
- name: redis:6.0-alpine
|
||||||
- name: elasticsearch:7.17.0
|
- name: elasticsearch:7.17.6
|
||||||
command: ["elasticsearch", "-E", "discovery.type=single-node", "-E", "xpack.security.enabled=false"]
|
command: ["elasticsearch", "-E", "discovery.type=single-node", "-E", "xpack.security.enabled=false"]
|
||||||
variables:
|
variables:
|
||||||
POSTGRES_HOST_AUTH_METHOD: trust
|
POSTGRES_HOST_AUTH_METHOD: trust
|
||||||
PG_VERSION: "12"
|
PG_VERSION: "12"
|
||||||
|
|
||||||
.use-pg13-ee:
|
.use-pg13-es7-ee:
|
||||||
services:
|
services:
|
||||||
- name: postgres:13
|
- name: postgres:13
|
||||||
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||||
- name: redis:6.2-alpine
|
- name: redis:6.2-alpine
|
||||||
- name: elasticsearch:7.17.0
|
- name: elasticsearch:7.17.6
|
||||||
command: ["elasticsearch", "-E", "discovery.type=single-node", "-E", "xpack.security.enabled=false"]
|
command: ["elasticsearch", "-E", "discovery.type=single-node", "-E", "xpack.security.enabled=false"]
|
||||||
variables:
|
variables:
|
||||||
POSTGRES_HOST_AUTH_METHOD: trust
|
POSTGRES_HOST_AUTH_METHOD: trust
|
||||||
|
@ -313,7 +313,7 @@
|
||||||
- name: postgres:12
|
- name: postgres:12
|
||||||
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||||
- name: redis:6.0-alpine
|
- name: redis:6.0-alpine
|
||||||
- name: elasticsearch:8.3.3
|
- name: elasticsearch:8.4.1
|
||||||
variables:
|
variables:
|
||||||
POSTGRES_HOST_AUTH_METHOD: trust
|
POSTGRES_HOST_AUTH_METHOD: trust
|
||||||
PG_VERSION: "12"
|
PG_VERSION: "12"
|
||||||
|
@ -325,7 +325,19 @@
|
||||||
- name: postgres:12
|
- name: postgres:12
|
||||||
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||||
- name: redis:6.0-alpine
|
- name: redis:6.0-alpine
|
||||||
- name: opensearchproject/opensearch:1.2.4
|
- name: opensearchproject/opensearch:1.3.5
|
||||||
|
alias: elasticsearch
|
||||||
|
command: ["bin/opensearch", "-E", "discovery.type=single-node", "-E", "plugins.security.disabled=true"]
|
||||||
|
variables:
|
||||||
|
POSTGRES_HOST_AUTH_METHOD: trust
|
||||||
|
PG_VERSION: "12"
|
||||||
|
|
||||||
|
.use-pg12-opensearch2-ee:
|
||||||
|
services:
|
||||||
|
- name: postgres:12
|
||||||
|
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||||
|
- name: redis:6.0-alpine
|
||||||
|
- name: opensearchproject/opensearch:2.2.1
|
||||||
alias: elasticsearch
|
alias: elasticsearch
|
||||||
command: ["bin/opensearch", "-E", "discovery.type=single-node", "-E", "plugins.security.disabled=true"]
|
command: ["bin/opensearch", "-E", "discovery.type=single-node", "-E", "plugins.security.disabled=true"]
|
||||||
variables:
|
variables:
|
||||||
|
|
|
@ -574,11 +574,6 @@ rspec-ee unit pg12 es8:
|
||||||
- .rspec-ee-base-pg12-es8
|
- .rspec-ee-base-pg12-es8
|
||||||
- .rspec-ee-unit-parallel
|
- .rspec-ee-unit-parallel
|
||||||
|
|
||||||
rspec-ee unit pg12 opensearch1:
|
|
||||||
extends:
|
|
||||||
- .rspec-ee-base-pg12-opensearch1
|
|
||||||
- .rspec-ee-unit-parallel
|
|
||||||
|
|
||||||
rspec-ee unit pg12 minimal:
|
rspec-ee unit pg12 minimal:
|
||||||
extends:
|
extends:
|
||||||
- rspec-ee unit pg12
|
- rspec-ee unit pg12
|
||||||
|
@ -602,11 +597,6 @@ rspec-ee integration pg12 es8:
|
||||||
- .rspec-ee-base-pg12-es8
|
- .rspec-ee-base-pg12-es8
|
||||||
- .rspec-ee-integration-parallel
|
- .rspec-ee-integration-parallel
|
||||||
|
|
||||||
rspec-ee integration pg12 opensearch1:
|
|
||||||
extends:
|
|
||||||
- .rspec-ee-base-pg12-opensearch1
|
|
||||||
- .rspec-ee-integration-parallel
|
|
||||||
|
|
||||||
rspec-ee integration pg12 minimal:
|
rspec-ee integration pg12 minimal:
|
||||||
extends:
|
extends:
|
||||||
- rspec-ee integration pg12
|
- rspec-ee integration pg12
|
||||||
|
@ -630,11 +620,6 @@ rspec-ee system pg12 es8:
|
||||||
- .rspec-ee-base-pg12-es8
|
- .rspec-ee-base-pg12-es8
|
||||||
- .rspec-ee-system-parallel
|
- .rspec-ee-system-parallel
|
||||||
|
|
||||||
rspec-ee system pg12 opensearch1:
|
|
||||||
extends:
|
|
||||||
- .rspec-ee-base-pg12-opensearch1
|
|
||||||
- .rspec-ee-system-parallel
|
|
||||||
|
|
||||||
rspec-ee system pg12 minimal:
|
rspec-ee system pg12 minimal:
|
||||||
extends:
|
extends:
|
||||||
- rspec-ee system pg12
|
- rspec-ee system pg12
|
||||||
|
@ -743,6 +728,61 @@ rspec-ee system pg11:
|
||||||
- .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
|
- .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
|
||||||
- .rspec-ee-system-parallel
|
- .rspec-ee-system-parallel
|
||||||
|
|
||||||
|
# PG12
|
||||||
|
rspec-ee unit pg12 es7:
|
||||||
|
extends:
|
||||||
|
- .rspec-ee-base-pg12-es7
|
||||||
|
- .rspec-ee-unit-parallel
|
||||||
|
- .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
|
||||||
|
|
||||||
|
rspec-ee unit pg12 opensearch1:
|
||||||
|
extends:
|
||||||
|
- .rspec-ee-base-pg12-opensearch1
|
||||||
|
- .rspec-ee-unit-parallel
|
||||||
|
- .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
|
||||||
|
|
||||||
|
rspec-ee unit pg12 opensearch2:
|
||||||
|
extends:
|
||||||
|
- .rspec-ee-base-pg12-opensearch2
|
||||||
|
- .rspec-ee-unit-parallel
|
||||||
|
- .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
|
||||||
|
|
||||||
|
rspec-ee integration pg12 es7:
|
||||||
|
extends:
|
||||||
|
- .rspec-ee-base-pg12-es7
|
||||||
|
- .rspec-ee-integration-parallel
|
||||||
|
- .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
|
||||||
|
|
||||||
|
rspec-ee integration pg12 opensearch1:
|
||||||
|
extends:
|
||||||
|
- .rspec-ee-base-pg12-opensearch1
|
||||||
|
- .rspec-ee-integration-parallel
|
||||||
|
- .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
|
||||||
|
|
||||||
|
rspec-ee integration pg12 opensearch2:
|
||||||
|
extends:
|
||||||
|
- .rspec-ee-base-pg12-opensearch2
|
||||||
|
- .rspec-ee-integration-parallel
|
||||||
|
- .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
|
||||||
|
|
||||||
|
rspec-ee system pg12 es7:
|
||||||
|
extends:
|
||||||
|
- .rspec-ee-base-pg12-es7
|
||||||
|
- .rspec-ee-system-parallel
|
||||||
|
- .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
|
||||||
|
|
||||||
|
rspec-ee system pg12 opensearch1:
|
||||||
|
extends:
|
||||||
|
- .rspec-ee-base-pg12-opensearch1
|
||||||
|
- .rspec-ee-system-parallel
|
||||||
|
- .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
|
||||||
|
|
||||||
|
rspec-ee system pg12 opensearch2:
|
||||||
|
extends:
|
||||||
|
- .rspec-ee-base-pg12-opensearch2
|
||||||
|
- .rspec-ee-system-parallel
|
||||||
|
- .rails:rules:default-branch-schedule-nightly--code-backstage-ee-only
|
||||||
|
|
||||||
# PG13
|
# PG13
|
||||||
rspec-ee migration pg13:
|
rspec-ee migration pg13:
|
||||||
extends:
|
extends:
|
||||||
|
|
|
@ -109,12 +109,18 @@ include:
|
||||||
.rspec-ee-base-pg11:
|
.rspec-ee-base-pg11:
|
||||||
extends:
|
extends:
|
||||||
- .rspec-base
|
- .rspec-base
|
||||||
- .use-pg11-ee
|
- .use-pg11-es7-ee
|
||||||
|
|
||||||
.rspec-ee-base-pg12:
|
.rspec-ee-base-pg12:
|
||||||
extends:
|
extends:
|
||||||
- .rspec-base
|
- .rspec-base
|
||||||
- .use-pg12-ee
|
- .use-pg12-es7-ee
|
||||||
|
|
||||||
|
.rspec-ee-base-pg12-es7:
|
||||||
|
extends:
|
||||||
|
- .rspec-base
|
||||||
|
- .use-pg12-es7-ee
|
||||||
|
- .rails:rules:run-search-tests
|
||||||
|
|
||||||
.rspec-ee-base-pg12-es8:
|
.rspec-ee-base-pg12-es8:
|
||||||
extends:
|
extends:
|
||||||
|
@ -128,10 +134,16 @@ include:
|
||||||
- .use-pg12-opensearch1-ee
|
- .use-pg12-opensearch1-ee
|
||||||
- .rails:rules:run-search-tests
|
- .rails:rules:run-search-tests
|
||||||
|
|
||||||
|
.rspec-ee-base-pg12-opensearch2:
|
||||||
|
extends:
|
||||||
|
- .rspec-base
|
||||||
|
- .use-pg12-opensearch2-ee
|
||||||
|
- .rails:rules:run-search-tests
|
||||||
|
|
||||||
.rspec-ee-base-pg13:
|
.rspec-ee-base-pg13:
|
||||||
extends:
|
extends:
|
||||||
- .rspec-base
|
- .rspec-base
|
||||||
- .use-pg13-ee
|
- .use-pg13-es7-ee
|
||||||
|
|
||||||
.db-job-base:
|
.db-job-base:
|
||||||
extends:
|
extends:
|
||||||
|
|
|
@ -573,6 +573,8 @@
|
||||||
rules:
|
rules:
|
||||||
- <<: *if-merge-request-labels-group-global-search
|
- <<: *if-merge-request-labels-group-global-search
|
||||||
changes: *search-backend-patterns
|
changes: *search-backend-patterns
|
||||||
|
- <<: *if-merge-request-labels-group-global-search
|
||||||
|
changes: *ci-patterns
|
||||||
|
|
||||||
.rails:rules:ee-and-foss-default-rules:
|
.rails:rules:ee-and-foss-default-rules:
|
||||||
rules:
|
rules:
|
||||||
|
|
|
@ -145,7 +145,8 @@ export default {
|
||||||
},
|
},
|
||||||
currentTabIndex: {
|
currentTabIndex: {
|
||||||
get() {
|
get() {
|
||||||
return this.$options.tabsConfig.findIndex((tab) => tab.id === this.activeTab);
|
const tabIndex = this.$options.tabsConfig.findIndex((tab) => tab.id === this.activeTab);
|
||||||
|
return tabIndex >= 0 ? tabIndex : 0;
|
||||||
},
|
},
|
||||||
set(tabIdx) {
|
set(tabIdx) {
|
||||||
const tabId = this.$options.tabsConfig[tabIdx].id;
|
const tabId = this.$options.tabsConfig[tabIdx].id;
|
||||||
|
|
|
@ -5,9 +5,26 @@ import { joinPaths } from '~/lib/utils/url_utility';
|
||||||
Vue.use(VueRouter);
|
Vue.use(VueRouter);
|
||||||
|
|
||||||
export default function createRouter(base) {
|
export default function createRouter(base) {
|
||||||
return new VueRouter({
|
const router = new VueRouter({
|
||||||
mode: 'history',
|
mode: 'history',
|
||||||
base: joinPaths(gon.relative_url_root || '', base),
|
base: joinPaths(gon.relative_url_root || '', base),
|
||||||
routes: [{ path: '/:tabId', name: 'tab' }],
|
routes: [{ path: '/:tabId', name: 'tab' }],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
Backward-compatible behavior. Redirects hash mode URLs to history mode ones.
|
||||||
|
Ex: from #/overview to /overview
|
||||||
|
from #/metrics to /metrics
|
||||||
|
from #/activity to /activity
|
||||||
|
*/
|
||||||
|
router.beforeEach((to, _, next) => {
|
||||||
|
if (to.hash.startsWith('#/')) {
|
||||||
|
const path = to.fullPath.substring(2);
|
||||||
|
next(path);
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return router;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,128 +38,6 @@ This content has been converted to a Rake task, see [verify database values can
|
||||||
|
|
||||||
This content has been moved to [Troubleshooting Repository mirroring](../../user/project/repository/mirror/index.md#transfer-mirror-users-and-tokens-to-a-single-service-account-in-rails-console).
|
This content has been moved to [Troubleshooting Repository mirroring](../../user/project/repository/mirror/index.md#transfer-mirror-users-and-tokens-to-a-single-service-account-in-rails-console).
|
||||||
|
|
||||||
## Users
|
|
||||||
|
|
||||||
### Create new user
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
u = User.new(username: 'test_user', email: 'test@example.com', name: 'Test User', password: 'password', password_confirmation: 'password')
|
|
||||||
u.skip_confirmation! # Use it only if you wish user to be automatically confirmed. If skipped, user receives confirmation e-mail
|
|
||||||
u.save!
|
|
||||||
```
|
|
||||||
|
|
||||||
### Skip reconfirmation
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
user = User.find_by_username('<username>')
|
|
||||||
user.skip_reconfirmation!
|
|
||||||
```
|
|
||||||
|
|
||||||
### Disable 2fa for single user
|
|
||||||
|
|
||||||
**In GitLab 13.5 and later:**
|
|
||||||
|
|
||||||
Use the code under [Disable 2FA | For a single user](../../security/two_factor_authentication.md#for-a-single-user) so that the target user
|
|
||||||
is notified that 2FA has been disabled.
|
|
||||||
|
|
||||||
**In GitLab 13.4 and earlier:**
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
user = User.find_by_username('<username>')
|
|
||||||
user.disable_two_factor!
|
|
||||||
```
|
|
||||||
|
|
||||||
### Active users & Historical users
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# Active users on the instance, now
|
|
||||||
User.active.count
|
|
||||||
|
|
||||||
# Users taking a seat on the instance
|
|
||||||
User.billable.count
|
|
||||||
|
|
||||||
# The historical max on the instance as of the past year
|
|
||||||
::HistoricalData.max_historical_user_count(from: 1.year.ago.beginning_of_day, to: Time.current.end_of_day)
|
|
||||||
```
|
|
||||||
|
|
||||||
Using cURL and jq (up to a max 100, see [Pagination](../../api/index.md#pagination)):
|
|
||||||
|
|
||||||
```shell
|
|
||||||
curl --silent --header "Private-Token: ********************" \
|
|
||||||
"https://gitlab.example.com/api/v4/users?per_page=100&active" | jq --compact-output '.[] | [.id,.name,.username]'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Update Daily Billable & Historical users
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# Forces recount of historical (max) users
|
|
||||||
::HistoricalDataWorker.new.perform
|
|
||||||
|
|
||||||
# Forces recount of daily billable users
|
|
||||||
identifier = Analytics::UsageTrends::Measurement.identifiers[:billable_users]
|
|
||||||
::Analytics::UsageTrends::CounterJobWorker.new.perform(identifier, User.minimum(:id), User.maximum(:id), Time.zone.now)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Block or Delete Users that have no projects or groups
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
users = User.where('id NOT IN (select distinct(user_id) from project_authorizations)')
|
|
||||||
|
|
||||||
# How many users are removed?
|
|
||||||
users.count
|
|
||||||
|
|
||||||
# If that count looks sane:
|
|
||||||
|
|
||||||
# You can either block the users:
|
|
||||||
users.each { |user| user.blocked? ? nil : user.block! }
|
|
||||||
|
|
||||||
# Or you can delete them:
|
|
||||||
# need 'current user' (your user) for auditing purposes
|
|
||||||
current_user = User.find_by(username: '<your username>')
|
|
||||||
|
|
||||||
users.each do |user|
|
|
||||||
DeleteUserWorker.perform_async(current_user.id, user.id)
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
### Deactivate Users that have no recent activity
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
days_inactive = 90
|
|
||||||
inactive_users = User.active.where("last_activity_on <= ?", days_inactive.days.ago)
|
|
||||||
|
|
||||||
inactive_users.each do |user|
|
|
||||||
puts "user '#{user.username}': #{user.last_activity_on}"
|
|
||||||
user.deactivate!
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
### Block Users that have no recent activity
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
days_inactive = 90
|
|
||||||
inactive_users = User.active.where("last_activity_on <= ?", days_inactive.days.ago)
|
|
||||||
|
|
||||||
inactive_users.each do |user|
|
|
||||||
puts "user '#{user.username}': #{user.last_activity_on}"
|
|
||||||
user.block!
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
### Find a user's max permissions for project/group
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
user = User.find_by_username 'username'
|
|
||||||
project = Project.find_by_full_path 'group/project'
|
|
||||||
user.max_member_access_for_project project.id
|
|
||||||
```
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
user = User.find_by_username 'username'
|
|
||||||
group = Group.find_by_full_path 'group'
|
|
||||||
user.max_member_access_for_group group.id
|
|
||||||
```
|
|
||||||
|
|
||||||
## Merge requests
|
## Merge requests
|
||||||
|
|
||||||
## CI
|
## CI
|
||||||
|
|
|
@ -85,7 +85,7 @@ In this mode, `jest` would resolve all the dependencies of related to the change
|
||||||
|
|
||||||
In addition, there are a few circumstances where we would always run the full Jest tests:
|
In addition, there are a few circumstances where we would always run the full Jest tests:
|
||||||
|
|
||||||
- when the `pipeline:run-all-jest` label is set on the merge request
|
- when the `pipeline:run-all-jest` label is set on the merge request or is set by triage automation when the merge request is approved by any reviewer
|
||||||
- when the merge request is created by an automation (for example, Gitaly update or MR targeting a stable branch)
|
- when the merge request is created by an automation (for example, Gitaly update or MR targeting a stable branch)
|
||||||
- when the merge request is created in a security mirror
|
- when the merge request is created in a security mirror
|
||||||
- when any CI configuration file is changed (for example, `.gitlab-ci.yml` or `.gitlab/ci/**/*`)
|
- when any CI configuration file is changed (for example, `.gitlab-ci.yml` or `.gitlab/ci/**/*`)
|
||||||
|
|
|
@ -104,6 +104,8 @@ instead of the Rails console.
|
||||||
Using the [Rails console](../administration/operations/rails_console.md), 2FA for a single user can be disabled.
|
Using the [Rails console](../administration/operations/rails_console.md), 2FA for a single user can be disabled.
|
||||||
Connect to the Rails console and run:
|
Connect to the Rails console and run:
|
||||||
|
|
||||||
|
**In GitLab 13.5 and later:**
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
admin = User.find_by_username('<USERNAME>')
|
admin = User.find_by_username('<USERNAME>')
|
||||||
user_to_disable = User.find_by_username('<USERNAME>')
|
user_to_disable = User.find_by_username('<USERNAME>')
|
||||||
|
@ -111,6 +113,9 @@ user_to_disable = User.find_by_username('<USERNAME>')
|
||||||
TwoFactor::DestroyService.new(admin, user: user_to_disable).execute
|
TwoFactor::DestroyService.new(admin, user: user_to_disable).execute
|
||||||
```
|
```
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
The target user is notified that 2FA has been disabled.
|
||||||
|
|
||||||
### For all users
|
### 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
|
||||||
|
|
|
@ -460,3 +460,40 @@ If your credit card is declined when purchasing a GitLab subscription, possible
|
||||||
|
|
||||||
Check with your financial institution to confirm if any of these reasons apply. If they don't
|
Check with your financial institution to confirm if any of these reasons apply. If they don't
|
||||||
apply, contact [GitLab Support](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293).
|
apply, contact [GitLab Support](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293).
|
||||||
|
|
||||||
|
### Check daily and historical billable users
|
||||||
|
|
||||||
|
You can get a list of daily and historical billable users in your GitLab instance.
|
||||||
|
|
||||||
|
1. [Start a rails console session](../../administration/operations/rails_console.md#starting-a-rails-console-session).
|
||||||
|
|
||||||
|
1. Count the number of users in the instance:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
User.billable.count
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Get the historical maximum number of users on the instance from the past year:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
::HistoricalData.max_historical_user_count(from: 1.year.ago.beginning_of_day, to: Time.current.end_of_day)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update daily billable and historical users
|
||||||
|
|
||||||
|
You can trigger a manual update of the daily and historical billable users in your GitLab instance.
|
||||||
|
|
||||||
|
1. [Start a rails console session](../../administration/operations/rails_console.md#starting-a-rails-console-session).
|
||||||
|
|
||||||
|
1. Force an update of the daily billable users:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
identifier = Analytics::UsageTrends::Measurement.identifiers[:billable_users]
|
||||||
|
::Analytics::UsageTrends::CounterJobWorker.new.perform(identifier, User.minimum(:id), User.maximum(:id), Time.zone.now)
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Force an update of the historical max billable users:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
::HistoricalDataWorker.new.perform
|
||||||
|
```
|
||||||
|
|
|
@ -283,3 +283,62 @@ You can also delete a user and their contributions, such as merge requests, issu
|
||||||
|
|
||||||
NOTE:
|
NOTE:
|
||||||
Before 15.1, additionally groups of which deleted user were the only owner among direct members were deleted.
|
Before 15.1, additionally groups of which deleted user were the only owner among direct members were deleted.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
When moderating users, you may need to perform bulk actions on them based on certain conditions. The following rails console scripts show some examples of this. You may [start a rails console session](../../administration/operations/rails_console.md#starting-a-rails-console-session) and use scripts similar to the following:
|
||||||
|
|
||||||
|
### Deactivate Users that have no recent activity
|
||||||
|
|
||||||
|
WARNING:
|
||||||
|
Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
days_inactive = 90
|
||||||
|
inactive_users = User.active.where("last_activity_on <= ?", days_inactive.days.ago)
|
||||||
|
|
||||||
|
inactive_users.each do |user|
|
||||||
|
puts "user '#{user.username}': #{user.last_activity_on}"
|
||||||
|
user.deactivate!
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Block Users that have no recent activity
|
||||||
|
|
||||||
|
WARNING:
|
||||||
|
Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
days_inactive = 90
|
||||||
|
inactive_users = User.active.where("last_activity_on <= ?", days_inactive.days.ago)
|
||||||
|
|
||||||
|
inactive_users.each do |user|
|
||||||
|
puts "user '#{user.username}': #{user.last_activity_on}"
|
||||||
|
user.block!
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Block or Delete Users that have no projects or groups
|
||||||
|
|
||||||
|
WARNING:
|
||||||
|
Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
users = User.where('id NOT IN (select distinct(user_id) from project_authorizations)')
|
||||||
|
|
||||||
|
# How many users are removed?
|
||||||
|
users.count
|
||||||
|
|
||||||
|
# If that count looks sane:
|
||||||
|
|
||||||
|
# You can either block the users:
|
||||||
|
users.each { |user| user.blocked? ? nil : user.block! }
|
||||||
|
|
||||||
|
# Or you can delete them:
|
||||||
|
# need 'current user' (your user) for auditing purposes
|
||||||
|
current_user = User.find_by(username: '<your username>')
|
||||||
|
|
||||||
|
users.each do |user|
|
||||||
|
DeleteUserWorker.perform_async(current_user.id, user.id)
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
|
@ -859,3 +859,17 @@ Commands that change data can cause damage if not run correctly or under the rig
|
||||||
```ruby
|
```ruby
|
||||||
GroupDestroyWorker.new.perform(group_id, user_id)
|
GroupDestroyWorker.new.perform(group_id, user_id)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Find a user's max permissions for a group or project
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
user = User.find_by_username 'username'
|
||||||
|
project = Project.find_by_full_path 'group/project'
|
||||||
|
user.max_member_access_for_project project.id
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
user = User.find_by_username 'username'
|
||||||
|
group = Group.find_by_full_path 'group'
|
||||||
|
user.max_member_access_for_group group.id
|
||||||
|
```
|
||||||
|
|
|
@ -49,3 +49,16 @@ Users are:
|
||||||
- Created when first signing with [Group SAML](../../group/saml_sso/index.md).
|
- Created when first signing with [Group SAML](../../group/saml_sso/index.md).
|
||||||
- Automatically created by [SCIM](../../group/saml_sso/scim_setup.md) when the user is created in
|
- Automatically created by [SCIM](../../group/saml_sso/scim_setup.md) when the user is created in
|
||||||
the identity provider.
|
the identity provider.
|
||||||
|
|
||||||
|
## Create users through the Rails console
|
||||||
|
|
||||||
|
WARNING:
|
||||||
|
Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore.
|
||||||
|
|
||||||
|
To create a user through the rails console, [start a console session](../../../administration/operations/rails_console.md#starting-a-rails-console-session) and run the following commands:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
u = User.new(username: 'test_user', email: 'test@example.com', name: 'Test User', password: 'password', password_confirmation: 'password')
|
||||||
|
u.skip_confirmation! # Use it only if you wish user to be automatically confirmed. If skipped, user receives confirmation e-mail
|
||||||
|
u.save!
|
||||||
|
```
|
||||||
|
|
|
@ -171,6 +171,7 @@ module API
|
||||||
namespace do
|
namespace do
|
||||||
mount ::API::AccessRequests
|
mount ::API::AccessRequests
|
||||||
mount ::API::Appearance
|
mount ::API::Appearance
|
||||||
|
mount ::API::BulkImports
|
||||||
mount ::API::Ci::Runner
|
mount ::API::Ci::Runner
|
||||||
mount ::API::Clusters::Agents
|
mount ::API::Clusters::Agents
|
||||||
mount ::API::Clusters::AgentTokens
|
mount ::API::Clusters::AgentTokens
|
||||||
|
@ -207,7 +208,6 @@ module API
|
||||||
mount ::API::Boards
|
mount ::API::Boards
|
||||||
mount ::API::Branches
|
mount ::API::Branches
|
||||||
mount ::API::BroadcastMessages
|
mount ::API::BroadcastMessages
|
||||||
mount ::API::BulkImports
|
|
||||||
mount ::API::Ci::JobArtifacts
|
mount ::API::Ci::JobArtifacts
|
||||||
mount ::API::Ci::Jobs
|
mount ::API::Ci::Jobs
|
||||||
mount ::API::Ci::PipelineSchedules
|
mount ::API::Ci::PipelineSchedules
|
||||||
|
|
|
@ -41,7 +41,15 @@ module API
|
||||||
resource :bulk_imports do
|
resource :bulk_imports do
|
||||||
desc 'Start a new GitLab Migration' do
|
desc 'Start a new GitLab Migration' do
|
||||||
detail 'This feature was introduced in GitLab 14.2.'
|
detail 'This feature was introduced in GitLab 14.2.'
|
||||||
success Entities::BulkImport
|
success code: 200, model: Entities::BulkImport
|
||||||
|
consumes ['application/x-www-form-urlencoded']
|
||||||
|
failure [
|
||||||
|
{ code: 401, message: 'Unauthorized' },
|
||||||
|
{ code: 400, message: 'Bad request' },
|
||||||
|
{ code: 404, message: 'Not found' },
|
||||||
|
{ code: 422, message: 'Unprocessable entity' },
|
||||||
|
{ code: 503, message: 'Service unavailable' }
|
||||||
|
]
|
||||||
end
|
end
|
||||||
params do
|
params do
|
||||||
requires :configuration, type: Hash, desc: 'The source GitLab instance configuration' do
|
requires :configuration, type: Hash, desc: 'The source GitLab instance configuration' do
|
||||||
|
@ -88,7 +96,13 @@ module API
|
||||||
|
|
||||||
desc 'List all GitLab Migrations' do
|
desc 'List all GitLab Migrations' do
|
||||||
detail 'This feature was introduced in GitLab 14.1.'
|
detail 'This feature was introduced in GitLab 14.1.'
|
||||||
success Entities::BulkImport
|
is_array true
|
||||||
|
success code: 200, model: Entities::BulkImport
|
||||||
|
failure [
|
||||||
|
{ code: 401, message: 'Unauthorized' },
|
||||||
|
{ code: 404, message: 'Not found' },
|
||||||
|
{ code: 503, message: 'Service unavailable' }
|
||||||
|
]
|
||||||
end
|
end
|
||||||
params do
|
params do
|
||||||
use :pagination
|
use :pagination
|
||||||
|
@ -103,7 +117,13 @@ module API
|
||||||
|
|
||||||
desc "List all GitLab Migrations' entities" do
|
desc "List all GitLab Migrations' entities" do
|
||||||
detail 'This feature was introduced in GitLab 14.1.'
|
detail 'This feature was introduced in GitLab 14.1.'
|
||||||
success Entities::BulkImports::Entity
|
is_array true
|
||||||
|
success code: 200, model: Entities::BulkImports::Entity
|
||||||
|
failure [
|
||||||
|
{ code: 401, message: 'Unauthorized' },
|
||||||
|
{ code: 404, message: 'Not found' },
|
||||||
|
{ code: 503, message: 'Service unavailable' }
|
||||||
|
]
|
||||||
end
|
end
|
||||||
params do
|
params do
|
||||||
use :pagination
|
use :pagination
|
||||||
|
@ -123,7 +143,12 @@ module API
|
||||||
|
|
||||||
desc 'Get GitLab Migration details' do
|
desc 'Get GitLab Migration details' do
|
||||||
detail 'This feature was introduced in GitLab 14.1.'
|
detail 'This feature was introduced in GitLab 14.1.'
|
||||||
success Entities::BulkImport
|
success code: 200, model: Entities::BulkImport
|
||||||
|
failure [
|
||||||
|
{ code: 401, message: 'Unauthorized' },
|
||||||
|
{ code: 404, message: 'Not found' },
|
||||||
|
{ code: 503, message: 'Service unavailable' }
|
||||||
|
]
|
||||||
end
|
end
|
||||||
params do
|
params do
|
||||||
requires :import_id, type: Integer, desc: "The ID of user's GitLab Migration"
|
requires :import_id, type: Integer, desc: "The ID of user's GitLab Migration"
|
||||||
|
@ -134,7 +159,13 @@ module API
|
||||||
|
|
||||||
desc "List GitLab Migration entities" do
|
desc "List GitLab Migration entities" do
|
||||||
detail 'This feature was introduced in GitLab 14.1.'
|
detail 'This feature was introduced in GitLab 14.1.'
|
||||||
success Entities::BulkImports::Entity
|
is_array true
|
||||||
|
success code: 200, model: Entities::BulkImports::Entity
|
||||||
|
failure [
|
||||||
|
{ code: 401, message: 'Unauthorized' },
|
||||||
|
{ code: 404, message: 'Not found' },
|
||||||
|
{ code: 503, message: 'Service unavailable' }
|
||||||
|
]
|
||||||
end
|
end
|
||||||
params do
|
params do
|
||||||
requires :import_id, type: Integer, desc: "The ID of user's GitLab Migration"
|
requires :import_id, type: Integer, desc: "The ID of user's GitLab Migration"
|
||||||
|
@ -148,7 +179,12 @@ module API
|
||||||
|
|
||||||
desc 'Get GitLab Migration entity details' do
|
desc 'Get GitLab Migration entity details' do
|
||||||
detail 'This feature was introduced in GitLab 14.1.'
|
detail 'This feature was introduced in GitLab 14.1.'
|
||||||
success Entities::BulkImports::Entity
|
success code: 200, model: Entities::BulkImports::Entity
|
||||||
|
failure [
|
||||||
|
{ code: 401, message: 'Unauthorized' },
|
||||||
|
{ code: 404, message: 'Not found' },
|
||||||
|
{ code: 503, message: 'Service unavailable' }
|
||||||
|
]
|
||||||
end
|
end
|
||||||
params do
|
params do
|
||||||
requires :import_id, type: Integer, desc: "The ID of user's GitLab Migration"
|
requires :import_id, type: Integer, desc: "The ID of user's GitLab Migration"
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
module API
|
module API
|
||||||
module Entities
|
module Entities
|
||||||
class BulkImport < Grape::Entity
|
class BulkImport < Grape::Entity
|
||||||
expose :id
|
expose :id, documentation: { type: 'integer', example: 1 }
|
||||||
expose :status_name, as: :status
|
expose :status_name, as: :status, documentation: {
|
||||||
expose :source_type
|
type: 'string', example: 'finished', values: %w[created started finished timeout failed]
|
||||||
expose :created_at
|
}
|
||||||
expose :updated_at
|
expose :source_type, documentation: { type: 'string', example: 'gitlab' }
|
||||||
|
expose :created_at, documentation: { type: 'dateTime', example: '2012-05-28T04:42:42-07:00' }
|
||||||
|
expose :updated_at, documentation: { type: 'dateTime', example: '2012-05-28T04:42:42-07:00' }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,19 +4,21 @@ module API
|
||||||
module Entities
|
module Entities
|
||||||
module BulkImports
|
module BulkImports
|
||||||
class Entity < Grape::Entity
|
class Entity < Grape::Entity
|
||||||
expose :id
|
expose :id, documentation: { type: 'integer', example: 1 }
|
||||||
expose :bulk_import_id
|
expose :bulk_import_id, documentation: { type: 'integer', example: 1 }
|
||||||
expose :status_name, as: :status
|
expose :status_name, as: :status, documentation: {
|
||||||
expose :source_full_path
|
type: 'string', example: 'created', values: %w[created started finished timeout failed]
|
||||||
expose :destination_name # deprecated
|
}
|
||||||
expose :destination_slug
|
expose :source_full_path, documentation: { type: 'string', example: 'source_group' }
|
||||||
expose :destination_namespace
|
expose :destination_name, documentation: { type: 'string', example: 'destination_slug' } # deprecated
|
||||||
expose :parent_id
|
expose :destination_slug, documentation: { type: 'string', example: 'destination_slug' }
|
||||||
expose :namespace_id
|
expose :destination_namespace, documentation: { type: 'string', example: 'destination_path' }
|
||||||
expose :project_id
|
expose :parent_id, documentation: { type: 'integer', example: 1 }
|
||||||
expose :created_at
|
expose :namespace_id, documentation: { type: 'integer', example: 1 }
|
||||||
expose :updated_at
|
expose :project_id, documentation: { type: 'integer', example: 1 }
|
||||||
expose :failures, using: EntityFailure
|
expose :created_at, documentation: { type: 'dateTime', example: '2012-05-28T04:42:42-07:00' }
|
||||||
|
expose :updated_at, documentation: { type: 'dateTime', example: '2012-05-28T04:42:42-07:00' }
|
||||||
|
expose :failures, using: EntityFailure, documentation: { is_array: true }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,16 +4,18 @@ module API
|
||||||
module Entities
|
module Entities
|
||||||
module BulkImports
|
module BulkImports
|
||||||
class EntityFailure < Grape::Entity
|
class EntityFailure < Grape::Entity
|
||||||
expose :relation
|
expose :relation, documentation: { type: 'string', example: 'group' }
|
||||||
expose :pipeline_step, as: :step
|
expose :pipeline_step, as: :step, documentation: { type: 'string', example: 'extractor' }
|
||||||
expose :exception_message do |failure|
|
expose :exception_message, documentation: { type: 'string', example: 'error message' } do |failure|
|
||||||
::Projects::ImportErrorFilter.filter_message(failure.exception_message.truncate(72))
|
::Projects::ImportErrorFilter.filter_message(failure.exception_message.truncate(72))
|
||||||
end
|
end
|
||||||
expose :exception_class
|
expose :exception_class, documentation: { type: 'string', example: 'Exception' }
|
||||||
expose :correlation_id_value
|
expose :correlation_id_value, documentation: { type: 'string', example: 'dfcf583058ed4508e4c7c617bd7f0edd' }
|
||||||
expose :created_at
|
expose :created_at, documentation: { type: 'dateTime', example: '2012-05-28T04:42:42-07:00' }
|
||||||
expose :pipeline_class
|
expose :pipeline_class, documentation: {
|
||||||
expose :pipeline_step
|
type: 'string', example: 'BulkImports::Groups::Pipelines::GroupPipeline'
|
||||||
|
}
|
||||||
|
expose :pipeline_step, documentation: { type: 'string', example: 'extractor' }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
35
spec/frontend/vue_shared/alert_details/router_spec.js
Normal file
35
spec/frontend/vue_shared/alert_details/router_spec.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import createRouter from '~/vue_shared/alert_details/router';
|
||||||
|
import setWindowLocation from 'helpers/set_window_location_helper';
|
||||||
|
|
||||||
|
const BASE_PATH = '/-/alert_management/1/details';
|
||||||
|
const EMPTY_HASH = '';
|
||||||
|
const NOOP = () => {};
|
||||||
|
|
||||||
|
describe('AlertDetails router', () => {
|
||||||
|
const originalLocation = window.location.href;
|
||||||
|
let router;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
setWindowLocation(originalLocation);
|
||||||
|
router = createRouter(BASE_PATH);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('redirects hash route mode URLs to history route mode', () => {
|
||||||
|
it.each`
|
||||||
|
hashPath | historyPath
|
||||||
|
${'/#/overview'} | ${'/overview'}
|
||||||
|
${'#/overview'} | ${'/overview'}
|
||||||
|
${'/#/'} | ${'/'}
|
||||||
|
${'#/'} | ${'/'}
|
||||||
|
${'/#'} | ${'/'}
|
||||||
|
${'#'} | ${'/'}
|
||||||
|
${'/'} | ${'/'}
|
||||||
|
${'/overview'} | ${'/overview'}
|
||||||
|
`('should redirect "$hashPath" to "$historyPath"', ({ hashPath, historyPath }) => {
|
||||||
|
router.push(hashPath, NOOP);
|
||||||
|
|
||||||
|
expect(window.location.hash).toBe(EMPTY_HASH);
|
||||||
|
expect(window.location.pathname).toBe(BASE_PATH + historyPath);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in a new issue