Commit f3cd24a9 authored by Imre Farkas's avatar Imre Farkas

Display impersonation token value only after creation

Since we migrated all PersonlAccessTokens to store only its hash in the
DB, the token value can no longer be shown to the user.
parent 369631c8
...@@ -11,6 +11,7 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController ...@@ -11,6 +11,7 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController
@impersonation_token = finder.build(impersonation_token_params) @impersonation_token = finder.build(impersonation_token_params)
if @impersonation_token.save if @impersonation_token.save
PersonalAccessToken.redis_store!(current_user.id, @impersonation_token.token)
redirect_to admin_user_impersonation_tokens_path, notice: "A new impersonation token has been created." redirect_to admin_user_impersonation_tokens_path, notice: "A new impersonation token has been created."
else else
set_index_vars set_index_vars
...@@ -53,6 +54,8 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController ...@@ -53,6 +54,8 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController
@impersonation_token ||= finder.build @impersonation_token ||= finder.build
@inactive_impersonation_tokens = finder(state: 'inactive').execute @inactive_impersonation_tokens = finder(state: 'inactive').execute
@active_impersonation_tokens = finder(state: 'active').execute.order(:expires_at) @active_impersonation_tokens = finder(state: 'active').execute.order(:expires_at)
@new_impersonation_token = PersonalAccessToken.redis_getdel(current_user.id)
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
end end
...@@ -5,6 +5,11 @@ ...@@ -5,6 +5,11 @@
.row.prepend-top-default .row.prepend-top-default
.col-lg-12 .col-lg-12
- if @new_impersonation_token
= render "shared/personal_access_tokens_created_container", new_token_value: @new_impersonation_token,
container_title: 'Your New Impersonation Token',
clipboard_button_title: 'Copy impersonation token to clipboard'
= render "shared/personal_access_tokens_form", path: admin_user_impersonation_tokens_path, impersonation: true, token: @impersonation_token, scopes: @scopes = render "shared/personal_access_tokens_form", path: admin_user_impersonation_tokens_path, impersonation: true, token: @impersonation_token, scopes: @scopes
= render "shared/personal_access_tokens_table", impersonation: true, active_tokens: @active_impersonation_tokens, inactive_tokens: @inactive_impersonation_tokens = render "shared/personal_access_tokens_table", impersonation: true, active_tokens: @active_impersonation_tokens, inactive_tokens: @inactive_impersonation_tokens
...@@ -14,17 +14,7 @@ ...@@ -14,17 +14,7 @@
.col-lg-8 .col-lg-8
- if @new_personal_access_token - if @new_personal_access_token
.created-personal-access-token-container = render "shared/personal_access_tokens_created_container", new_token_value: @new_personal_access_token
%h5.prepend-top-0
Your New Personal Access Token
.form-group
.input-group
= text_field_tag 'created-personal-access-token', @new_personal_access_token, readonly: true, class: "form-control js-select-on-focus", 'aria-describedby' => "created-personal-access-token-help-block"
%span.input-group-append
= clipboard_button(text: @new_personal_access_token, title: "Copy personal access token to clipboard", placement: "left", class: "input-group-text btn-default btn-clipboard")
%span#created-personal-access-token-help-block.form-text.text-muted.text-danger Make sure you save it - you won't be able to access it again.
%hr
= render "shared/personal_access_tokens_form", path: profile_personal_access_tokens_path, impersonation: false, token: @personal_access_token, scopes: @scopes = render "shared/personal_access_tokens_form", path: profile_personal_access_tokens_path, impersonation: false, token: @personal_access_token, scopes: @scopes
......
- container_title = local_assigns.fetch(:container_title, 'Your New Personal Access Token')
- clipboard_button_title = local_assigns.fetch(:clipboard_button_title, 'Copy personal access token to clipboard')
.created-personal-access-token-container
%h5.prepend-top-0
= container_title
.form-group
.input-group
= text_field_tag 'created-personal-access-token', new_token_value, readonly: true, class: "form-control js-select-on-focus", 'aria-describedby' => "created-token-help-block"
%span.input-group-append
= clipboard_button(text: new_token_value, title: clipboard_button_title, placement: "left", class: "input-group-text btn-default btn-clipboard")
%span#created-token-help-block.form-text.text-muted.text-danger Make sure you save it - you won't be able to access it again.
%hr
...@@ -15,8 +15,6 @@ ...@@ -15,8 +15,6 @@
%th Created %th Created
%th Expires %th Expires
%th Scopes %th Scopes
- if impersonation
%th Token
%th %th
%tbody %tbody
- active_tokens.each do |token| - active_tokens.each do |token|
...@@ -30,10 +28,6 @@ ...@@ -30,10 +28,6 @@
- else - else
%span.token-never-expires-label Never %span.token-never-expires-label Never
%td= token.scopes.present? ? token.scopes.join(", ") : "<no scopes selected>" %td= token.scopes.present? ? token.scopes.join(", ") : "<no scopes selected>"
- if impersonation
%td.token-token-container
= text_field_tag 'impersonation-token-token', token.token, readonly: true, class: "form-control"
= clipboard_button(text: token.token)
- path = impersonation ? revoke_admin_user_impersonation_token_path(token.user, token) : revoke_profile_personal_access_token_path(token) - path = impersonation ? revoke_admin_user_impersonation_token_path(token.user, token) : revoke_profile_personal_access_token_path(token)
%td= link_to "Revoke", path, method: :put, class: "btn btn-danger float-right", data: { confirm: "Are you sure you want to revoke this #{type} Token? This action cannot be undone." } %td= link_to "Revoke", path, method: :put, class: "btn btn-danger float-right", data: { confirm: "Are you sure you want to revoke this #{type} Token? This action cannot be undone." }
- else - else
......
---
title: Display impersonation token value only after creation
merge_request: 22916
author:
type: fixed
...@@ -1072,7 +1072,6 @@ Example response: ...@@ -1072,7 +1072,6 @@ Example response:
[ [
{ {
"active" : true, "active" : true,
"token" : "EsMo-vhKfXGwX9RKrwiy",
"scopes" : [ "scopes" : [
"api" "api"
], ],
...@@ -1089,7 +1088,6 @@ Example response: ...@@ -1089,7 +1088,6 @@ Example response:
"read_user" "read_user"
], ],
"revoked" : true, "revoked" : true,
"token" : "ZcZRpLeEuQRprkRjYydY",
"name" : "mytoken2", "name" : "mytoken2",
"created_at" : "2017-03-17T17:19:28.697Z", "created_at" : "2017-03-17T17:19:28.697Z",
"id" : 3, "id" : 3,
...@@ -1125,7 +1123,6 @@ Example response: ...@@ -1125,7 +1123,6 @@ Example response:
```json ```json
{ {
"active" : true, "active" : true,
"token" : "EsMo-vhKfXGwX9RKrwiy",
"scopes" : [ "scopes" : [
"api" "api"
], ],
...@@ -1142,6 +1139,8 @@ Example response: ...@@ -1142,6 +1139,8 @@ Example response:
> Requires admin permissions. > Requires admin permissions.
> Token values are returned once. Make sure you save it - you won't be able to access it again.
It creates a new impersonation token. Note that only administrators can do this. It creates a new impersonation token. Note that only administrators can do this.
You are only able to create impersonation tokens to impersonate the user and perform 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 their profile both API calls and Git reads and writes. The user will not see these tokens in their profile
......
...@@ -1263,7 +1263,11 @@ module API ...@@ -1263,7 +1263,11 @@ module API
expose :token expose :token
end end
class ImpersonationToken < PersonalAccessTokenWithToken class ImpersonationToken < PersonalAccessToken
expose :impersonation
end
class ImpersonationTokenWithToken < PersonalAccessTokenWithToken
expose :impersonation expose :impersonation
end end
......
...@@ -531,7 +531,7 @@ module API ...@@ -531,7 +531,7 @@ module API
desc 'Create a impersonation token. Available only for admins.' do desc 'Create a impersonation token. Available only for admins.' do
detail 'This feature was introduced in GitLab 9.0' detail 'This feature was introduced in GitLab 9.0'
success Entities::ImpersonationToken success Entities::ImpersonationTokenWithToken
end end
params do params do
requires :name, type: String, desc: 'The name of the impersonation token' requires :name, type: String, desc: 'The name of the impersonation token'
...@@ -542,7 +542,7 @@ module API ...@@ -542,7 +542,7 @@ module API
impersonation_token = finder.build(declared_params(include_missing: false)) impersonation_token = finder.build(declared_params(include_missing: false))
if impersonation_token.save if impersonation_token.save
present impersonation_token, with: Entities::ImpersonationToken present impersonation_token, with: Entities::ImpersonationTokenWithToken
else else
render_validation_error!(impersonation_token) render_validation_error!(impersonation_token)
end end
......
...@@ -8,7 +8,7 @@ module QA ...@@ -8,7 +8,7 @@ module QA
element :scopes_api_radios, "label :scopes" # rubocop:disable QA/ElementWithPattern element :scopes_api_radios, "label :scopes" # rubocop:disable QA/ElementWithPattern
end end
view 'app/views/profiles/personal_access_tokens/index.html.haml' do view 'app/views/shared/_personal_access_tokens_created_container.html.haml' do
element :create_token_field, "text_field_tag 'created-personal-access-token'" # rubocop:disable QA/ElementWithPattern element :create_token_field, "text_field_tag 'created-personal-access-token'" # rubocop:disable QA/ElementWithPattern
end end
......
...@@ -39,8 +39,10 @@ describe Profiles::PersonalAccessTokensController do ...@@ -39,8 +39,10 @@ describe Profiles::PersonalAccessTokensController do
let!(:active_personal_access_token) { create(:personal_access_token, user: user) } let!(:active_personal_access_token) { create(:personal_access_token, user: user) }
let!(:inactive_personal_access_token) { create(:personal_access_token, :revoked, user: user) } let!(:inactive_personal_access_token) { create(:personal_access_token, :revoked, user: user) }
let!(:impersonation_personal_access_token) { create(:personal_access_token, :impersonation, user: user) } let!(:impersonation_personal_access_token) { create(:personal_access_token, :impersonation, user: user) }
let(:token_value) { 's3cr3t' }
before do before do
PersonalAccessToken.redis_store!(user.id, token_value)
get :index get :index
end end
...@@ -56,5 +58,9 @@ describe Profiles::PersonalAccessTokensController do ...@@ -56,5 +58,9 @@ describe Profiles::PersonalAccessTokensController do
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
it "retrieves newly created personal access token value" do
expect(assigns(:new_personal_access_token)).to eql(token_value)
end
end end
end end
...@@ -12,6 +12,10 @@ describe 'Admin > Users > Impersonation Tokens', :js do ...@@ -12,6 +12,10 @@ describe 'Admin > Users > Impersonation Tokens', :js do
find(".settings-message") find(".settings-message")
end end
def created_impersonation_token
find("#created-personal-access-token").value
end
before do before do
sign_in(admin) sign_in(admin)
end end
...@@ -39,6 +43,7 @@ describe 'Admin > Users > Impersonation Tokens', :js do ...@@ -39,6 +43,7 @@ describe 'Admin > Users > Impersonation Tokens', :js do
expect(active_impersonation_tokens).to have_text('api') expect(active_impersonation_tokens).to have_text('api')
expect(active_impersonation_tokens).to have_text('read_user') expect(active_impersonation_tokens).to have_text('read_user')
expect(PersonalAccessTokensFinder.new(impersonation: true).execute.count).to equal(1) expect(PersonalAccessTokensFinder.new(impersonation: true).execute.count).to equal(1)
expect(created_impersonation_token).not_to be_empty
end end
end end
......
...@@ -43,10 +43,12 @@ describe 'Profile > Personal Access Tokens', :js do ...@@ -43,10 +43,12 @@ describe 'Profile > Personal Access Tokens', :js do
check "read_user" check "read_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')
expect(active_personal_access_tokens).to have_text('api') expect(active_personal_access_tokens).to have_text('api')
expect(active_personal_access_tokens).to have_text('read_user') expect(active_personal_access_tokens).to have_text('read_user')
expect(created_personal_access_token).not_to be_empty
end end
context "when creation fails" do context "when creation fails" do
...@@ -57,6 +59,7 @@ describe 'Profile > Personal Access Tokens', :js do ...@@ -57,6 +59,7 @@ describe 'Profile > Personal Access Tokens', :js do
expect { click_on "Create personal access token" }.not_to change { PersonalAccessToken.count } expect { click_on "Create personal access token" }.not_to change { PersonalAccessToken.count }
expect(page).to have_content("Name cannot be nil") expect(page).to have_content("Name cannot be nil")
expect(page).not_to have_selector("#created-personal-access-token")
end end
end end
end end
......
...@@ -2018,11 +2018,11 @@ describe API::Users do ...@@ -2018,11 +2018,11 @@ describe API::Users do
expect(json_response['message']).to eq('403 Forbidden') expect(json_response['message']).to eq('403 Forbidden')
end end
it 'returns a personal access token' do it 'returns an impersonation token' do
get api("/users/#{user.id}/impersonation_tokens/#{impersonation_token.id}", admin) get api("/users/#{user.id}/impersonation_tokens/#{impersonation_token.id}", admin)
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(json_response['token']).to be_present expect(json_response['token']).not_to be_present
expect(json_response['impersonation']).to be_truthy expect(json_response['impersonation']).to be_truthy
end end
end end
......
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