Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-02-02 06:09:47 +00:00
parent 7580728fc2
commit bcd0f3a2f6
18 changed files with 624 additions and 91 deletions

View file

@ -16,15 +16,23 @@
- source scripts/utils.sh - source scripts/utils.sh
- source scripts/prepare_build.sh - source scripts/prepare_build.sh
.rails-cache: .setup-test-env-cache:
cache: cache:
key: "rails-v3" key: "setup-test-env-v1"
paths: paths:
- vendor/ruby/ - vendor/ruby/
- vendor/gitaly-ruby/ - vendor/gitaly-ruby/
- .go/pkg/mod/ - .go/pkg/mod/
policy: pull policy: pull
.rails-cache:
cache:
key: "rails-v4"
paths:
- vendor/ruby/
- vendor/gitaly-ruby/
policy: pull
.static-analysis-cache: .static-analysis-cache:
cache: cache:
key: "static-analysis-v2" key: "static-analysis-v2"

View file

@ -141,6 +141,7 @@
setup-test-env: setup-test-env:
extends: extends:
- .rails-job-base - .rails-job-base
- .setup-test-env-cache
- .rails:rules:default-refs-code-backstage-qa - .rails:rules:default-refs-code-backstage-qa
- .use-pg11 - .use-pg11
stage: prepare stage: prepare
@ -180,14 +181,20 @@ setup-test-env:
- tmp/tests/second_storage/ - tmp/tests/second_storage/
when: always when: always
update-rails-cache: update-setup-test-env-cache:
extends: extends:
- setup-test-env - setup-test-env
- .shared:rules:update-cache - .shared:rules:update-cache
artifacts: {} # This job's purpose is only to update the cache. artifacts:
paths: [] # This job's purpose is only to update the cache.
cache: cache:
policy: push # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up. policy: push # We want to rebuild the cache from scratch to ensure stale dependencies are cleaned up.
update-rails-cache:
extends:
- update-setup-test-env-cache
- .rails-cache
.coverage-base: .coverage-base:
extends: extends:
- .default-retry - .default-retry

View file

@ -1 +1 @@
aeec1e34a8f0fc6b453b7f091e3712f17956b580 827531393ddfbafcdd89c09f1798772f6f087ba8

View file

@ -26,7 +26,7 @@ class ApplicationExperiment < Gitlab::Experiment
private private
def resolve_variant_name def resolve_variant_name
return variant_names.first if Feature.enabled?(name, self, type: :experiment) return variant_names.first if Feature.enabled?(name, self, type: :experiment, default_enabled: :yaml)
nil # Returning nil vs. :control is important for not caching and rollouts. nil # Returning nil vs. :control is important for not caching and rollouts.
end end

View file

@ -0,0 +1,5 @@
---
title: Add rake task to reset user password
merge_request: 52347
author:
type: added

View file

@ -1,8 +0,0 @@
---
name: deploy_tokens_api
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25219
rollout_issue_url:
milestone: '12.9'
type: development
group: group::package
default_enabled: true

View file

