devise-async-stretch 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +14 -0
  3. data/.travis.yml +9 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +31 -0
  7. data/Rakefile +12 -0
  8. data/devise-async-stretch.gemspec +29 -0
  9. data/lib/devise/async/stretch.rb +52 -0
  10. data/lib/devise/async/stretch/backend.rb +15 -0
  11. data/lib/devise/async/stretch/backend/base.rb +25 -0
  12. data/lib/devise/async/stretch/backend/sidekiq.rb +17 -0
  13. data/lib/devise/async/stretch/model.rb +49 -0
  14. data/lib/devise/async/stretch/version.rb +7 -0
  15. data/lib/devise/async/stretch/worker.rb +19 -0
  16. data/lib/generators/devise/async/stretch/install_generator.rb +25 -0
  17. data/lib/generators/templates/devise_async_stretch.rb +21 -0
  18. data/log/test.log +0 -0
  19. data/test/devise/async/stretch/backend/base_test.rb +41 -0
  20. data/test/devise/async/stretch/backend/sidekiq_test.rb +42 -0
  21. data/test/devise/async/stretch/backend_test.rb +25 -0
  22. data/test/devise/async/stretch/model_test.rb +27 -0
  23. data/test/devise/async/stretch/worker_test.rb +19 -0
  24. data/test/devise/async/stretch_test.rb +24 -0
  25. data/test/rails_app/.gitignore +16 -0
  26. data/test/rails_app/README.rdoc +28 -0
  27. data/test/rails_app/Rakefile +6 -0
  28. data/test/rails_app/app/assets/images/.keep +0 -0
  29. data/test/rails_app/app/assets/javascripts/application.js +16 -0
  30. data/test/rails_app/app/assets/stylesheets/application.css +15 -0
  31. data/test/rails_app/app/controllers/application_controller.rb +5 -0
  32. data/test/rails_app/app/controllers/concerns/.keep +0 -0
  33. data/test/rails_app/app/helpers/application_helper.rb +2 -0
  34. data/test/rails_app/app/mailers/.keep +0 -0
  35. data/test/rails_app/app/models/.keep +0 -0
  36. data/test/rails_app/app/models/concerns/.keep +0 -0
  37. data/test/rails_app/app/models/user.rb +5 -0
  38. data/test/rails_app/app/views/layouts/application.html.erb +14 -0
  39. data/test/rails_app/bin/bundle +3 -0
  40. data/test/rails_app/bin/rails +8 -0
  41. data/test/rails_app/bin/rake +8 -0
  42. data/test/rails_app/bin/spring +18 -0
  43. data/test/rails_app/config.ru +4 -0
  44. data/test/rails_app/config/application.rb +29 -0
  45. data/test/rails_app/config/boot.rb +4 -0
  46. data/test/rails_app/config/database.yml +25 -0
  47. data/test/rails_app/config/environment.rb +5 -0
  48. data/test/rails_app/config/environments/development.rb +37 -0
  49. data/test/rails_app/config/environments/production.rb +78 -0
  50. data/test/rails_app/config/environments/test.rb +39 -0
  51. data/test/rails_app/config/initializers/assets.rb +8 -0
  52. data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  53. data/test/rails_app/config/initializers/cookies_serializer.rb +3 -0
  54. data/test/rails_app/config/initializers/devise.rb +259 -0
  55. data/test/rails_app/config/initializers/devise_async_stretch.rb +21 -0
  56. data/test/rails_app/config/initializers/filter_parameter_logging.rb +4 -0
  57. data/test/rails_app/config/initializers/inflections.rb +16 -0
  58. data/test/rails_app/config/initializers/mime_types.rb +4 -0
  59. data/test/rails_app/config/initializers/session_store.rb +3 -0
  60. data/test/rails_app/config/initializers/wrap_parameters.rb +14 -0
  61. data/test/rails_app/config/locales/devise.en.yml +60 -0
  62. data/test/rails_app/config/locales/en.yml +23 -0
  63. data/test/rails_app/config/routes.rb +57 -0
  64. data/test/rails_app/config/secrets.yml +22 -0
  65. data/test/rails_app/db/migrate/20141227205721_devise_create_users.rb +42 -0
  66. data/test/rails_app/db/schema.rb +34 -0
  67. data/test/rails_app/db/seeds.rb +7 -0
  68. data/test/rails_app/lib/assets/.keep +0 -0
  69. data/test/rails_app/lib/tasks/.keep +0 -0
  70. data/test/rails_app/log/.keep +0 -0
  71. data/test/rails_app/public/404.html +67 -0
  72. data/test/rails_app/public/422.html +67 -0
  73. data/test/rails_app/public/500.html +66 -0
  74. data/test/rails_app/public/favicon.ico +0 -0
  75. data/test/rails_app/public/robots.txt +5 -0
  76. data/test/rails_app/test/controllers/.keep +0 -0
  77. data/test/rails_app/test/fixtures/.keep +0 -0
  78. data/test/rails_app/test/fixtures/users.yml +7 -0
  79. data/test/rails_app/test/helpers/.keep +0 -0
  80. data/test/rails_app/test/integration/.keep +0 -0
  81. data/test/rails_app/test/mailers/.keep +0 -0
  82. data/test/rails_app/test/models/.keep +0 -0
  83. data/test/rails_app/test/models/user_test.rb +32 -0
  84. data/test/rails_app/test/test_helper.rb +11 -0
  85. data/test/rails_app/vendor/assets/javascripts/.keep +0 -0
  86. data/test/rails_app/vendor/assets/stylesheets/.keep +0 -0
  87. data/test/test_helper.rb +56 -0
  88. metadata +298 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZjAyNzE3NzFhNWE4YzUxNzllZDI5YmYxYWQ5MDc0OTE4ZDE3ZGE0ZA==
