devise_sessionable 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +109 -0
  4. data/Rakefile +26 -0
  5. data/app/assets/config/devise_sessionable_manifest.js +2 -0
  6. data/app/assets/javascripts/devise_sessionable/application.js +13 -0
  7. data/app/assets/stylesheets/devise_sessionable/application.css +15 -0
  8. data/app/controllers/devise_sessionable/application_controller.rb +5 -0
  9. data/app/helpers/devise_sessionable/application_helper.rb +4 -0
  10. data/app/jobs/devise_sessionable/application_job.rb +4 -0
  11. data/app/mailers/devise_sessionable/application_mailer.rb +6 -0
  12. data/app/models/devise_sessionable/application_record.rb +5 -0
  13. data/app/models/devise_sessionable/session.rb +11 -0
  14. data/app/views/layouts/devise_sessionable/application.html.erb +14 -0
  15. data/config/initializers/simple_token_authentication.rb +31 -0
  16. data/config/routes.rb +3 -0
  17. data/lib/devise_sessionable.rb +15 -0
  18. data/lib/devise_sessionable/acts_as_sessionable.rb +17 -0
  19. data/lib/devise_sessionable/engine.rb +12 -0
  20. data/lib/devise_sessionable/version.rb +3 -0
  21. data/lib/generators/devise_sessionable/install/install_generator.rb +24 -0
  22. data/lib/generators/devise_sessionable/install/templates/migration.rb +16 -0
  23. data/lib/tasks/devise_sessionable_tasks.rake +4 -0
  24. data/spec/factories/sessions.rb +5 -0
  25. data/spec/factories/users.rb +4 -0
  26. data/spec/models/session_spec.rb +18 -0
  27. data/spec/rails_helper.rb +28 -0
  28. data/spec/spec_helper.rb +13 -0
  29. data/spec/test_app/Rakefile +6 -0
  30. data/spec/test_app/app/assets/config/manifest.js +5 -0
  31. data/spec/test_app/app/assets/javascripts/application.js +13 -0
  32. data/spec/test_app/app/assets/javascripts/cable.js +13 -0
  33. data/spec/test_app/app/assets/stylesheets/application.css +15 -0
  34. data/spec/test_app/app/channels/application_cable/channel.rb +4 -0
  35. data/spec/test_app/app/channels/application_cable/connection.rb +4 -0
  36. data/spec/test_app/app/controllers/application_controller.rb +3 -0
  37. data/spec/test_app/app/helpers/application_helper.rb +2 -0
  38. data/spec/test_app/app/jobs/application_job.rb +2 -0
  39. data/spec/test_app/app/mailers/application_mailer.rb +4 -0
  40. data/spec/test_app/app/models/application_record.rb +3 -0
  41. data/spec/test_app/app/models/user.rb +2 -0
  42. data/spec/test_app/app/views/layouts/application.html.erb +14 -0
  43. data/spec/test_app/app/views/layouts/mailer.html.erb +13 -0
  44. data/spec/test_app/app/views/layouts/mailer.text.erb +1 -0
  45. data/spec/test_app/bin/bundle +3 -0
  46. data/spec/test_app/bin/rails +4 -0
  47. data/spec/test_app/bin/rake +4 -0
  48. data/spec/test_app/bin/setup +38 -0
  49. data/spec/test_app/bin/update +29 -0
  50. data/spec/test_app/bin/yarn +11 -0
  51. data/spec/test_app/config.ru +5 -0
  52. data/spec/test_app/config/application.rb +25 -0
  53. data/spec/test_app/config/boot.rb +5 -0
  54. data/spec/test_app/config/cable.yml +10 -0
  55. data/spec/test_app/config/database.yml +85 -0
  56. data/spec/test_app/config/environment.rb +5 -0
  57. data/spec/test_app/config/environments/development.rb +54 -0
  58. data/spec/test_app/config/environments/production.rb +91 -0
  59. data/spec/test_app/config/environments/test.rb +42 -0
  60. data/spec/test_app/config/initializers/application_controller_renderer.rb +6 -0
  61. data/spec/test_app/config/initializers/assets.rb +14 -0
  62. data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
  63. data/spec/test_app/config/initializers/cookies_serializer.rb +5 -0
  64. data/spec/test_app/config/initializers/filter_parameter_logging.rb +4 -0
  65. data/spec/test_app/config/initializers/inflections.rb +16 -0
  66. data/spec/test_app/config/initializers/mime_types.rb +4 -0
  67. data/spec/test_app/config/initializers/wrap_parameters.rb +14 -0
  68. data/spec/test_app/config/locales/en.yml +33 -0
  69. data/spec/test_app/config/puma.rb +56 -0
  70. data/spec/test_app/config/routes.rb +3 -0
  71. data/spec/test_app/config/secrets.yml +32 -0
  72. data/spec/test_app/config/spring.rb +6 -0
  73. data/spec/test_app/db/migrate/20170803015911_devise_sessionable_create_sessions.rb +16 -0
  74. data/spec/test_app/db/migrate/20170803015941_create_users.rb +8 -0
  75. data/spec/test_app/db/schema.rb +33 -0
  76. data/spec/test_app/log/development.log +274 -0
  77. data/spec/test_app/log/test.log +595 -0
  78. data/spec/test_app/package.json +5 -0
  79. data/spec/test_app/public/404.html +67 -0
  80. data/spec/test_app/public/422.html +67 -0
  81. data/spec/test_app/public/500.html +66 -0
  82. data/spec/test_app/public/apple-touch-icon-precomposed.png +0 -0
  83. data/spec/test_app/public/apple-touch-icon.png +0 -0
  84. data/spec/test_app/public/favicon.ico +0 -0
  85. metadata +312 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a95a21c555e179b7fb4a070da9b5216559f6d4f9
