7 Commits

Author SHA1 Message Date
Râu Cao
ec9bcacd46 Add specs for RemoteStorageAuthorization model
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2023-07-14 15:31:20 +02:00
Râu Cao
645abac810 Rename RS token expiry job 2023-07-14 15:29:29 +02:00
Râu Cao
e11be727a1 Indentation 2023-07-14 15:29:04 +02:00
Râu Cao
12b24337e7 Fix typo 2023-07-14 15:28:45 +02:00
Râu Cao
b0bfc290c4 Refactor code for newer Redis 2023-07-14 15:28:09 +02:00
Râu Cao
4c6c81171b Fix typo 2023-07-14 15:27:57 +02:00
Râu Cao
4d88a40109 Add separate config for RS Redis 2023-07-14 15:27:30 +02:00
11 changed files with 264 additions and 31 deletions

View File

@@ -21,6 +21,7 @@ steps:
environment:
RAILS_ENV: test
REDIS_URL: redis://redis:6379/0
RS_REDIS_URL: redis://redis:6379/1
commands:
- bundle config unset deployment
- bundle config set cache_all 'true'

View File

@@ -26,6 +26,7 @@ GITEA_PUBLIC_URL='https://gitea.kosmos.org'
MASTODON_PUBLIC_URL='https://kosmos.social'
MEDIAWIKI_PUBLIC_URL='https://wiki.kosmos.org'
RS_STORAGE_URL='https://storage.kosmos.org'
RS_REDIS_URL='redis://localhost:6379/2'
EJABBERD_ADMIN_URL='https://xmpp.kosmos.org/admin'
EJABBERD_API_URL='https://xmpp.kosmos.org/api'

View File

@@ -1,6 +1,6 @@
PRIMARY_DOMAIN=kosmos.org
REDIS_URL='redis://localhost:6379/21'
REDIS_URL='redis://localhost:6379/0'
DISCOURSE_PUBLIC_URL='http://discourse.example.com'
DISCOURSE_CONNECT_SECRET='discourse_connect_ftw'
@@ -14,5 +14,6 @@ LNDHUB_PUBLIC_URL='https://lndhub.kosmos.org'
LNDHUB_PUBLIC_KEY='024cd3be18617f39cf645851e3ba63f51fc13f0bb09e3bb25e6fd4de556486d946'
RS_STORAGE_URL='https://storage.kosmos.org'
RS_REDIS_URL='redis://localhost:6379/1'
WEBHOOKS_ALLOWED_IPS='10.1.1.23'

View File

@@ -1,5 +1,5 @@
class ExpireRemoteStorageAuthorizationJob < ApplicationJob
queue_as :remote_storage
class RemoteStorageExpireAuthorizationJob < ApplicationJob
queue_as :remotestorage
def perform(rs_auth_id)
rs_auth = RemoteStorageAuthorization.find rs_auth_id

View File

@@ -10,7 +10,7 @@ class RemoteStorageAuthorization < ApplicationRecord
scope :expired, -> { where(expire_at: ..(DateTime.now)) }
after_initialize do |a|
a.permisisons = [] if a.permissions == nil
a.permissions = [] if a.permissions == nil
end
before_create :generate_token
@@ -30,34 +30,34 @@ class RemoteStorageAuthorization < ApplicationRecord
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) }
redis.srem? key, redis.smembers(key)
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]
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 "rs:authorizations:#{user.address}:#{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
queue = Sidekiq::Queue.new(RemoteStorageExpireAuthorizationJob.queue_name)
queue.each do |job|
next unless job.display_class == "RemoteStorageExpireAuthorizationJob"
job.delete if job.display_args == [id]
end
end
end
end

View File

@@ -12,7 +12,7 @@ class Setting < RailsSettings::Base
# Internal services
#
field :redis_url, type: :string, readonly: true,
field :redis_url, type: :string,
default: ENV["REDIS_URL"] || "redis://localhost:6379/0"
#
@@ -131,4 +131,7 @@ class Setting < RailsSettings::Base
field :rs_storage_url, type: :string,
default: ENV["RS_STORAGE_URL"].presence
field :rs_redis_url, type: :string,
default: ENV["RS_REDIS_URL"] || "redis://localhost:6379/1"
end