5
+ data.tar.gz: !binary |-
6
+ MjFkYjg0NDUyYTg1NjcwNzE1YWM2MjRhM2ZjY2VjMTc4MTZlNGYyZg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YTg3ODA2YmM4YTFmNjIzMDIyMzE3MmI1NzIwNzQ3ODk0YTQ3YTFhMTQ1OTNl
10
+ NDY4ZWIxNWEwNzk1NzViNDUzYjU5MGE3NDZhMjdhZDdlYjc3MTYxNGQ1ZTcw
11
+ M2E4ZjA0NzQ4NDZjYjMyZTU0NjdlYmNlODljMjUzODNlZjVkNWI=
12
+ data.tar.gz: !binary |-
13
+ NzQ2ZjY3MjM5ZTI3MGMyMWEwYzE1YzA5MTdhMGE5YTg0YWUwMTIyNzkyODJh
14
+ YjBmZGY3MzIyODJjN2E1NjgzMTg5ZWEzZDBiY2NjNTUwMDcxZWJlNzE2ZDMy
15
+ ZTliZjc2ZTVkNGM0N2Q2ZWM3NTBhNTMzYTcyNGZjMzEyNjg4MTM=
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - "2.2.0"
4
+ script:
5
+ - bundle exec rake test
6
+ deploy:
7
+ provider: rubygems
8
+ api_key:
9
+ secure: F1Drh/pkUEDBvn0l6ZpFJeCeJk0FGReu98Ce3PMiIHjHngGsC3xDOTbkQ7+TUQHlfc0g3Hpc4pYmHxL3w/6TJwbx9csRX0Np4yaNfJZJdHR8uzAi7mpY32APXeLCJkV5En3h1B/G1V3qQ+AhH0Ez6nlOiSEhocJ+MVq9jFXagM4=
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in devise-async-stretch.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Daniel Westendorf
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Devise::Async::Stretch
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'devise-async-stretch'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install devise-async-stretch
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/[my-github-username]/devise-async-stretch/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require "rake/testtask"
4
+
5
+ task :default => :test
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.libs << "lib"
9
+ t.libs << "test"
10
+ t.test_files = FileList["test/**/*_test.rb"]
11
+ t.verbose = true
12
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'devise/async/stretch/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "devise-async-stretch"
8
+ spec.version = Devise::Async::Stretch::VERSION
9
+ spec.authors = ["Daniel Westendorf"]
10
+ spec.email = ["daniel@prowestech.com"]
11
+ spec.summary = %q{Move password stretching into a background job for fast user creation.}
12
+ spec.description = %q{Uncompromised security for your user's passwords. Move password stretching into a background job for fast user creation, but maintainging safety.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "devise"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.7"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rails", "~> 4.1.0"
26
+ spec.add_development_dependency "mocha", "~> 0.11"
27
+ spec.add_development_dependency "sqlite3"
28
+ spec.add_development_dependency "sidekiq"
29
+ end
@@ -0,0 +1,52 @@
1
+ require "active_support/dependencies"
2
+ require "devise"
3
+ require "devise/async/stretch/version"
4
+
5
+ module Devise
6
+ module Async
7
+ module Stretch
8
+
9
+ autoload :Worker, "devise/async/stretch/worker"
10
+ autoload :Backend, "devise/async/stretch/backend"
11
+ autoload :Model, "devise/async/stretch/model"
12
+
13
+ module Backend
14
+ autoload :Base, "devise/async/stretch/backend/base"
15
+ autoload :Sidekiq, "devise/async/stretch/backend/sidekiq"
16
+ end
17
+
18
+ # Defines the queue backend to be used. Sidekiq by default.
19
+ mattr_accessor :backend
20
+ @@backend = :sidekiq
21
+
22
+ # Defines the queue in which the background job will be enqueued. Default is :default.
23
+ mattr_accessor :queue
24
+ @@queue = :default
25
+
26
+ # Defines the enabled configuration that if set to false the stetching will be done synchronously
27
+ mattr_accessor :enabled
28
+ @@enabled = true
29
+
30
+ # Defines the value for stretching at initial user creation
31
+ mattr_accessor :intermediate_stretch
32
+ @@intermediate_stretch = 1
33
+
34
+
35
+ # Allow configuring Devise::Async::Stretch with a block
36
+ #
37
+ # Example:
38
+ #
39
+ # Devise::Async::Stretch.setup do |config|
40
+ # config.backend = :resque
41
+ # config.queue = :my_custom_queue
42
+ # end
43
+ def self.setup
44
+ yield self
45
+ end
46
+
47
+ end
48
+ end
49
+ end
50
+
51
+ # Register devise-async model in Devise
52
+ ::Devise.add_module(:stretchable, :model => 'devise/async/stretch/model')
@@ -0,0 +1,15 @@
1
+ module Devise
2
+ module Async
3
+ module Stretch
4
+ module Backend
5
+ # Gives the desired backend driver class to be used to enqueue
6
+ # jobs.
7
+ def self.for(backend)
8
+ const_get(backend.to_s.camelize)
9
+ rescue NameError
10
+ raise ArgumentError, "unsupported backend for devise-async-stretch."
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ module Devise
2
+ module Async
3
+ module Stretch
4
+ module Backend
5
+ class Base
6
+ def self.enqueue(*args)
7
+ raise NotImplementedError, "Any Devise::Async::Stretch::Backend subclass should implement `self.enqueue`."
8
+ end
9
+
10
+ # Loads the resource record and sends the email.
11
+ #
12
+ # It uses `orm_adapter` API to fetch the record in order to enforce
13
+ # compatibility among diferent ORMs.
14
+ def perform(klass, id, password)
15
+ resource = klass.constantize.to_adapter.get!(id)
16
+ encrypted_password = resource.bcrypt(password, resource.class.stretches)
17
+
18
+ resource.update(encrypted_password: encrypted_password)
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ module Devise
2
+ module Async
3
+ module Stretch
4
+ module Backend
5
+ class Sidekiq < Base
6
+ include ::Sidekiq::Worker
7
+
8
+ sidekiq_options queue: Devise::Async::Stretch.queue
9
+
10
+ def self.enqueue(klass, id, password)
11
+ perform_async(klass, id, password)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,49 @@
1
+ module Devise
2
+ module Models
3
+ module Stretchable
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ # Enhance the stretches!
8
+ after_save :enqueue_stretch_worker if Devise::Async::Stretch.enabled
9
+ end
10
+
11
+ def self.required_fields(klass)
12
+ if Devise::Async::Stretch.enabled
13
+ klass.authentication_keys
14
+ else
15
+ [:encrypted_password] + klass.authentication_keys
16
+ end
17
+ end
18
+
19
+ # Our own bcrypt mehtod which supports arbitrary stretches
20
+ def bcrypt(password, stretches=nil)
21
+ stretches ||= self.class.stretches
22
+ ::BCrypt::Password.create("#{password}#{self.class.pepper}", cost: stretches).to_s
23
+ end
24
+
25
+ protected
26
+
27
+ def enqueue_stretch_worker
28
+ Devise::Async::Stretch::Worker.enqueue(self.class, id, @password) unless @password.nil?
29
+ @password = nil
30
+ end
31
+
32
+ # Digests the password using bcrypt. Custom encryption should override
33
+ # this method to apply their own algorithm.
34
+ #
35
+ # See https://github.com/plataformatec/devise-encryptable for examples
36
+ # of other encryption engines.
37
+ def password_digest(password)
38
+ if Devise::Async::Stretch.enabled
39
+ stretch = Devise::Async::Stretch.intermediate_stretch
40
+
41
+ bcrypt(password, stretch)
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,7 @@
1
+ module Devise
2
+ module Async
3
+ module Stretch
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,19 @@
1
+ module Devise
2
+ module Async
3
+ module Stretch
4
+ class Worker
5
+
6
+ def self.enqueue(klass, id, password)
7
+ backend_klass.enqueue(klass, id, password)
8
+ end
9
+
10
+ private
11
+
12
+ def self.backend_klass
13
+ Backend.for(Devise::Async::Stretch.backend)
14
+ end
15
+
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ require 'rails/generators/named_base'
2
+ require 'rails/generators/active_record'
3
+
4
+ module Devise
5
+ module Async
6
+ module Stretch
7
+ class InstallGenerator < Rails::Generators::NamedBase
8
+ source_root File.expand_path("../../../../templates", __FILE__)
9
+
10
+ desc "Creates a Devise::Async:Stretch initializer to your application and generates need migrations."
11
+ class_option :orm
12
+
13
+ def copy_initializer
14
+ template "devise_async_stretch.rb", "config/initializers/devise_async_stretch.rb"
15
+ end
16
+
17
+ def inject_devise_invitable_content
18
+ path = File.join("app", "models", "#{file_path}.rb")
19
+ inject_into_file(path, ", :stretchable", :after => ":database_authenticatable") if File.exists?(path)
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ Devise::Async::Stretch.setup do |config|
2
+
3
+ # Enable/Disable application wide
4
+ config.enabled = true
5
+
6
+ # Backend for background jobs
7
+ config.backend = :sidekiq
8
+
9
+ # Backend Queue
10
+ config.queue = :default
11
+
12
+ # Enable/Disable Intermediate Stretching. This will be the stretching that is done
13
+ # before the background job runs. If disabled, the user will not be able to log in
14
+ # until the background job has run.
15
+
16
+ # Intermediate stretching. Set this value low, as it will be the initial stretched
17
+ # password, replaced with the Devise.stretch value in the background job. This will
18
+ # allow quick initial user creation
19
+ config.intermediate_stretch = 1
20
+
21
+ end
data/log/test.log ADDED
File without changes
@@ -0,0 +1,41 @@
1
+ require 'test_helper'
2
+
3
+ module Devise
4
+ module Async
5
+ module Stretch
6
+ module Backend
7
+ class BaseTest < ActiveSupport::TestCase
8
+
9
+ setup do
10
+ @user = users(:bob)
11
+ Devise::Async::Stretch.enabled = true
12
+ Devise::Async::Stretch.backend = :sidekiq
13
+ end
14
+
15
+ test "an error is raised with the built in enqueue" do
16
+ assert_raises NotImplementedError do
17
+ Base.enqueue
18
+ end
19
+ end
20
+
21
+ test "the password gets updated when peformed" do
22
+ Base.new.perform("User", @user.id, 'password')
23
+ assert_not_empty @user.reload.encrypted_password
24
+ end
25
+
26
+ test "intermidiate encrypted_password gets set" do
27
+ user = User.new(email: 'ed@example.com', password: 'password1')
28
+ encrypted_password = user.encrypted_password
29
+ assert_not_empty encrypted_password
30
+
31
+ user.save
32
+ Base.new.perform("User", user.id, 'password1')
33
+
34
+ refute_equal encrypted_password, user.reload.encrypted_password
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,42 @@
1
+ require 'test_helper'
2
+
3
+ module Devise
4
+ module Async
5
+ module Stretch
6
+ module Backend
7
+ class SidekiqTest < ActiveSupport::TestCase
8
+
9
+ setup do
10
+ @user = users(:bob)
11
+ Devise::Async::Stretch.enabled = true
12
+ Devise::Async::Stretch.backend = :sidekiq
13
+ end
14
+
15
+ test "the job is queued" do
16
+ assert_difference 'Devise::Async::Stretch::Backend::Sidekiq.jobs.size' do
17
+ Sidekiq.enqueue("User", @user.id, "password")
18
+ end
19
+ end
20
+
21
+ test "the password gets updated when peformed" do
22
+ Sidekiq.new.perform("User", @user.id, 'password')
23
+ assert_not_empty @user.reload.encrypted_password
24
+ end
25
+
26
+ test "the stretch_mark gets updated" do
27
+ User.stretches = 2
28
+ ::Sidekiq::Testing.inline!
29
+
30
+ user = User.new(email: 'ed@example.com', password: 'password1')
31
+ encrypted_password = user.encrypted_password
32
+
33
+ user.save # Should trigger the Sidekiq job
34
+
35
+ refute_equal encrypted_password, user.reload.encrypted_password
36
+ end
37
+
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end