Commit c2b1cdef authored by Simon Vocella's avatar Simon Vocella Committed by Tiago Botelho

add admin panel for personal access tokens

parent 09dd6a7e
...@@ -24,3 +24,14 @@ ...@@ -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
});
...@@ -21,4 +21,6 @@ ...@@ -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
...@@ -2,6 +2,11 @@ namespace :admin do ...@@ -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
......
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
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