refactors documentation and personal access tokens form to not allow admins to generate non impersionation tokens

This commit is contained in:
Tiago Botelho 2017-02-09 15:21:09 +00:00
parent c2b1cdef7e
commit f0ea7130f7
14 changed files with 238 additions and 135 deletions

View File

@ -6,7 +6,8 @@ class Admin::PersonalAccessTokensController < Admin::ApplicationController
end end
def create def create
@personal_access_token = user.personal_access_tokens.generate(personal_access_token_params) # We never want to non-impersonate a user
@personal_access_token = user.personal_access_tokens.generate(personal_access_token_params.merge(impersonation: true))
if @personal_access_token.save if @personal_access_token.save
flash[:personal_access_token] = @personal_access_token.token flash[:personal_access_token] = @personal_access_token.token

View File

@ -1,28 +0,0 @@
- personal_access_token = local_assigns.fetch(:personal_access_token)
- scopes = local_assigns.fetch(:scopes)
= form_for [:admin_user, personal_access_token], method: :post, html: { class: 'js-requires-input' } do |f|
= form_errors(personal_access_token)
.form-group
= f.label :name, class: 'label-light'
= f.text_field :name, class: "form-control", required: true
.form-group
= f.label :expires_at, class: 'label-light'
= f.text_field :expires_at, class: "datepicker form-control"
.form-group
= f.label :scopes, class: 'label-light'
= render 'shared/tokens/scopes_form', prefix: 'personal_access_token', token: personal_access_token, scopes: scopes
.form-group
= f.label :impersonation, class: 'label-light'
%fieldset
= f.check_box :impersonation
= f.label 'impersonation', 'You can impersonate the user'
%span= "(Normal users will not see this type of token)"
.prepend-top-default
= f.submit 'Create Personal Access Token', class: "btn btn-create"

View File

