diff options
7 files changed, 94 insertions, 20 deletions
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 5a4d0a5c8b0..8c62c97215f 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -207,4 +207,8 @@ } .personal-access-tokens-revoked-label { color: #bbb; +} + +.personal-access-tokens-never-expires-label { + color: #bbb; }
\ No newline at end of file diff --git a/app/controllers/profiles/personal_access_tokens_controller.rb b/app/controllers/profiles/personal_access_tokens_controller.rb index 59de0b26eee..d01afbfe119 100644 --- a/app/controllers/profiles/personal_access_tokens_controller.rb +++ b/app/controllers/profiles/personal_access_tokens_controller.rb @@ -31,6 +31,6 @@ class Profiles::PersonalAccessTokensController < ApplicationController private def personal_access_token_params - params.require(:personal_access_token).permit(:name) + params.require(:personal_access_token).permit(:name, :expires_at) end end diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb index e5f1f9749f8..dd64374481f 100644 --- a/app/models/personal_access_token.rb +++ b/app/models/personal_access_token.rb @@ -1,7 +1,7 @@ class PersonalAccessToken < ActiveRecord::Base 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) personal_access_token = self.new(params) diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml index 02d15269c85..f3d5f07cdd3 100644 --- a/app/views/profiles/personal_access_tokens/index.html.haml +++ b/app/views/profiles/personal_access_tokens/index.html.haml @@ -18,6 +18,10 @@ = 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: "form-control datepicker", required: false + .prepend-top-default = f.submit 'Add Personal Access Token', class: "btn btn-create" @@ -34,13 +38,19 @@ %th Name %th Token %th Created At + %th Expires At %th Actions %tbody - - @user.personal_access_tokens.order(:revoked).each do |token| + - @user.personal_access_tokens.order("revoked, expires_at").each do |token| %tr %td= token.name %td= token.token %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? %td %span.personal-access-tokens-revoked-label Revoked @@ -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')} - else - %span You don't have any tokens yet.
\ No newline at end of file + %span You don't have any tokens yet. + +: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 diff --git a/db/migrate/20160418085954_add_column_expires_at_to_personal_access_tokens.rb b/db/migrate/20160418085954_add_column_expires_at_to_personal_access_tokens.rb new file mode 100644 index 00000000000..9d99ebeadf5 --- /dev/null +++ b/db/migrate/20160418085954_add_column_expires_at_to_personal_access_tokens.rb @@ -0,0 +1,5 @@ +class AddColumnExpiresAtToPersonalAccessTokens < ActiveRecord::Migration + def change + add_column :personal_access_tokens, :expires_at, :datetime + end +end diff --git a/spec/factories/personal_access_tokens.rb b/spec/factories/personal_access_tokens.rb new file mode 100644 index 00000000000..da4c72bcb5b --- /dev/null +++ b/spec/factories/personal_access_tokens.rb @@ -0,0 +1,9 @@ +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 diff --git a/spec/requests/api/api_authentication_spec.rb b/spec/requests/api/api_authentication_spec.rb index 8bed3bb119b..0416e57c68b 100644 --- a/spec/requests/api/api_authentication_spec.rb +++ b/spec/requests/api/api_authentication_spec.rb @@ -41,24 +41,64 @@ describe API::Helpers::Authentication, api: true do end describe ".current_user" do - it "should return nil for an invalid token" do - env[API::Helpers::Authentication::PRIVATE_TOKEN_HEADER] = 'invalid token' - allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false } - expect(current_user).to be_nil + describe "when authenticating using a user's private token" do + it "should return nil for an invalid token" do + env[API::Helpers::Authentication::PRIVATE_TOKEN_HEADER] = 'invalid token' + 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 - 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) + describe "when authenticating using a user's personal access tokens" do + let(:personal_access_token) { create(:personal_access_token, user: user) } + + it "should return nil for an invalid token" do + env[API::Helpers::Authentication::PERSONAL_ACCESS_TOKEN_HEADER] = 'invalid token' + 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::PERSONAL_ACCESS_TOKEN_HEADER] = personal_access_token.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::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 it "should change current user to sudo when admin" do |