api_guard 0.2.1 → 0.5.1
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 +5 -5
 - data/README.md +95 -15
 - data/Rakefile +2 -5
 - data/app/controllers/api_guard/application_controller.rb +2 -0
 - data/app/controllers/api_guard/authentication_controller.rb +6 -4
 - data/app/controllers/api_guard/passwords_controller.rb +4 -2
 - data/app/controllers/api_guard/registration_controller.rb +4 -2
 - data/app/controllers/api_guard/tokens_controller.rb +5 -3
 - data/config/locales/en.yml +22 -0
 - data/config/routes.rb +2 -0
 - data/lib/api_guard.rb +11 -6
 - data/lib/api_guard/app_secret_key.rb +22 -0
 - data/lib/api_guard/engine.rb +4 -5
 - data/lib/api_guard/jwt_auth/authentication.rb +34 -12
 - data/lib/api_guard/jwt_auth/blacklist_token.rb +7 -3
 - data/lib/api_guard/jwt_auth/json_web_token.rb +11 -5
 - data/lib/api_guard/jwt_auth/refresh_jwt_token.rb +4 -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 +5 -2
 - 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 +9 -7
 - data/lib/generators/api_guard/controllers/templates/authentication_controller.rb +4 -4
 - data/lib/generators/api_guard/controllers/templates/passwords_controller.rb +3 -3
 - data/lib/generators/api_guard/controllers/templates/registration_controller.rb +3 -3
 - data/lib/generators/api_guard/controllers/templates/tokens_controller.rb +7 -4
 - data/lib/generators/api_guard/initializer/initializer_generator.rb +3 -1
 - data/lib/generators/api_guard/initializer/templates/initializer.rb +6 -4
 - metadata +54 -69
 - data/app/models/api_guard/application_record.rb +0 -5
 - data/app/views/layouts/api_guard/application.html.erb +0 -14
 
| 
         @@ -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,22 +16,26 @@ 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
         
     | 
| 
       20 
26 
     | 
    
         | 
| 
       21 
27 
     | 
    
         
             
                    @token = request.headers['Authorization']&.split('Bearer ')&.last
         
     | 