@ -3,18 +3,15 @@
.row.prepend-top-default .row.prepend-top-default
.col-lg-12 .col-lg-12
%h5.prepend-top-0 %h5.prepend-top-0
Add a Personal Access Token Add a Personal Access Token
%p.profile-settings-content %p.profile-settings-content
Pick a name for the application, and we'll give you a unique token. Pick a name for the application, and we'll give you a unique token.
= render "profiles/personal_access_tokens/form", user: :admin_user, personal_access_token: @personal_access_token, scopes: @scopes
= render "form", personal_access_token: @personal_access_token, scopes: @scopes
%hr %hr
%h5 Active Personal Access Tokens (#{@active_personal_access_tokens.length}) %h5 Active Personal Access Tokens (#{@active_personal_access_tokens.length})
- if @active_personal_access_tokens.present? - if @active_personal_access_tokens.present?
.table-responsive .table-responsive
%table.table.active-personal-access-tokens %table.table.active-personal-access-tokens
@ -44,7 +41,6 @@
= clipboard_button(clipboard_text: personal_access_token.token) = clipboard_button(clipboard_text: personal_access_token.token)
%td= personal_access_token.impersonation %td= personal_access_token.impersonation
%td= link_to "Revoke", revoke_admin_user_personal_access_token_path(id: personal_access_token.id, user_id: personal_access_token.user.username), method: :put, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to revoke this token? This action cannot be undone." } %td= link_to "Revoke", revoke_admin_user_personal_access_token_path(id: personal_access_token.id, user_id: personal_access_token.user.username), method: :put, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to revoke this token? This action cannot be undone." }
- else - else
.settings-message.text-center .settings-message.text-center
This user has no active tokens. This user has no active tokens.
@ -52,7 +48,6 @@
%hr %hr
%h5 Inactive Personal Access Tokens (#{@inactive_personal_access_tokens.length}) %h5 Inactive Personal Access Tokens (#{@inactive_personal_access_tokens.length})
- if @inactive_personal_access_tokens.present? - if @inactive_personal_access_tokens.present?
.table-responsive .table-responsive
%table.table.inactive-personal-access-tokens %table.table.inactive-personal-access-tokens
@ -65,16 +60,20 @@
%tr %tr
%td= token.name %td= token.name
%td= token.created_at.to_date.to_s(:medium) %td= token.created_at.to_date.to_s(:medium)
- else - else
.settings-message.text-center .settings-message.text-center
This user has no inactive tokens. This user has no inactive tokens.
:javascript :javascript
var date = $('#personal_access_token_expires_at').val(); var $dateField = $('#personal_access_token_expires_at');
var date = $dateField.val();
var datepicker = $(".datepicker").datepicker({ new Pikaday({
dateFormat: "yy-mm-dd", field: $dateField.get(0),
minDate: 0 theme: 'gitlab-theme',
format: 'YYYY-MM-DD',
minDate: new Date(),
onSelect: function(dateText) {
$dateField.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
}
}); });

View File

@ -1,7 +1,7 @@
- personal_access_token = local_assigns.fetch(:personal_access_token) - personal_access_token = local_assigns.fetch(:personal_access_token)
- scopes = local_assigns.fetch(:scopes) - scopes = local_assigns.fetch(:scopes)
= form_for [:profile, personal_access_token], method: :post, html: { class: 'js-requires-input' } do |f| = form_for [user, personal_access_token], method: :post, html: { class: 'js-requires-input' } do |f|
= form_errors(personal_access_token) = form_errors(personal_access_token)

View File

@ -29,7 +29,7 @@
%p.profile-settings-content %p.profile-settings-content
Pick a name for the application, and we'll give you a unique token. Pick a name for the application, and we'll give you a unique token.
= render "form", personal_access_token: @personal_access_token, scopes: @scopes = render "form", user: :profile, personal_access_token: @personal_access_token, scopes: @scopes
%hr %hr

View File

@ -8,7 +8,7 @@ under [`/lib/api`](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/api).
Documentation for various API resources can be found separately in the Documentation for various API resources can be found separately in the
following locations: following locations:
- [Access Tokens](personal_access_tokens.md) - [Personal Access Tokens](personal_access_tokens.md)
- [Award Emoji](award_emoji.md) - [Award Emoji](award_emoji.md)
- [Branches](branches.md) - [Branches](branches.md)
- [Broadcast Messages](broadcast_messages.md) - [Broadcast Messages](broadcast_messages.md)

View File

@ -14,13 +14,13 @@ An example:
"name": "mytoken", "name": "mytoken",
"revoked": false, "revoked": false,
"expires_at": "2017-01-04", "expires_at": "2017-01-04",
"scopes": ['api'], "scopes": ["api"],
"active": true "active": true
} }
] ]
``` ```
In addition, you can filter users based on state: `all`, `active` and `inactive` In addition, you can filter tokens based on state: `all`, `active` and `inactive`
``` ```
GET /personal_access_tokens?state=all GET /personal_access_tokens?state=all
@ -34,6 +34,18 @@ GET /personal_access_tokens?state=active
GET /personal_access_tokens?state=inactive GET /personal_access_tokens?state=inactive
``` ```
## Show
```
GET /personal_access_tokens/:personal_access_token_id
```
Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `personal_access_token_id` | integer | yes | The ID of the personal access token |
## Create ## Create
``` ```

View File

