devise-token_authenticatable 0.1.0.beta1

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 (46) hide show
  1. data/.gitignore +21 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +20 -0
  4. data/README.md +31 -0
  5. data/Rakefile +1 -0
  6. data/devise-token_authenticatable.gemspec +36 -0
  7. data/lib/devise-token_authenticatable.rb +1 -0
  8. data/lib/devise/token_authenticatable.rb +27 -0
  9. data/lib/devise/token_authenticatable/model.rb +90 -0
  10. data/lib/devise/token_authenticatable/strategy.rb +102 -0
  11. data/lib/devise/token_authenticatable/version.rb +5 -0
  12. data/spec/factories/admin.rb +24 -0
  13. data/spec/factories/user.rb +25 -0
  14. data/spec/models/devise/token_authenticatable/model_spec.rb +79 -0
  15. data/spec/requests/devise/token_authenticatable/strategy_spec.rb +340 -0
  16. data/spec/spec_helper.rb +42 -0
  17. data/spec/support/helpers.rb +33 -0
  18. data/spec/support/integration.rb +8 -0
  19. data/spec/support/rails_app.rb +19 -0
  20. data/spec/support/rails_app/Rakefile +6 -0
  21. data/spec/support/rails_app/app/controllers/admins/sessions_controller.rb +6 -0
  22. data/spec/support/rails_app/app/controllers/admins_controller.rb +11 -0
  23. data/spec/support/rails_app/app/controllers/application_controller.rb +9 -0
  24. data/spec/support/rails_app/app/controllers/home_controller.rb +25 -0
  25. data/spec/support/rails_app/app/controllers/publisher/registrations_controller.rb +2 -0
  26. data/spec/support/rails_app/app/controllers/publisher/sessions_controller.rb +2 -0
  27. data/spec/support/rails_app/app/controllers/users_controller.rb +31 -0
  28. data/spec/support/rails_app/app/mailers/users/mailer.rb +12 -0
  29. data/spec/support/rails_app/app/models/admin.rb +9 -0
  30. data/spec/support/rails_app/app/models/user.rb +25 -0
  31. data/spec/support/rails_app/app/views/users/index.html.erb +1 -0
  32. data/spec/support/rails_app/config.ru +4 -0
  33. data/spec/support/rails_app/config/database.yml +11 -0
  34. data/spec/support/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  35. data/spec/support/rails_app/config/initializers/devise.rb +173 -0
  36. data/spec/support/rails_app/config/initializers/inflections.rb +2 -0
  37. data/spec/support/rails_app/config/initializers/secret_token.rb +4 -0
  38. data/spec/support/rails_app/config/routes.rb +104 -0
  39. data/spec/support/rails_app/db/migrate/20100401102949_create_tables.rb +74 -0
  40. data/spec/support/rails_app/db/schema.rb +52 -0
  41. data/spec/support/rails_app/public/404.html +26 -0
  42. data/spec/support/rails_app/public/422.html +26 -0
  43. data/spec/support/rails_app/public/500.html +26 -0
  44. data/spec/support/rails_app/public/favicon.ico +0 -0
  45. data/spec/support/session_helper.rb +27 -0
  46. metadata +289 -0
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ *.log
4
+ .bundle
5
+ .config
6
+ .rspec
7
+ .rspec-local
8
+ .ruby-version
9
+ .yardoc
10
+ Gemfile.lock
11
+ InstalledFiles
12
+ _yardoc
13
+ coverage
14
+ doc/
15
+ lib/bundler/man
16
+ pkg
17
+ rdoc
18
+ spec/reports
19
+ test/tmp
20
+ test/version_tmp
21
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in devise-token_authenticatable.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Sebastian Oelke
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Devise::TokenAuthenticatable
2
+
3
+ **Note that this gem is not ready for usage, yet!**
4
+
5
+ TODO: Write a gem description
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'devise-token_authenticatable'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install devise-token_authenticatable
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it
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 new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'devise/token_authenticatable/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "devise-token_authenticatable"
8
+ spec.version = Devise::TokenAuthenticatable::VERSION.dup
9
+ spec.platform = Gem::Platform::RUBY
10
+ spec.authors = ["Sebastian Oelke"]
11
+ spec.email = ["dev@sohleeatsworld.de"]
12
+ spec.description = %q{This gem provides the extracted Token Authenticatable module of devise.
13
+ It enables the user to sign in via an authentication token. This token
14
+ can be given via a query string or HTTP Basic Authentication.}
15
+ spec.summary = %q{Provides authentication based on an authentication token for devise 3.2 and up.}
16
+ spec.homepage = "https://github.com/baschtl/devise-token_authenticatable"
17
+ spec.license = "MIT"
18
+
19
+ spec.files = `git ls-files`.split($/)
20
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
+ spec.require_paths = ["lib"]
23
+
24
+
25
+ spec.add_dependency "devise", "~> 3.2.0"
26
+
27
+ spec.add_development_dependency "activerecord", ">= 3.2"
28
+ spec.add_development_dependency "actionmailer", ">= 3.2"
29
+ spec.add_development_dependency "rspec-rails"
30
+ spec.add_development_dependency "pry"
31
+ spec.add_development_dependency "factory_girl_rails"
32
+ spec.add_development_dependency "timecop"
33
+ spec.add_development_dependency "sqlite3", "~> 1.3"
34
+ spec.add_development_dependency "bundler", "~> 1.3"
35
+ spec.add_development_dependency "rake"
36
+ end
@@ -0,0 +1 @@
1
+ require 'devise/token_authenticatable'
@@ -0,0 +1,27 @@
1
+ require "devise/token_authenticatable/strategy"
2
+
3
+ module Devise
4
+ module TokenAuthenticatable
5
+
6
+ # Authentication token params key name of choice. E.g. /users/sign_in?some_key=...
7
+ mattr_accessor :token_authentication_key
8
+ @@token_authentication_key = :auth_token
9
+
10
+ # Enable the configuration of the TokenAuthenticatable
11
+ # strategy with a block:
12
+ #
13
+ # Devise::TokenAuthenticatable.setup do |config|
14
+ # config.token_authentication_key = :other_key
15
+ # end
16
+ #
17
+ def self.setup
18
+ yield self
19
+ end
20
+ end
21
+ end
22
+
23
+ # Register TokenAuthenticatable module in Devise.
24
+ Devise::add_module :token_authenticatable,
25
+ model: 'devise/token_authenticatable/model',
26
+ strategy: true,
27
+ no_input: true
@@ -0,0 +1,90 @@
1
+ module Devise
2
+ module Models
3
+ # The TokenAuthenticatable module is responsible for generating an authentication token and
4
+ # validating the authenticity of the same while signing in.
5
+ #
6
+ # This module only provides a few helpers to help you manage the token, but it is up to you
7
+ # to choose how to use it. For example, if you want to have a new token every time the user
8
+ # saves his account, you can do the following:
9
+ #
10
+ # before_save :reset_authentication_token
11
+ #
12
+ # On the other hand, if you want to generate token unless one exists, you should use instead:
13
+ #
14
+ # before_save :ensure_authentication_token
15
+ #
16
+ # If you want to delete the token after it is used, you can do so in the
17
+ # after_token_authentication callback.
18
+ #
19
+ # == APIs
20
+ #
21
+ # If you are using token authentication with APIs and using trackable. Every
22
+ # request will be considered as a new sign in (since there is no session in
23
+ # APIs). You can disable this by creating a before filter as follow:
24
+ #
25
+ # before_filter :skip_trackable
26
+ #
27
+ # def skip_trackable
28
+ # request.env['devise.skip_trackable'] = true
29
+ # end
30
+ #
31
+ # == Options
32
+ #
33
+ # TokenAuthenticatable adds the following options to devise_for:
34
+ #
35
+ # * +token_authentication_key+: Defines name of the authentication token params key. E.g. /users/sign_in?some_key=...
36
+ #
37
+ module TokenAuthenticatable
38
+ extend ActiveSupport::Concern
39
+
40
+ def self.required_fields(klass)
41
+ [:authentication_token]
42
+ end
43
+
44
+ # Generate new authentication token (a.k.a. "single access token").
45
+ def reset_authentication_token
46
+ self.authentication_token = self.class.authentication_token
47
+ end
48
+
49
+ # Generate new authentication token and save the record.
50
+ def reset_authentication_token!
51
+ reset_authentication_token
52
+ save(:validate => false)
53
+ end
54
+
55
+ # Generate authentication token unless already exists.
56
+ def ensure_authentication_token
57
+ reset_authentication_token if authentication_token.blank?
58
+ end
59
+
60
+ # Generate authentication token unless already exists and save the record.
61
+ def ensure_authentication_token!
62
+ reset_authentication_token! if authentication_token.blank?
63
+ end
64
+
65
+ # Hook called after token authentication.
66
+ def after_token_authentication
67
+ end
68
+
69
+ def expire_auth_token_on_timeout
70
+ self.class.expire_auth_token_on_timeout
71
+ end
72
+
73
+ module ClassMethods
74
+ def find_for_token_authentication(conditions)
75
+ find_for_authentication(:authentication_token => conditions[Devise::TokenAuthenticatable.token_authentication_key])
76
+ end
77
+
78
+ # Generate a token checking if one does not already exist in the database.
79
+ def authentication_token
80
+ loop do
81
+ token = Devise.friendly_token
82
+ break token unless to_adapter.find_first({ :authentication_token => token })
83
+ end
84
+ end
85
+
86
+ Devise::Models.config(self, :token_authentication_key, :expire_auth_token_on_timeout)
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,102 @@
1
+ require 'devise/strategies/base'
2
+
3
+ module Devise
4
+ module Strategies
5
+ #
6
+ # The +TokenAuthenticatable+ strategy was extracted from Devise 3.1.0. Its purpose is
7
+ # to provide the deprecated functionality of the +TokenAuthenticatable+ strategy. The
8
+ # following description was adapted accordingly.
9
+ #
10
+ # See: https://github.com/plataformatec/devise/blob/v3.1/lib/devise/strategies/token_authenticatable.rb
11
+ #
12
+ #
13
+ # Strategy for signing in a user, based on a authenticatable token. This works for both params
14
+ # and http. For the former, all you need to do is to pass the params in the URL:
15
+ #
16
+ # http://myapp.example.com/?user_token=SECRET
17
+ #
18
+ # For headers, you can use basic authentication passing the token as username and
19
+ # blank password. Since some clients may require a password, you can pass "X" as
20
+ # password and it will simply be ignored.
21
+ #
22
+ # You may also pass the token using the Token authentication mechanism provided
23
+ # by Rails: http://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token.html
24
+ # The token options are stored in request.env['devise.token_options']
25
+ #
26
+ #
27
+ # Changes regarding the original +TokenAuthenticatable+ implementation:
28
+ #
29
+ # The private method +remember_me?+ in +TokenAuthenticatable+ returns +false+.
30
+ # For +TokenAuthenticatable+ this method was removed. This results in the
31
+ # usage of the default implementation in +Authenticatable+.
32
+ #
33
+ class TokenAuthenticatable < Authenticatable
34
+ def store?
35
+ super && !mapping.to.skip_session_storage.include?(:token_auth)
36
+ end
37
+
38
+ def valid?
39
+ super || valid_for_token_auth?
40
+ end
41
+
42
+ def authenticate!
43
+ resource = mapping.to.find_for_token_authentication(authentication_hash)
44
+ return fail(:invalid_token) unless resource
45
+
46
+ if validate(resource)
47
+ resource.after_token_authentication
48
+ success!(resource)
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ # Token Authenticatable can be authenticated with params in any controller and any verb.
55
+ def valid_params_request?
56
+ true
57
+ end
58
+
59
+ # Check if the model accepts this strategy as token authenticatable.
60
+ def token_authenticatable?
61
+ mapping.to.http_authenticatable?(:token_options)
62
+ end
63
+
64
+ # Check if this is strategy is valid for token authentication by:
65
+ #
66
+ # * Validating if the model allows http token authentication;
67
+ # * If the http auth token exists;
68
+ # * If all authentication keys are present;
69
+ #
70
+ def valid_for_token_auth?
71
+ token_authenticatable? && auth_token.present? && with_authentication_hash(:token_auth, token_auth_hash)
72
+ end
73
+
74
+ # Extract the auth token from the request
75
+ def auth_token
76
+ @auth_token ||= ActionController::HttpAuthentication::Token.token_and_options(request)
77
+ end
78
+
79
+ # Extract a hash with attributes:values from the auth_token
80
+ def token_auth_hash
81
+ request.env['devise.token_options'] = auth_token.last
82
+ { authentication_keys.first => auth_token.first }
83
+ end
84
+
85
+ # Try both scoped and non scoped keys
86
+ def params_auth_hash
87
+ if params[scope].kind_of?(Hash) && params[scope].has_key?(authentication_keys.first)
88
+ params[scope]
89
+ else
90
+ params
91
+ end
92
+ end
93
+
94
+ # Overwrite authentication keys to use token_authentication_key.
95
+ def authentication_keys
96
+ @authentication_keys ||= [Devise::TokenAuthenticatable.token_authentication_key]
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ Warden::Strategies.add(:token_authenticatable, Devise::Strategies::TokenAuthenticatable)
@@ -0,0 +1,5 @@
1
+ module Devise
2
+ module TokenAuthenticatable
3
+ VERSION = "0.1.0.beta1".freeze
4
+ end
5
+ end
@@ -0,0 +1,24 @@
1
+ FactoryGirl.define do
2
+
3
+ factory :admin do
4
+ sequence(:email) { |n| "admin#{n}@domain.com" }
5
+ password 'some_password'
6
+ password_confirmation 'some_password'
7
+
8
+ ignore do
9
+ confirm_account true
10
+ end
11
+
12
+ after(:create) do |u, evaluator|
13
+ u.confirm! if evaluator.confirm_account
14
+ end
15
+
16
+ trait :with_reset_password_token do
17
+ reset_password_token { SecureRandom.hex }
18
+ end
19
+
20
+ trait :with_authentication_token do
21
+ authentication_token { SecureRandom.hex }
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ FactoryGirl.define do
2
+
3
+ factory :user do
4
+ username 'testuser'
5
+ sequence(:email) { |n| "user#{n}@domain.com" }
6
+ password 'some_password'
7
+ password_confirmation 'some_password'
8
+
9
+ ignore do
10
+ confirm_account true
11
+ end
12
+
13
+ after(:create) do |u, evaluator|
14
+ u.confirm! if evaluator.confirm_account
15
+ end
16
+
17
+ trait :with_reset_password_token do
18
+ reset_password_token { SecureRandom.hex }
19
+ end
20
+
21
+ trait :with_authentication_token do
22
+ authentication_token { SecureRandom.hex }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,79 @@
1
+ require 'spec_helper'
2
+
3
+ ##
4
+ # If a model that is plain token authenticatable should be tested with
5
+ # this shared example the corresponding factory has to provide a trait
6
+ # +:with_authentication_token+ that sets the attribute +authentication_token+.
7
+ #
8
+ # See spec/factories/account.rb for an example.
9
+ #
10
+ shared_examples "plain token authenticatable" do
11
+
12
+ context "instance methods" do
13
+
14
+ describe "#reset_authentication_token" do
15
+ let(:entity) { create(described_class.name.underscore.to_sym, :with_authentication_token) }
16
+
17
+ it "should reset authentication token" do
18
+ expect { entity.reset_authentication_token }.to change { entity.authentication_token }
19
+ end
20
+ end
21
+
22
+ describe "#ensure_authentication_token" do
23
+
24
+ context "with existing authentication token" do
25
+ let(:entity) { create(described_class.name.underscore.to_sym, :with_authentication_token) }
26
+
27
+ it "should not change the authentication token" do
28
+ expect { entity.ensure_authentication_token }.to_not change { entity.authentication_token }
29
+ end
30
+ end
31
+
32
+ context "without existing authentication token" do
33
+ let(:entity) { create(described_class.name.underscore.to_sym) }
34
+
35
+ it "should create an authentication token" do
36
+ entity.authentication_token = nil
37
+ expect { entity.ensure_authentication_token }.to change { entity.authentication_token }
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ context "class methods" do
44
+
45
+ describe "#find_for_authentication_token" do
46
+ let(:entity) { create(described_class.name.underscore.to_sym, :with_authentication_token) }
47
+
48
+ it "should authenticate a valid entity with authentication token and return it" do
49
+ authenticated_entity = described_class.find_for_token_authentication(auth_token: entity.authentication_token)
50
+ expect(entity.authentication_token).to eq(authenticated_entity.authentication_token)
51
+ end
52
+
53
+ it "should return nil when authenticating an invalid entity by authentication token" do
54
+ authenticated_entity = described_class.find_for_token_authentication(auth_token: entity.authentication_token.reverse)
55
+ expect(authenticated_entity).to be_nil
56
+ end
57
+
58
+ it "should not be subject to injection" do
59
+ entity2 = create(described_class.name.underscore.to_sym, :with_authentication_token)
60
+
61
+ authenticated_entity = described_class.find_for_token_authentication(auth_token: { '$ne' => entity.authentication_token })
62
+ expect(authenticated_entity).to be_nil
63
+ end
64
+ end
65
+
66
+ describe "#required_fields" do
67
+
68
+ it "should contain the fields that Devise uses" do
69
+ expect(Devise::Models::TokenAuthenticatable.required_fields(described_class)).to eq([
70
+ :authentication_token
71
+ ])
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ describe User do
78
+ it_behaves_like "plain token authenticatable"
79
+ end