Files
danbooru/app/models/api_key.rb
evazion 25fda1ecc2 api keys: add IP whitelist and API permission system.
Add the ability to restrict API keys so that they can only be used with
certain IP addresses or certain API endpoints.

Restricting your key is useful to limit damage in case it gets leaked or
stolen. For example, if your key is on a remote server and it gets
hacked, or if you accidentally check-in your key to Github.

Restricting your key's API permissions is useful if a third-party app or
script wants your key, but you don't want to give full access to your
account.

If you're an app or userscript developer, and your app needs an API key
from the user, you should only request a key with the minimum
permissions needed by your app.

If you have a privileged account, and you have scripts running under
your account, you are highly encouraged to restrict your key to limit
damage in case your key gets leaked or stolen.
2021-02-14 21:02:07 -06:00

68 lines
1.7 KiB
Ruby

class ApiKey < ApplicationRecord
array_attribute :permissions
array_attribute :permitted_ip_addresses
normalize :permissions, :normalize_permissions
normalize :name, :normalize_text
belongs_to :user
validate :validate_permissions, if: :permissions_changed?
validates :key, uniqueness: true, if: :key_changed?
has_secure_token :key
def self.visible(user)
if user.is_owner?
all
else
where(user: user)
end
end
def self.search(params)
q = search_attributes(params, :id, :created_at, :updated_at, :key, :user)
q = q.apply_default_order(params)
q
end
concerning :PermissionMethods do
def has_permission?(ip, controller, action)
ip_permitted?(ip) && action_permitted?(controller, action)
end
def ip_permitted?(ip)
return true if permitted_ip_addresses.empty?
permitted_ip_addresses.any? { |permitted_ip| ip.in?(permitted_ip) }
end
def action_permitted?(controller, action)
return true if permissions.empty?
permissions.any? do |permission|
permission == "#{controller}:#{action}"
end
end
def validate_permissions
permissions.each do |permission|
if !permission.in?(ApiKey.permissions_list)
errors.add(:permissions, "can't allow invalid permission '#{permission}'")
end
end
end
class_methods do
def normalize_permissions(permissions)
permissions.compact_blank
end
def permissions_list
Rails.application.routes.routes.select do |route|
route.defaults[:controller].present? && !route.internal
end.map do |route|
"#{route.defaults[:controller]}:#{route.defaults[:action]}"
end.uniq.sort
end
end
end
end