@ -858,7 +858,7 @@ An example:
] ]
``` ```
In addition, you can filter users based on state: `all`, `active` and `inactive` In addition, you can filter tokens based on state: `all`, `active` and `inactive`
``` ```
GET /users/:user_id/personal_access_tokens?state=all GET /users/:user_id/personal_access_tokens?state=all
@ -878,12 +878,27 @@ Finally, you can filter based on impersonation: `true` or `false`.
GET /users/:user_id/personal_access_tokens?impersonation=true GET /users/:user_id/personal_access_tokens?impersonation=true
``` ```
## Show a user personal access token
It shows a user's personal access token. Note that only administrators can do this.
```
GET /users/:user_id/personal_access_tokens/:personal_access_token_id
```
Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `user_id` | integer | yes | The ID of the user |
| `personal_access_token_id` | integer | yes | The ID of the personal access token |
## Create a personal access token ## Create a personal access token
It creates a new personal access token. Note that only administrators can do this. It creates a new personal access token. Note that only administrators can do this.
If you set the impersonation flag to true, you can impersonate the user and You are only able to create impersonation tokens to impersonate the user and perform
performing both API calls and Git reads and writes. The user will not see these both API calls and Git reads and writes. The user will not see these tokens in his profile
tokens in his profile settings. settings page.
``` ```
POST /users/:user_id/personal_access_tokens POST /users/:user_id/personal_access_tokens

View File

@ -3,7 +3,10 @@ module API
before { authenticate! } before { authenticate! }
resource :personal_access_tokens do resource :personal_access_tokens do
desc 'Retrieve personal access tokens' desc 'Retrieve personal access tokens' do
detail 'This feature was introduced in GitLab 9.0'
success Entities::BasicPersonalAccessToken
end
params do params do
optional :state, type: String, default: 'all', values: %w[all active inactive], desc: 'Filters (all|active|inactive) personal_access_tokens' optional :state, type: String, default: 'all', values: %w[all active inactive], desc: 'Filters (all|active|inactive) personal_access_tokens'
end end
@ -20,7 +23,24 @@ module API
present personal_access_tokens, with: Entities::BasicPersonalAccessToken present personal_access_tokens, with: Entities::BasicPersonalAccessToken
end end
desc 'Create a personal access token' desc 'Retrieve personal access token' do
detail 'This feature was introduced in GitLab 9.0'
success Entities::BasicPersonalAccessToken
end
params do
requires :personal_access_token_id, type: Integer, desc: 'The ID of the personal access token'
end
get ':personal_access_token_id' do
personal_access_token = PersonalAccessToken.find_by(id: params[:personal_access_token_id], user_id: current_user.id)
not_found!('PersonalAccessToken') unless personal_access_token
present personal_access_token, with: Entities::BasicPersonalAccessToken
end
desc 'Create a personal access token' do
detail 'This feature was introduced in GitLab 9.0'
success Entities::BasicPersonalAccessToken
end
params do params do
requires :name, type: String, desc: 'The name of the personal access token' requires :name, type: String, desc: 'The name of the personal access token'
optional :expires_at, type: Date, desc: 'The expiration date in the format YEAR-MONTH-DAY of the personal access token' optional :expires_at, type: Date, desc: 'The expiration date in the format YEAR-MONTH-DAY of the personal access token'
@ -39,7 +59,10 @@ module API
end end
end end
desc 'Revoke a personal access token' desc 'Revoke a personal access token' do
detail 'This feature was introduced in GitLab 9.0'
success Entities::BasicPersonalAccessToken
end
params do params do
requires :personal_access_token_id, type: Integer, desc: 'The ID of the personal access token' requires :personal_access_token_id, type: Integer, desc: 'The ID of the personal access token'
end end
@ -49,7 +72,7 @@ module API
personal_access_token.revoke! personal_access_token.revoke!
present personal_access_token, with: Entities::BasicPersonalAccessToken no_content!
end end
end end
end end

View File

