Compare commits
7 Commits
d9b39b36fb
...
ec9bcacd46
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec9bcacd46
|
||
|
|
645abac810 | ||
|
|
e11be727a1 | ||
|
|
12b24337e7 | ||
|
|
b0bfc290c4 | ||
|
|
4c6c81171b | ||
|
|
4d88a40109
|
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
212
spec/models/remote_storage_authorization_spec.rb
Normal file
212
spec/models/remote_storage_authorization_spec.rb
Normal 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
|
||||
@@ -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"
|
||||
|
||||
|
||||
8
spec/support/helpers/redis_helper.rb
Normal file
8
spec/support/helpers/redis_helper.rb
Normal 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
|
||||
Reference in New Issue
Block a user