KnockRails3 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +39 -0
  4. data/app/controllers/knock_rails3/application_controller.rb +11 -0
  5. data/app/controllers/knock_rails3/auth_token_controller.rb +47 -0
  6. data/app/model/knock_rails3/auth_token.rb +78 -0
  7. data/config/routes.rb +3 -0
  8. data/lib/KnockRails3.rb +31 -0
  9. data/lib/generators/knock_rails3/install_generator.rb +13 -0
  10. data/lib/generators/knock_rails3/token_controller_generator.rb +27 -0
  11. data/lib/generators/templates/entity_token_controller.rb.erb +2 -0
  12. data/lib/generators/templates/knock_rails3.rb +59 -0
  13. data/lib/knock_rails3/authenticable.rb +60 -0
  14. data/lib/knock_rails3/engine.rb +6 -0
  15. data/lib/knock_rails3/version.rb +3 -0
  16. data/lib/tasks/KnockRails3_tasks.rake +4 -0
  17. data/test/dummy/README.rdoc +28 -0
  18. data/test/dummy/Rakefile +6 -0
  19. data/test/dummy/app/assets/javascripts/application.js +13 -0
  20. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  21. data/test/dummy/app/controllers/admin_protected_controller.rb +7 -0
  22. data/test/dummy/app/controllers/admin_token_controller.rb +2 -0
  23. data/test/dummy/app/controllers/application_controller.rb +7 -0
  24. data/test/dummy/app/controllers/composite_name_entity_protected_controller.rb +7 -0
  25. data/test/dummy/app/controllers/current_users_controller.rb +9 -0
  26. data/test/dummy/app/controllers/custom_unauthorized_entity_controller.rb +13 -0
  27. data/test/dummy/app/controllers/guest_protected_controller.rb +7 -0
  28. data/test/dummy/app/controllers/protected_resources_controller.rb +7 -0
  29. data/test/dummy/app/controllers/v1/test_namespaced_controller.rb +17 -0
  30. data/test/dummy/app/controllers/vendor_protected_controller.rb +11 -0
  31. data/test/dummy/app/controllers/vendor_token_controller.rb +2 -0
  32. data/test/dummy/app/helpers/application_helper.rb +2 -0
  33. data/test/dummy/app/models/admin.rb +16 -0
  34. data/test/dummy/app/models/composite_name_entity.rb +3 -0
  35. data/test/dummy/app/models/guest.rb +7 -0
  36. data/test/dummy/app/models/user.rb +3 -0
  37. data/test/dummy/app/models/v1/user.rb +5 -0
  38. data/test/dummy/app/models/vendor.rb +3 -0
  39. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  40. data/test/dummy/bin/bundle +3 -0
  41. data/test/dummy/bin/rails +4 -0
  42. data/test/dummy/bin/rake +4 -0
  43. data/test/dummy/bin/setup +29 -0
  44. data/test/dummy/config.ru +4 -0
  45. data/test/dummy/config/application.rb +28 -0
  46. data/test/dummy/config/boot.rb +5 -0
  47. data/test/dummy/config/database.yml +25 -0
  48. data/test/dummy/config/environment.rb +5 -0
  49. data/test/dummy/config/environments/development.rb +41 -0
  50. data/test/dummy/config/environments/production.rb +79 -0
  51. data/test/dummy/config/environments/test.rb +47 -0
  52. data/test/dummy/config/initializers/assets.rb +11 -0
  53. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  54. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  55. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  56. data/test/dummy/config/initializers/inflections.rb +16 -0
  57. data/test/dummy/config/initializers/knock.rb +8 -0
  58. data/test/dummy/config/initializers/mime_types.rb +4 -0
  59. data/test/dummy/config/initializers/session_store.rb +3 -0
  60. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  61. data/test/dummy/config/locales/en.yml +23 -0
  62. data/test/dummy/config/routes.rb +17 -0
  63. data/test/dummy/config/secrets.yml +22 -0
  64. data/test/dummy/db/migrate/20150713101607_create_users.rb +10 -0
  65. data/test/dummy/db/migrate/20160519075733_create_admins.rb +10 -0
  66. data/test/dummy/db/migrate/20160522051816_create_vendors.rb +10 -0
  67. data/test/dummy/db/migrate/20160522181712_create_composite_name_entities.rb +10 -0
  68. data/test/dummy/db/migrate/20161127203222_create_v1_users.rb +12 -0
  69. data/test/dummy/db/schema.rb +50 -0
  70. data/test/dummy/public/404.html +67 -0
  71. data/test/dummy/public/422.html +67 -0
  72. data/test/dummy/public/500.html +66 -0
  73. data/test/dummy/public/favicon.ico +0 -0
  74. data/test/dummy/test/controllers/admin_protected_controller_test.rb +49 -0
  75. data/test/dummy/test/controllers/admin_token_controller_test.rb +22 -0
  76. data/test/dummy/test/controllers/composite_name_entity_protected_controller_test.rb +49 -0
  77. data/test/dummy/test/controllers/current_users_controller_test.rb +31 -0
  78. data/test/dummy/test/controllers/custom_unauthorized_entity_controller_test.rb +42 -0
  79. data/test/dummy/test/controllers/guest_protected_controller_test.rb +22 -0
  80. data/test/dummy/test/controllers/protected_resources_controller_test.rb +62 -0
  81. data/test/dummy/test/controllers/v1/test_namespaced_controller_test.rb +19 -0
  82. data/test/dummy/test/controllers/vendor_protected_controller_test.rb +55 -0
  83. data/test/dummy/test/controllers/vendor_token_controller_test.rb +22 -0
  84. data/test/dummy/test/models/admin_test.rb +7 -0
  85. data/test/dummy/test/models/user_test.rb +4 -0
  86. data/test/dummy/test/models/vendor_test.rb +7 -0
  87. data/test/fixtures/admins.yml +5 -0
  88. data/test/fixtures/composite_name_entities.yml +5 -0
  89. data/test/fixtures/users.yml +9 -0
  90. data/test/fixtures/v1_users.yml +6 -0
  91. data/test/fixtures/vendors.yml +5 -0
  92. data/test/generators/install_generator_test.rb +12 -0
  93. data/test/generators/token_controller_generator_test.rb +31 -0
  94. data/test/knock_test.rb +9 -0
  95. data/test/model/knock/auth_token_test.rb +123 -0
  96. data/test/support/generators_test_helper.rb +9 -0
  97. data/test/test_helper.rb +45 -0
  98. metadata +292 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e9f0af023a92177f1547e7aab56b1a72a49a8aa4
