devise-token_authenticatable 0.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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