Files
akkounts/app/models/remote_storage_authorization.rb
T
raucao cd87c1f61a
CI / Test (pull_request) Successful in 41s
Add storage dashboard card and per-app details modal
- Rework RsAuthComponent into a card that shows the app, its device, an
  access summary and the permission status
- Add RsAuthDetailsModalComponent with the full scope list, device,
  connected date and permission expiry, plus Launch and Revoke actions
  (built on the existing ModalComponent)
- Add RemoteStorageAuthorization#device_name (parsed from the user agent)
  and #expired?
- Sort connected apps by name

Part of #254
2026-11-02 16:40:33 +04:00

151 lines
4.3 KiB
Ruby

class RemoteStorageAuthorization < ApplicationRecord
belongs_to :user
belongs_to :web_app, class_name: "AppCatalog::WebApp", optional: true
serialize :permissions, coder: YAML unless Rails.env.production?
validates_presence_of :permissions
validates_presence_of :client_id
scope :valid, -> { where(expire_at: nil).or(where(expire_at: (DateTime.now)..)) }
scope :expired, -> { where(expire_at: ..(DateTime.now)) }
after_initialize do |a|
a.permissions = [] if a.permissions == nil
end
before_create :generate_token
before_create :store_token_in_redis
before_create :find_or_create_web_app
after_create :schedule_token_expiry
after_create :notify_user
before_destroy :delete_token_from_redis
after_destroy :remove_token_expiry_job
def url
uri = URI.parse self.redirect_uri
"#{uri.scheme}://#{client_id}"
end
def launch_url
return url unless web_app && web_app.metadata[:start_url].present?
start_url = web_app.metadata[:start_url]
if start_url.match("^https?:\/\/")
return start_url.start_with?(url) ? start_url : url
else
path = start_url.gsub(/^\.\.\//, "").gsub(/^\.\//, "").gsub(/^\//, "")
"#{url}/#{path}"
end
end
def expired?
expire_at.present? && expire_at.past?
end
def device_name
return nil if user_agent.blank?
browser =
case user_agent
when /Firefox/ then "Firefox"
when /Edg\// then "Edge"
when /Chrome/ then "Chrome"
when /Safari/ then "Safari"
else "Unknown browser"
end
os =
case user_agent
when /Android/ then "Android"
when /iPhone|iPad|iPod/ then "iOS"
when /Mac OS X/ then "macOS"
when /Windows/ then "Windows"
when /Linux/ then "Linux"
end
os ? "#{browser} on #{os}" : browser
end
def delete_token_from_redis
key = "authorizations:#{user.cn}:#{token}"
redis.srem? key, redis.smembers(key)
rescue => e
Rails.logger.error e
Sentry.capture_exception(e) if Setting.sentry_enabled?
end
private
def redis
@redis ||= Redis.new(url: Setting.rs_redis_url)
end
def generate_token(length=16)
self.token = SecureRandom.hex(length) if self.token.blank?
end
def store_token_in_redis
redis.sadd "authorizations:#{user.cn}:#{token}", permissions
end
def schedule_token_expiry
return unless expire_at.present?
RemoteStorageExpireAuthorizationJob.set(wait_until: expire_at)
.perform_later(id)
end
def remove_token_expiry_job
job_class = RemoteStorageExpireAuthorizationJob
job_args = [id]
query = SolidQueue::Job.where(class_name: job_class.to_s)
case ActiveRecord::Base.connection.adapter_name.downcase
when /sqlite/
query.where("json_extract(arguments, '$.arguments') = ?", job_args.to_json)
when /postgres/
query.where("CAST(arguments AS jsonb)->>'arguments' = ?", job_args.to_json)
else
raise "Unsupported database adapter"
end.destroy_all
end
def find_or_create_web_app
if looks_like_hosted_origin?
web_app = AppCatalog::WebApp.find_or_create_by!(url: self.url)
web_app.update_metadata unless web_app.name.present?
self.web_app = web_app
self.app_name = web_app.name.presence || client_id
else
self.app_name = client_id
end
end
def looks_like_hosted_origin?
uri = URI.parse self.redirect_uri
!!(uri.host =~ /(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$)/)
rescue URI::InvalidURIError
false
end
def notify_user
notify = user.preferences[:remotestorage_notify_auth_created]
case notify
when "xmpp"
router = Router.new
payload = {
type: "normal", to: user.address,
from: Setting.xmpp_notifications_from_address,
body: "You have just granted '#{self.client_id}' access to your Kosmos Storage. Visit your Storage dashboard to check on your connected apps and revoke permissions anytime: #{router.services_storage_url}"
}
XmppSendMessageJob.perform_later(payload)
when "email"
NotificationMailer.with(user: user, auth: self)
.remotestorage_auth_created.deliver_later
end
end
end