diff --git a/app/jobs/expire_remote_storage_authorization_job.rb b/app/jobs/expire_remote_storage_authorization_job.rb new file mode 100644 index 0000000..8007e2c --- /dev/null +++ b/app/jobs/expire_remote_storage_authorization_job.rb @@ -0,0 +1,10 @@ +class ExpireRemoteStorageAuthorizationJob < ApplicationJob + queue_as :remote_storage + + def perform(rs_auth_id) + rs_auth = RemoteStorageAuthorization.find rs_auth_id + return unless rs_auth.expire_at.nil? || rs_auth.expire_at <= DateTime.now + + rs_auth.destroy! + end +end diff --git a/app/models/remote_storage_authorization.rb b/app/models/remote_storage_authorization.rb index e42deca..13dfd9c 100644 --- a/app/models/remote_storage_authorization.rb +++ b/app/models/remote_storage_authorization.rb @@ -14,6 +14,10 @@ class RemoteStorageAuthorization < ApplicationRecord end before_create :generate_token + before_create :store_token_in_redis + after_create :schedule_token_expiry + before_destroy :delete_token_from_redis + after_destroy :remove_token_expiry_job def url if self.redirect_uri @@ -24,9 +28,36 @@ class RemoteStorageAuthorization < ApplicationRecord end end + def delete_token_from_redis + key = "rs:authorizations:#{user.address}:#{token}" + # You can't delete multiple members of a set with Redis 2 + redis.smembers(key).each { |auth| redis.srem(key, auth) } + end + private + def redis + @redis ||= Redis.new(url: Setting.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 "rs:authorizations:#{user.address}:#{token}", permissions + end + + def schedule_token_expiry + return unless expire_at.present? + ExpireRemoteStorageAuthorizationJob.set(wait_unil: expire_at).perform_later(id) + end + + def remove_token_expiry_job + queue = Sidekiq::Queue.new(ExpireRemoteStorageAuthorizationJob.queue_name) + queue.each do |job| + next unless job.display_class == "ExpireRemoteStorageAuthorizationJob" + job.delete if job.display_args == [id] + end + end end diff --git a/spec/factories/remote_storage_authorizations.rb b/spec/factories/remote_storage_authorizations.rb new file mode 100644 index 0000000..be7c810 --- /dev/null +++ b/spec/factories/remote_storage_authorizations.rb @@ -0,0 +1,9 @@ +FactoryBot.define do + factory :remote_storage_authorization do + permissions { ["documents:rw"] } + client_id { "some-fancy-app" } + redirect_uri { "https://example.com/some-fancy-app" } + app_name { "Fancy App" } + expire_at { nil } + end +end diff --git a/spec/jobs/expire_remote_storage_authorization_job_spec.rb b/spec/jobs/expire_remote_storage_authorization_job_spec.rb new file mode 100644 index 0000000..f2771fa --- /dev/null +++ b/spec/jobs/expire_remote_storage_authorization_job_spec.rb @@ -0,0 +1,36 @@ +require 'rails_helper' + +RSpec.describe ExpireRemoteStorageAuthorizationJob, type: :job do + before do + @user = create :user, cn: "ronald", ou: "kosmos.org" + @rs_authorization = create :remote_storage_authorization, user: @user, expire_at: 1.day.ago + end + + after do + clear_enqueued_jobs + clear_performed_jobs + end + + subject(:job) { + described_class.perform_later(@rs_authorization.id) + } + + let(:redis) { + @redis ||= Redis.new(url: Setting.redis_url) + } + + it "removes the RS authorization from redis" do + redis_key = "rs:authorizations:#{@user.address}:#{@rs_authorization.token}" + expect(redis.keys(redis_key)).to_not be_empty + + perform_enqueued_jobs { job } + + expect(redis.keys(redis_key)).to be_empty + end + + it "deletes the RS authorization object" do + expect { + perform_enqueued_jobs { job } + }.to change(RemoteStorageAuthorization, :count).by(-1) + end +end