4
+ data.tar.gz: 338c6fa0ded927243c6494ed48845832e9948b78
5
+ SHA512:
6
+ metadata.gz: 24ddfc4566e7085fddc548a50de36d199f970a706ef791862250b5bc5c5b2a008382af2bed4af91d4ec0afb7f2e5df1d74ff3de60ed21c67a821bae85d53a643
7
+ data.tar.gz: 72bd573b72411fa926c10239cf695e3a058245dcf5d94ede9b53d51925d407baa989e66279a833bea2fa1bdb3f2877e7dc24e8b0e37983cc0e4e697892d382e0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2017 Isaac Norman
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # DeviseSessionable
2
+ Devise Sessionable extends the [Simple Token Authentication](https://github.com/gonzalo-bulnes/simple_token_authentication) gem into a `Session` object to allow for easier and more secure token authentication.
3
+
4
+ ## Installation
5
+ Add this line to your application's Gemfile:
6
+
7
+ ```ruby
8
+ gem 'devise_sessionable'
9
+ ```
10
+
11
+ And then execute:
12
+ ```bash
13
+ $ bundle
14
+ ```
15
+
16
+ Or install it yourself as:
17
+ ```bash
18
+ $ gem install devise_sessionable
19
+ ```
20
+
21
+ ## Getting Started
22
+
23
+ First things first, run the installer:
24
+
25
+ ```bash
26
+ rails generate devise_sessionable:install
27
+ ```
28
+
29
+ This will generate a migration for the session object.
30
+
31
+ **NOTE:** This gem is setup to work with UUIDs as default. If you are _NOT_ using uuids you will need to update the migration to reflect this correctly.
32
+
33
+ Then simply run:
34
+
35
+ ```bash
36
+ rails db:migrate
37
+ ```
38
+
39
+ ## Adding Session Authentication on a Model
40
+
41
+ simply add `acts_as_sessionable` to the devise enabled model that you wish to be session authable.
42
+
43
+ ```ruby
44
+ class User < ApplicationRecord
45
+ acts_as_sessionable
46
+
47
+ # Include default devise modules. Others available are:
48
+ # :confirmable, :lockable, :timeoutable and :omniauthable
49
+ devise :database_authenticatable, :registerable,
50
+ :recoverable, :rememberable, :trackable, :validatable
51
+ ```
52
+
53
+ ## Enabling Session Authentication in the Controller
54
+
55
+ Once you have a model with session authentication enabled on a model, you can start using it in your controller with just a few simple steps.
56
+
57
+ First, you need to put your controllers behind a layer of token authentication:
58
+
59
+ ```ruby
60
+ module Api
61
+ module V1
62
+ class ApiController < ApplicationController
63
+ acts_as_token_authentication_handler_for DeviseSessionable::Session,
64
+ as: :session,
65
+ fallback: :exception
66
+ end
67
+ end
68
+ end
69
+ ```
70
+
71
+ Secondly, because we are using the `Session` to authenticate, but are actually authenticating a `User`, we need to define the `current_authable` scope in our `ApiController`
72
+
73
+ ```ruby
74
+ private
75
+
76
+ def current_authable
77
+ current_user
78
+ end
79
+ ```
80
+
81
+ Finally, we need to setup our Simple Token Authentication to refer to the sessions `id` when authenticating
82
+
83
+ ```ruby
84
+ # config/initializers/simple_token_authentication.rb
85
+
86
+ SimpleTokenAuthentication.configure do |config|
87
+ config.identifiers = { session: 'id' }
88
+ end
89
+ ```
90
+
91
+ _(This is something that we plan to integrate into the gem itself in a future release)_
92
+
93
+ ## Using Sessions for Authentication
94
+
95
+ Now that we have everything in place, we can authenticate using our new session objects. How you want to handle the creation, deletion and expiration of sessions is up to you, all the gem cares about is that a valid session is passed through to authenticate.
96
+
97
+ Underneath the gem we are still using the Simple Token Authentication gem to handle authentication, the usage is essentially the same, and you can refer to their documentation [here](https://github.com/gonzalo-bulnes/simple_token_authentication#usage)
98
+
99
+ Default Header Keys:
100
+
101
+ ```ruby
102
+ 'X-Session-Id' => session.id,
103
+ 'X-Session-Token' => session.authentication_token
104
+ ```
105
+
106
+ These can be overidden in the `SimpleTokenAuthentication` initializer, using the same methods the base gem uses.
107
+
108
+ ## License
109
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'DeviseSessionable'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../spec/test_app/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+ load 'rails/tasks/statistics.rake'
22
+
23
+
24
+
25
+ require 'bundler/gem_tasks'
26
+
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts/devise_sessionable .js
2
+ //= link_directory ../stylesheets/devise_sessionable .css
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,5 @@
1
+ module DeviseSessionable
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module DeviseSessionable
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module DeviseSessionable
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module DeviseSessionable
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module DeviseSessionable
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ require 'simple_token_authentication'
2
+
3
+ module DeviseSessionable
4
+ class Session < ApplicationRecord
5
+ acts_as_token_authenticatable
6
+
7
+ belongs_to :authable, polymorphic: true, touch: true
8
+
9
+ validates :authable, presence: true
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Devise sessionable</title>
5
+ <%= stylesheet_link_tag "devise_sessionable/application", media: "all" %>
6
+ <%= javascript_include_tag "devise_sessionable/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,31 @@
1
+ SimpleTokenAuthentication::ExceptionFallbackHandler.module_eval do
2
+ def fallback!(controller, entity)
3
+ return unless controller.send(:current_authable).nil?
4
+
5
+ throw(:warden, scope: entity.name_underscore.to_sym)
6
+ end
7
+ end
8
+
9
+ # Overrides
10
+ SimpleTokenAuthentication::TokenAuthenticationHandler.module_eval do
11
+ # All the token authentication actually happens on the session object,
12
+ # However we want to `sign_in!` method to be passed the user.
13
+ # So we override it here.
14
+ def authenticate_entity_from_token!(entity)
15
+ record = find_record_from_identifier(entity)
16
+
17
+ return unless token_correct?(record, entity, token_comparator)
18
+ perform_sign_in!(record.authable, sign_in_handler)
19
+ end
20
+
21
+ def find_record_from_identifier(entity)
22
+ identifier_param_value = entity.get_identifier_from_params_or_headers(self)
23
+ .presence
24
+
25
+ # The finder method should be compatible with all the model adapters,
26
+ # namely ActiveRecord and Mongoid in all their supported versions.
27
+ identifier_param_value && entity.model.find_by(
28
+ entity.identifier => identifier_param_value
29
+ )
30
+ end
31
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ DeviseSessionable::Engine.routes.draw do
2
+
3
+ end
@@ -0,0 +1,15 @@
1
+ require 'simple_token_authentication'
2
+ require "devise_sessionable/engine"
3
+
4
+ module DeviseSessionable
5
+ extend ActiveSupport::Autoload
6
+
7
+ autoload :ActsAsSessionable, 'devise_sessionable/acts_as_sessionable'
8
+ end
9
+
10
+ module ActiveRecord
11
+ class Base
12
+ include DeviseSessionable::ActsAsSessionable
13
+ end
14
+ end
15
+
@@ -0,0 +1,17 @@
1
+ module DeviseSessionable
2
+ module ActsAsSessionable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ end
7
+
8
+ module ClassMethods
9
+ def acts_as_sessionable(_options = {})
10
+ has_many :sessions,
11
+ as: :authable,
12
+ class_name: 'DeviseSessionable::Session',
13
+ dependent: :destroy
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ module DeviseSessionable
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace DeviseSessionable
4
+
5
+ config.generators do |g|
6
+ g.test_framework :rspec, :fixture => false
7
+ g.fixture_replacement :factory_girl, :dir => 'spec/factories'
8
+ g.assets false
9
+ g.helper false
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module DeviseSessionable
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,24 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ module DeviseSessionable
4
+ class InstallGenerator < Rails::Generators::Base
5
+ include Rails::Generators::Migration
6
+
7
+ source_root File.expand_path('../templates', __FILE__)
8
+
9
+ def copy_migration
10
+ migration_template 'migration.rb',
11
+ 'db/migrate/devise_sessionable_create_sessions.rb'
12
+ end
13
+
14
+ # Implement the required interface for Rails::Generators::Migration.
15
+ def self.next_migration_number(dirname)
16
+ next_migration_number = current_migration_number(dirname) + 1
17
+ if ActiveRecord::Base.timestamped_migrations
18
+ [Time.now.utc.strftime('%Y%m%d%H%M%S'), '%.14d' % next_migration_number].max
19
+ else
20
+ '%.3d' % next_migration_number
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ class DeviseSessionableCreateSessions < ActiveRecord::Migration
2
+ def change
3
+ create_table :devise_sessionable_sessions, id: :uuid do |t|
4
+ t.string :authentication_token
5
+ t.uuid :authable_id, null: false
6
+ t.string :authable_type, null: false
7
+
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :devise_sessionable_sessions,
12
+ [:authable_id, :authable_type],
13
+ name: 'index_devise_sessionable_sessions_on_authable'
14
+ add_index :devise_sessionable_sessions, :authentication_token
15
+ end
16
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :devise_sessionable do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,5 @@
1
+ FactoryGirl.define do
2
+ factory :session, class: 'DeviseSessionable::Session' do
3
+ association :authable, factory: :user
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ FactoryGirl.define do
2
+ factory :user do
3
+ end
4
+ end
@@ -0,0 +1,18 @@
1
+ require 'rails_helper'
2
+
3
+ module DeviseSessionable
4
+ RSpec.describe Session, type: :model do
5
+ it { is_expected.to belong_to(:authable) }
6
+ it { is_expected.to validate_presence_of(:authable) }
7
+
8
+ describe 'Simple token auth' do
9
+ it 'sets the authentication_token automatically' do
10
+ session = build(:session, authentication_token: nil)
11
+
12
+ expect do
13
+ session.save
14
+ end.to change(session, :authentication_token).from nil
15
+ end
16
+ end
17
+ end
18
+ end