4
+ data.tar.gz: 5ee385e7811b37f4d0cfb9a69db494f50600383b
5
+ SHA512:
6
+ metadata.gz: '0739ec58775ca1e7e8930266029600ede81f86ea59a614aa6a8914ad485decec68ac9574f3178a3a6ffa4d9c069353c97d0b4cffea47c22b6cdf84ae74b73fb4'
7
+ data.tar.gz: aca684c4d0b825404052d246991a6d3e6c45e740624a18e5f7b1ec4aafb1c351c6a6d4c670e0e80ebe2ef1dd1aede41155416d86c99c3fbf8985993bb9efbcdc
@@ -0,0 +1,20 @@
1
+ Copyright 2015 Arnaud MESUREUR
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.
@@ -0,0 +1,39 @@
1
+ require "rubygems"
2
+
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rdoc/task'
10
+
11
+ RDoc::Task.new(:rdoc) do |rdoc|
12
+ rdoc.rdoc_dir = 'rdoc'
13
+ rdoc.title = 'KnockRails3'
14
+ rdoc.options << '--line-numbers'
15
+ rdoc.rdoc_files.include('README.rdoc')
16
+ rdoc.rdoc_files.include('lib/**/*.rb')
17
+ end
18
+
19
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
20
+ load 'rails/tasks/engine.rake'
21
+
22
+
23
+ load 'rails/tasks/statistics.rake'
24
+
25
+
26
+
27
+ Bundler::GemHelper.install_tasks
28
+
29
+ require 'rake/testtask'
30
+
31
+ Rake::TestTask.new(:test) do |t|
32
+ t.libs << 'lib'
33
+ t.libs << 'test'
34
+ t.pattern = 'test/**/*_test.rb'
35
+ t.verbose = false
36
+ end
37
+
38
+
39
+ task default: :test
@@ -0,0 +1,11 @@
1
+ module KnockRails3
2
+ class ApplicationController < ActionController::Base
3
+ rescue_from KnockRails3.not_found_exception_class_name, with: :not_found
4
+
5
+ private
6
+
7
+ def not_found
8
+ head :not_found
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,47 @@
1
+ require_dependency "knock_rails3/application_controller"
2
+
3
+ module KnockRails3
4
+ class AuthTokenController < ApplicationController
5
+ before_filter :authenticate
6
+
7
+ def create
8
+ render json: auth_token, status: :created
9
+ end
10
+
11
+ private
12
+ def authenticate
13
+ unless entity.present? && entity.authenticate(auth_params[:password])
14
+ raise KnockRails3.not_found_exception_class
15
+ end
16
+ end
17
+
18
+ def auth_token
19
+ if entity.respond_to? :to_token_payload
20
+ AuthToken.new payload: entity.to_token_payload
21
+ else
22
+ AuthToken.new payload: { sub: entity.id }
23
+ end
24
+ end
25
+
26
+ def entity
27
+ @entity ||=
28
+ if entity_class.respond_to? :from_token_request
29
+ entity_class.from_token_request request
30
+ else
31
+ entity_class.find_by_email(auth_params[:email])
32
+ end
33
+ end
34
+
35
+ def entity_class
36
+ entity_name.constantize
37
+ end
38
+
39
+ def entity_name
40
+ self.class.name.scan(/\w+/).last.split('TokenController').first
41
+ end
42
+
43
+ def auth_params
44
+ params[:auth]
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,78 @@
1
+ require 'jwt'
2
+
3
+ module KnockRails3
4
+ class AuthToken
5
+ attr_reader :token
6
+ attr_reader :payload
7
+
8
+ def initialize payload: {}, token: nil, verify_options: {}
9
+ if token.present?
10
+ @payload, _ = JWT.decode token.to_s, decode_key, true, options.merge(verify_options)
11
+ @token = token
12
+ else
13
+ @payload = claims.merge(payload)
14
+ @token = JWT.encode @payload,
15
+ secret_key,
16
+ KnockRails3.token_signature_algorithm
17
+ end
18
+ end
19
+
20
+ def entity_for entity_class
21
+ if entity_class.respond_to? :from_token_payload
22
+ entity_class.from_token_payload @payload
23
+ else
24
+ entity_class.find @payload['sub']
25
+ end
26
+ end
27
+
28
+ def to_json options = {}
29
+ {jwt: @token}.to_json
30
+ end
31
+
32
+ private
33
+ def secret_key
34
+ KnockRails3.token_secret_signature_key.call
35
+ end
36
+
37
+ def decode_key
38
+ KnockRails3.token_public_key || secret_key
39
+ end
40
+
41
+ def options
42
+ verify_claims.merge({
43
+ algorithm: KnockRails3.token_signature_algorithm
44
+ })
45
+ end
46
+
47
+ def claims
48
+ _claims = {}
49
+ _claims[:exp] = token_lifetime if verify_lifetime?
50
+ _claims[:aud] = token_audience if verify_audience?
51
+ _claims
52
+ end
53
+
54
+ def token_lifetime
55
+ KnockRails3.token_lifetime.from_now.to_i if verify_lifetime?
56
+ end
57
+
58
+ def verify_lifetime?
59
+ !KnockRails3.token_lifetime.nil?
60
+ end
61
+
62
+ def verify_claims
63
+ {
64
+ aud: token_audience,
65
+ verify_aud: verify_audience?,
66
+ verify_expiration: verify_lifetime?
67
+ }
68
+ end
69
+
70
+ def token_audience
71
+ verify_audience? && KnockRails3.token_audience.call
72
+ end
73
+
74
+ def verify_audience?
75
+ KnockRails3.token_audience.present?
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,3 @@
1
+ KnockRails3::Engine.routes.draw do
2
+ post 'auth_token' => 'auth_token#create'
3
+ end
@@ -0,0 +1,31 @@
1
+ require "knock_rails3/engine"
2
+
3
+ module KnockRails3
4
+ mattr_accessor :token_lifetime
5
+ self.token_lifetime = 1.day
6
+
7
+ mattr_accessor :token_audience
8
+ self.token_audience = nil
9
+
10
+ mattr_accessor :token_signature_algorithm
11
+ self.token_signature_algorithm = 'HS256'
12
+
13
+ mattr_accessor :token_secret_signature_key
14
+ self.token_secret_signature_key = -> { Rails.application.secrets.secret_key_base }
15
+
16
+ mattr_accessor :token_public_key
17
+ self.token_public_key = nil
18
+
19
+ mattr_accessor :not_found_exception_class_name
20
+ self.not_found_exception_class_name = 'ActiveRecord::RecordNotFound'
21
+
22
+ def self.not_found_exception_class
23
+ not_found_exception_class_name.to_s.constantize
24
+ end
25
+
26
+ # Default way to setup KnockRails3. Run `rails generate KnockRails3:install` to create
27
+ # a fresh initializer with all configuration values.
28
+ def self.setup
29
+ yield self
30
+ end
31
+ end
@@ -0,0 +1,13 @@
1
+ require 'rails/generators'
2
+
3
+ module KnockRails3
4
+ class InstallGenerator < Rails::Generators::Base
5
+ source_root File.expand_path("../../templates", __FILE__)
6
+
7
+ desc "Creates a KnockRails3 initializer."
8
+
9
+ def copy_initializer
10
+ template 'knock_rails3.rb', 'config/initializers/knock_rails3.rb'
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ require 'rails/generators'
2
+
3
+ module KnockRails3
4
+ class TokenControllerGenerator < Rails::Generators::Base
5
+ source_root File.expand_path("../../templates", __FILE__)
6
+ argument :name, type: :string
7
+
8
+ desc <<-DESC
9
+ Creates a KnockRails3 token controller for the given entity
10
+ and add the corresponding routes.
11
+ DESC
12
+
13
+ def copy_controller_file
14
+ template 'entity_token_controller.rb.erb', "app/controllers/#{name.underscore}_token_controller.rb"
15
+ end
16
+
17
+ def add_route
18
+ route "post '#{name.underscore}_token' => '#{name.underscore}_token#create'"
19
+ end
20
+
21
+ private
22
+
23
+ def entity_name
24
+ name
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,2 @@
1
+ class <%= entity_name.camelize %>TokenController < KnockRails3::AuthTokenController
2
+ end
@@ -0,0 +1,59 @@
1
+ KnockRails3.setup do |config|
2
+
3
+ ## Expiration claim
4
+ ## ----------------
5
+ ##
6
+ ## How long before a token is expired. If nil is provided, token will
7
+ ## last forever.
8
+ ##
9
+ ## Default:
10
+ # config.token_lifetime = 1.day
11
+
12
+
13
+ ## Audience claim
14
+ ## --------------
15
+ ##
16
+ ## Configure the audience claim to identify the recipients that the token
17
+ ## is intended for.
18
+ ##
19
+ ## Default:
20
+ # config.token_audience = nil
21
+
22
+ ## If using Auth0, uncomment the line below
23
+ # config.token_audience = -> { Rails.application.secrets.auth0_client_id }
24
+
25
+ ## Signature algorithm
26
+ ## -------------------
27
+ ##
28
+ ## Configure the algorithm used to encode the token
29
+ ##
30
+ ## Default:
31
+ # config.token_signature_algorithm = 'HS256'
32
+
33
+ ## Signature key
34
+ ## -------------
35
+ ##
36
+ ## Configure the key used to sign tokens.
37
+ ##
38
+ ## Default:
39
+ # config.token_secret_signature_key = -> { Rails.application.secrets.secret_key_base }
40
+
41
+ ## If using Auth0, uncomment the line below
42
+ # config.token_secret_signature_key = -> { JWT.base64url_decode Rails.application.secrets.auth0_client_secret }
43
+
44
+ ## Public key
45
+ ## ----------
46
+ ##
47
+ ## Configure the public key used to decode tokens, if required.
48
+ ##
49
+ ## Default:
50
+ # config.token_public_key = nil
51
+
52
+ ## Exception Class
53
+ ## ---------------
54
+ ##
55
+ ## Configure the exception to be used when user cannot be found.
56
+ ##
57
+ ## Default:
58
+ # config.not_found_exception_class_name = 'ActiveRecord::RecordNotFound'
59
+ end
@@ -0,0 +1,60 @@
1
+ module KnockRails3::Authenticable
2
+ def authenticate_for entity_class
3
+ getter_name = "current_api_#{entity_class.to_s.parameterize.underscore}"
4
+ define_current_entity_getter(entity_class, getter_name)
5
+ public_send(getter_name)
6
+ end
7
+
8
+ private
9
+
10
+ def token
11
+ params[:token] || token_from_request_headers
12
+ end
13
+
14
+ def method_missing(method, *args)
15
+ prefix, api_prefix, entity_name = method.to_s.split('_', 3)
16
+ case prefix
17
+ when 'authenticate'
18
+ unauthorized_entity(entity_name) unless authenticate_entity(entity_name)
19
+ when 'current'
20
+ authenticate_entity(entity_name)
21
+ else
22
+ super
23
+ end
24
+ end
25
+
26
+ def authenticate_entity(entity_name)
27
+ if token
28
+ entity_class = entity_name.camelize.constantize
29
+ send(:authenticate_for, entity_class)
30
+ end
31
+ end
32
+
33
+ def unauthorized_entity(entity_name)
34
+ head(:unauthorized)
35
+ end
36
+
37
+ def token_from_request_headers
38
+ unless request.headers['Authorization'].nil?
39
+ request.headers['Authorization'].split.last
40
+ end
41
+ end
42
+
43
+ def define_current_entity_getter entity_class, getter_name
44
+ unless self.respond_to?(getter_name)
45
+ memoization_var_name = "@_#{getter_name}"
46
+ self.class.send(:define_method, getter_name) do
47
+ unless instance_variable_defined?(memoization_var_name)
48
+ current =
49
+ begin
50
+ KnockRails3::AuthToken.new(token: token).entity_for(entity_class)
51
+ rescue KnockRails3.not_found_exception_class, JWT::DecodeError
52
+ nil
53
+ end
54
+ instance_variable_set(memoization_var_name, current)
55
+ end
56
+ instance_variable_get(memoization_var_name)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,6 @@
1
+ module KnockRails3
2
+ class Engine < ::Rails::Engine
3
+ config.eager_load_paths += Dir["#{config.root}/lib/**/"]
4
+ isolate_namespace KnockRails3
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ module KnockRails3
2
+ VERSION = "2.1.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :KnockRails3 do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,28 @@
1
+ == README
2
+
3
+ This README would normally document whatever steps are necessary to get the
4
+ application up and running.
5
+
6
+ Things you may want to cover:
7
+
8
+ * Ruby version
9
+
10
+ * System dependencies
11
+
12
+ * Configuration
13
+
14
+ * Database creation
15
+
16
+ * Database initialization
17
+
18
+ * How to run the test suite
19
+
20
+ * Services (job queues, cache servers, search engines, etc.)
21
+
22
+ * Deployment instructions
23
+
24
+ * ...
25
+
26
+
27
+ Please feel free to use a different markup language if you do not plan to run
28
+ <tt>rake doc:app</tt>.