diff --git a/.gitignore b/.gitignore index e02bc74..aaab8ab 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,6 @@ yarn-debug.log* # Ignore local dotenv config file .env + +# Ignore redis dumps from sidekiq +dump.rdb diff --git a/README.md b/README.md index 82c130b..61287a9 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,10 @@ Running the dev server: bundle exec rails server +Running the background workers (requires Redis): + + bundle exec sidekiq -C config/sidekiq.yml + Running all specs: bundle exec rspec @@ -62,6 +66,11 @@ manual LDIF imports etc. (or provide a staging instance) * [devise_ldap_authenticatable](https://github.com/cschiewek/devise_ldap_authenticatable) * [net/ldap](https://www.rubydoc.info/gems/net-ldap/Net/LDAP) +### Asynchronous jobs/workers + +* [Sidekiq](https://github.com/mperham/sidekiq/wiki/) +* [ActiveJob](https://github.com/mperham/sidekiq/wiki/Active-Job) + ## License [GNU Affero General Public License v3.0](https://choosealicense.com/licenses/agpl-3.0/) diff --git a/app/jobs/create_ldap_user_job.rb b/app/jobs/create_ldap_user_job.rb new file mode 100644 index 0000000..da5e533 --- /dev/null +++ b/app/jobs/create_ldap_user_job.rb @@ -0,0 +1,32 @@ +class CreateLdapUserJob < ApplicationJob + queue_as :default + + def perform(username, domain, email, hashed_pw) + dn = "cn=#{username},ou=#{domain},cn=users,dc=kosmos,dc=org" + attr = { + objectclass: ["top", "account", "person", "extensibleObject"], + cn: username, + sn: username, + uid: username, + mail: email, + userPassword: hashed_pw + } + + ldap_client.add(dn: dn, attributes: attr) + end + + def ldap_client + ldap_client ||= Net::LDAP.new host: ldap_config['host'], + port: ldap_config['port'], + encryption: ldap_config['ssl'], + auth: { + method: :simple, + username: ldap_config['admin_user'], + password: ldap_config['admin_password'] + } + end + + def ldap_config + ldap_config ||= YAML.load(ERB.new(File.read("#{Rails.root}/config/ldap.yml")).result)[Rails.env] + end +end diff --git a/app/services/create_account.rb b/app/services/create_account.rb index eb60824..e31a01d 100644 --- a/app/services/create_account.rb +++ b/app/services/create_account.rb @@ -33,33 +33,10 @@ class CreateAccount < ApplicationService @invitation.update! invited_user_id: user_id, used_at: DateTime.now end + # TODO move to confirmation def add_ldap_document - dn = "cn=#{@username},ou=kosmos.org,cn=users,dc=kosmos,dc=org" - attr = { - objectclass: ["top", "account", "person", "extensibleObject"], - cn: @username, - sn: @username, - uid: @username, - mail: @email, - userPassword: Devise.ldap_auth_password_builder.call(@password) - } - - ldap_client.add(dn: dn, attributes: attr) - end - - def ldap_client - ldap_client ||= Net::LDAP.new host: ldap_config['host'], - port: ldap_config['port'], - encryption: ldap_config['ssl'], - auth: { - method: :simple, - username: ldap_config['admin_user'], - password: ldap_config['admin_password'] - } - end - - def ldap_config - ldap_config ||= YAML.load(ERB.new(File.read("#{Rails.root}/config/ldap.yml")).result)[Rails.env] + hashed_pw = Devise.ldap_auth_password_builder.call(@password) + CreateLdapUserJob.perform_later(@username, "kosmos.org", @email, hashed_pw) end def exchange_xmpp_contacts diff --git a/config/application.rb b/config/application.rb index 263387b..98cc27c 100644 --- a/config/application.rb +++ b/config/application.rb @@ -41,5 +41,6 @@ module Akkounts end config.active_job.queue_adapter = :sidekiq + config.action_mailer.deliver_later_queue_name = nil # use "default" queue end end diff --git a/config/environments/test.rb b/config/environments/test.rb index ac775c3..f3c0b8f 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -49,4 +49,6 @@ Rails.application.configure do protocol: "https", from: "accounts@kosmos.org" } + + config.active_job.queue_adapter = :test end diff --git a/spec/jobs/create_ldap_user_job_spec.rb b/spec/jobs/create_ldap_user_job_spec.rb new file mode 100644 index 0000000..78aae67 --- /dev/null +++ b/spec/jobs/create_ldap_user_job_spec.rb @@ -0,0 +1,34 @@ +require 'rails_helper' + +RSpec.describe CreateLdapUserJob, type: :job do + let(:ldap_client_mock) { instance_double(Net::LDAP) } + + subject(:job) { + described_class.any_instance.stub(:ldap_client).and_return(ldap_client_mock) + described_class.perform_later( + 'halfinney', 'kosmos.org', 'halfinney@example.com', + 'remember-remember-the-5th-of-november' + ) + } + + it "creates a new document with the correct attributes" do + ldap_client_mock.should_receive(:add).with( + dn: "cn=halfinney,ou=kosmos.org,cn=users,dc=kosmos,dc=org", + attributes: { + objectclass: ["top", "account", "person", "extensibleObject"], + cn: "halfinney", + sn: "halfinney", + uid: "halfinney", + mail: "halfinney@example.com", + userPassword: "remember-remember-the-5th-of-november" + } + ) + + perform_enqueued_jobs { job } + end + + after do + clear_enqueued_jobs + clear_performed_jobs + end +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index d11311e..c4a74a4 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -69,5 +69,6 @@ RSpec.configure do |config| config.include Devise::Test::ControllerHelpers, :type => :controller config.include Warden::Test::Helpers config.include FactoryBot::Syntax::Methods + config.include ActiveJob::TestHelper, type: :job config.extend ControllerMacros, :type => :controller end diff --git a/spec/services/create_account_spec.rb b/spec/services/create_account_spec.rb index c6e74d5..f90ecfc 100644 --- a/spec/services/create_account_spec.rb +++ b/spec/services/create_account_spec.rb @@ -3,12 +3,6 @@ require 'webmock/rspec' require 'json' RSpec.describe CreateAccount, type: :model do - let(:ldap_client_mock) { instance_double(Net::LDAP) } - - before do - allow(service).to receive(:ldap_client).and_return(ldap_client_mock) - end - describe "#create_user_in_database" do let(:service) { CreateAccount.new( username: 'isaacnewton', @@ -48,26 +42,28 @@ RSpec.describe CreateAccount, type: :model do end describe "#add_ldap_document" do + include ActiveJob::TestHelper + let(:service) { CreateAccount.new( username: 'halfinney', email: 'halfinney@example.com', password: 'remember-remember-the-5th-of-november' )} - it "creates a new document with the correct attributes" do - expect(ldap_client_mock).to receive(:add).with( - dn: "cn=halfinney,ou=kosmos.org,cn=users,dc=kosmos,dc=org", - attributes: { - objectclass: ["top", "account", "person", "extensibleObject"], - cn: "halfinney", - sn: "halfinney", - uid: "halfinney", - mail: "halfinney@example.com", - userPassword: /^{SSHA512}.{171}=/ - } - ) - + it "enqueues a job to create the LDAP user document" do service.send(:add_ldap_document) + + expect(enqueued_jobs.size).to eq(1) + + args = enqueued_jobs.first['arguments'] + expect(args[0]).to eq('halfinney') + expect(args[1]).to eq('kosmos.org') + expect(args[2]).to eq('halfinney@example.com') + expect(args[3]).to match(/^{SSHA512}.{171}=/) + end + + after do + clear_enqueued_jobs end end