Commit e5cf527f authored by Timothy Andrew's avatar Timothy Andrew

Allow expiration of personal access tokens.

parent 1541d1de
...@@ -207,4 +207,8 @@ ...@@ -207,4 +207,8 @@
} }
.personal-access-tokens-revoked-label { .personal-access-tokens-revoked-label {
color: #bbb; color: #bbb;
}
.personal-access-tokens-never-expires-label {
color: #bbb;
} }
\ No newline at end of file
...@@ -31,6 +31,6 @@ class Profiles::PersonalAccessTokensController < ApplicationController ...@@ -31,6 +31,6 @@ class Profiles::PersonalAccessTokensController < ApplicationController
private private
def personal_access_token_params def personal_access_token_params
params.require(:personal_access_token).permit(:name) params.require(:personal_access_token).permit(:name, :expires_at)
end end
end end
class PersonalAccessToken < ActiveRecord::Base class PersonalAccessToken < ActiveRecord::Base
belongs_to :user belongs_to :user
scope :active, -> { where.not(revoked: true) } scope :active, -> { where.not(revoked: true).where("expires_at >= :current", current: Time.current) }
def self.generate(params) def self.generate(params)
personal_access_token = self.new(params) personal_access_token = self.new(params)
......
...@@ -18,6 +18,10 @@ ...@@ -18,6 +18,10 @@
= f.label :name, class: 'label-light' = f.label :name, class: 'label-light'
= f.text_field :name, class: "form-control", required: true = f.text_field :name, class: "form-control", required: true
.form-group
= f.label :expires_at, class: 'label-light'
= f.text_field :expires_at, class: "form-control datepicker", required: false
.prepend-top-default .prepend-top-default
= f.submit 'Add Personal Access Token', class: "btn btn-create" = f.submit 'Add Personal Access Token', class: "btn btn-create"
...@@ -34,13 +38,19 @@ ...@@ -34,13 +38,19 @@
%th Name %th Name
%th Token %th Token
%th Created At %th Created At
%th Expires At
%th Actions %th Actions
%tbody %tbody
- @user.personal_access_tokens.order(:revoked).each do |token| - @user.personal_access_tokens.order("revoked, expires_at").each do |token|
%tr %tr
%td= token.name %td= token.name
%td= token.token %td= token.token
%td= token.created_at %td= token.created_at
- if token.expires_at.present?
%td= token.expires_at.to_date
- else
%td
%span.personal-access-tokens-never-expires-label Never
- if token.revoked? - if token.revoked?
%td %td
%span.personal-access-tokens-revoked-label Revoked %span.personal-access-tokens-revoked-label Revoked
...@@ -48,4 +58,10 @@ ...@@ -48,4 +58,10 @@
%td= link_to "Revoke", revoke_profile_personal_access_token_path(token), method: :put, class: "btn btn-danger", data: {confirm: t('profile.personal_access_tokens.revoke.confirmation')} %td= link_to "Revoke", revoke_profile_personal_access_token_path(token), method: :put, class: "btn btn-danger", data: {confirm: t('profile.personal_access_tokens.revoke.confirmation')}
- else - else
%span You don't have any tokens yet. %span You don't have any tokens yet.
\ No newline at end of file
:javascript
$(".datepicker").datepicker({
dateFormat: "yy-mm-dd",
onSelect: function(dateText, inst) { $("#personal_access_token_expires_at").val(dateText) }
}).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', $('#personal_access_token_expires_at').val()));
\ No newline at end of file
class AddColumnExpiresAtToPersonalAccessTokens < ActiveRecord::Migration
def change
add_column :personal_access_tokens, :expires_at, :datetime
end
end
FactoryGirl.define do
factory :personal_access_token do
user
token { SecureRandom.hex(50) }
name { FFaker::Product.brand }
revoked false
expires_at { 5.days.from_now }
end
end
...@@ -41,24 +41,64 @@ describe API::Helpers::Authentication, api: true do ...@@ -41,24 +41,64 @@ describe API::Helpers::Authentication, api: true do
end end
describe ".current_user" do describe ".current_user" do
it "should return nil for an invalid token" do describe "when authenticating using a user's private token" do
env[API::Helpers::Authentication::PRIVATE_TOKEN_HEADER] = 'invalid token' it "should return nil for an invalid token" do
allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false } env[API::Helpers::Authentication::PRIVATE_TOKEN_HEADER] = 'invalid token'
expect(current_user).to be_nil allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false }
expect(current_user).to be_nil
end
it "should return nil for a user without access" do
env[API::Helpers::Authentication::PRIVATE_TOKEN_HEADER] = user.private_token
allow(Gitlab::UserAccess).to receive(:allowed?).and_return(false)
expect(current_user).to be_nil
end
it "should leave user as is when sudo not specified" do
env[API::Helpers::Authentication::PRIVATE_TOKEN_HEADER] = user.private_token
expect(current_user).to eq(user)
clear_env
params[API::Helpers::Authentication::PRIVATE_TOKEN_PARAM] = user.private_token
expect(current_user).to eq(user)
end
end end
it "should return nil for a user without access" do describe "when authenticating using a user's personal access tokens" do
env[API::Helpers::Authentication::PRIVATE_TOKEN_HEADER] = user.private_token let(:personal_access_token) { create(:personal_access_token, user: user) }
allow(Gitlab::UserAccess).to receive(:allowed?).and_return(false)
expect(current_user).to be_nil it "should return nil for an invalid token" do
end env[API::Helpers::Authentication::PERSONAL_ACCESS_TOKEN_HEADER] = 'invalid token'
allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false }
it "should leave user as is when sudo not specified" do expect(current_user).to be_nil
env[API::Helpers::Authentication::PRIVATE_TOKEN_HEADER] = user.private_token end
expect(current_user).to eq(user)
clear_env it "should return nil for a user without access" do
params[API::Helpers::Authentication::PRIVATE_TOKEN_PARAM] = user.private_token env[API::Helpers::Authentication::PERSONAL_ACCESS_TOKEN_HEADER] = personal_access_token.token
expect(current_user).to eq(user) allow(Gitlab::UserAccess).to receive(:allowed?).and_return(false)
expect(current_user).to be_nil
end
it "should leave user as is when sudo not specified" do
env[API::Helpers::Authentication::PERSONAL_ACCESS_TOKEN_HEADER] = personal_access_token.token
expect(current_user).to eq(user)
clear_env
params[API::Helpers::Authentication::PERSONAL_ACCESS_TOKEN_PARAM] = personal_access_token.token
expect(current_user).to eq(user)
end
it 'does not allow revoked tokens' do
personal_access_token.revoke!
env[API::Helpers::Authentication::PERSONAL_ACCESS_TOKEN_HEADER] = personal_access_token.token
allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false }
expect(current_user).to be_nil
end
it 'does not allow expired tokens' do
personal_access_token.update_attributes!(expires_at: 1.day.ago)
env[API::Helpers::Authentication::PERSONAL_ACCESS_TOKEN_HEADER] = personal_access_token.token
allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false }
expect(current_user).to be_nil
end
end end
it "should change current user to sudo when admin" do it "should change current user to sudo when admin" do
......
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