@ -5502,6 +5502,81 @@ type DastOnDemandScanCreatePayload {
pipelineUrl: String pipelineUrl: String
} }
"""
Represents a DAST Profile
"""
type DastProfile {
"""
The associated scanner profile.
"""
dastScannerProfile: DastScannerProfile
"""
The associated site profile.
"""
dastSiteProfile: DastSiteProfile
"""
The description of the scan.
"""
description: String
"""
Relative web path to the edit page of a profile.
"""
editPath: String
"""
ID of the profile.
"""
id: DastProfileID!
"""
The name of the profile.
"""
name: String
}
"""
The connection type for DastProfile.
"""
type DastProfileConnection {
"""
A list of edges.
"""
edges: [DastProfileEdge]
"""
A list of nodes.
"""
nodes: [DastProfile]
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type DastProfileEdge {
"""
A cursor for use in pagination.
"""
cursor: String!
"""
The item at the end of the edge.
"""
node: DastProfile
}
"""
Identifier of Dast::Profile.
"""
scalar DastProfileID
enum DastScanTypeEnum { enum DastScanTypeEnum {
""" """
Active DAST scan. This scan will make active attacks against the target site. Active DAST scan. This scan will make active attacks against the target site.
@ -18195,7 +18270,32 @@ type Project {
createdAt: Time createdAt: Time
""" """
The DAST scanner profiles associated with the project DAST Profiles associated with the project. Always returns no nodes if `dast_saved_scans` is disabled.
"""
dastProfiles(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
): DastProfileConnection
"""
The DAST scanner profiles associated with the project.
""" """
dastScannerProfiles( dastScannerProfiles(
""" """
@ -18220,7 +18320,7 @@ type Project {
): DastScannerProfileConnection ): DastScannerProfileConnection
""" """
DAST Site Profile associated with the project DAST Site Profile associated with the project.
""" """
dastSiteProfile( dastSiteProfile(
""" """
@ -18230,7 +18330,7 @@ type Project {
): DastSiteProfile ): DastSiteProfile
""" """
DAST Site Profiles associated with the project DAST Site Profiles associated with the project.
""" """
dastSiteProfiles( dastSiteProfiles(
""" """
@ -18255,8 +18355,8 @@ type Project {
): DastSiteProfileConnection ): DastSiteProfileConnection
""" """
DAST Site Validations associated with the project. Will always return no nodes DAST Site Validations associated with the project. Always returns no nodes if
if `security_on_demand_scans_site_validation` is disabled `security_on_demand_scans_site_validation` is disabled.
""" """
dastSiteValidations( dastSiteValidations(
""" """

View file

@ -15047,6 +15047,229 @@
"enumValues": null, "enumValues": null,
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "OBJECT",
"name": "DastProfile",
"description": "Represents a DAST Profile",
"fields": [
{
"name": "dastScannerProfile",
"description": "The associated scanner profile.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "DastScannerProfile",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "dastSiteProfile",
"description": "The associated site profile.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "DastSiteProfile",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "description",
"description": "The description of the scan.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "editPath",
"description": "Relative web path to the edit page of a profile.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": "ID of the profile.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DastProfileID",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "name",
"description": "The name of the profile.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "DastProfileConnection",
"description": "The connection type for DastProfile.",
"fields": [
{
"name": "edges",
"description": "A list of edges.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "DastProfileEdge",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "nodes",
"description": "A list of nodes.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "DastProfile",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "pageInfo",
"description": "Information to aid in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PageInfo",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "DastProfileEdge",
"description": "An edge in a connection.",
"fields": [
{
"name": "cursor",
"description": "A cursor for use in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "node",
"description": "The item at the end of the edge.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "DastProfile",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "SCALAR",
"name": "DastProfileID",
"description": "Identifier of Dast::Profile.",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "ENUM", "kind": "ENUM",
"name": "DastScanTypeEnum", "name": "DastScanTypeEnum",
@ -53583,9 +53806,62 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "dastProfiles",
"description": "DAST Profiles associated with the project. Always returns no nodes if `dast_saved_scans` is disabled.",
"args": [
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "before",
"description": "Returns the elements in the list that come before the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "first",
"description": "Returns the first _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "last",
"description": "Returns the last _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "DastProfileConnection",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "dastScannerProfiles", "name": "dastScannerProfiles",
"description": "The DAST scanner profiles associated with the project", "description": "The DAST scanner profiles associated with the project.",
"args": [ "args": [
{ {
"name": "after", "name": "after",
@ -53638,7 +53914,7 @@
}, },
{ {
"name": "dastSiteProfile", "name": "dastSiteProfile",
"description": "DAST Site Profile associated with the project", "description": "DAST Site Profile associated with the project.",
"args": [ "args": [
{ {
"name": "id", "name": "id",
@ -53665,7 +53941,7 @@
}, },
{ {
"name": "dastSiteProfiles", "name": "dastSiteProfiles",
"description": "DAST Site Profiles associated with the project", "description": "DAST Site Profiles associated with the project.",
"args": [ "args": [
{ {
"name": "after", "name": "after",
@ -53718,7 +53994,7 @@
}, },
{ {
"name": "dastSiteValidations", "name": "dastSiteValidations",
"description": "DAST Site Validations associated with the project. Will always return no nodes if `security_on_demand_scans_site_validation` is disabled", "description": "DAST Site Validations associated with the project. Always returns no nodes if `security_on_demand_scans_site_validation` is disabled.",
"args": [ "args": [
{ {
"name": "normalizedTargetUrls", "name": "normalizedTargetUrls",

View file

@ -878,6 +878,19 @@ Autogenerated return type of DastOnDemandScanCreate.
| `errors` | String! => Array | Errors encountered during execution of the mutation. | | `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `pipelineUrl` | String | URL of the pipeline that was created. | | `pipelineUrl` | String | URL of the pipeline that was created. |
### DastProfile
Represents a DAST Profile.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `dastScannerProfile` | DastScannerProfile | The associated scanner profile. |
| `dastSiteProfile` | DastSiteProfile | The associated site profile. |
| `description` | String | The description of the scan. |
| `editPath` | String | Relative web path to the edit page of a profile. |
| `id` | DastProfileID! | ID of the profile. |
| `name` | String | The name of the profile. |
### DastScannerProfile ### DastScannerProfile
Represents a DAST scanner profile. Represents a DAST scanner profile.
@ -2745,10 +2758,11 @@ Autogenerated return type of PipelineRetry.
| `containerRepositories` | ContainerRepositoryConnection | Container repositories of the project | | `containerRepositories` | ContainerRepositoryConnection | Container repositories of the project |
| `containerRepositoriesCount` | Int! | Number of container repositories in the project | | `containerRepositoriesCount` | Int! | Number of container repositories in the project |
| `createdAt` | Time | Timestamp of the project creation | | `createdAt` | Time | Timestamp of the project creation |
| `dastScannerProfiles` | DastScannerProfileConnection | The DAST scanner profiles associated with the project | | `dastProfiles` | DastProfileConnection | DAST Profiles associated with the project. Always returns no nodes if `dast_saved_scans` is disabled. |
| `dastSiteProfile` | DastSiteProfile | DAST Site Profile associated with the project | | `dastScannerProfiles` | DastScannerProfileConnection | The DAST scanner profiles associated with the project. |
| `dastSiteProfiles` | DastSiteProfileConnection | DAST Site Profiles associated with the project | | `dastSiteProfile` | DastSiteProfile | DAST Site Profile associated with the project. |
| `dastSiteValidations` | DastSiteValidationConnection | DAST Site Validations associated with the project. Will always return no nodes if `security_on_demand_scans_site_validation` is disabled | | `dastSiteProfiles` | DastSiteProfileConnection | DAST Site Profiles associated with the project. |
| `dastSiteValidations` | DastSiteValidationConnection | DAST Site Validations associated with the project. Always returns no nodes if `security_on_demand_scans_site_validation` is disabled. |
| `description` | String | Short description of the project | | `description` | String | Short description of the project |
| `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` | | `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` |
| `environment` | Environment | A single environment of the project | | `environment` | Environment | A single environment of the project |

View file

@ -6,7 +6,7 @@ disqus_identifier: 'https://docs.gitlab.com/ee/user/project/pipelines/schedules.
type: reference, howto type: reference, howto
--- ---
# Pipeline schedules # Pipeline schedules **(FREE)**
> - Introduced in GitLab 9.1 as [Trigger Schedule](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10533). > - Introduced in GitLab 9.1 as [Trigger Schedule](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10533).
> - [Renamed to Pipeline Schedule](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10853) in GitLab 9.2. > - [Renamed to Pipeline Schedule](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10853) in GitLab 9.2.
@ -83,7 +83,7 @@ job:
- make build - make build
``` ```
### Advanced configuration ### Advanced configuration **(FREE SELF)**
The pipelines are not executed exactly on schedule because schedules are handled by The pipelines are not executed exactly on schedule because schedules are handled by
Sidekiq, which runs according to its interval. Sidekiq, which runs according to its interval.

View file

@ -493,6 +493,7 @@ request, be sure to start the `dont-interrupt-me` job before pushing.
1. We currently have several different caches defined in 1. We currently have several different caches defined in
[`.gitlab/ci/global.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/global.gitlab-ci.yml), [`.gitlab/ci/global.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/global.gitlab-ci.yml),
with fixed keys: with fixed keys:
- `.setup-test-env-cache`.
- `.rails-cache`. - `.rails-cache`.
- `.static-analysis-cache`. - `.static-analysis-cache`.
- `.coverage-cache` - `.coverage-cache`
@ -500,6 +501,7 @@ request, be sure to start the `dont-interrupt-me` job before pushing.
- `.yarn-cache`. - `.yarn-cache`.
- `.assets-compile-cache` (the key includes `${NODE_ENV}` so it's actually two different caches). - `.assets-compile-cache` (the key includes `${NODE_ENV}` so it's actually two different caches).
1. Only 6 specific jobs, running in 2-hourly scheduled pipelines, are pushing (i.e. updating) to the caches: 1. Only 6 specific jobs, running in 2-hourly scheduled pipelines, are pushing (i.e. updating) to the caches:
- `update-setup-test-env-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/rails.gitlab-ci.yml).
- `update-rails-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/rails.gitlab-ci.yml). - `update-rails-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/rails.gitlab-ci.yml).
- `update-static-analysis-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/rails.gitlab-ci.yml). - `update-static-analysis-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/rails.gitlab-ci.yml).
- `update-coverage-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/rails.gitlab-ci.yml). - `update-coverage-cache`, defined in [`.gitlab/ci/rails.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/rails.gitlab-ci.yml).
@ -642,6 +644,7 @@ that are scoped to a single [configuration keyword](../ci/yaml/README.md#job-key
|------------------|-------------| |------------------|-------------|
| `.default-retry` | Allows a job to [retry](../ci/yaml/README.md#retry) upon `unknown_failure`, `api_failure`, `runner_system_failure`, `job_execution_timeout`, or `stuck_or_timeout_failure`. | | `.default-retry` | Allows a job to [retry](../ci/yaml/README.md#retry) upon `unknown_failure`, `api_failure`, `runner_system_failure`, `job_execution_timeout`, or `stuck_or_timeout_failure`. |
| `.default-before_script` | Allows a job to use a default `before_script` definition suitable for Ruby/Rails tasks that may need a database running (e.g. tests). | | `.default-before_script` | Allows a job to use a default `before_script` definition suitable for Ruby/Rails tasks that may need a database running (e.g. tests). |
| `.setup-test-env-cache` | Allows a job to use a default `cache` definition suitable for setuping test environment for subsequent Ruby/Rails tasks. |
| `.rails-cache` | Allows a job to use a default `cache` definition suitable for Ruby/Rails tasks. | | `.rails-cache` | Allows a job to use a default `cache` definition suitable for Ruby/Rails tasks. |
| `.static-analysis-cache` | Allows a job to use a default `cache` definition suitable for static analysis tasks. | | `.static-analysis-cache` | Allows a job to use a default `cache` definition suitable for static analysis tasks. |
| `.coverage-cache` | Allows a job to use a default `cache` definition suitable for coverage tasks. | | `.coverage-cache` | Allows a job to use a default `cache` definition suitable for coverage tasks. |

View file

@ -7,70 +7,91 @@ type: howto
# How to reset user password # How to reset user password
To reset the password of a user, first log into your server with root privileges. There are a few ways to reset the password of a user.
Start a Ruby on Rails console with this command: ## Rake Task
GitLab provides a Rake Task to reset passwords of users using their usernames,
which can be invoked by the following command:
```shell ```shell
gitlab-rails console -e production sudo gitlab-rake "gitlab:password:reset"
``` ```
Wait until the console has loaded. You will be asked for username, password, and password confirmation. Upon giving
proper values for them, the password of the specified user will be updated.
## Find the user The Rake task also takes the username as an argument, as shown in the example
below:
There are multiple ways to find your user. You can search by email or user ID number.
```shell ```shell
user = User.where(id: 7).first sudo gitlab-rake "gitlab:password:reset[johndoe]"
``` ```
or NOTE:
To reset the default admin password, run this Rake task with the username
`root`, which is the default username of that admin account.
```shell ## Rails console
user = User.find_by(email: 'user@example.com')
```
## Reset the password The Rake task is capable of finding users via their usernames. However, if only
user ID or email ID of the user is known, Rails console can be used to find user
using user ID and then change password of the user manually.
Now you can change your password: 1. Start a Rails console
```shell ```shell
user.password = 'secret_pass' sudo gitlab-rails console -e production
user.password_confirmation = 'secret_pass' ```
```
It's important that you change both password and password_confirmation to make it work. 1. Find the user either by user ID or email ID:
When using this method instead of the [Users API](../api/users.md#user-modification), GitLab sends an email to the user stating that the user changed their password. ```ruby
user = User.find(123)
If the password was changed by an administrator, execute the following command to notify the user by email: #or
```shell user = User.find_by(email: 'user@example.com')
user.send_only_admin_changed_your_password_notification! ```
```
Don't forget to save the changes. 1. Reset the password
```shell ```ruby
user.save! user.password = 'secret_pass'
``` user.password_confirmation = 'secret_pass'
```
Exit the console, and then try to sign in with your new password. 1. When using this method instead of the [Users API](../api/users.md#user-modification),
GitLab sends an email to the user stating that the user changed their
password. If the password was changed by an administrator, execute the
following command to notify the user by email:
```ruby
user.send_only_admin_changed_your_password_notification!
```
1. Save the changes:
```ruby
user.save!
```
1. Exit the console, and then try to sign in with your new password.
NOTE: NOTE:
You can also reset passwords by using the [Users API](../api/users.md#user-modification). You can also reset passwords by using the [Users API](../api/users.md#user-modification).
### Reset your root password ## Reset your root password
The previously described steps can also be used to reset the root password. First, The previously described steps can also be used to reset the root password.
identify the root user, with an `id` of `1`. To do so, run the following command:
```shell In normal installations where the username of root account hasn't been changed
user = User.where(id: 1).first manually, the Rake task can be used with username `root` to reset the root
``` password.
After finding the user, follow the steps mentioned in the [Reset the password](#reset-the-password) section to reset the password of the root user. If the username was changed to something else and has been forgotten, one
possible way is to reset the password using Rails console with user ID `1` (in
almost all the cases, the first user will be the default admin account).
<!-- ## Troubleshooting <!-- ## Troubleshooting

View file

@ -28,8 +28,6 @@ module API
use :pagination use :pagination
end end
get 'deploy_tokens' do get 'deploy_tokens' do
service_unavailable! unless Feature.enabled?(:deploy_tokens_api, default_enabled: true)
authenticated_as_admin! authenticated_as_admin!
present paginate(DeployToken.all), with: Entities::DeployToken present paginate(DeployToken.all), with: Entities::DeployToken
@ -39,10 +37,6 @@ module API
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
before do
service_unavailable! unless Feature.enabled?(:deploy_tokens_api, user_project, default_enabled: true)
end
params do params do
use :pagination use :pagination
end end
@ -102,10 +96,6 @@ module API
requires :id, type: String, desc: 'The ID of a group' requires :id, type: String, desc: 'The ID of a group'
end end
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
before do
service_unavailable! unless Feature.enabled?(:deploy_tokens_api, user_group, default_enabled: true)
end
params do params do
use :pagination use :pagination
end end

View file

@ -66,6 +66,18 @@ module Gitlab
answer answer
end end
# Prompt the user to input a password
#
# message - custom message to display before input
def prompt_for_password(message = 'Enter password: ')
unless STDIN.tty?
print(message)
return STDIN.gets.chomp
end
STDIN.getpass(message)
end
# Runs the given command and matches the output against the given pattern # Runs the given command and matches the output against the given pattern
# #
# Returns nil if nothing matched # Returns nil if nothing matched

View file

@ -0,0 +1,31 @@
# frozen_string_literal: true
namespace :gitlab do
namespace :password do
desc "GitLab | Password | Reset a user's password"
task :reset, [:username] => :environment do |_, args|
username = args[:username] || Gitlab::TaskHelpers.prompt('Enter username: ')
abort('Username can not be empty.') if username.blank?
user = User.find_by(username: username)
abort("Unable to find user with username #{username}.") unless user
password = Gitlab::TaskHelpers.prompt_for_password
password_confirm = Gitlab::TaskHelpers.prompt_for_password('Confirm password: ')
user.password = password
user.password_confirmation = password_confirm
user.send_only_admin_changed_your_password_notification!
unless user.save
message = <<~EOF
Unable to change password of the user with username #{username}.
#{user.errors.full_messages.to_sentence}
EOF
abort(message)
end
puts "Password successfully updated for user with username #{username}."
end
end
end

View file

@ -5,6 +5,14 @@ require 'spec_helper'
RSpec.describe ApplicationExperiment, :experiment do RSpec.describe ApplicationExperiment, :experiment do
subject { described_class.new(:stub) } subject { described_class.new(:stub) }
let(:feature_definition) { { name: 'stub', type: 'experiment', group: 'group::adoption', default_enabled: false } }
around do |example|
Feature::Definition.definitions[:stub] = Feature::Definition.new('stub.yml', feature_definition)
example.run
Feature::Definition.definitions.delete(:stub)
end
before do before do
allow(subject).to receive(:enabled?).and_return(true) allow(subject).to receive(:enabled?).and_return(true)
end end
@ -105,6 +113,12 @@ RSpec.describe ApplicationExperiment, :experiment do
end end
describe "variant resolution" do describe "variant resolution" do
it "uses the default value as specified in the yaml" do
expect(Feature).to receive(:enabled?).with('stub', subject, type: :experiment, default_enabled: :yaml)
expect(subject.variant.name).to eq('control')
end
it "returns nil when not rolled out" do it "returns nil when not rolled out" do
stub_feature_flags(stub: false) stub_feature_flags(stub: false)

View file

@ -10,24 +10,12 @@ RSpec.describe API::DeployTokens do
let!(:deploy_token) { create(:deploy_token, projects: [project]) } let!(:deploy_token) { create(:deploy_token, projects: [project]) }
let!(:group_deploy_token) { create(:deploy_token, :group, groups: [group]) } let!(:group_deploy_token) { create(:deploy_token, :group, groups: [group]) }
shared_examples 'with feature flag disabled' do
context 'disabled feature flag' do
before do
stub_feature_flags(deploy_tokens_api: false)
end
it { is_expected.to have_gitlab_http_status(:service_unavailable) }
end
end
describe 'GET /deploy_tokens' do describe 'GET /deploy_tokens' do
subject do subject do
get api('/deploy_tokens', user) get api('/deploy_tokens', user)
response response
end end
it_behaves_like 'with feature flag disabled'
context 'when unauthenticated' do context 'when unauthenticated' do
let(:user) { nil } let(:user) { nil }
@ -81,8 +69,6 @@ RSpec.describe API::DeployTokens do
project.add_maintainer(user) project.add_maintainer(user)
end end
it_behaves_like 'with feature flag disabled'
it { is_expected.to have_gitlab_http_status(:ok) } it { is_expected.to have_gitlab_http_status(:ok) }
it 'returns all deploy tokens for the project' do it 'returns all deploy tokens for the project' do
@ -128,8 +114,6 @@ RSpec.describe API::DeployTokens do
group.add_maintainer(user) group.add_maintainer(user)
end end
it_behaves_like 'with feature flag disabled'
it { is_expected.to have_gitlab_http_status(:ok) } it { is_expected.to have_gitlab_http_status(:ok) }
it 'returns all deploy tokens for the group' do it 'returns all deploy tokens for the group' do

View file

@ -0,0 +1,76 @@
# frozen_string_literal: true
require 'rake_helper'
RSpec.describe 'gitlab:password rake tasks' do
let_it_be(:user_1) { create(:user, username: 'foobar', password: 'initial_password') }
def stub_username(username)
allow(Gitlab::TaskHelpers).to receive(:prompt).with('Enter username: ').and_return(username)
end
def stub_password(password, confirmation = nil)
confirmation ||= password
allow(Gitlab::TaskHelpers).to receive(:prompt_for_password).and_return(password)
allow(Gitlab::TaskHelpers).to receive(:prompt_for_password).with('Confirm password: ').and_return(confirmation)
end
before do
Rake.application.rake_require 'tasks/gitlab/password'
stub_username('foobar')
stub_password('secretpassword')
end
describe ':reset' do
context 'when all inputs are correct' do
it 'updates the password properly' do
run_rake_task('gitlab:password:reset', user_1.username)
expect(user_1.reload.valid_password?('secretpassword')).to eq(true)
end
end
context 'when username is not provided' do
it 'asks for username' do
expect(Gitlab::TaskHelpers).to receive(:prompt).with('Enter username: ')
run_rake_task('gitlab:password:reset')
end
context 'when username is empty' do
it 'aborts with an error' do
stub_username('')
expect { run_rake_task('gitlab:password:reset') }.to raise_error(/Username can not be empty./)
end
end
end
context 'when username is passed as argument' do
it 'does not ask for username' do
expect(Gitlab::TaskHelpers).not_to receive(:prompt)
run_rake_task('gitlab:password:reset', 'foobar')
end
end
context 'when passwords do not match' do
before do
stub_password('randompassword', 'differentpassword')
end
it 'aborts with an error' do
expect { run_rake_task('gitlab:password:reset') }.to raise_error(%r{Unable to change password of the user with username foobar.\nPassword confirmation doesn't match Password})
end
end
context 'when user cannot be found' do
before do
stub_username('nonexistentuser')
end
it 'aborts with an error' do
expect { run_rake_task('gitlab:password:reset') }.to raise_error(/Unable to find user with username nonexistentuser./)
end
end
end
end