@ -363,72 +363,98 @@ module API
present paginate(events), with: Entities::Event present paginate(events), with: Entities::Event
end end
desc 'Retrieve personal access tokens. Available only for admins.'
params do
requires :user_id, type: Integer
optional :state, type: String, default: 'all', values: %w[all active inactive], desc: 'Filters (all|active|inactive) personal_access_tokens'
optional :impersonation, type: Boolean, default: false, desc: 'Filters only impersonation personal_access_token'
end
get ':user_id/personal_access_tokens' do
authenticated_as_admin!
user = User.find_by(id: params[:user_id])
not_found!('User') unless user
personal_access_tokens = PersonalAccessToken.and_impersonation_tokens.where(user_id: user.id)
personal_access_tokens = personal_access_tokens.impersonation if params[:impersonation]
case params[:state]
when "active"
personal_access_tokens = personal_access_tokens.active
when "inactive"
personal_access_tokens = personal_access_tokens.inactive
end
present personal_access_tokens, with: Entities::PersonalAccessToken
end
desc 'Create a personal access token. Available only for admins.'
params do params do
requires :user_id, type: Integer, desc: 'The ID of the user' requires :user_id, type: Integer, desc: 'The ID of the user'
requires :name, type: String, desc: 'The name of the personal access token'
optional :expires_at, type: Date, desc: 'The expiration date in the format YEAR-MONTH-DAY of the personal access token'
optional :scopes, type: Array, desc: 'The array of scopes of the personal access token'
optional :impersonation, type: Boolean, default: false, desc: 'The impersonation flag of the personal access token'
end end
post ':user_id/personal_access_tokens' do segment ':user_id' do
authenticated_as_admin! resource :personal_access_tokens do
before { authenticated_as_admin! }
user = User.find_by(id: params[:user_id]) desc 'Retrieve personal access tokens. Available only for admins.' do
not_found!('User') unless user detail 'This feature was introduced in GitLab 9.0'
success Entities::PersonalAccessToken
end
params do
optional :state, type: String, default: 'all', values: %w[all active inactive], desc: 'Filters (all|active|inactive) personal_access_tokens'
optional :impersonation, type: Boolean, default: false, desc: 'Filters only impersonation personal_access_tokens'
end
get do
user = User.find_by(id: params[:user_id])
not_found!('User') unless user
personal_access_token = PersonalAccessToken.generate(declared_params(include_missing: false)) personal_access_tokens = PersonalAccessToken.and_impersonation_tokens.where(user_id: user.id)
personal_access_tokens = personal_access_tokens.impersonation if params[:impersonation]
if personal_access_token.save case params[:state]
present personal_access_token, with: Entities::PersonalAccessToken when "active"
else personal_access_tokens = personal_access_tokens.active
render_validation_error!(personal_access_token) when "inactive"
personal_access_tokens = personal_access_tokens.inactive
end
present personal_access_tokens, with: Entities::PersonalAccessToken
end
desc 'Create a personal access token. Available only for admins.' do
detail 'This feature was introduced in GitLab 9.0'
success Entities::PersonalAccessToken
end
params do
requires :name, type: String, desc: 'The name of the personal access token'
optional :expires_at, type: Date, desc: 'The expiration date in the format YEAR-MONTH-DAY of the personal access token'
optional :scopes, type: Array, desc: 'The array of scopes of the personal access token'
optional :impersonation, type: Boolean, default: false, desc: 'The impersonation flag of the personal access token'
end
post do
user = User.find_by(id: params[:user_id])
not_found!('User') unless user
personal_access_token = PersonalAccessToken.generate(declared_params(include_missing: false, include_parent_namespaces: true))
if personal_access_token.save
present personal_access_token, with: Entities::PersonalAccessToken
else
render_validation_error!(personal_access_token)
end
end
desc 'Retrieve personal access token. Available only for admins.' do
detail 'This feature was introduced in GitLab 9.0'
success Entities::PersonalAccessToken
end
params do
requires :personal_access_token_id, type: Integer, desc: 'The ID of the personal access token'
end
get '/:personal_access_token_id' do
user = User.find_by(id: params[:user_id])
not_found!('User') unless user
personal_access_token = PersonalAccessToken.and_impersonation_tokens.find_by(user_id: user.id, id: params[:personal_access_token_id])
not_found!('PersonalAccessToken') unless personal_access_token
present personal_access_token, with: Entities::PersonalAccessToken
end
desc 'Revoke a personal access token. Available only for admins.' do
detail 'This feature was introduced in GitLab 9.0'
success Entities::PersonalAccessToken
end
params do
requires :personal_access_token_id, type: Integer, desc: 'The ID of the personal access token'
end
delete '/:personal_access_token_id' do
user = User.find_by(id: params[:user_id])
not_found!('User') unless user
personal_access_token = PersonalAccessToken.and_impersonation_tokens.find_by(user_id: user.id, id: params[:personal_access_token_id])
not_found!('PersonalAccessToken') unless personal_access_token
personal_access_token.revoke!
no_content!
end
end end
end end
desc 'Revoke a personal access token. Available only for admins.'
params do
requires :user_id, type: Integer, desc: 'The ID of the user'
requires :personal_access_token_id, type: Integer, desc: 'The ID of the personal access token'
end
delete ':user_id/personal_access_tokens/:personal_access_token_id' do
authenticated_as_admin!
user = User.find_by(id: params[:user_id])
not_found!('User') unless user
personal_access_token = PersonalAccessToken.and_impersonation_tokens.find_by(user_id: user.id, id: params[:personal_access_token_id])
not_found!('PersonalAccessToken') unless personal_access_token
personal_access_token.revoke!
present personal_access_token, with: Entities::PersonalAccessToken
end
end end
resource :user do resource :user do

