Commit 5187a14e by Tiago

refactors documentation and personal access tokens form to not allow admins to…

refactors documentation and personal access tokens form to not allow admins to generate non impersionation tokens
parent 4a88c527
Pipeline #6613695 passed with stages
in 86 minutes 36 seconds
......@@ -6,7 +6,8 @@ class Admin::PersonalAccessTokensController < Admin::ApplicationController
end
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
flash[:personal_access_token] = @personal_access_token.token
......
- 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"
......@@ -9,7 +9,7 @@
%p.profile-settings-content
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 "profiles/personal_access_tokens/form", user: :admin_user, personal_access_token: @personal_access_token, scopes: @scopes
%hr
......@@ -72,9 +72,15 @@
:javascript
var date = $('#personal_access_token_expires_at').val();
var $dateField = $('#personal_access_token_expires_at');
var date = $dateField.val();
var datepicker = $(".datepicker").datepicker({
dateFormat: "yy-mm-dd",
minDate: 0
new Pikaday({
field: $dateField.get(0),
theme: 'gitlab-theme',
format: 'YYYY-MM-DD',
minDate: new Date(),
onSelect: function(dateText) {
$dateField.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
}
});
- personal_access_token = local_assigns.fetch(:personal_access_token)
- 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)
......
......@@ -29,7 +29,7 @@
%p.profile-settings-content
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
......
......@@ -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
following locations:
- [Access Tokens](personal_access_tokens.md)
- [Personal Access Tokens](personal_access_tokens.md)
- [Award Emoji](award_emoji.md)
- [Branches](branches.md)
- [Broadcast Messages](broadcast_messages.md)
......
......@@ -14,13 +14,13 @@ An example:
"name": "mytoken",
"revoked": false,
"expires_at": "2017-01-04",
"scopes": ['api'],
"scopes": ["api"],
"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
......
......@@ -860,7 +860,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
......@@ -883,9 +883,9 @@ GET /users/:user_id/personal_access_tokens?impersonation=true
## Create a personal access token
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
performing both API calls and Git reads and writes. The user will not see these
tokens in his profile settings.
You are only able to create impersonation tokens to impersonate the user and perform
both API calls and Git reads and writes. The user will not see these tokens in his profile
settings page.
```
POST /users/:user_id/personal_access_tokens
......
......@@ -363,71 +363,89 @@ module API
present paginate(events), with: Entities::Event
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!
segment '/:user_id' do
resource '/personal_access_tokens' do
desc 'Retrieve personal access tokens. Available only for admins.'
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_token'
end
get do
authenticated_as_admin!
user = User.find_by(id: params[:user_id])
not_found!('User') unless user
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]
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
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
present personal_access_tokens, with: Entities::PersonalAccessToken
end
desc 'Create a personal access token. Available only for admins.'
params do
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
post ':user_id/personal_access_tokens' do
authenticated_as_admin!
desc 'Create a personal access token. Available only for admins.'
params do
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
post do
authenticated_as_admin!
user = User.find_by(id: params[:user_id])
not_found!('User') unless user
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_token = PersonalAccessToken.generate(declared_params(include_missing: false))
if personal_access_token.save
present personal_access_token, with: Entities::PersonalAccessToken
else
render_validation_error!(personal_access_token)
end
end
if personal_access_token.save
present personal_access_token, with: Entities::PersonalAccessToken
else
render_validation_error!(personal_access_token)
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!
desc 'Retrieve personal access token. Available only for admins.'
params do
requires :personal_access_token_id, type: Integer, desc: 'The ID of the personal access token'
end
get '/:personal_access_token_id' do
authenticated_as_admin!
user = User.find_by(id: params[:user_id])
not_found!('User') unless user
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 = 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
present personal_access_token, with: Entities::PersonalAccessToken
desc 'Revoke a personal access token. Available only for admins.'
params do
requires :personal_access_token_id, type: Integer, desc: 'The ID of the personal access token'
end
delete '/: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
end
......
......@@ -35,15 +35,13 @@ describe 'Admin > Users > Personal Access Tokens', feature: true, js: true do
# Set date to 1st of next month
find_field("Expires at").trigger('focus')
find("a[title='Next']").click
find(".pika-next").click
click_on "1"
# Scopes
check "api"
check "read_user"
check "You can impersonate the user"
click_on "Create Personal Access Token"
expect(active_personal_access_tokens).to have_text(name)
expect(active_personal_access_tokens).to have_text('In')
......
......@@ -1260,10 +1260,51 @@ describe API::Users, api: true do
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/50", 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!(:impersonation_token) { create(:impersonation_personal_access_token, user: user, revoked: false) }
let(:not_existing_pat_id) { (PersonalAccessToken.maximum('id') || 0 ) + 10 }
it 'returns a 404 error if user not found' do
delete api("/users/#{not_existing_user_id}/personal_access_tokens/1", admin)
......@@ -1272,7 +1313,7 @@ describe API::Users, api: true do
end
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(json_response['message']).to eq('404 PersonalAccessToken Not Found')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment