Require explicit scopes on personal access tokens
Gitlab::Auth and API::APIGuard already check for at least one valid scope on personal access tokens, so if the scopes are empty the token will always fail validation.
This commit is contained in:
parent
eefbc83730
commit
8699c8338f
4 changed files with 26 additions and 29 deletions
|
@ -9,7 +9,8 @@ class PersonalAccessToken < ActiveRecord::Base
|
|||
scope :active, -> { where(revoked: false).where("expires_at >= NOW() OR expires_at IS NULL") }
|
||||
scope :inactive, -> { where("revoked = true OR expires_at < NOW()") }
|
||||
|
||||
validate :validate_scopes
|
||||
validates :scopes, presence: true
|
||||
validate :validate_api_scopes
|
||||
|
||||
def self.generate(params)
|
||||
personal_access_token = self.new(params)
|
||||
|
@ -24,8 +25,8 @@ class PersonalAccessToken < ActiveRecord::Base
|
|||
|
||||
protected
|
||||
|
||||
def validate_scopes
|
||||
unless Set.new(scopes.map(&:to_sym)).subset?(Set.new(Gitlab::Auth::API_SCOPES))
|
||||
def validate_api_scopes
|
||||
unless scopes.all? { |scope| Gitlab::Auth::API_SCOPES.include?(scope.to_sym) }
|
||||
errors.add :scopes, "can only contain API scopes"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,10 +5,13 @@ module Gitlab
|
|||
# Scopes used for GitLab API access
|
||||
API_SCOPES = [:api, :read_user].freeze
|
||||
|
||||
# Scopes used by doorkeeper-openid_connect
|
||||
# Scopes used for OpenID Connect
|
||||
OPENID_SCOPES = [:openid].freeze
|
||||
|
||||
# Default scopes for OAuth applications that don't define their own
|
||||
DEFAULT_SCOPES = [:api].freeze
|
||||
|
||||
# Other available scopes
|
||||
OPTIONAL_SCOPES = (API_SCOPES + OPENID_SCOPES - DEFAULT_SCOPES).freeze
|
||||
|
||||
class << self
|
||||
|
|
|
@ -2,6 +2,7 @@ require 'spec_helper'
|
|||
|
||||
describe Profiles::PersonalAccessTokensController do
|
||||
let(:user) { create(:user) }
|
||||
let(:token_attributes) { attributes_for(:personal_access_token) }
|
||||
|
||||
describe '#create' do
|
||||
def created_token
|
||||
|
@ -10,40 +11,24 @@ describe Profiles::PersonalAccessTokensController do
|
|||
|
||||
before { sign_in(user) }
|
||||
|
||||
it "allows creation of a token" do
|
||||
name = FFaker::Product.brand
|
||||
it "allows creation of a token with scopes" do
|
||||
scopes = %w[api read_user]
|
||||
|
||||
post :create, personal_access_token: { name: name }
|
||||
post :create, personal_access_token: token_attributes.merge(scopes: scopes)
|
||||
|
||||
expect(created_token).not_to be_nil
|
||||
expect(created_token.name).to eq(name)
|
||||
expect(created_token.expires_at).to be_nil
|
||||
expect(created_token.name).to eq(token_attributes[:name])
|
||||
expect(created_token.scopes).to eq(scopes)
|
||||
expect(PersonalAccessToken.active).to include(created_token)
|
||||
end
|
||||
|
||||
it "allows creation of a token with an expiry date" do
|
||||
expires_at = 5.days.from_now
|
||||
|
||||
post :create, personal_access_token: { name: FFaker::Product.brand, expires_at: expires_at }
|
||||
post :create, personal_access_token: token_attributes.merge(expires_at: expires_at)
|
||||
|
||||
expect(created_token).not_to be_nil
|
||||
expect(created_token.expires_at.to_i).to eq(expires_at.to_i)
|
||||
end
|
||||
|
||||
context "scopes" do
|
||||
it "allows creation of a token with scopes" do
|
||||
post :create, personal_access_token: { name: FFaker::Product.brand, scopes: %w(api read_user) }
|
||||
|
||||
expect(created_token).not_to be_nil
|
||||
expect(created_token.scopes).to eq(%w(api read_user))
|
||||
end
|
||||
|
||||
it "allows creation of a token with no scopes" do
|
||||
post :create, personal_access_token: { name: FFaker::Product.brand, scopes: [] }
|
||||
|
||||
expect(created_token).not_to be_nil
|
||||
expect(created_token.scopes).to eq([])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,19 +13,27 @@ describe PersonalAccessToken, models: true do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'validate_scopes' do
|
||||
context "validations" do
|
||||
let(:personal_access_token) { build(:personal_access_token) }
|
||||
|
||||
it "requires at least one scope" do
|
||||
personal_access_token.scopes = []
|
||||
|
||||
expect(personal_access_token).not_to be_valid
|
||||
expect(personal_access_token.errors[:scopes].first).to eq "can't be blank"
|
||||
end
|
||||
|
||||
it "allows creating a token with API scopes" do
|
||||
personal_access_token = build(:personal_access_token)
|
||||
personal_access_token.scopes = [:api, :read_user]
|
||||
|
||||
expect(personal_access_token).to be_valid
|
||||
end
|
||||
|
||||
it "rejects creating a token with non-API scopes" do
|
||||
personal_access_token = build(:personal_access_token)
|
||||
personal_access_token.scopes = [:openid, :api]
|
||||
|
||||
expect(personal_access_token).not_to be_valid
|
||||
expect(personal_access_token.errors[:scopes].first).to eq "can only contain API scopes"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue