knock_knock 0.1.1.pre.preview1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8d4bf4e3ad03c136e8975a450464a1f0d4627dbe07deddbb3a09e58adecfda5f
4
+ data.tar.gz: a68fde0d3b770805f7c48114c1a4a1af584453d88a8710aab683c601b9617a61
5
+ SHA512:
6
+ metadata.gz: df1263298dc157f4a2d2d7ad50fafb03b74a43c1e4f2ba77e6997d01dc895d72ca745a39d89fd4c651d0022361ef45f2f2f016d8e10436b3b59775a826344702
7
+ data.tar.gz: b3e424c276398b12a7a3334cc2f4cd7a53653da65f5b65b1b417904a3d8cee542ea7589613db3d633e4752f5e91e6fe8d88590f6aa86997c4a73d63316099a09
@@ -0,0 +1,20 @@
1
+ Copyright 2019 Mayurifag
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,21 @@
1
+ # gem knock_knock
2
+
3
+ lalala work in progress im gonna write something here.
4
+
5
+ ```ruby
6
+ gem 'knock_knock', github: 'Mayurifag/knock_knock'
7
+ ```
8
+
9
+ And then execute:
10
+ ```bash
11
+ $ bundle
12
+ ```
13
+
14
+ ## License
15
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
16
+
17
+ ## TODO:
18
+
19
+ [] Readme and gemspec summaries etc +badges +ci beautify
20
+ [] changelog
21
+ [] COC
@@ -0,0 +1,33 @@
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 = "KnockKnock"
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("test/dummy/Rakefile", __dir__)
18
+ load "rails/tasks/engine.rake"
19
+
20
+ load "rails/tasks/statistics.rake"
21
+
22
+ require "bundler/gem_tasks"
23
+
24
+ require "rake/testtask"
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ # t.libs << "lib"
28
+ t.libs << "test"
29
+ t.pattern = "test/**/*_test.rb"
30
+ t.verbose = false
31
+ end
32
+
33
+ task default: :test
@@ -0,0 +1,11 @@
1
+ module KnockKnock
2
+ class ApplicationController < ActionController::API
3
+ rescue_from KnockKnock.not_found_exception_class_name, with: :record_invaild
4
+
5
+ private
6
+
7
+ def record_invaild
8
+ render json: "Record invalid", status: :unprocessable_entity
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,48 @@
1
+ require_dependency "knock_knock/application_controller"
2
+
3
+ module KnockKnock
4
+ class AuthTokenController < ApplicationController
5
+ before_action :authenticate
6
+
7
+ def create
8
+ render json: auth_token, status: :created
9
+ end
10
+
11
+ private
12
+
13
+ def authenticate
14
+ unless entity&.authenticate(auth_params[:password])
15
+ raise KnockKnock.not_found_exception_class
16
+ end
17
+ end
18
+
19
+ def auth_token
20
+ if entity.respond_to? :to_token_payload
21
+ AuthToken.new payload: entity.to_token_payload
22
+ else
23
+ AuthToken.new payload: {sub: entity.id}
24
+ end
25
+ end
26
+
27
+ def entity
28
+ @entity ||=
29
+ if entity_class.respond_to? :from_token_request
30
+ entity_class.from_token_request request
31
+ else
32
+ entity_class.find_by email: auth_params[:email]
33
+ end
34
+ end
35
+
36
+ def entity_class
37
+ entity_name.constantize
38
+ end
39
+
40
+ def entity_name
41
+ self.class.name.scan(/\w+/).last.split("TokenController").first
42
+ end
43
+
44
+ def auth_params
45
+ params.permit :email, :password
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,5 @@
1
+ module KnockKnock
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_dependency "jwt"
4
+
5
+ module KnockKnock
6
+ class AuthToken
7
+ attr_reader :token, :payload
8
+
9
+ def initialize(payload: {}, token: nil, verify_options: {})
10
+ if token.present?
11
+ @payload, _ = JWT.decode(token.to_s, decode_key, true, Claims.to_decode.merge(verify_options))
12
+ @token = token
13
+ else
14
+ @payload = Claims.to_encode.merge(payload)
15
+ @token = JWT.encode @payload, secret_key, KnockKnock.token_signature_algorithm
16
+ end
17
+ end
18
+
19
+ def entity_for(entity_class)
20
+ if entity_class.respond_to? :from_token_payload
21
+ entity_class.from_token_payload @payload
22
+ else
23
+ entity_class.find @payload["sub"]
24
+ end
25
+ end
26
+
27
+ def to_json(_options = {})
28
+ {jwt: @token}.to_json
29
+ end
30
+
31
+ private
32
+
33
+ def secret_key
34
+ KnockKnock.token_secret_signature_key.call
35
+ end
36
+
37
+ def decode_key
38
+ KnockKnock.token_public_key || secret_key
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,39 @@
1
+ module KnockKnock
2
+ class Claims
3
+ class << self
4
+ def to_encode
5
+ claims = {}
6
+ claims[:exp] = token_lifetime if verify_lifetime?
7
+ claims[:aud] = token_audience if verify_audience?
8
+ claims
9
+ end
10
+
11
+ def to_decode
12
+ {
13
+ algorithm: KnockKnock.token_signature_algorithm,
14
+ aud: token_audience,
15
+ verify_aud: verify_audience?,
16
+ verify_expiration: verify_lifetime?,
17
+ }
18
+ end
19
+
20
+ private
21
+
22
+ def token_lifetime
23
+ KnockKnock.token_lifetime.from_now.to_i if verify_lifetime?
24
+ end
25
+
26
+ def verify_lifetime?
27
+ KnockKnock.token_lifetime.present?
28
+ end
29
+
30
+ def token_audience
31
+ verify_audience? && KnockKnock.token_audience.call
32
+ end
33
+
34
+ def verify_audience?
35
+ KnockKnock.token_audience.present?
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,3 @@
1
+ KnockKnock::Engine.routes.draw do
2
+ post :auth_token, to: "auth_token#create"
3
+ end
@@ -0,0 +1,20 @@
1
+ require_dependency "rails/generators"
2
+
3
+ module KnockKnock
4
+ class InstallGenerator < Rails::Generators::Base
5
+ source_root File.expand_path("../templates", __FILE__)
6
+ desc "Creates a KnockKnock initializer."
7
+
8
+ TEMPLATE_FILENAME = "knock_knock.rb".freeze
9
+
10
+ def copy_initializer
11
+ template TEMPLATE_FILENAME, app_controller_filepath
12
+ end
13
+
14
+ private
15
+
16
+ def app_controller_filepath
17
+ "config/initializers/#{TEMPLATE_FILENAME}"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,2 @@
1
+ class <%= entity_name.camelize %>TokenController < KnockKnock::AuthTokenController
2
+ end
@@ -0,0 +1,57 @@
1
+ KnockKnock.setup do |config|
2
+ ## Expiration claim
3
+ ## ----------------
4
+ ##
5
+ ## How long before a token is expired. If nil is provided, token will
6
+ ## last forever.
7
+ ##
8
+ ## Default:
9
+ # config.token_lifetime = 1.day
10
+
11
+ ## Audience claim
12
+ ## --------------
13
+ ##
14
+ ## Configure the audience claim to identify the recipients that the token
15
+ ## is intended for.
16
+ ##
17
+ ## Default:
18
+ # config.token_audience = nil
19
+
20
+ ## If using Auth0, uncomment the line below
21
+ # config.token_audience = -> { Rails.application.secrets.auth0_client_id }
22
+
23
+ ## Signature algorithm
24
+ ## -------------------
25
+ ##
26
+ ## Configure the algorithm used to encode the token
27
+ ##
28
+ ## Default:
29
+ # config.token_signature_algorithm = 'HS256'
30
+
31
+ ## Signature key
32
+ ## -------------
33
+ ##
34
+ ## Configure the key used to sign tokens.
35
+ ##
36
+ ## Default:
37
+ # config.token_secret_signature_key = -> { Rails.application.credentials.fetch(:secret_key_base) }
38
+
39
+ ## If using Auth0, uncomment the line below
40
+ # config.token_secret_signature_key = -> { JWT.base64url_decode Rails.application.secrets.auth0_client_secret }
41
+
42
+ ## Public key
43
+ ## ----------
44
+ ##
45
+ ## Configure the public key used to decode tokens, if required.
46
+ ##
47
+ ## Default:
48
+ # config.token_public_key = nil
49
+
50
+ ## Exception Class
51
+ ## ---------------
52
+ ##
53
+ ## Configure the exception to be used when user cannot be found.
54
+ ##
55
+ ## Default:
56
+ # config.not_found_exception_class_name = 'ActiveRecord::RecordNotFound'
57
+ end
@@ -0,0 +1,35 @@
1
+ require_dependency "rails/generators"
2
+
3
+ module KnockKnock
4
+ # Creates a Knockknock token controller for the given entity
5
+ # and add the corresponding routes.
6
+ class TokenControllerGenerator < Rails::Generators::Base
7
+ source_root File.expand_path("../templates", __FILE__)
8
+ argument :entity_name, type: :string
9
+
10
+ TEMPLATE_FILENAME = "entity_token_controller.rb.erb".freeze
11
+
12
+ desc <<-DESC
13
+ Creates a Knockknock token controller for the given entity and add the
14
+ corresponding routes.
15
+ DESC
16
+
17
+ def copy_controller_file
18
+ template TEMPLATE_FILENAME, app_controller_filepath
19
+ end
20
+
21
+ def add_route
22
+ route "post :#{undescored_name}_token, to: '#{undescored_name}_token#create'"
23
+ end
24
+
25
+ private
26
+
27
+ def undescored_name
28
+ @undescored_name ||= entity_name.underscore
29
+ end
30
+
31
+ def app_controller_filepath
32
+ "app/controllers/#{undescored_name}_token_controller.rb"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ require "knock_knock/engine"
2
+ require_dependency 'knock_knock/authenticable'
3
+
4
+ module KnockKnock
5
+ mattr_accessor :token_lifetime
6
+ self.token_lifetime = 1.day
7
+
8
+ mattr_accessor :token_audience
9
+ self.token_audience = nil
10
+
11
+ mattr_accessor :token_signature_algorithm
12
+ self.token_signature_algorithm = "HS256"
13
+
14
+ mattr_accessor :token_secret_signature_key
15
+ self.token_secret_signature_key = -> { Rails.application.secrets.secret_key_base }
16
+
17
+ mattr_accessor :token_public_key
18
+ self.token_public_key = nil
19
+
20
+ mattr_accessor :not_found_exception_class_name
21
+ self.not_found_exception_class_name = "ActiveRecord::RecordNotFound"
22
+
23
+ def self.not_found_exception_class
24
+ not_found_exception_class_name.to_s.constantize
25
+ end
26
+
27
+ # Default way to setup KnockKnock. Run `rails generate knock_knock:install` to create
28
+ # a fresh initializer with all configuration values.
29
+ def self.setup
30
+ yield self
31
+ end
32
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KnockKnock::Authenticable
4
+ def authenticate_for(entity_class)
5
+ getter_name = "current_#{entity_class.to_s.parameterize.underscore}"
6
+ define_current_entity_getter(entity_class, getter_name)
7
+ public_send(getter_name)
8
+ end
9
+
10
+ private
11
+
12
+ def token
13
+ params[:token] || token_from_request_headers
14
+ end
15
+
16
+ def method_missing(method, *args)
17
+ prefix, entity_name = method.to_s.split("_", 2)
18
+ case prefix
19
+ when "authenticate"
20
+ unauthorized_entity(entity_name) unless authenticate_entity(entity_name)
21
+ when "current"
22
+ authenticate_entity(entity_name)
23
+ else
24
+ super
25
+ end
26
+ end
27
+
28
+ def respond_to_missing?(method_name, include_private = false)
29
+ method_name.to_s.start_with?("authenticate_") || super
30
+ end
31
+
32
+ def authenticate_entity(entity_name)
33
+ if token
34
+ entity_class = entity_name.camelize.constantize
35
+ send(:authenticate_for, entity_class)
36
+ end
37
+ end
38
+
39
+ def unauthorized_entity(_entity_name)
40
+ head(:unauthorized)
41
+ end
42
+
43
+ def token_from_request_headers
44
+ request.headers["Authorization"]&.split&.last
45
+ end
46
+
47
+ def define_current_entity_getter(entity_class, getter_name)
48
+ unless respond_to?(getter_name)
49
+ memoization_var_name = "@_#{getter_name}"
50
+ self.class.send(:define_method, getter_name) do
51
+ unless instance_variable_defined?(memoization_var_name)
52
+ current =
53
+ begin
54
+ KnockKnock::AuthToken.new(token: token).entity_for(entity_class)
55
+ rescue KnockKnock.not_found_exception_class, JWT::DecodeError, JWT::EncodeError
56
+ nil
57
+ end
58
+ instance_variable_set(memoization_var_name, current)
59
+ end
60
+ instance_variable_get(memoization_var_name)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,7 @@
1
+ module KnockKnock
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace KnockKnock
4
+
5
+ config.generators.api_only = true
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module KnockKnock
2
+ VERSION = "0.1.1-preview1"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :knock_knock do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: knock_knock
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1.pre.preview1
5
+ platform: ruby
6
+ authors:
7
+ - Mayurifag
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-11-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 6.0.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 6.0.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: jwt
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.2.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.2.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: bcrypt
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: timecop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sqlite3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: standard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Description of KnockKnock.
98
+ email:
99
+ - farazeus@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - MIT-LICENSE
105
+ - README.md
106
+ - Rakefile
107
+ - app/controllers/knock_knock/application_controller.rb
108
+ - app/controllers/knock_knock/auth_token_controller.rb
109
+ - app/models/knock_knock/application_record.rb
110
+ - app/models/knock_knock/auth_token.rb
111
+ - app/models/knock_knock/claims.rb
112
+ - config/routes.rb
113
+ - lib/generators/knock_knock/install_generator.rb
114
+ - lib/generators/knock_knock/templates/entity_token_controller.rb.erb
115
+ - lib/generators/knock_knock/templates/knock_knock.rb
116
+ - lib/generators/knock_knock/token_controller_generator.rb
117
+ - lib/knock_knock.rb
118
+ - lib/knock_knock/authenticable.rb
119
+ - lib/knock_knock/engine.rb
120
+ - lib/knock_knock/version.rb
121
+ - lib/tasks/knock_knock_tasks.rake
122
+ homepage: https://github.com/Mayurifag/knock_knock
123
+ licenses:
124
+ - MIT
125
+ metadata:
126
+ allowed_push_host: https://rubygems.org
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">"
139
+ - !ruby/object:Gem::Version
140
+ version: 1.3.1
141
+ requirements: []
142
+ rubygems_version: 3.0.3
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: Summary of KnockKnock.
146
+ test_files: []