View File

@ -52,21 +52,17 @@ describe Profiles::PersonalAccessTokensController do
let!(:inactive_personal_access_token) { create(:revoked_personal_access_token, user: user) } let!(:inactive_personal_access_token) { create(:revoked_personal_access_token, user: user) }
let!(:impersonation_personal_access_token) { create(:impersonation_personal_access_token, user: user) } let!(:impersonation_personal_access_token) { create(:impersonation_personal_access_token, user: user) }
it "retrieves active personal access tokens" do before { get :index }
get :index
it "retrieves active personal access tokens" do
expect(assigns(:active_personal_access_tokens)).to include(active_personal_access_token) expect(assigns(:active_personal_access_tokens)).to include(active_personal_access_token)
end end
it "retrieves inactive personal access tokens" do it "retrieves inactive personal access tokens" do
get :index
expect(assigns(:inactive_personal_access_tokens)).to include(inactive_personal_access_token) expect(assigns(:inactive_personal_access_tokens)).to include(inactive_personal_access_token)
end end
it "does not retrieve impersonation personal access tokens" do it "does not retrieve impersonation personal access tokens" do
get :index
expect(assigns(:active_personal_access_tokens)).not_to include(impersonation_personal_access_token) expect(assigns(:active_personal_access_tokens)).not_to include(impersonation_personal_access_token)
expect(assigns(:inactive_personal_access_tokens)).not_to include(impersonation_personal_access_token) expect(assigns(:inactive_personal_access_tokens)).not_to include(impersonation_personal_access_token)
end end

View File

@ -22,9 +22,7 @@ describe 'Admin > Users > Personal Access Tokens', feature: true, js: true do
allow_any_instance_of(PersonalAccessToken).to receive(:errors).and_return(errors) allow_any_instance_of(PersonalAccessToken).to receive(:errors).and_return(errors)
end end
before do before { login_as(admin) }
login_as(admin)
end
describe "token creation" do describe "token creation" do
it "allows creation of a token" do it "allows creation of a token" do
@ -35,15 +33,13 @@ describe 'Admin > Users > Personal Access Tokens', feature: true, js: true do
# Set date to 1st of next month # Set date to 1st of next month
find_field("Expires at").trigger('focus') find_field("Expires at").trigger('focus')
find("a[title='Next']").click find(".pika-next").click
click_on "1" click_on "1"
# Scopes # Scopes
check "api" check "api"
check "read_user" check "read_user"
check "You can impersonate the user"
click_on "Create Personal Access Token" click_on "Create Personal Access Token"
expect(active_personal_access_tokens).to have_text(name) expect(active_personal_access_tokens).to have_text(name)
expect(active_personal_access_tokens).to have_text('In') expect(active_personal_access_tokens).to have_text('In')