| 
       22 
     | 
    
         
            -
                    return render_error(401, message: ' 
     | 
| 
      
 28 
     | 
    
         
            +
                    return render_error(401, message: I18n.t('api_guard.access_token.missing')) unless @token
         
     | 
| 
       23 
29 
     | 
    
         | 
| 
       24 
30 
     | 
    
         
             
                    authenticate_token
         
     | 
| 
       25 
31 
     | 
    
         | 
| 
       26 
32 
     | 
    
         
             
                    # Render error response only if no resource found and no previous render happened
         
     | 
| 
       27 
     | 
    
         
            -
                    render_error(401, message: ' 
     | 
| 
      
 33 
     | 
    
         
            +
                    render_error(401, message: I18n.t('api_guard.access_token.invalid')) if !current_resource && !performed?
         
     | 
| 
       28 
34 
     | 
    
         
             
                  rescue JWT::DecodeError => e
         
     | 
| 
       29 
35 
     | 
    
         
             
                    if e.message == 'Signature has expired'
         
     | 
| 
       30 
     | 
    
         
            -
                      render_error(401, message: ' 
     | 
| 
      
 36 
     | 
    
         
            +
                      render_error(401, message: I18n.t('api_guard.access_token.expired'))
         
     | 
| 
       31 
37 
     | 
    
         
             
                    else
         
     | 
| 
       32 
     | 
    
         
            -
                      render_error(401, message: ' 
     | 
| 
      
 38 
     | 
    
         
            +
                      render_error(401, message: I18n.t('api_guard.access_token.invalid'))
         
     | 
| 
       33 
39 
     | 
    
         
             
                    end
         
     | 
| 
       34 
40 
     | 
    
         
             
                  end
         
     | 
| 
       35 
41 
     | 
    
         | 
| 
         @@ -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 
     | 
    
         
            +
                    define_singleton_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,21 +70,27 @@ 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
         
     | 
| 
       71 
92 
     | 
    
         
             
                    return unless respond_to?("current_#{@resource_name}")
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
       72 
94 
     | 
    
         
             
                    public_send("current_#{@resource_name}")
         
     | 
| 
       73 
95 
     | 
    
         
             
                  end
         
     | 
| 
       74 
96 
     | 
    
         
             
                end
         
     | 
| 
         @@ -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,14 +18,16 @@ 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 
     | 
    
         
            -
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def blacklisted?(resource)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    return false unless token_blacklisting_enabled?(resource)
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                    blacklisted_tokens_for(resource).exists?(token: @token)
         
     | 
| 
       22 
25 
     | 
    
         
             
                  end
         
     | 
| 
       23 
26 
     | 
    
         | 
| 
       24 
27 
     | 
    
         
             
                  # Blacklist the current JWT token from future access
         
     | 
| 
       25 
28 
     | 
    
         
             
                  def blacklist_token
         
     | 
| 
       26 
29 
     | 
    
         
             
                    return unless token_blacklisting_enabled?(current_resource)
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
       27 
31 
     | 
    
         
             
                    blacklisted_tokens_for(current_resource).create(token: @token, expire_at: Time.at(@decoded_token[:exp]).utc)
         
     | 
| 
       28 
32 
     | 
    
         
             
                  end
         
     | 
| 
       29 
33 
     | 
    
         
             
                end
         
     | 
| 
         @@ -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 
     | 
    
         
            +
                    }
         
     | 
| 
       41 
43 
     | 
    
         | 
| 
       42 
     | 
    
         
            -
                     
     | 
| 
      
 44 
     | 
    
         
            +
                    # Add custom data in the JWT token payload
         
     | 
| 
      
 45 
     | 
    
         
            +
                    payload.merge!(resource.jwt_token_payload) if resource.respond_to?(:jwt_token_payload)
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    [encode(payload), new_refresh_token(resource)]
         
     | 
| 
       43 
48 
     | 
    
         
             
                  end
         
     | 
| 
       44 
49 
     | 
    
         | 
| 
       45 
50 
     | 
    
         
             
                  # Create tokens and set response headers
         
     | 
| 
         @@ -59,6 +64,7 @@ module ApiGuard 
     | 
|
| 
       59 
64 
     | 
    
         
             
                  # to restrict access to old access(JWT) tokens
         
     | 
| 
       60 
65 
     | 
    
         
             
                  def invalidate_old_jwt_tokens(resource)
         
     | 
| 
       61 
66 
     | 
    
         
             
                    return unless ApiGuard.invalidate_old_tokens_on_password_change
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
       62 
68 
     | 
    
         
             
                    resource.token_issued_at = Time.at(token_issued_at).utc
         
     | 
| 
       63 
69 
     | 
    
         
             
                  end
         
     | 
| 
       64 
70 
     | 
    
         
             
                end
         
     | 
| 
         @@ -1,3 +1,5 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module ApiGuard
         
     | 
| 
       2 
4 
     | 
    
         
             
              module JwtAuth
         
     | 
| 
       3 
5 
     | 
    
         
             
                # Common module for refresh token functionality
         
     | 
| 
         @@ -30,11 +32,13 @@ module ApiGuard 
     | 
|
| 
       30 
32 
     | 
    
         
             
                  # Create a new refresh_token for the current resource
         
     | 
| 
       31 
33 
     | 
    
         
             
                  def new_refresh_token(resource)
         
     | 
| 
       32 
34 
     | 
    
         
             
                    return unless refresh_token_enabled?(resource)
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
       33 
36 
     | 
    
         
             
                    refresh_tokens_for(resource).create(token: uniq_refresh_token(resource)).token
         
     | 
| 
       34 
37 
     | 
    
         
             
                  end
         
     | 
| 
       35 
38 
     | 
    
         | 
| 
       36 
39 
     | 
    
         
             
                  def destroy_all_refresh_tokens(resource)
         
     | 
| 
       37 
40 
     | 
    
         
             
                    return unless refresh_token_enabled?(resource)
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
       38 
42 
     | 
    
         
             
                    refresh_tokens_for(resource).destroy_all
         
     | 
| 
       39 
43 
     | 
    
         
             
                  end
         
     | 
| 
       40 
44 
     | 
    
         
             
                end
         
     | 
| 
         @@ -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,15 +1,18 @@ 
     | 
|
| 
      
 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 
     | 
    
         
            -
                    resp_data = { status: 'success' }
         
     | 
| 
      
 7 
     | 
    
         
            +
                    resp_data = { status: I18n.t('api_guard.response.success') }
         
     | 
| 
       6 
8 
     | 
    
         
             
                    resp_data[:message] = message if message
         
     | 
| 
      
 9 
     | 
    
         
            +
                    resp_data[:data] = data if data
         
     | 
| 
       7 
10 
     | 
    
         | 
| 
       8 
11 
     | 
    
         
             
                    render json: resp_data, status: 200
         
     | 
| 
       9 
12 
     | 
    
         
             
                  end
         
     | 
| 
       10 
13 
     | 
    
         | 
| 
       11 
14 
     | 
    
         
             
                  def render_error(status, options = {})
         
     | 
| 
       12 
     | 
    
         
            -
                    data = { status: 'error' }
         
     | 
| 
      
 15 
     | 
    
         
            +
                    data = { status: I18n.t('api_guard.response.error') }
         
     | 
| 
       13 
16 
     | 
    
         
             
                    data[:error] = options[:object] ? options[:object].errors.full_messages[0] : options[:message]
         
     | 
| 
       14 
17 
     | 
    
         | 
| 
       15 
18 
     | 
    
         
             
                    render json: data, status: status
         
     | 
| 
         @@ -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,22 +1,24 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module ApiGuard
         
     | 
| 
       2 
4 
     | 
    
         
             
              class ControllersGenerator < Rails::Generators::Base
         
     | 
| 
       3 
     | 
    
         
            -
                CONTROLLERS = %i[registration authentication tokens passwords]
         
     | 
| 
      
 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 
     | 
    
         
            -
                argument :scope, required: true,
         
     | 
| 
       9 
     | 
    
         
            -
                         desc: "The scope to create controllers in, e.g. users, admins"
         
     | 
| 
      
 10 
     | 
    
         
            +
                argument :scope, required: true, desc: 'The scope to create controllers in, e.g. users, admins'
         
     | 
| 
       10 
11 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
                class_option :controllers, aliases:  
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
      
 12 
     | 
    
         
            +
                class_option :controllers, aliases: '-c', type: :array,
         
     | 
| 
      
 13 
     | 
    
         
            +
                                           desc: "Specify the controllers to generate (#{CONTROLLERS.join(', ')})"
         
     | 
| 
       13 
14 
     | 
    
         | 
| 
       14 
15 
     | 
    
         
             
                def create_controllers
         
     | 
| 
       15 
16 
     | 
    
         
             
                  @controller_scope = scope.camelize
         
     | 
| 
       16 
17 
     | 
    
         
             
                  controllers = options[:controllers] || CONTROLLERS
         
     | 
| 
       17 
18 
     | 
    
         | 
| 
       18 
19 
     | 
    
         
             
                  controllers.each do |controller_name|
         
     | 
| 
       19 
     | 
    
         
            -
                    template "#{controller_name}_controller.rb", 
     | 
| 
      
 20 
     | 
    
         
            +
                    template "#{controller_name}_controller.rb",
         
     | 
| 
      
 21 
     | 
    
         
            +
                             "app/controllers/#{scope}/#{controller_name}_controller.rb"
         
     | 
| 
       20 
22 
     | 
    
         
             
                  end
         
     | 
| 
       21 
23 
     | 
    
         
             
                end
         
     | 
| 
       22 
24 
     | 
    
         
             
              end
         
     |