View File

@@ -11,7 +11,11 @@
<% if Setting.remotestorage_enabled? %>
<%= render FormElements::FieldsetResettableSettingComponent.new(
key: :rs_storage_url,
title: "Storage URL"
title: "Storage Base URL"
) %>
<%= render FormElements::FieldsetResettableSettingComponent.new(
key: :rs_redis_url,
title: "Redis URL"
) %>
<% end %>
</ul>

View File

@@ -38,6 +38,7 @@ services:
RAILS_ENV: development
PRIMARY_DOMAIN: kosmos.org
REDIS_URL: redis://redis:6379/0
RS_REDIS_URL: redis://redis:6379/1
LDAP_HOST: ldap
LDAP_PORT: 3389
LDAP_ADMIN_PASSWORD: passthebutter
@@ -57,6 +58,7 @@ services:
RAILS_ENV: development
PRIMARY_DOMAIN: kosmos.org
REDIS_URL: redis://redis:6379/0
RS_REDIS_URL: redis://redis:6379/1
LDAP_HOST: ldap
LDAP_PORT: 3389
LDAP_ADMIN_PASSWORD: passthebutter

View File

@@ -0,0 +1,212 @@
require 'rails_helper'
RSpec.describe RemoteStorageAuthorization, type: :model do
include ActiveJob::TestHelper
let(:user) { create :user }
describe "#create" do
after(:each) { clear_enqueued_jobs }
after(:all) { redis_rs_delete_keys("rs:authorizations:*") }
let(:auth) do
user.remote_storage_authorizations.create!(
permissions: %w(documents photos contacts:rw videos:r tasks/work:r),
client_id: "example.com",
redirect_uri: "https://example.com"
)
end
it "generates a token" do
expect(auth.token).to match(/[a-zA-Z0-9]+/)
end
it "stores a token in redis" do
user_auth_keys = redis_rs.keys("rs:authorizations:#{user.address}:*")
expect(user_auth_keys.length).to eq(1)
authorizations = redis_rs.smembers(user_auth_keys.first)
expect(authorizations.sort).to eq(%w(documents photos contacts:rw videos:r tasks/work:r).sort)
end
context "with expiry set" do
it "enqueues an expiration job" do
auth_with_expiry = user.remote_storage_authorizations.create!(
permissions: %w(documents:rw), client_id: "example.com",
redirect_uri: "https://example.com",
expire_at: 1.month.from_now
)
job = enqueued_jobs.select{|j| j['job_class'] == "RemoteStorageExpireAuthorizationJob"}.first
expect(job['arguments'][0]).to eq(auth_with_expiry.id)
end
end
end
describe "#destroy" do
after(:each) { clear_enqueued_jobs }
after(:all) { redis_rs_delete_keys("rs:authorizations:*") }
it "removes the token from redis" do
auth = user.remote_storage_authorizations.create!(
permissions: %w(shares:rw documents pictures:r),
client_id: "sharesome.5apps.com",
redirect_uri: "https://sharesome.5apps.com"
)
auth.destroy!
expect(redis_rs.keys("rs:authorizations:#{user.address}:*")).to be_empty
end
context "with expiry set" do
it "removes the expiration job" do
auth_with_expiry = user.remote_storage_authorizations.create!(
permissions: %w(documents:rw), client_id: "example.com",
redirect_uri: "https://example.com",
expire_at: 1.month.from_now
)
# Cannot test for removal from the actual Sidekiq::Queue, because it is
# not used in specs, but the method directly removes jobs from there
expect(auth_with_expiry).to receive(:remove_token_expiry_job)
auth_with_expiry.destroy!
end
end
end
# describe "#find_or_create_web_app" do
# context "with origin that looks hosted" do
# before do
# auth = user.remote_storage_authorizations.create!(
# permissions: %w(documents photos contacts:rw videos:r tasks/work:r),
# client_id: "example.com",
# redirect_uri: "https://example.com",
# expire_at: 1.month.from_now
# )
# end
#
# it "generates a web_app" do
# expect(auth.web_app).to be_a(AppCatalog::WebApp)
# end
#
# it "uses the Web App's name as app name" do
# expect(auth.app_name).to eq("Example Domain")
# end
# end
#
# context "when creating two authorizations for the same app" do
# before do
# user_2 = create :user
# ResqueSpec.reset!
# auth_1 = user.remote_storage_authorizations.create!(
# permissions: %w(documents photos contacts:rw videos:r tasks/work:r),
# client_id: "example.com",
# redirect_uri: "https://example.com",
# expire_at: 1.month.from_now
# )
# auth_2 = user_2.remote_storage_authorizations.create!(
# permissions: %w(documents photos contacts:rw videos:r tasks/work:r),
# client_id: "example.com",
# redirect_uri: "https://example.com",
# expire_at: 1.month.from_now
# )
# end
#
# after do
# auth_1.destroy
# auth_2.destroy
# user_2.destroy
# end
#
# it "uses the same web app instance for both authorizations" do
# expect(auth_1.web_app).to be_a(AppCatalog::WebApp)
# expect(auth_1.web_app).to eq(auth_2.web_app)
# end
# end
#
# describe "non-production app origins" do
# context "when host is not an FQDN" do
# before do
# auth = user.remote_storage_authorizations.create!(
# permissions: %w(recipes),
# client_id: "localhost:4200",
# redirect_uri: "http://localhost:4200"
# )
# end
#
# it "does not create a web app" do
# expect(auth.web_app).to be_nil
# expect(auth.app_name).to eq("localhost:4200")
# end
# end
#
# context "when host is an IP address" do
# before do
# auth = user.remote_storage_authorizations.create!(
# permissions: %w(recipes),
# client_id: "192.168.0.23:3000",
# redirect_uri: "http://192.168.0.23:3000"
# )
# end
#
# it "does not create a web app" do
# expect(auth.web_app).to be_nil
# expect(auth.app_name).to eq("192.168.0.23:3000")
# end
# end
#
# context "when host is an extension URL" do # before do
# auth = user.remote_storage_authorizations.create!(
# permissions: %w(bookmarks),
# client_id: "123.addons.allizom.org",
# redirect_uri: "123.addons.allizom.org/foo"
# )
# end
#
# it "does not create a web app" do
# expect(auth.web_app).to be_nil
# expect(auth.app_name).to eq("123.addons.allizom.org")
# end
# end
# end
# end
# describe "auth notifications" do
# context "with auth notifications enabled" do
# before do
# ResqueSpec.reset!
# user.push(mailing_lists: "rs-auth-notifications-#{Rails.env}")
# auth = user.remote_storage_authorizations.create!(
# :permissions => %w(documents photos contacts:rw videos:r tasks/work:r),
# :client_id => "example.com",
# :redirect_uri => "https://example.com"
# )
# end
#
# it "notifies the user via email" do
# expect(enqueued_jobs.size).to eq(1)
# job = enqueued_jobs.first
# expect(job).to eq(
# job: ActionMailer::DeliveryJob,
# args: ['StorageAuthorizationMailer', 'authorized_rs_app', 'deliver_now',
# auth.id.to_s],
# queue: 'mailers'
# )
# end
# end
#
# context "with auth notifications disabled" do
# before do
# ResqueSpec.reset!
# user.pull(mailing_lists: "rs-auth-notifications-#{Rails.env}")
# auth = user.remote_storage_authorizations.create!(
# :permissions => %w(documents photos contacts:rw videos:r tasks/work:r),
# :client_id => "example.com",
# :redirect_uri => "https://example.com"
# )
# end
#
# it "does not notify the user via email about new RS app" do
# expect(enqueued_jobs.size).to eq(0)
# end
# end
# end
end

View File

@@ -10,6 +10,7 @@ require 'capybara'
require 'devise'
require 'support/controller_macros'
require 'support/database_cleaner'
require 'support/helpers/redis_helper'
require "view_component/test_helpers"
require "capybara/rspec"

View File

@@ -0,0 +1,8 @@
def redis_rs
@redis_rs ||= Redis.new(url: Setting.rs_redis_url)
end
def redis_rs_delete_keys(pattern)
keys = redis_rs.keys(pattern)
redis_rs.del(*keys)
end