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