69 lines
2.2 KiB
Ruby
69 lines
2.2 KiB
Ruby
require_dependency 'carto/oauth_provider/errors'
|
|
require_dependency 'carto/oauth_provider/scopes/scopes'
|
|
|
|
module Carto
|
|
class OauthRefreshToken < ActiveRecord::Base
|
|
include OauthProvider::Scopes
|
|
|
|
# Multiple of 3 for pretty base64
|
|
TOKEN_RANDOM_BYTES = 15
|
|
REFRESH_TOKEN_EXPIRATION_TIME = 6.months
|
|
MAX_TOKENS_PER_OAUTH_APP_USER = 50
|
|
|
|
belongs_to :oauth_app_user, inverse_of: :oauth_refresh_tokens
|
|
|
|
validates :oauth_app_user, presence: true
|
|
validates :scopes, scopes: true
|
|
validate :check_offline_scope
|
|
|
|
before_create :remove_oldest_ones, unless: :skip_token_regeneration
|
|
before_create :regenerate_token, unless: :skip_token_regeneration
|
|
|
|
scope :expired, -> { where('updated_at < ?', Time.now - REFRESH_TOKEN_EXPIRATION_TIME) }
|
|
|
|
attr_accessor :skip_token_regeneration
|
|
|
|
def exchange!(requested_scopes: scopes)
|
|
raise OauthProvider::Errors::InvalidGrant.new if expired?
|
|
invalid_scopes = Carto::OauthProvider::Scopes.subtract_scopes(requested_scopes, scopes, oauth_app_user.user.database_schema)
|
|
raise OauthProvider::Errors::InvalidScope.new(invalid_scopes) if invalid_scopes.any?
|
|
|
|
ActiveRecord::Base.transaction do
|
|
regenerate_token
|
|
save!
|
|
[oauth_app_user.oauth_access_tokens.create!(scopes: requested_scopes), self]
|
|
end
|
|
end
|
|
|
|
def oauth_app
|
|
oauth_app_user.oauth_app
|
|
end
|
|
|
|
private
|
|
|
|
def remove_oldest_ones
|
|
oldest_tokens = OauthRefreshToken.where(oauth_app_user: oauth_app_user)
|
|
.order(updated_at: :desc)
|
|
.offset(MAX_TOKENS_PER_OAUTH_APP_USER - 1)
|
|
|
|
unless oldest_tokens.count.zero?
|
|
CartoDB::Logger.debug(message: "Removing #{oldest_tokens.count} refresh tokens",
|
|
oauth_app_user: oauth_app_user)
|
|
oldest_tokens.find_each(&:destroy!)
|
|
end
|
|
end
|
|
|
|
def expired?
|
|
updated_at < Time.now - REFRESH_TOKEN_EXPIRATION_TIME
|
|
end
|
|
|
|
def check_offline_scope
|
|
errors.add(:scopes, "must contain `#{SCOPE_OFFLINE}`") unless scopes.include?(SCOPE_OFFLINE)
|
|
end
|
|
|
|
def regenerate_token
|
|
self.token = SecureRandom.urlsafe_base64(TOKEN_RANDOM_BYTES)
|
|
end
|
|
end
|
|
end
|