View File

@ -4,6 +4,7 @@ describe API::PersonalAccessTokens, api: true do
include ApiHelpers include ApiHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:not_found_token) { (PersonalAccessToken.maximum('id') || 0) + 10 }
describe "GET /personal_access_tokens" do describe "GET /personal_access_tokens" do
let!(:active_personal_access_token) { create(:personal_access_token, user: user) } let!(:active_personal_access_token) { create(:personal_access_token, user: user) }
@ -76,12 +77,38 @@ describe API::PersonalAccessTokens, api: true do
end end
end end
describe 'GET /personal_access_tokens/:personal_access_token_id' do
let!(:personal_access_token) { create(:personal_access_token, user: user, revoked: false) }
let!(:personal_access_token_of_another_user) { create(:personal_access_token, revoked: false) }
it 'returns a 404 error if personal access token not found' do
get api("/personal_access_tokens/#{not_found_token}", user)
expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 PersonalAccessToken Not Found')
end
it 'returns a 404 error if personal access token exists but it is a personal access tokens of another user' do
get api("/personal_access_tokens/#{personal_access_token_of_another_user.id}", user)
expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 PersonalAccessToken Not Found')
end
it 'returns a personal access token and does not expose token in the json response' do
get api("/personal_access_tokens/#{personal_access_token.id}", user)
expect(response).to have_http_status(200)
expect(json_response['token']).not_to be_present
end
end
describe 'DELETE /personal_access_tokens/:personal_access_token_id' do describe 'DELETE /personal_access_tokens/:personal_access_token_id' do
let!(:personal_access_token) { create(:personal_access_token, user: user, revoked: false) } let!(:personal_access_token) { create(:personal_access_token, user: user, revoked: false) }
let!(:personal_access_token_of_another_user) { create(:personal_access_token, revoked: false) } let!(:personal_access_token_of_another_user) { create(:personal_access_token, revoked: false) }
it 'returns a 404 error if personal access token not found' do it 'returns a 404 error if personal access token not found' do
delete api("/personal_access_tokens/42", user) delete api("/personal_access_tokens/#{not_found_token}", user)
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 PersonalAccessToken Not Found') expect(json_response['message']).to eq('404 PersonalAccessToken Not Found')
@ -97,11 +124,9 @@ describe API::PersonalAccessTokens, api: true do
it 'revokes a personal access token and does not expose token in the json response' do it 'revokes a personal access token and does not expose token in the json response' do
delete api("/personal_access_tokens/#{personal_access_token.id}", user) delete api("/personal_access_tokens/#{personal_access_token.id}", user)
expect(response).to have_http_status(200) expect(response).to have_http_status(204)
expect(personal_access_token.revoked).to eq(false) expect(personal_access_token.revoked).to eq(false)
expect(personal_access_token.reload.revoked).to eq(true) expect(personal_access_token.reload.revoked).to eq(true)
expect(json_response['revoked']).to eq(true)
expect(json_response['token']).not_to be_present
end end
end end
end end

View File

