knock_knock 0.1.1.pre.preview1

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