Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
1bd955a90f
commit
a40c1d5046
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add DevOps adoption end_time column
|
||||
merge_request: 50257
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add ability for admins to create PAT for other users via API
|
||||
merge_request: 49222
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
---
|
||||
name: pat_creation_api_for_admin
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45152
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/267553
|
||||
type: development
|
||||
group: group::access
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddDevopsAdoptionSnapshotRangeEnd < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
add_column :analytics_devops_adoption_snapshots, :end_time, :datetime_with_timezone
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddDevopsSnapshotIndex < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
INDEX_NAME = 'index_on_snapshots_segment_id_end_time'
|
||||
|
||||
def up
|
||||
add_concurrent_index :analytics_devops_adoption_snapshots, [:segment_id, :end_time], name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :analytics_devops_adoption_snapshots, INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddDevopsAdoptionSnapshotNotNull < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
with_lock_retries do
|
||||
execute(
|
||||
<<~SQL
|
||||
LOCK TABLE analytics_devops_adoption_snapshots IN ACCESS EXCLUSIVE MODE;
|
||||
|
||||
UPDATE analytics_devops_adoption_snapshots SET end_time = date_trunc('month', recorded_at) - interval '1 millisecond';
|
||||
|
||||
ALTER TABLE analytics_devops_adoption_snapshots ALTER COLUMN end_time SET NOT NULL;
|
||||
SQL
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
execute(<<~SQL)
|
||||
ALTER TABLE analytics_devops_adoption_snapshots ALTER COLUMN end_time DROP NOT NULL;
|
||||
SQL
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
c878874bbb9bf2314b90c1328d6e27846fe71513ba1ce688a262d36761b66665
|
||||
|
|
@ -0,0 +1 @@
|
|||
3647e8b944aedeb58c41d9b3f08c8fb657fab231b0ff25c276f6d2aaa2ea93ae
|
||||
|
|
@ -0,0 +1 @@
|
|||
15517956f3b5d7ce2c05d196a34881fa169df7b1bf5ccf0dbfbee74fb3143ba7
|
||||
|
|
@ -9003,7 +9003,8 @@ CREATE TABLE analytics_devops_adoption_snapshots (
|
|||
runner_configured boolean NOT NULL,
|
||||
pipeline_succeeded boolean NOT NULL,
|
||||
deploy_succeeded boolean NOT NULL,
|
||||
security_scan_succeeded boolean NOT NULL
|
||||
security_scan_succeeded boolean NOT NULL,
|
||||
end_time timestamp with time zone NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE analytics_devops_adoption_snapshots_id_seq
|
||||
|
|
@ -22102,6 +22103,8 @@ CREATE UNIQUE INDEX index_on_segment_selections_project_id_segment_id ON analyti
|
|||
|
||||
CREATE INDEX index_on_segment_selections_segment_id ON analytics_devops_adoption_segment_selections USING btree (segment_id);
|
||||
|
||||
CREATE INDEX index_on_snapshots_segment_id_end_time ON analytics_devops_adoption_snapshots USING btree (segment_id, end_time);
|
||||
|
||||
CREATE INDEX index_on_snapshots_segment_id_recorded_at ON analytics_devops_adoption_snapshots USING btree (segment_id, recorded_at);
|
||||
|
||||
CREATE INDEX index_on_users_lower_email ON users USING btree (lower((email)::text));
|
||||
|
|
|
|||
|
|
@ -96,4 +96,4 @@ curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://git
|
|||
|
||||
## Create a personal access token (admin only)
|
||||
|
||||
See the [Users API documentation](users.md#create-a-personal-access-token-admin-only) for information on creating a personal access token.
|
||||
See the [Users API documentation](users.md#create-a-personal-access-token) for information on creating a personal access token.
|
||||
|
|
|
|||
|
|
@ -1480,19 +1480,13 @@ Parameters:
|
|||
| `user_id` | integer | yes | The ID of the user |
|
||||
| `impersonation_token_id` | integer | yes | The ID of the impersonation token |
|
||||
|
||||
## Create a personal access token (admin only)
|
||||
## Create a personal access token **(CORE ONLY)**
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/17176) in GitLab 13.6.
|
||||
> - It's [deployed behind a feature flag](../user/feature_flags.md), disabled by default.
|
||||
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-an-administrators-ability-to-use-the-api-to-create-personal-access-tokens). **(CORE)**
|
||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/267553) in GitLab 13.8.
|
||||
|
||||
WARNING:
|
||||
This feature might not be available to you. Check the **version history** note above for details.
|
||||
|
||||
> Requires admin permissions.
|
||||
> Token values are returned once. Make sure you save it - you can't access it again.
|
||||
|
||||
It creates a new personal access token.
|
||||
Use this API to create a new personal access token. Token values are returned once.
|
||||
Make sure you save it as you can't access it again.
|
||||
|
||||
```plaintext
|
||||
POST /users/:user_id/personal_access_tokens
|
||||
|
|
@ -1632,22 +1626,3 @@ Example response:
|
|||
},
|
||||
]
|
||||
```
|
||||
|
||||
## Enable or disable an administrator's ability to use the API to create personal access tokens **(CORE)**
|
||||
|
||||
An administrator's ability to create personal access tokens through the API is
|
||||
deployed behind a feature flag that is **disabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../administration/feature_flags.md)
|
||||
can enable it.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:pat_creation_api_for_admin)
|
||||
```
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:pat_creation_api_for_admin)
|
||||
```
|
||||
|
|
|
|||
|
|
@ -745,8 +745,6 @@ module API
|
|||
optional :expires_at, type: Date, desc: 'The expiration date in the format YEAR-MONTH-DAY of the personal access token'
|
||||
end
|
||||
post feature_category: :authentication_and_authorization do
|
||||
not_found! unless Feature.enabled?(:pat_creation_api_for_admin)
|
||||
|
||||
response = ::PersonalAccessTokens::CreateService.new(
|
||||
current_user: current_user, target_user: target_user, params: declared_params(include_missing: false)
|
||||
).execute
|
||||
|
|
|
|||
|
|
@ -2853,115 +2853,91 @@ RSpec.describe API::Users do
|
|||
let(:expires_at) { 3.days.from_now.to_date.to_s }
|
||||
let(:scopes) { %w(api read_user) }
|
||||
|
||||
context 'when feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(pat_creation_api_for_admin: true)
|
||||
end
|
||||
it 'returns error if required attributes are missing' do
|
||||
post api("/users/#{user.id}/personal_access_tokens", admin)
|
||||
|
||||
it 'returns error if required attributes are missing' do
|
||||
post api("/users/#{user.id}/personal_access_tokens", admin)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['error']).to eq('name is missing, scopes is missing, scopes does not have a valid value')
|
||||
end
|
||||
|
||||
it 'returns a 404 error if user not found' do
|
||||
post api("/users/#{non_existing_record_id}/personal_access_tokens", admin),
|
||||
params: {
|
||||
name: name,
|
||||
scopes: scopes,
|
||||
expires_at: expires_at
|
||||
}
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
expect(json_response['message']).to eq('404 User Not Found')
|
||||
end
|
||||
|
||||
it 'returns a 401 error when not authenticated' do
|
||||
post api("/users/#{user.id}/personal_access_tokens"),
|
||||
params: {
|
||||
name: name,
|
||||
scopes: scopes,
|
||||
expires_at: expires_at
|
||||
}
|
||||
|
||||
expect(response).to have_gitlab_http_status(:unauthorized)
|
||||
expect(json_response['message']).to eq('401 Unauthorized')
|
||||
end
|
||||
|
||||
it 'returns a 403 error when authenticated as normal user' do
|
||||
post api("/users/#{user.id}/personal_access_tokens", user),
|
||||
params: {
|
||||
name: name,
|
||||
scopes: scopes,
|
||||
expires_at: expires_at
|
||||
}
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
expect(json_response['message']).to eq('403 Forbidden')
|
||||
end
|
||||
|
||||
it 'creates a personal access token when authenticated as admin' do
|
||||
post api("/users/#{user.id}/personal_access_tokens", admin),
|
||||
params: {
|
||||
name: name,
|
||||
expires_at: expires_at,
|
||||
scopes: scopes
|
||||
}
|
||||
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(json_response['name']).to eq(name)
|
||||
expect(json_response['scopes']).to eq(scopes)
|
||||
expect(json_response['expires_at']).to eq(expires_at)
|
||||
expect(json_response['id']).to be_present
|
||||
expect(json_response['created_at']).to be_present
|
||||
expect(json_response['active']).to be_truthy
|
||||
expect(json_response['revoked']).to be_falsey
|
||||
expect(json_response['token']).to be_present
|
||||
end
|
||||
|
||||
context 'when an error is thrown by the model' do
|
||||
let!(:admin_personal_access_token) { create(:personal_access_token, user: admin) }
|
||||
let(:error_message) { 'error message' }
|
||||
|
||||
before do
|
||||
allow_next_instance_of(PersonalAccessToken) do |personal_access_token|
|
||||
allow(personal_access_token).to receive_message_chain(:errors, :full_messages)
|
||||
.and_return([error_message])
|
||||
|
||||
allow(personal_access_token).to receive(:save).and_return(false)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns the error' do
|
||||
post api("/users/#{user.id}/personal_access_tokens", personal_access_token: admin_personal_access_token),
|
||||
params: {
|
||||
name: name,
|
||||
expires_at: expires_at,
|
||||
scopes: scopes
|
||||
}
|
||||
|
||||
expect(response).to have_gitlab_http_status(:unprocessable_entity)
|
||||
expect(json_response['message']).to eq(error_message)
|
||||
end
|
||||
end
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['error']).to eq('name is missing, scopes is missing, scopes does not have a valid value')
|
||||
end
|
||||
|
||||
context 'when feature flag is disabled' do
|
||||
it 'returns a 404 error if user not found' do
|
||||
post api("/users/#{non_existing_record_id}/personal_access_tokens", admin),
|
||||
params: {
|
||||
name: name,
|
||||
scopes: scopes,
|
||||
expires_at: expires_at
|
||||
}
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
expect(json_response['message']).to eq('404 User Not Found')
|
||||
end
|
||||
|
||||
it 'returns a 401 error when not authenticated' do
|
||||
post api("/users/#{user.id}/personal_access_tokens"),
|
||||
params: {
|
||||
name: name,
|
||||
scopes: scopes,
|
||||
expires_at: expires_at
|
||||
}
|
||||
|
||||
expect(response).to have_gitlab_http_status(:unauthorized)
|
||||
expect(json_response['message']).to eq('401 Unauthorized')
|
||||
end
|
||||
|
||||
it 'returns a 403 error when authenticated as normal user' do
|
||||
post api("/users/#{user.id}/personal_access_tokens", user),
|
||||
params: {
|
||||
name: name,
|
||||
scopes: scopes,
|
||||
expires_at: expires_at
|
||||
}
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
expect(json_response['message']).to eq('403 Forbidden')
|
||||
end
|
||||
|
||||
it 'creates a personal access token when authenticated as admin' do
|
||||
post api("/users/#{user.id}/personal_access_tokens", admin),
|
||||
params: {
|
||||
name: name,
|
||||
expires_at: expires_at,
|
||||
scopes: scopes
|
||||
}
|
||||
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(json_response['name']).to eq(name)
|
||||
expect(json_response['scopes']).to eq(scopes)
|
||||
expect(json_response['expires_at']).to eq(expires_at)
|
||||
expect(json_response['id']).to be_present
|
||||
expect(json_response['created_at']).to be_present
|
||||
expect(json_response['active']).to be_truthy
|
||||
expect(json_response['revoked']).to be_falsey
|
||||
expect(json_response['token']).to be_present
|
||||
end
|
||||
|
||||
context 'when an error is thrown by the model' do
|
||||
let!(:admin_personal_access_token) { create(:personal_access_token, user: admin) }
|
||||
let(:error_message) { 'error message' }
|
||||
|
||||
before do
|
||||
stub_feature_flags(pat_creation_api_for_admin: false)
|
||||
allow_next_instance_of(PersonalAccessToken) do |personal_access_token|
|
||||
allow(personal_access_token).to receive_message_chain(:errors, :full_messages)
|
||||
.and_return([error_message])
|
||||
|
||||
allow(personal_access_token).to receive(:save).and_return(false)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns a 404' do
|
||||
post api("/users/#{user.id}/personal_access_tokens", admin),
|
||||
it 'returns the error' do
|
||||
post api("/users/#{user.id}/personal_access_tokens", personal_access_token: admin_personal_access_token),
|
||||
params: {
|
||||
name: name,
|
||||
expires_at: expires_at,
|
||||
scopes: scopes
|
||||
}
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
expect(json_response['message']).to eq('404 Not Found')
|
||||
expect(response).to have_gitlab_http_status(:unprocessable_entity)
|
||||
expect(json_response['message']).to eq(error_message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue