rapid_api 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +120 -0
- data/lib/rapid_api/action_controller/errors.rb +75 -0
- data/lib/rapid_api/action_controller/filterable_params.rb +29 -0
- data/lib/rapid_api/action_controller/macros.rb +28 -0
- data/lib/rapid_api/action_controller/permitted_params.rb +38 -0
- data/lib/rapid_api/action_controller/rest_actions.rb +158 -0
- data/lib/rapid_api/action_controller/scope.rb +44 -0
- data/lib/rapid_api/action_controller.rb +11 -0
- data/lib/rapid_api/auth/concerns/authenticated_controller.rb +54 -0
- data/lib/rapid_api/auth/concerns/jwt_helpers.rb +33 -0
- data/lib/rapid_api/auth/concerns/sessions_controller.rb +49 -0
- data/lib/rapid_api/auth/concerns.rb +11 -0
- data/lib/rapid_api/auth/support/jwt.rb +28 -0
- data/lib/rapid_api/auth/support.rb +9 -0
- data/lib/rapid_api/auth.rb +8 -0
- data/lib/rapid_api/configuration.rb +11 -0
- data/lib/rapid_api/model_adapters/abstract.rb +33 -0
- data/lib/rapid_api/model_adapters/active_record_adapter.rb +49 -0
- data/lib/rapid_api/model_adapters/query_result.rb +22 -0
- data/lib/rapid_api/model_adapters.rb +9 -0
- data/lib/rapid_api/serializer_adapters/abstract.rb +22 -0
- data/lib/rapid_api/serializer_adapters/ams_adapter.rb +20 -0
- data/lib/rapid_api/serializer_adapters.rb +8 -0
- data/lib/rapid_api/version.rb +3 -0
- data/lib/rapid_api.rb +36 -0
- data/test/integration/ams_ar_test.rb +44 -0
- data/test/support/code_climate.rb +2 -0
- data/test/support/models.rb +24 -0
- data/test/support/schema.rb +14 -0
- data/test/support/serializers.rb +7 -0
- data/test/support/test_model_adapter.rb +23 -0
- data/test/support/test_module/test_model_adapter.rb +25 -0
- data/test/support/test_serializer_adapter.rb +10 -0
- data/test/test_helper.rb +28 -0
- data/test/unit/rapid_api/action_controller/errors_test.rb +53 -0
- data/test/unit/rapid_api/action_controller/filterable_params_test.rb +32 -0
- data/test/unit/rapid_api/action_controller/permitted_params_test.rb +30 -0
- data/test/unit/rapid_api/action_controller/rest_actions_test.rb +160 -0
- data/test/unit/rapid_api/action_controller/scope_test.rb +29 -0
- data/test/unit/rapid_api/auth/concerns/authenticated_controller_test.rb +56 -0
- data/test/unit/rapid_api/auth/concerns/sessions_controller_test.rb +55 -0
- data/test/unit/rapid_api/auth/support/jwt_test.rb +28 -0
- data/test/unit/rapid_api/model_adapters/abstract_test.rb +45 -0
- data/test/unit/rapid_api/model_adapters/active_record_adapter_test.rb +121 -0
- data/test/unit/rapid_api/serializer_adapters/abstract_test.rb +27 -0
- data/test/unit/rapid_api/serializer_adapters/ams_adapter_test.rb +31 -0
- data/test/unit/rapid_api/test_configuration.rb +17 -0
- metadata +197 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 85d057310dcd01fbb26108d9496df7aa2b2bf668
|
4
|
+
data.tar.gz: 52a2e15349493659e8c90f9e36fd9af0732682c1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5d6d87ab91566e286945631ff759c7f7ca1cb22877e4f6a1c983a525c1d9bd2e6ceaff5af124c8d27e07bb2f98f53494b211d7be230c610180962a7ba2afe9dd
|
7
|
+
data.tar.gz: 154d0b89aa34045576b14ee5b3bfe1dda5ce7d4ea497dcd9cd0bdd320895501e881872077b8d7d448860a98fb18fac4844d7adc864f4ada421356ee07b444dba
|
data/README.md
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# rAPId [![Code Climate](https://codeclimate.com/github/briandavidwetzel/rapid_api/badges/gpa.svg)](https://codeclimate.com/github/briandavidwetzel/rapid_api) [![Build Status](https://travis-ci.org/briandavidwetzel/rapid_api.svg)](https://travis-ci.org/briandavidwetzel/rapid_api) [![Test Coverage](https://codeclimate.com/github/briandavidwetzel/rapid_api/badges/coverage.svg)](https://codeclimate.com/github/briandavidwetzel/rapid_api/coverage)
|
2
|
+
|
3
|
+
*NOT PRODUCTION READY*
|
4
|
+
|
5
|
+
__A framework for rapid development of Rails APIs__-- *RapidApi* aims to reduce code maintenance and testing costs, while increasing speed of development. Because the ceremony of rendering REST actions is performed for you, you can focus more time on what makes your app unique.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Include in your Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'rapid_api', git: 'git://github.com/briandavidwetzel/rapid_api.git'
|
12
|
+
```
|
13
|
+
|
14
|
+
## Documentation
|
15
|
+
### Rapid Actions
|
16
|
+
Including `rapid_actions` in your controller will give you conventional RESTful actions: `#index, #show, #create, #update, and #destroy`.
|
17
|
+
```ruby
|
18
|
+
class BricksController < ApplicationController
|
19
|
+
rapid_actions
|
20
|
+
|
21
|
+
permit_params :color, :weight, :material
|
22
|
+
filterable_params :color
|
23
|
+
end
|
24
|
+
```
|
25
|
+
#### Permitted Params
|
26
|
+
The `permit_params` macro secures member requests using strong params. Just pass the attributes that you want to be used in creation or update of your model.
|
27
|
+
|
28
|
+
#### Filterable Params
|
29
|
+
The `filterable_params` macro is similar `permit_params`, except that it applies to the collection instead of the member. RapidApi supports basic filtering on the index action. A hash of keys and values will be passed to the model adapter secured via strong params.
|
30
|
+
|
31
|
+
#### Error Handling
|
32
|
+
Error handling is provided automatically for the generated actions. There are currently three potential errors that can be raised and are rendered:
|
33
|
+
* __NotFoundError__ (`:not_found`) - obviously, when a member is not found to be shown, updated, or destroyed.
|
34
|
+
* __NotProcessableError__ (`:unprocessable_entity`) - this will be rendered when a member has errors added during the create or update process.
|
35
|
+
* *NOTE: For ActiveRecord, this pattern emphasizes using errors.add over raising exceptions. Also, for AMS, this convention works well with Ember Data's ActiveModelAdapter errors object out of the box.*
|
36
|
+
* __StandardError__ (`:internal_server_error`) - Everything else gets caught as an internal server error. The error message is serialized in the json response body.
|
37
|
+
|
38
|
+
#### Configuration
|
39
|
+
By default, RapidApi will attempt to use ActiveRecord, ActiveModelSerializer and the Rails naming conventions to discover your model and serializer per controller, but you are able to customize the definition. You can pass optional arguments to the `rapid_actions` macro as follows.
|
40
|
+
```ruby
|
41
|
+
rapid_actions model: YourModel, serializer: YourSerializer, model_adapter: CustomModelAdapter, serializer_adapter: CustomSerializerAdapter
|
42
|
+
```
|
43
|
+
These settings could also be set globally using the configuration object. For example,
|
44
|
+
```ruby
|
45
|
+
RapidApi.config.model_adapter = CustomModelAdapter
|
46
|
+
RapidApi.config.serializer_adapter = CustomSerializerAdapter
|
47
|
+
```
|
48
|
+
You can also override the response codes, if necessary.
|
49
|
+
```ruby
|
50
|
+
RapidApi.config.response_codes[:created] = :ok
|
51
|
+
```
|
52
|
+
|
53
|
+
### Scoping
|
54
|
+
If you want to scope your controller by a given attribute or attributes, you can use the `scope_by` macro
|
55
|
+
```ruby
|
56
|
+
class BricksController < ApplicationController
|
57
|
+
rapid_actions
|
58
|
+
|
59
|
+
permit_params :color, :weight, :material
|
60
|
+
filterable_params :color
|
61
|
+
|
62
|
+
scope_by :user_id do |controller|
|
63
|
+
controller.authenticated.id
|
64
|
+
end
|
65
|
+
end
|
66
|
+
```
|
67
|
+
The scope by block should return the corresponding value for the attribute(s), passed to `scope_by`. In the event that there are multiple scoping attributes, you should return an array of values in the block. The scope will apply to all generated actions.
|
68
|
+
|
69
|
+
### Authentication
|
70
|
+
*NOTE: The authentication functionality is likely to be moved to another gem by the release of v1*
|
71
|
+
|
72
|
+
Some basic API appropriate authentication is available in the form [json web tokens](http://jwt.io). However, you can use RapidApi without taking advantage of this functionality.
|
73
|
+
|
74
|
+
#### The Sessions Controller
|
75
|
+
```ruby
|
76
|
+
class SessionsController < ApplicationController
|
77
|
+
rapid_sessions_controller
|
78
|
+
|
79
|
+
authenticates_with :username, :password do |params|
|
80
|
+
User.where(username: params[:username], password: params[:password]).first
|
81
|
+
end
|
82
|
+
|
83
|
+
responds_with do |authenticated|
|
84
|
+
{
|
85
|
+
token: jwt_encode({ secret: 'foo' }),
|
86
|
+
user: {
|
87
|
+
id: authenticated.id,
|
88
|
+
username: authenticated.username
|
89
|
+
}
|
90
|
+
}
|
91
|
+
end
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
`rapid_sessions_controller` will generate an authenticate method, that can be called to initiate a session with your api. The `authenticates_with` macro accepts the parameters that will be used to authenticate, and a block that will do the actual authentication. The block should return the authenticated object. *In the example given, the authenticated User.* Returning nil, well render an `:unauthorized` response. Otherwise, `responds_with` is passed the authenticated object to build the auth token payload for the authentication response.
|
96
|
+
|
97
|
+
#### Authenticated Controllers
|
98
|
+
```ruby
|
99
|
+
class ApplicationController
|
100
|
+
rapid_base_controller
|
101
|
+
|
102
|
+
authenticate do |controller|
|
103
|
+
token = controller.decode_jwt_token!(controller.request.headers.env['Authorization'])
|
104
|
+
user_id = token[0].try :[], 'user_id'
|
105
|
+
if user_id.present?
|
106
|
+
User.find user_id
|
107
|
+
else
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
```
|
113
|
+
`rapid_base_controller` can be added to the base class for your api controllers to provide a before_filter that checks authentication. Note if your SessionController is derived from the same base class, then you should add `skip_before_filter :authenticate!` to your SessionsController. The `authenticate` macro should be passed a block that returns the authenticated object. *In the example, the 'Authorization' token is parsed to return the current user*. If your authenticate block returns nil, then `:unauthorized` will be rendered.
|
114
|
+
|
115
|
+
Also note that the `decode_jwt_token!` can raise errors that will result in the rendering of an `:unauthorized` response.
|
116
|
+
|
117
|
+
### Extending RapidApi
|
118
|
+
If you don't want to use ActiveRecord or ActiveModelSerializer, that is OK. You can create your own adapters.
|
119
|
+
|
120
|
+
There are two abstract classes that can be used as base classes for your own implementation: `RapidApi::ModelAdapters::Abstract` and `RapidApi::SerializerAdapters::Abstract`. See the implemented adapters and tests for examples on how to implement an adapter. Post an issue if you get stuck.
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module RapidApi
|
2
|
+
module ActionController
|
3
|
+
|
4
|
+
module Errors
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
NotAuthenticatedError = Class.new StandardError
|
8
|
+
NotFoundError = Class.new StandardError
|
9
|
+
|
10
|
+
class NotProcessableError < StandardError
|
11
|
+
attr_accessor :errors
|
12
|
+
|
13
|
+
def initialize(errors)
|
14
|
+
@errors = errors
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
included do
|
20
|
+
rescue_from StandardError, with: :_server_error
|
21
|
+
rescue_from NotAuthenticatedError, with: :_not_authenticated
|
22
|
+
rescue_from NotFoundError, with: :_not_found
|
23
|
+
rescue_from NotProcessableError, with: :_not_processable
|
24
|
+
end
|
25
|
+
|
26
|
+
def not_authenticated!
|
27
|
+
raise NotAuthenticatedError
|
28
|
+
end
|
29
|
+
|
30
|
+
def not_found!
|
31
|
+
raise NotFoundError
|
32
|
+
end
|
33
|
+
|
34
|
+
def not_processable!(errors)
|
35
|
+
raise NotProcessableError.new(errors)
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
def render_error_message(message, status, e)
|
41
|
+
render json: { errors: [message] }, status: response_code_for(status)
|
42
|
+
end
|
43
|
+
|
44
|
+
def response_code_for(status)
|
45
|
+
RapidApi.config.response_codes[status]
|
46
|
+
end
|
47
|
+
|
48
|
+
def log_error(e)
|
49
|
+
puts e.message
|
50
|
+
e.backtrace.map { |m| puts m }
|
51
|
+
end
|
52
|
+
|
53
|
+
def _not_authenticated(e)
|
54
|
+
render_error_message 'Not Authenticated', :unauthorized, e
|
55
|
+
end
|
56
|
+
|
57
|
+
def _not_found(e)
|
58
|
+
render_error_message 'Not Found', :not_found, e
|
59
|
+
end
|
60
|
+
|
61
|
+
def _not_processable(e)
|
62
|
+
render json: { errors: e.errors }, status: response_code_for(:unprocessable_entity)
|
63
|
+
end
|
64
|
+
|
65
|
+
def _server_error(e)
|
66
|
+
render_error_message "Server Error: #{e.message}", :internal_server_error, e
|
67
|
+
end
|
68
|
+
|
69
|
+
module ClassMethods
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module RapidApi
|
2
|
+
module ActionController
|
3
|
+
|
4
|
+
module FilterableParams
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
def filterable_params
|
12
|
+
params.permit(self.class.permitted_filterable_params)
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
attr_accessor :permitted_filterable_params
|
17
|
+
|
18
|
+
def permitted_filterable_params
|
19
|
+
@filterable_params ||= []
|
20
|
+
end
|
21
|
+
|
22
|
+
def filterable_params(*params)
|
23
|
+
[*params].each { |p| self.permitted_filterable_params << p }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module RapidApi
|
2
|
+
module ActionController
|
3
|
+
module Macros
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def rapid_actions(options={})
|
8
|
+
include RapidApi::ActionController::RestActions
|
9
|
+
self.model = options[:model] if options[:model]
|
10
|
+
self.serializer = options[:serializer] if options[:serializer]
|
11
|
+
self.model_adapter = options[:model_adapter] if options[:model_adapter]
|
12
|
+
self.serializer_adapter = options[:serializer_adapter] if options[:serializer_adapter]
|
13
|
+
end
|
14
|
+
|
15
|
+
def rapid_sessions_controller
|
16
|
+
include RapidApi::ActionController::Errors
|
17
|
+
include RapidApi::Auth::Concerns::SessionsController
|
18
|
+
end
|
19
|
+
|
20
|
+
def rapid_base_controller
|
21
|
+
include RapidApi::ActionController::Errors
|
22
|
+
include RapidApi::Auth::Concerns::AuthenticatedController
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module RapidApi
|
2
|
+
module ActionController
|
3
|
+
|
4
|
+
module PermittedParams
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
def permitted_params
|
12
|
+
self.class.permitted_params
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def _permitted_params_for(key)
|
18
|
+
params.require(key).permit(*permitted_params)
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
|
24
|
+
def permit_params(*params)
|
25
|
+
@permitted_params ||= []
|
26
|
+
@permitted_params += params
|
27
|
+
@permitted_params.uniq!
|
28
|
+
end
|
29
|
+
|
30
|
+
def permitted_params
|
31
|
+
@permitted_params
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
module RapidApi
|
2
|
+
module ActionController
|
3
|
+
|
4
|
+
module RestActions
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
include PermittedParams
|
7
|
+
include FilterableParams
|
8
|
+
include Errors
|
9
|
+
include Scope
|
10
|
+
|
11
|
+
included do
|
12
|
+
self.serializer_adapter = RapidApi.config.serializer_adapter
|
13
|
+
self.model_adapter = RapidApi.config.model_adapter
|
14
|
+
end
|
15
|
+
|
16
|
+
def index
|
17
|
+
query_result = _adapted_model.find_all filterable_params, scope
|
18
|
+
render json: _adapted_serializer.serialize_collection(query_result.data), status: response_code_for(:ok)
|
19
|
+
end
|
20
|
+
|
21
|
+
def show
|
22
|
+
query_result = _adapted_model.find params[:id], scope
|
23
|
+
if query_result.found?
|
24
|
+
render json: _adapted_serializer.serialize(query_result.data) , status: response_code_for(:ok)
|
25
|
+
else
|
26
|
+
not_found!
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def create
|
31
|
+
query_result = _adapted_model.create _member_params, scope
|
32
|
+
if query_result.has_errors?
|
33
|
+
not_processable! query_result.errors
|
34
|
+
else
|
35
|
+
render json: _adapted_serializer.serialize(query_result.data), status: response_code_for(:created)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def update
|
40
|
+
query_result = _adapted_model.update params[:id], _member_params, scope
|
41
|
+
if query_result.found?
|
42
|
+
if query_result.has_errors?
|
43
|
+
not_processable! query_result.errors
|
44
|
+
else
|
45
|
+
render json: _adapted_serializer.serialize(query_result.data), status: response_code_for(:ok)
|
46
|
+
end
|
47
|
+
else
|
48
|
+
not_found!
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def destroy
|
53
|
+
query_result = _adapted_model.destroy params[:id], scope
|
54
|
+
if query_result.has_errors?
|
55
|
+
not_processable! query_result.errors
|
56
|
+
else
|
57
|
+
head :no_content
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def _adapted_model
|
64
|
+
self.class.adapted_model
|
65
|
+
end
|
66
|
+
|
67
|
+
def _adapted_serializer
|
68
|
+
self.class.adapted_serializer
|
69
|
+
end
|
70
|
+
|
71
|
+
def _member_params
|
72
|
+
_permitted_params_for(_params_key)
|
73
|
+
end
|
74
|
+
|
75
|
+
def _model
|
76
|
+
self.class.model
|
77
|
+
end
|
78
|
+
|
79
|
+
def _params_key
|
80
|
+
self.class.params_key
|
81
|
+
end
|
82
|
+
|
83
|
+
module ClassMethods
|
84
|
+
|
85
|
+
attr_accessor :model_class_name,
|
86
|
+
:model,
|
87
|
+
:model_adapter,
|
88
|
+
:adapted_model,
|
89
|
+
:params_key,
|
90
|
+
:serializer,
|
91
|
+
:serializer_adapter,
|
92
|
+
:adapted_serializer
|
93
|
+
|
94
|
+
def model_class_name
|
95
|
+
@model_class_name ||= controller_name.classify.singularize
|
96
|
+
end
|
97
|
+
|
98
|
+
def model
|
99
|
+
@model ||= begin
|
100
|
+
model_class_name.constantize
|
101
|
+
rescue NameError
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def model=(model)
|
106
|
+
@model = model
|
107
|
+
_reset_params_key
|
108
|
+
_initialize_model_adapter
|
109
|
+
@model
|
110
|
+
end
|
111
|
+
|
112
|
+
def serializer
|
113
|
+
#TODO: BDW - This convention is too dependent on AMS. This should be
|
114
|
+
# decoupled in some way.
|
115
|
+
@serializer ||= begin
|
116
|
+
"#{model_class_name}Serializer".constantize
|
117
|
+
rescue NameError
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def serializer=(serializer)
|
122
|
+
@serializer = serializer
|
123
|
+
_initalize_serializer_adaper
|
124
|
+
@serializer
|
125
|
+
end
|
126
|
+
|
127
|
+
def params_key
|
128
|
+
@params_key ||= model.to_s.underscore
|
129
|
+
end
|
130
|
+
|
131
|
+
def model_adapter=(adapter)
|
132
|
+
@model_adapter = adapter
|
133
|
+
_initialize_model_adapter
|
134
|
+
end
|
135
|
+
|
136
|
+
def serializer_adapter=(adapter)
|
137
|
+
@serializer_adapter = adapter
|
138
|
+
_initalize_serializer_adaper
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def _initalize_serializer_adaper
|
144
|
+
self.adapted_serializer = @serializer_adapter.new(serializer, params_key)
|
145
|
+
end
|
146
|
+
|
147
|
+
def _initialize_model_adapter
|
148
|
+
self.adapted_model = @model_adapter.new(model)
|
149
|
+
end
|
150
|
+
|
151
|
+
def _reset_params_key
|
152
|
+
@params_key = model.to_s.underscore
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module RapidApi
|
2
|
+
module ActionController
|
3
|
+
|
4
|
+
module Scope
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
attr_accessor :scope
|
8
|
+
|
9
|
+
included do
|
10
|
+
before_filter :define_scope
|
11
|
+
end
|
12
|
+
|
13
|
+
def scope
|
14
|
+
@scope ||= {}
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def define_scope
|
20
|
+
return unless self.class.scope_by_proc.present?
|
21
|
+
scope_by = self.class.scope_by_proc.call(self)
|
22
|
+
scope_by_array = [*scope_by]
|
23
|
+
self.class.scope_params.each_with_index do |key, index|
|
24
|
+
scope[key] = scope_by_array[index]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
attr_accessor :scope_by_proc, :scope_params
|
31
|
+
|
32
|
+
def scope_params
|
33
|
+
@scope_params ||= []
|
34
|
+
end
|
35
|
+
|
36
|
+
def scope_by(*params, &block)
|
37
|
+
self.scope_by_proc = Proc.new { |p| block.call(p) }
|
38
|
+
[*params].each { |p| self.scope_params << p }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rapid_api/action_controller/permitted_params'
|
2
|
+
require 'rapid_api/action_controller/errors'
|
3
|
+
require 'rapid_api/action_controller/scope'
|
4
|
+
require 'rapid_api/action_controller/filterable_params'
|
5
|
+
require 'rapid_api/action_controller/rest_actions'
|
6
|
+
require 'rapid_api/action_controller/macros'
|
7
|
+
|
8
|
+
module RapidApi
|
9
|
+
module ActionController
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module RapidApi
|
2
|
+
module Auth
|
3
|
+
module Concerns
|
4
|
+
|
5
|
+
module AuthenticatedController
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
before_filter :authenticate!
|
10
|
+
|
11
|
+
attr_accessor :authenticated
|
12
|
+
end
|
13
|
+
|
14
|
+
def authenticate!
|
15
|
+
self.extend JWTHelpers
|
16
|
+
self.extend AuthenticationHelpers
|
17
|
+
self.authenticated = self.class.authenticate_proc.call(self)
|
18
|
+
not_authenticated! if authenticated.nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
attr_accessor :authenticate_proc
|
25
|
+
|
26
|
+
def authenticate(&block)
|
27
|
+
self.authenticate_proc = Proc.new { |request| block.call(request) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def inherited(child)
|
31
|
+
child.authenticate_proc = authenticate_proc
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
module AuthenticationHelpers
|
37
|
+
|
38
|
+
def decode_jwt_token!(token)
|
39
|
+
begin
|
40
|
+
jwt_decode(token)
|
41
|
+
rescue JWT::ExpiredSignature
|
42
|
+
not_authenticated!
|
43
|
+
rescue JWT::VerificationError, JWT::DecodeError
|
44
|
+
not_authenticated!
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module RapidApi
|
2
|
+
module Auth
|
3
|
+
module Concerns
|
4
|
+
|
5
|
+
module JWTHelpers
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
def jwt_encode(payload)
|
13
|
+
Support::JWT.encode(payload)
|
14
|
+
end
|
15
|
+
|
16
|
+
def jwt_decode(token)
|
17
|
+
Support::JWT.decode(token)
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
def jwt_encode(payload)
|
22
|
+
Support::JWT.encode(payload)
|
23
|
+
end
|
24
|
+
|
25
|
+
def jwt_decode(token)
|
26
|
+
Support::JWT.decode(token)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module RapidApi
|
2
|
+
module Auth
|
3
|
+
module Concerns
|
4
|
+
|
5
|
+
module SessionsController
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
include JWTHelpers
|
8
|
+
|
9
|
+
included do
|
10
|
+
skip_before_filter :authenticate!
|
11
|
+
end
|
12
|
+
|
13
|
+
def authenticate
|
14
|
+
authenticated = self.class.auth_proc.call(permitted_auth_params)
|
15
|
+
if authenticated.present?
|
16
|
+
render json: self.class.responds_with_proc.call(authenticated),
|
17
|
+
status: :ok
|
18
|
+
else
|
19
|
+
render json: { errors: ['Invalid credentials'] }, status: :unauthorized
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def permitted_auth_params
|
26
|
+
params.permit(*self.class.auth_params)
|
27
|
+
end
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
attr_accessor :auth_params, :auth_proc, :responds_with_proc
|
31
|
+
|
32
|
+
def auth_params
|
33
|
+
@auth_params ||= []
|
34
|
+
end
|
35
|
+
|
36
|
+
def authenticates_with(*params, &block)
|
37
|
+
self.auth_proc = Proc.new { |params| block.call(params) }
|
38
|
+
[*params].each { |p| self.auth_params << p }
|
39
|
+
end
|
40
|
+
|
41
|
+
def responds_with(&block)
|
42
|
+
self.responds_with_proc = Proc.new { |authenticated| block.call(authenticated) }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'jwt'
|
2
|
+
|
3
|
+
module RapidApi
|
4
|
+
module Auth
|
5
|
+
module Support
|
6
|
+
|
7
|
+
class JWT
|
8
|
+
|
9
|
+
def self.encode(payload={}, ttl=3600)
|
10
|
+
if ttl.present?
|
11
|
+
payload[:exp] = Time.current.to_i + ttl
|
12
|
+
end
|
13
|
+
encrypt_key = nil; #TODO: Make this configurable
|
14
|
+
encrypt_alg = 'none'
|
15
|
+
|
16
|
+
::JWT.encode payload, encrypt_key, encrypt_alg
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.decode(token)
|
20
|
+
decrypt_key = nil;
|
21
|
+
::JWT.decode token, decrypt_key, false
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|