api_guard 0.3.0 → 0.4.0
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.
- checksums.yaml +4 -4
- data/README.md +73 -9
- data/Rakefile +2 -5
- data/app/controllers/api_guard/application_controller.rb +2 -0
- data/app/controllers/api_guard/authentication_controller.rb +2 -0
- data/app/controllers/api_guard/passwords_controller.rb +2 -0
- data/app/controllers/api_guard/registration_controller.rb +2 -0
- data/app/controllers/api_guard/tokens_controller.rb +2 -0
- data/config/routes.rb +2 -0
- data/lib/api_guard.rb +9 -6
- data/lib/api_guard/app_secret_key.rb +2 -0
- data/lib/api_guard/engine.rb +3 -1
- data/lib/api_guard/jwt_auth/authentication.rb +29 -8
- data/lib/api_guard/jwt_auth/blacklist_token.rb +5 -3
- data/lib/api_guard/jwt_auth/json_web_token.rb +10 -5
- data/lib/api_guard/jwt_auth/refresh_jwt_token.rb +2 -0
- data/lib/api_guard/models/concerns.rb +8 -6
- data/lib/api_guard/modules.rb +13 -11
- data/lib/api_guard/resource_mapper.rb +3 -1
- data/lib/api_guard/response_formatters/renderer.rb +3 -0
- data/lib/api_guard/route_mapper.rb +58 -54
- data/lib/api_guard/test/controller_helper.rb +2 -0
- data/lib/api_guard/version.rb +3 -1
- data/lib/generators/api_guard/controllers/controllers_generator.rb +4 -2
- data/lib/generators/api_guard/initializer/initializer_generator.rb +3 -1
- data/lib/generators/api_guard/initializer/templates/initializer.rb +2 -0
- metadata +40 -28
- data/app/models/api_guard/application_record.rb +0 -5
- data/app/views/layouts/api_guard/application.html.erb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7fd3e30531e593d102344eae722507a2bb93397
|
4
|
+
data.tar.gz: 98bbbbd0ad3d8d3dc541483e79d8db46381da316
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 867e07e76fb1957adb353d2f667e43ef10cad61ea360e917dc1e60d72f57c1cb05962af42d855ae5ccdacc88b0a16834487568b81e13aec04ae1fc087d78ff91
|
7
|
+
data.tar.gz: f57dae8c1baba2ad90c15a2001b4e139d10f44dccbbdc19ab6548b592d9d97ab681ae9efd9d72128ef03f2f1f9ed9feb185ff58b8cf7d5625d8d425dc33a61da
|
data/README.md
CHANGED
@@ -35,8 +35,12 @@ for cryptographic signing.
|
|
35
35
|
* [Overriding defaults](#overriding-defaults)
|
36
36
|
* [Controllers](#controllers)
|
37
37
|
* [Routes](#routes)
|
38
|
+
* [Adding custom data in JWT token payload](#adding-custom-data-in-jwt-token-payload)
|
39
|
+
* [Override finding resource](#override-finding-resource)
|
38
40
|
* [Customizing / translating response messages using I18n](#customizing--translating-response-messages-using-i18n)
|
39
41
|
* [Testing](#testing)
|
42
|
+
* [Wiki](https://github.com/Gokul595/api_guard/wiki)
|
43
|
+
* [Using API Guard with Devise](https://github.com/Gokul595/api_guard/wiki/Using-API-Guard-with-Devise)
|
40
44
|
* [Contributing](#contributing)
|
41
45
|
* [License](#license)
|
42
46
|
|
@@ -64,20 +68,22 @@ Below steps are provided assuming the model in `User`.
|
|
64
68
|
|
65
69
|
### Creating User model
|
66
70
|
|
67
|
-
Create a model for User with below command
|
71
|
+
Create a model for User with below command.
|
68
72
|
|
69
73
|
```bash
|
70
74
|
$ rails generate model user name:string email:string:uniq password_digest:string
|
71
75
|
```
|
72
76
|
|
73
|
-
Then, run migration to create the `users` table
|
77
|
+
Then, run migration to create the `users` table.
|
74
78
|
|
75
79
|
```bash
|
76
80
|
$ rails db:migrate
|
77
81
|
```
|
78
82
|
|
79
83
|
Add [has_secure_password](https://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password)
|
80
|
-
in `User` model for password authentication
|
84
|
+
in `User` model for password authentication.
|
85
|
+
|
86
|
+
> Refer [this Wiki](https://github.com/Gokul595/api_guard/wiki/Using-API-Guard-with-Devise#authentication) for configuring API Guard authentication to work with Devise instead of using `has_secure_password`.
|
81
87
|
|
82
88
|
```ruby
|
83
89
|
class User < ApplicationRecord
|
@@ -87,7 +93,7 @@ end
|
|
87
93
|
|
88
94
|
Then, add `bcrypt` gem in your Gemfile which is used by
|
89
95
|
[has_secure_password](https://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password)
|
90
|
-
for encrypting password and authentication
|
96
|
+
for encrypting password and authentication.
|
91
97
|
|
92
98
|
```ruby
|
93
99
|
gem 'bcrypt', '~> 3.1.7'
|
@@ -109,6 +115,8 @@ api_guard_routes for: 'users'
|
|
109
115
|
|
110
116
|
This will generate default routes such as sign up, sign in, sign out, token refresh, password change for User.
|
111
117
|
|
118
|
+
> Refer [this Wiki](https://github.com/Gokul595/api_guard/wiki/Using-API-Guard-with-Devise#routes) for configuring API Guard routes to work with Devise.
|
119
|
+
|
112
120
|
### Registration
|
113
121
|
|
114
122
|
This will create an user and responds with access token, refresh token and access token expiry in the response header.
|
@@ -390,6 +398,10 @@ Override this by configuring `token_signing_secret`
|
|
390
398
|
config.token_signing_secret = 'my_signing_secret'
|
391
399
|
```
|
392
400
|
|
401
|
+
>**Note:** Avoid committing this token signing secret in your version control (GIT) and always keep this secure. As,
|
402
|
+
>exposing this allow anyone to generate JWT access token and give full access to APIs. Better way is storing this value
|
403
|
+
>in environment variable or in encrypted secrets (Rails 5.2+)
|
404
|
+
|
393
405
|
### Invalidate tokens on password change
|
394
406
|
|
395
407
|
By default, API Guard will not invalidate old JWT access tokens on changing password. If you need, you can enable it by
|
@@ -540,22 +552,74 @@ api_guard_routes for: 'users', only: [:authentication]
|
|
540
552
|
|
541
553
|
>**Available controllers:** registration, authentication, tokens, passwords
|
542
554
|
|
543
|
-
**Customizing the
|
555
|
+
**Customizing the route path:**
|
544
556
|
|
545
|
-
|
557
|
+
You can customize the path of the default routes of the API Guard using the `api_guard_scope` as below,
|
546
558
|
|
547
559
|
```ruby
|
548
560
|
api_guard_routes for: 'users', except: [:registration]
|
549
561
|
|
550
562
|
api_guard_scope 'users' do
|
551
|
-
post 'account/create' => '
|
552
|
-
delete 'account/delete' => '
|
563
|
+
post 'account/create' => 'api_guard/registration#create'
|
564
|
+
delete 'account/delete' => 'api_guard/registration#destroy'
|
553
565
|
end
|
554
566
|
```
|
555
567
|
|
556
568
|
Above configuration will replace default registration routes `users/sign_up` & `users/delete` with `account/create` &
|
557
569
|
`account/delete`
|
558
570
|
|
571
|
+
### Adding custom data in JWT token payload
|
572
|
+
|
573
|
+
You can add custom data in the JWT token payload in the format of Hash and use the data after decoding the token on
|
574
|
+
every request.
|
575
|
+
|
576
|
+
To add custom data, you need to create an instance method `jwt_token_payload` in the resource model as below which
|
577
|
+
should return a Hash,
|
578
|
+
|
579
|
+
```ruby
|
580
|
+
class User < ApplicationRecord
|
581
|
+
def jwt_token_payload
|
582
|
+
{ custom_key: 'value' }
|
583
|
+
end
|
584
|
+
end
|
585
|
+
```
|
586
|
+
|
587
|
+
API Guard will add the hash returned by this method to the JWT token payload in addition to the default payload values.
|
588
|
+
This data (including default payload values) will be available in the instance variable `@decoded_token` on each request
|
589
|
+
if the token has been successfully decoded. You can access the values as below,
|
590
|
+
|
591
|
+
```ruby
|
592
|
+
@decoded_token[:custom_key]
|
593
|
+
```
|
594
|
+
|
595
|
+
### Override finding resource
|
596
|
+
|
597
|
+
By default, API Guard will try to find the resource by it's `id`. If you wish to override this default behavior, you can
|
598
|
+
do it by creating a method `find_resource_from_token` in the specific controller or in `ApplicationController` as you
|
599
|
+
need.
|
600
|
+
|
601
|
+
**Adding custom logic in addition to the default logic:**
|
602
|
+
```ruby
|
603
|
+
def find_resource_from_token(resource_class)
|
604
|
+
user = super # This will call the actual method defined in API Guard
|
605
|
+
user if user&.active?
|
606
|
+
end
|
607
|
+
```
|
608
|
+
|
609
|
+
**Using custom query to find the user from the token:**
|
610
|
+
```ruby
|
611
|
+
def find_resource_from_token(resource_class)
|
612
|
+
resource_id = @decoded_token[:"#{@resource_name}_id"]
|
613
|
+
resource_class.find_by(id: resource_id, status: 'active') if resource_id
|
614
|
+
end
|
615
|
+
```
|
616
|
+
|
617
|
+
This method has an argument `resource_class` which is the class (model) of the current resource (`User`).
|
618
|
+
This method should return a resource object to successfully authenticate the request or `nil` to respond with 401.
|
619
|
+
|
620
|
+
You can also use the [custom data](#adding-custom-data-in-jwt-token-payload) added in the JWT token payload using
|
621
|
+
`@decoded_token` instance variable and customize the logic as you need.
|
622
|
+
|
559
623
|
### Customizing / translating response messages using I18n
|
560
624
|
|
561
625
|
API Guard uses [I18n](https://guides.rubyonrails.org/i18n.html) for success and error messages. You can create your own
|
@@ -577,7 +641,7 @@ https://github.com/Gokul595/api_guard/blob/master/config/locales/en.yml
|
|
577
641
|
API Guard comes with helper for creating JWT access token and refresh token for the resource which you can use it for
|
578
642
|
testing the controllers of your application.
|
579
643
|
|
580
|
-
For using it include the helper in
|
644
|
+
For using it, just include the helper in your test framework.
|
581
645
|
|
582
646
|
**RSpec**
|
583
647
|
|
data/Rakefile
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
begin
|
2
4
|
require 'bundler/setup'
|
3
5
|
rescue LoadError
|
@@ -14,11 +16,6 @@ RDoc::Task.new(:rdoc) do |rdoc|
|
|
14
16
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
17
|
end
|
16
18
|
|
17
|
-
|
18
|
-
|
19
19
|
load 'rails/tasks/statistics.rake'
|
20
20
|
|
21
|
-
|
22
|
-
|
23
21
|
require 'bundler/gem_tasks'
|
24
|
-
|
data/config/routes.rb
CHANGED
data/lib/api_guard.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'api_guard/engine'
|
4
|
+
require 'api_guard/route_mapper'
|
5
|
+
require 'api_guard/modules'
|
4
6
|
|
5
7
|
module ApiGuard
|
6
8
|
autoload :AppSecretKey, 'api_guard/app_secret_key'
|
@@ -24,14 +26,15 @@ module ApiGuard
|
|
24
26
|
mattr_accessor :api_guard_associations
|
25
27
|
self.api_guard_associations = {}
|
26
28
|
|
27
|
-
mattr_reader :mapped_resource
|
28
|
-
|
29
|
+
mattr_reader :mapped_resource do
|
30
|
+
{}
|
31
|
+
end
|
29
32
|
|
30
33
|
def self.setup
|
31
34
|
yield self
|
32
35
|
end
|
33
36
|
|
34
37
|
def self.map_resource(routes_for, class_name)
|
35
|
-
|
38
|
+
mapped_resource[routes_for.to_sym] = ApiGuard::ResourceMapper.new(routes_for, class_name)
|
36
39
|
end
|
37
40
|
end
|
data/lib/api_guard/engine.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ApiGuard
|
2
4
|
class Engine < ::Rails::Engine
|
3
5
|
isolate_namespace ApiGuard
|
4
6
|
|
5
7
|
config.generators do |g|
|
6
8
|
g.test_framework :rspec
|
7
|
-
g.fixture_replacement :factory_girl, :
|
9
|
+
g.fixture_replacement :factory_girl, dir: 'spec/factories'
|
8
10
|
end
|
9
11
|
|
10
12
|
# Use 'secret_key_base' from Rails secrets if 'token_signing_secret' is not configured
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ApiGuard
|
2
4
|
module JwtAuth
|
3
5
|
# Common module for API authentication
|
@@ -14,6 +16,10 @@ module ApiGuard
|
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
19
|
+
def respond_to_missing?(method_name, include_private = false)
|
20
|
+
method_name.to_s.start_with?('authenticate_and_set_') || super
|
21
|
+
end
|
22
|
+
|
17
23
|
# Authenticate the JWT token and set resource
|
18
24
|
def authenticate_and_set_resource(resource_name)
|
19
25
|
@resource_name = resource_name
|
@@ -43,9 +49,19 @@ module ApiGuard
|
|
43
49
|
|
44
50
|
# Returns whether the JWT token is issued after the last password change
|
45
51
|
# Returns true if password hasn't changed by the user
|
46
|
-
def valid_issued_at?
|
52
|
+
def valid_issued_at?(resource)
|
47
53
|
return true unless ApiGuard.invalidate_old_tokens_on_password_change
|
48
|
-
|
54
|
+
|
55
|
+
!resource.token_issued_at || @decoded_token[:iat] >= resource.token_issued_at.to_i
|
56
|
+
end
|
57
|
+
|
58
|
+
# Defines "current_{{resource_name}}" method and "@current_{{resource_name}}" instance variable
|
59
|
+
# that returns "resource" value
|
60
|
+
def define_current_resource_accessors(resource)
|
61
|
+
self.class.send(:define_method, "current_#{@resource_name}") do
|
62
|
+
instance_variable_get("@current_#{@resource_name}") ||
|
63
|
+
instance_variable_set("@current_#{@resource_name}", resource)
|
64
|
+
end
|
49
65
|
end
|
50
66
|
|
51
67
|
# Authenticate the resource with the '{{resource_name}}_id' in the decoded JWT token
|
@@ -54,17 +70,22 @@ module ApiGuard
|
|
54
70
|
# Also, set "current_{{resource_name}}" method and "@current_{{resource_name}}" instance variable
|
55
71
|
# for accessing the authenticated resource
|
56
72
|
def authenticate_token
|
57
|
-
return unless decode_token
|
73
|
+
return unless decode_token
|
58
74
|
|
59
|
-
resource = @resource_name.classify.constantize
|
75
|
+
resource = find_resource_from_token(@resource_name.classify.constantize)
|
60
76
|
|
61
|
-
|
62
|
-
|
77
|
+
if resource && valid_issued_at?(resource) && !blacklisted?(resource)
|
78
|
+
define_current_resource_accessors(resource)
|
79
|
+
else
|
80
|
+
render_error(401, message: I18n.t('api_guard.access_token.invalid'))
|
63
81
|
end
|
82
|
+
end
|
64
83
|
|
65
|
-
|
84
|
+
def find_resource_from_token(resource_class)
|
85
|
+
resource_id = @decoded_token[:"#{@resource_name}_id"]
|
86
|
+
return if resource_id.blank?
|
66
87
|
|
67
|
-
|
88
|
+
resource_class.find_by(id: resource_id)
|
68
89
|
end
|
69
90
|
|
70
91
|
def current_resource
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ApiGuard
|
2
4
|
module JwtAuth
|
3
5
|
# Common module for token blacklisting functionality
|
@@ -16,10 +18,10 @@ module ApiGuard
|
|
16
18
|
end
|
17
19
|
|
18
20
|
# Returns whether the JWT token is blacklisted or not
|
19
|
-
def blacklisted?
|
20
|
-
return false unless token_blacklisting_enabled?(
|
21
|
+
def blacklisted?(resource)
|
22
|
+
return false unless token_blacklisting_enabled?(resource)
|
21
23
|
|
22
|
-
blacklisted_tokens_for(
|
24
|
+
blacklisted_tokens_for(resource).exists?(token: @token)
|
23
25
|
end
|
24
26
|
|
25
27
|
# Blacklist the current JWT token from future access
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'jwt'
|
2
4
|
|
3
5
|
module ApiGuard
|
@@ -9,11 +11,11 @@ module ApiGuard
|
|
9
11
|
end
|
10
12
|
|
11
13
|
def token_expire_at
|
12
|
-
@
|
14
|
+
@token_expire_at ||= (current_time + ApiGuard.token_validity).to_i
|
13
15
|
end
|
14
16
|
|
15
17
|
def token_issued_at
|
16
|
-
@
|
18
|
+
@token_issued_at ||= current_time.to_i
|
17
19
|
end
|
18
20
|
|
19
21
|
# Encode the payload with the secret key and return the JWT token
|
@@ -33,13 +35,16 @@ module ApiGuard
|
|
33
35
|
#
|
34
36
|
# This creates expired JWT token if the argument 'expired_token' is true which can be used for testing.
|
35
37
|
def jwt_and_refresh_token(resource, resource_name, expired_token = false)
|
36
|
-
|
38
|
+
payload = {
|
37
39
|
"#{resource_name}_id": resource.id,
|
38
40
|
exp: expired_token ? token_issued_at : token_expire_at,
|
39
41
|
iat: token_issued_at
|
40
|
-
|
42
|
+
}
|
43
|
+
|
44
|
+
# Add custom data in the JWT token payload
|
45
|
+
payload.merge!(resource.jwt_token_payload) if resource.respond_to?(:jwt_token_payload)
|
41
46
|
|
42
|
-
[
|
47
|
+
[encode(payload), new_refresh_token(resource)]
|
43
48
|
end
|
44
49
|
|
45
50
|
# Create tokens and set response headers
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ApiGuard
|
2
4
|
module Models
|
3
5
|
module Concerns
|
@@ -5,19 +7,19 @@ module ApiGuard
|
|
5
7
|
|
6
8
|
class_methods do
|
7
9
|
def api_guard_associations(refresh_token: nil, blacklisted_token: nil)
|
8
|
-
return if ApiGuard.api_guard_associations[
|
10
|
+
return if ApiGuard.api_guard_associations[name]
|
9
11
|
|
10
|
-
ApiGuard.api_guard_associations[
|
11
|
-
ApiGuard.api_guard_associations[
|
12
|
-
ApiGuard.api_guard_associations[
|
12
|
+
ApiGuard.api_guard_associations[name] = {}
|
13
|
+
ApiGuard.api_guard_associations[name][:refresh_token] = refresh_token
|
14
|
+
ApiGuard.api_guard_associations[name][:blacklisted_token] = blacklisted_token
|
13
15
|
end
|
14
16
|
|
15
17
|
def refresh_token_association
|
16
|
-
ApiGuard.api_guard_associations.dig(
|
18
|
+
ApiGuard.api_guard_associations.dig(name, :refresh_token)
|
17
19
|
end
|
18
20
|
|
19
21
|
def blacklisted_token_association
|
20
|
-
ApiGuard.api_guard_associations.dig(
|
22
|
+
ApiGuard.api_guard_associations.dig(name, :blacklisted_token)
|
21
23
|
end
|
22
24
|
end
|
23
25
|
end
|
data/lib/api_guard/modules.rb
CHANGED
@@ -1,24 +1,26 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'api_guard/resource_mapper'
|
4
|
+
require 'api_guard/jwt_auth/json_web_token'
|
5
|
+
require 'api_guard/jwt_auth/authentication'
|
6
|
+
require 'api_guard/jwt_auth/refresh_jwt_token'
|
7
|
+
require 'api_guard/jwt_auth/blacklist_token'
|
8
|
+
require 'api_guard/response_formatters/renderer'
|
9
|
+
require 'api_guard/models/concerns'
|
8
10
|
|
9
11
|
module ApiGuard
|
10
12
|
module Modules
|
11
|
-
ActiveSupport.on_load(:action_controller)
|
13
|
+
ActiveSupport.on_load(:action_controller) do
|
12
14
|
include ApiGuard::Resource
|
13
15
|
include ApiGuard::JwtAuth::JsonWebToken
|
14
16
|
include ApiGuard::JwtAuth::Authentication
|
15
17
|
include ApiGuard::JwtAuth::RefreshJwtToken
|
16
18
|
include ApiGuard::JwtAuth::BlacklistToken
|
17
19
|
include ApiGuard::ResponseFormatters::Renderer
|
18
|
-
|
20
|
+
end
|
19
21
|
|
20
|
-
ActiveSupport.on_load(:active_record)
|
22
|
+
ActiveSupport.on_load(:active_record) do
|
21
23
|
include ApiGuard::Models::Concerns
|
22
|
-
|
24
|
+
end
|
23
25
|
end
|
24
26
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ApiGuard
|
2
4
|
class ResourceMapper
|
3
5
|
attr_reader :resource_name, :resource_class, :resource_instance_name
|
@@ -19,7 +21,7 @@ module ApiGuard
|
|
19
21
|
end
|
20
22
|
|
21
23
|
def current_resource_mapping
|
22
|
-
request.env[
|
24
|
+
request.env['api_guard.mapping']
|
23
25
|
end
|
24
26
|
|
25
27
|
def resource_name
|
@@ -1,9 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ApiGuard
|
2
4
|
module ResponseFormatters
|
3
5
|
module Renderer
|
4
6
|
def render_success(data: nil, message: nil)
|
5
7
|
resp_data = { status: I18n.t('api_guard.response.success') }
|
6
8
|
resp_data[:message] = message if message
|
9
|
+
resp_data[:data] = message if data
|
7
10
|
|
8
11
|
render json: resp_data, status: 200
|
9
12
|
end
|
@@ -1,81 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Referenced from devise gem:
|
2
4
|
# https://github.com/plataformatec/devise/blob/master/lib/devise/rails/routes.rb
|
3
5
|
#
|
4
6
|
# Customizable API routes
|
5
|
-
module ActionDispatch
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
7
|
+
module ActionDispatch
|
8
|
+
module Routing
|
9
|
+
class Mapper
|
10
|
+
def api_guard_routes(options = {})
|
11
|
+
routes_for = options.delete(:for).to_s || 'users'
|
12
|
+
|
13
|
+
controllers = default_controllers(options[:only], options[:except])
|
14
|
+
controller_options = options.delete(:controller)
|
15
|
+
|
16
|
+
options[:as] = options[:as] || routes_for.singularize
|
17
|
+
options[:path] = options[:path] || routes_for
|
18
|
+
|
19
|
+
api_guard_scope(routes_for) do |mapped_resource|
|
20
|
+
scope options do
|
21
|
+
generate_routes(mapped_resource, controller_options, controllers)
|
22
|
+
end
|
19
23
|
end
|
20
24
|
end
|
21
|
-
end
|
22
25
|
|
23
|
-
|
24
|
-
|
26
|
+
def api_guard_scope(routes_for)
|
27
|
+
mapped_resource = ApiGuard.mapped_resource[routes_for.to_sym].presence ||
|
28
|
+
ApiGuard.map_resource(routes_for, routes_for.classify)
|
25
29
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
+
constraint = lambda do |request|
|
31
|
+
request.env['api_guard.mapping'] = mapped_resource
|
32
|
+
true
|
33
|
+
end
|
30
34
|
|
31
|
-
|
32
|
-
|
35
|
+
constraints(constraint) do
|
36
|
+
yield(mapped_resource)
|
37
|
+
end
|
33
38
|
end
|
34
|
-
end
|
35
39
|
|
36
|
-
|
40
|
+
private
|
37
41
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
controllers = %i[registration authentication tokens passwords]
|
42
|
-
except ? (controllers - except) : controllers
|
43
|
-
end
|
42
|
+
def default_controllers(only, except)
|
43
|
+
return only if only
|
44
44
|
|
45
|
-
|
46
|
-
|
45
|
+
controllers = %i[registration authentication tokens passwords]
|
46
|
+
except ? (controllers - except) : controllers
|
47
|
+
end
|
47
48
|
|
48
|
-
|
49
|
+
def generate_routes(mapped_resource, options, controllers)
|
50
|
+
options ||= {}
|
51
|
+
controllers -= %i[tokens] unless mapped_resource.resource_class.refresh_token_association
|
49
52
|
|
50
|
-
|
51
|
-
|
53
|
+
controllers.each do |controller|
|
54
|
+
send("#{controller}_routes", options[controller])
|
55
|
+
end
|
52
56
|
end
|
53
|
-
end
|
54
57
|
|
55
|
-
|
56
|
-
|
58
|
+
def authentication_routes(controller_name = nil)
|
59
|
+
controller_name ||= 'api_guard/authentication'
|
57
60
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
+
post 'sign_in' => "#{controller_name}#create"
|
62
|
+
delete 'sign_out' => "#{controller_name}#destroy"
|
63
|
+
end
|
61
64
|
|
62
|
-
|
63
|
-
|
65
|
+
def registration_routes(controller_name = nil)
|
66
|
+
controller_name ||= 'api_guard/registration'
|
64
67
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
+
post 'sign_up' => "#{controller_name}#create"
|
69
|
+
delete 'delete' => "#{controller_name}#destroy"
|
70
|
+
end
|
68
71
|
|
69
|
-
|
70
|
-
|
72
|
+
def passwords_routes(controller_name = nil)
|
73
|
+
controller_name ||= 'api_guard/passwords'
|
71
74
|
|
72
|
-
|
73
|
-
|
75
|
+
patch 'passwords' => "#{controller_name}#update"
|
76
|
+
end
|
74
77
|
|
75
|
-
|
76
|
-
|
78
|
+
def tokens_routes(controller_name = nil)
|
79
|
+
controller_name ||= 'api_guard/tokens'
|
77
80
|
|
78
|
-
|
81
|
+
post 'tokens' => "#{controller_name}#create"
|
82
|
+
end
|
79
83
|
end
|
80
84
|
end
|
81
85
|
end
|
data/lib/api_guard/version.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ApiGuard
|
2
4
|
class ControllersGenerator < Rails::Generators::Base
|
3
5
|
CONTROLLERS = %i[registration authentication tokens passwords].freeze
|
4
6
|
|
5
7
|
desc 'Generates API Guard controllers in app/controllers/'
|
6
|
-
source_root File.expand_path('
|
8
|
+
source_root File.expand_path('templates', __dir__)
|
7
9
|
|
8
10
|
argument :scope, required: true, desc: 'The scope to create controllers in, e.g. users, admins'
|
9
11
|
|
10
|
-
class_option :controllers, aliases:
|
12
|
+
class_option :controllers, aliases: '-c', type: :array,
|
11
13
|
desc: "Specify the controllers to generate (#{CONTROLLERS.join(', ')})"
|
12
14
|
|
13
15
|
def create_controllers
|
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ApiGuard
|
2
4
|
class InitializerGenerator < Rails::Generators::Base
|
3
|
-
source_root File.expand_path('
|
5
|
+
source_root File.expand_path('templates', __dir__)
|
4
6
|
|
5
7
|
desc 'Creates initializer for configuring API Guard'
|
6
8
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: api_guard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gokul Murali
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-04-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jwt
|
@@ -31,65 +31,65 @@ dependencies:
|
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 2.1.0
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
34
|
+
name: bcrypt
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: '
|
39
|
+
version: '3.1'
|
40
40
|
- - ">="
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version:
|
42
|
+
version: 3.1.11
|
43
43
|
type: :development
|
44
44
|
prerelease: false
|
45
45
|
version_requirements: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
47
|
- - "~>"
|
48
48
|
- !ruby/object:Gem::Version
|
49
|
-
version: '
|
49
|
+
version: '3.1'
|
50
50
|
- - ">="
|
51
51
|
- !ruby/object:Gem::Version
|
52
|
-
version:
|
52
|
+
version: 3.1.11
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
|
-
name:
|
54
|
+
name: factory_bot_rails
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|
56
56
|
requirements:
|
57
57
|
- - "~>"
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version: '
|
59
|
+
version: '4.8'
|
60
60
|
- - ">="
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version:
|
62
|
+
version: 4.8.2
|
63
63
|
type: :development
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
67
|
- - "~>"
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: '
|
69
|
+
version: '4.8'
|
70
70
|
- - ">="
|
71
71
|
- !ruby/object:Gem::Version
|
72
|
-
version:
|
72
|
+
version: 4.8.2
|
73
73
|
- !ruby/object:Gem::Dependency
|
74
|
-
name:
|
74
|
+
name: rails
|
75
75
|
requirement: !ruby/object:Gem::Requirement
|
76
76
|
requirements:
|
77
77
|
- - "~>"
|
78
78
|
- !ruby/object:Gem::Version
|
79
|
-
version: '
|
79
|
+
version: '5.1'
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 5.1.5
|
83
83
|
type: :development
|
84
84
|
prerelease: false
|
85
85
|
version_requirements: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
89
|
+
version: '5.1'
|
90
90
|
- - ">="
|
91
91
|
- !ruby/object:Gem::Version
|
92
|
-
version:
|
92
|
+
version: 5.1.5
|
93
93
|
- !ruby/object:Gem::Dependency
|
94
94
|
name: rspec-rails
|
95
95
|
requirement: !ruby/object:Gem::Requirement
|
@@ -111,25 +111,19 @@ dependencies:
|
|
111
111
|
- !ruby/object:Gem::Version
|
112
112
|
version: 3.7.2
|
113
113
|
- !ruby/object:Gem::Dependency
|
114
|
-
name:
|
114
|
+
name: rubocop
|
115
115
|
requirement: !ruby/object:Gem::Requirement
|
116
116
|
requirements:
|
117
117
|
- - "~>"
|
118
118
|
- !ruby/object:Gem::Version
|
119
|
-
version:
|
120
|
-
- - ">="
|
121
|
-
- !ruby/object:Gem::Version
|
122
|
-
version: 4.8.2
|
119
|
+
version: 0.75.1
|
123
120
|
type: :development
|
124
121
|
prerelease: false
|
125
122
|
version_requirements: !ruby/object:Gem::Requirement
|
126
123
|
requirements:
|
127
124
|
- - "~>"
|
128
125
|
- !ruby/object:Gem::Version
|
129
|
-
version:
|
130
|
-
- - ">="
|
131
|
-
- !ruby/object:Gem::Version
|
132
|
-
version: 4.8.2
|
126
|
+
version: 0.75.1
|
133
127
|
- !ruby/object:Gem::Dependency
|
134
128
|
name: simplecov
|
135
129
|
requirement: !ruby/object:Gem::Requirement
|
@@ -150,6 +144,26 @@ dependencies:
|
|
150
144
|
- - ">="
|
151
145
|
- !ruby/object:Gem::Version
|
152
146
|
version: 0.16.1
|
147
|
+
- !ruby/object:Gem::Dependency
|
148
|
+
name: sqlite3
|
149
|
+
requirement: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - "~>"
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '1.3'
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: 1.3.13
|
157
|
+
type: :development
|
158
|
+
prerelease: false
|
159
|
+
version_requirements: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - "~>"
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: '1.3'
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: 1.3.13
|
153
167
|
description: JWT authentication solution for Rails APIs
|
154
168
|
email:
|
155
169
|
- m.gokul595@gmail.com
|
@@ -165,8 +179,6 @@ files:
|
|
165
179
|
- app/controllers/api_guard/passwords_controller.rb
|
166
180
|
- app/controllers/api_guard/registration_controller.rb
|
167
181
|
- app/controllers/api_guard/tokens_controller.rb
|
168
|
-
- app/models/api_guard/application_record.rb
|
169
|
-
- app/views/layouts/api_guard/application.html.erb
|
170
182
|
- config/locales/en.yml
|
171
183
|
- config/routes.rb
|
172
184
|
- lib/api_guard.rb
|
@@ -1,14 +0,0 @@
|
|
1
|
-
<!DOCTYPE html>
|
2
|
-
<html>
|
3
|
-
<head>
|
4
|
-
<title>API Guard</title>
|
5
|
-
<%= stylesheet_link_tag "api_guard/application", media: "all" %>
|
6
|
-
<%= javascript_include_tag "api_guard/application" %>
|
7
|
-
<%= csrf_meta_tags %>
|
8
|
-
</head>
|
9
|
-
<body>
|
10
|
-
|
11
|
-
<%= yield %>
|
12
|
-
|
13
|
-
</body>
|
14
|
-
</html>
|