@ -11,6 +11,7 @@ describe API::Users, api: true do
let(:ldap_user) { create(:omniauth_user, provider: 'ldapmain') } let(:ldap_user) { create(:omniauth_user, provider: 'ldapmain') }
let(:ldap_blocked_user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') } let(:ldap_blocked_user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') }
let(:not_existing_user_id) { (User.maximum('id') || 0 ) + 10 } let(:not_existing_user_id) { (User.maximum('id') || 0 ) + 10 }
let(:not_existing_pat_id) { (PersonalAccessToken.maximum('id') || 0 ) + 10 }
describe "GET /users" do describe "GET /users" do
context "when unauthenticated" do context "when unauthenticated" do
@ -1268,7 +1269,47 @@ describe API::Users, api: true do
end end
end end
describe 'DELETE /users/:id/personal_access_tokens/:personal_access_token_id' do describe 'GET /users/:user_id/personal_access_tokens/:personal_access_token_id' do
let!(:personal_access_token) { create(:personal_access_token, user: user, revoked: false) }
let!(:impersonation_token) { create(:impersonation_personal_access_token, user: user, revoked: false) }
it 'returns 404 error if user not found' do
get api("/users/#{not_existing_user_id}/personal_access_tokens/1", admin)
expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
end
it 'returns a 404 error if personal access token not found' do
get api("/users/#{user.id}/personal_access_tokens/#{not_existing_pat_id}", admin)
expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 PersonalAccessToken Not Found')
end
it 'returns a 403 error when authenticated as normal user' do
get api("/users/#{user.id}/personal_access_tokens/#{personal_access_token.id}", user)
expect(response).to have_http_status(403)
expect(json_response['message']).to eq('403 Forbidden')
end
it 'returns a personal access token' do
get api("/users/#{user.id}/personal_access_tokens/#{personal_access_token.id}", admin)
expect(response).to have_http_status(200)
expect(json_response['token']).to be_present
end
it 'returns an impersonation token' do
get api("/users/#{user.id}/personal_access_tokens/#{impersonation_token.id}", admin)
expect(response).to have_http_status(200)
expect(json_response['impersonation']).to eq(true)
end
end
describe 'DELETE /users/:user_id/personal_access_tokens/:personal_access_token_id' do
let!(:personal_access_token) { create(:personal_access_token, user: user, revoked: false) } let!(:personal_access_token) { create(:personal_access_token, user: user, revoked: false) }
let!(:impersonation_token) { create(:impersonation_personal_access_token, user: user, revoked: false) } let!(:impersonation_token) { create(:impersonation_personal_access_token, user: user, revoked: false) }
@ -1280,7 +1321,7 @@ describe API::Users, api: true do
end end
it 'returns a 404 error if personal access token not found' do it 'returns a 404 error if personal access token not found' do
delete api("/users/#{user.id}/personal_access_tokens/42", admin) delete api("/users/#{user.id}/personal_access_tokens/#{not_existing_pat_id}", admin)
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 PersonalAccessToken Not Found') expect(json_response['message']).to eq('404 PersonalAccessToken Not Found')
@ -1296,20 +1337,17 @@ describe API::Users, api: true do
it 'revokes a personal access token' do it 'revokes a personal access token' do
delete api("/users/#{user.id}/personal_access_tokens/#{personal_access_token.id}", admin) delete api("/users/#{user.id}/personal_access_tokens/#{personal_access_token.id}", admin)
expect(response).to have_http_status(200) expect(response).to have_http_status(204)
expect(personal_access_token.revoked).to eq(false) expect(personal_access_token.revoked).to eq(false)
expect(personal_access_token.reload.revoked).to eq(true) expect(personal_access_token.reload.revoked).to eq(true)
expect(json_response['revoked']).to eq(true)
expect(json_response['token']).to be_present
end end
it 'revokes an impersonation token' do it 'revokes an impersonation token' do
delete api("/users/#{user.id}/personal_access_tokens/#{impersonation_token.id}", admin) delete api("/users/#{user.id}/personal_access_tokens/#{impersonation_token.id}", admin)
expect(response).to have_http_status(200) expect(response).to have_http_status(204)
expect(impersonation_token.revoked).to eq(false) expect(impersonation_token.revoked).to eq(false)
expect(impersonation_token.reload.revoked).to eq(true) expect(impersonation_token.reload.revoked).to eq(true)
expect(json_response['revoked']).to eq(true)
end end
end end
end end