Skip to content
Snippets Groups Projects
Commit c2b1cdef authored by Simon Vocella's avatar Simon Vocella Committed by Tiago Botelho
Browse files

add admin panel for personal access tokens

parent 09dd6a7e
No related branches found
No related tags found
No related merge requests found
Loading
@@ -24,3 +24,14 @@
Loading
@@ -24,3 +24,14 @@
.service-settings .control-label { .service-settings .control-label {
padding-top: 0; padding-top: 0;
} }
.personal-access-token-token-container {
#personal-access-token-token {
width: 80%;
display: inline;
}
.btn-clipboard {
margin-left: 5px;
}
}
class Admin::PersonalAccessTokensController < Admin::ApplicationController
before_action :user
def index
set_index_vars
end
def create
@personal_access_token = user.personal_access_tokens.generate(personal_access_token_params)
if @personal_access_token.save
flash[:personal_access_token] = @personal_access_token.token
redirect_to admin_user_personal_access_tokens_path, notice: "A new personal access token has been created."
else
set_index_vars
render :index
end
end
def revoke
@personal_access_token = user.personal_access_tokens.find(params[:id])
if @personal_access_token.revoke!
flash[:notice] = "Revoked personal access token #{@personal_access_token.name}!"
else
flash[:alert] = "Could not revoke personal access token #{@personal_access_token.name}."
end
redirect_to admin_user_personal_access_tokens_path
end
private
def user
@user ||= User.find_by!(username: params[:user_id])
end
def personal_access_token_params
params.require(:personal_access_token).permit(:name, :expires_at, :impersonation, scopes: [])
end
def set_index_vars
@personal_access_token ||= user.personal_access_tokens.build
@scopes = Gitlab::Auth::SCOPES
@active_personal_access_tokens = PersonalAccessToken.and_impersonation_tokens.where(user_id: user.id).active.order(:expires_at)
@inactive_personal_access_tokens = PersonalAccessToken.and_impersonation_tokens.where(user_id: user.id).inactive
end
end
- 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"
- page_title "Personal Access Tokens"
= render 'admin/users/head'
.row.prepend-top-default
.col-lg-12
%h5.prepend-top-0
Add a Personal Access Token
%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
%hr
%h5 Active Personal Access Tokens (#{@active_personal_access_tokens.length})
- if @active_personal_access_tokens.present?
.table-responsive
%table.table.active-personal-access-tokens
%thead
%tr
%th Name
%th Created
%th Expires
%th Scopes
%th Token
%th Impersonation
%th
%tbody
- @active_personal_access_tokens.each do |personal_access_token|
%tr
%td= personal_access_token.name
%td= personal_access_token.created_at.to_date.to_s(:medium)
%td
- if personal_access_token.expires?
%span{ class: ('text-warning' if personal_access_token.expires_soon?) }
In #{distance_of_time_in_words_to_now(personal_access_token.expires_at)}
- else
%span.personal-access-personal_access_tokens-never-expires-label Never
%td= personal_access_token.scopes.present? ? personal_access_token.scopes.join(", ") : "<no scopes selected>"
%td.personal-access-token-token-container
= text_field_tag 'personal-access-token-token', personal_access_token.token, readonly: true, class: "form-control"
= clipboard_button(clipboard_text: personal_access_token.token)
%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." }
- else
.settings-message.text-center
This user has no active tokens.
%hr
%h5 Inactive Personal Access Tokens (#{@inactive_personal_access_tokens.length})
- if @inactive_personal_access_tokens.present?
.table-responsive
%table.table.inactive-personal-access-tokens
%thead
%tr
%th Name
%th Created
%tbody
- @inactive_personal_access_tokens.each do |token|
%tr
%td= token.name
%td= token.created_at.to_date.to_s(:medium)
- else
.settings-message.text-center
This user has no inactive tokens.
:javascript
var date = $('#personal_access_token_expires_at').val();
var datepicker = $(".datepicker").datepicker({
dateFormat: "yy-mm-dd",
minDate: 0
});
Loading
@@ -21,4 +21,6 @@
Loading
@@ -21,4 +21,6 @@
= link_to "SSH keys", keys_admin_user_path(@user) = link_to "SSH keys", keys_admin_user_path(@user)
= nav_link(controller: :identities) do = nav_link(controller: :identities) do
= link_to "Identities", admin_user_identities_path(@user) = link_to "Identities", admin_user_identities_path(@user)
= nav_link(controller: :personal_access_tokens) do
= link_to "Access Tokens", admin_user_personal_access_tokens_path(@user)
.append-bottom-default .append-bottom-default
Loading
@@ -2,6 +2,11 @@ namespace :admin do
Loading
@@ -2,6 +2,11 @@ namespace :admin do
resources :users, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ } do resources :users, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ } do
resources :keys, only: [:show, :destroy] resources :keys, only: [:show, :destroy]
resources :identities, except: [:show] resources :identities, except: [:show]
resources :personal_access_tokens, only: [:index, :create] do
member do
put :revoke
end
end
   
member do member do
get :projects get :projects
Loading
Loading
require 'spec_helper'
describe 'Admin > Users > Personal Access Tokens', feature: true, js: true do
let(:admin) { create(:admin) }
let!(:user) { create(:user) }
def active_personal_access_tokens
find(".table.active-personal-access-tokens")
end
def inactive_personal_access_tokens
find(".table.inactive-personal-access-tokens")
end
def created_personal_access_token
find("#created-personal-access-token").value
end
def disallow_personal_access_token_saves!
allow_any_instance_of(PersonalAccessToken).to receive(:save).and_return(false)
errors = ActiveModel::Errors.new(PersonalAccessToken.new).tap { |e| e.add(:name, "cannot be nil") }
allow_any_instance_of(PersonalAccessToken).to receive(:errors).and_return(errors)
end
before do
login_as(admin)
end
describe "token creation" do
it "allows creation of a token" do
name = FFaker::Product.brand
visit admin_user_personal_access_tokens_path(user_id: user.username)
fill_in "Name", with: name
# Set date to 1st of next month
find_field("Expires at").trigger('focus')
find("a[title='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')
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('true')
end
context "when creation fails" do
it "displays an error message" do
disallow_personal_access_token_saves!
visit admin_user_personal_access_tokens_path(user_id: user.username)
fill_in "Name", with: FFaker::Product.brand
expect { click_on "Create Personal Access Token" }.not_to change { PersonalAccessToken.count }
expect(page).to have_content("Name cannot be nil")
end
end
end
describe "inactive tokens" do
let!(:personal_access_token) { create(:personal_access_token, user: user) }
it "allows revocation of an active token" do
visit admin_user_personal_access_tokens_path(user_id: user.username)
click_on "Revoke"
expect(inactive_personal_access_tokens).to have_text(personal_access_token.name)
end
it "moves expired tokens to the 'inactive' section" do
personal_access_token.update(expires_at: 5.days.ago)
visit admin_user_personal_access_tokens_path(user_id: user.username)
expect(inactive_personal_access_tokens).to have_text(personal_access_token.name)
end
context "when revocation fails" do
it "displays an error message" do
disallow_personal_access_token_saves!
visit admin_user_personal_access_tokens_path(user_id: user.username)
click_on "Revoke"
expect(active_personal_access_tokens).to have_text(personal_access_token.name)
expect(page).to have_content("Could not revoke")
end
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment