111 lines
2.9 KiB
Ruby
111 lines
2.9 KiB
Ruby
require 'travis/api/app'
|
|
require 'securerandom'
|
|
|
|
class Travis::Api::App
|
|
class AccessToken
|
|
include ConditionalSkylight::Mixin
|
|
|
|
DEFAULT_SCOPES = [:public, :private]
|
|
attr_reader :token, :scopes, :user_id, :app_id, :expires_in, :extra
|
|
|
|
def self.create(options = {})
|
|
new(options).tap(&:save)
|
|
end
|
|
|
|
def self.for_travis_token(travis_token, options = {})
|
|
travis_token = Token.find_by_token(travis_token) unless travis_token.respond_to? :user
|
|
new(scope: :travis_token, app_id: 1, user: travis_token.user).tap(&:save) if travis_token
|
|
end
|
|
|
|
def self.find_by_token(token)
|
|
return token if token.is_a? self
|
|
user_id, app_id, *scopes = redis.lrange(key(token), 0, -1)
|
|
extra = decode_json(scopes.pop) if scopes.last && scopes.last =~ /^json:/
|
|
new(token: token, scopes: scopes, user_id: user_id, app_id: app_id, extra: extra) if user_id
|
|
end
|
|
|
|
instrument_method
|
|
def initialize(options = {})
|
|
raise ArgumentError, 'must supply either user_id or user' unless options.key?(:user) ^ options.key?(:user_id)
|
|
raise ArgumentError, 'must supply app_id' unless options.key?(:app_id)
|
|
|
|
begin
|
|
@expires_in = Integer(options[:expires_in]) if options[:expires_in]
|
|
rescue ArgumentError
|
|
raise ArgumentError, 'expires_in must be of integer type'
|
|
end
|
|
|
|
@app_id = Integer(options[:app_id])
|
|
@scopes = Array(options[:scopes] || options[:scope] || DEFAULT_SCOPES).map(&:to_sym)
|
|
@user = options[:user]
|
|
@user_id = Integer(options[:user_id] || @user.id)
|
|
@token = options[:token] || reuse_token || SecureRandom.urlsafe_base64(16)
|
|
@extra = options[:extra]
|
|
end
|
|
|
|
instrument_method
|
|
def save
|
|
key = key(token)
|
|
redis.del(key)
|
|
data = [user_id, app_id, *scopes]
|
|
data << encode_json(extra) if extra
|
|
redis.rpush(key, data.map(&:to_s))
|
|
redis.set(reuse_key, token)
|
|
|
|
if expires_in
|
|
redis.expire(reuse_key, expires_in)
|
|
redis.expire(key, expires_in)
|
|
end
|
|
end
|
|
|
|
def user
|
|
@user ||= User.find(user_id) if user_id
|
|
end
|
|
|
|
def user?
|
|
!!user
|
|
end
|
|
|
|
def to_s
|
|
token
|
|
end
|
|
|
|
module Helpers
|
|
private
|
|
def redis
|
|
Thread.current[:redis] ||= ::Redis.connect(url: Travis.config.redis.url)
|
|
end
|
|
|
|
def key(token)
|
|
"t:#{token}"
|
|
end
|
|
|
|
def encode_json(hash)
|
|
'json:' + Base64.encode64(hash.to_json)
|
|
end
|
|
|
|
def decode_json(json)
|
|
JSON.parse(Base64.decode64(json.gsub(/^json:/, '')))
|
|
end
|
|
end
|
|
|
|
include Helpers
|
|
extend Helpers
|
|
|
|
private
|
|
|
|
instrument_method
|
|
def reuse_token
|
|
redis.get(reuse_key) unless expires_in
|
|
end
|
|
|
|
def reuse_key
|
|
@reuse_key ||= begin
|
|
keys = ["r", user_id, app_id]
|
|
keys.append(scopes.map(&:to_s).sort) if scopes != DEFAULT_SCOPES
|
|
keys.join(':')
|
|
end
|
|
end
|
|
end
|
|
end
|