rhino_project_core 0.20.0.alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +28 -0
- data/Rakefile +35 -0
- data/app/assets/stripe_flow.png +0 -0
- data/app/controllers/concerns/rhino/authenticated.rb +18 -0
- data/app/controllers/concerns/rhino/error_handling.rb +60 -0
- data/app/controllers/concerns/rhino/paper_trail_whodunnit.rb +11 -0
- data/app/controllers/concerns/rhino/permit.rb +38 -0
- data/app/controllers/concerns/rhino/set_current_user.rb +13 -0
- data/app/controllers/rhino/account_controller.rb +34 -0
- data/app/controllers/rhino/active_model_extension_controller.rb +52 -0
- data/app/controllers/rhino/base_controller.rb +23 -0
- data/app/controllers/rhino/crud_controller.rb +57 -0
- data/app/controllers/rhino/simple_controller.rb +13 -0
- data/app/controllers/rhino/simple_stream_controller.rb +12 -0
- data/app/helpers/rhino/omniauth_helper.rb +67 -0
- data/app/helpers/rhino/policy_helper.rb +42 -0
- data/app/helpers/rhino/segment_helper.rb +62 -0
- data/app/models/rhino/account.rb +13 -0
- data/app/models/rhino/current.rb +7 -0
- data/app/models/rhino/user.rb +44 -0
- data/app/overrides/active_record/autosave_association_override.rb +18 -0
- data/app/overrides/active_record/delegated_type_override.rb +14 -0
- data/app/overrides/activestorage/direct_uploads_controller_override.rb +23 -0
- data/app/overrides/activestorage/redirect_controller_override.rb +21 -0
- data/app/overrides/activestorage/redirect_representation_controller_override.rb +21 -0
- data/app/overrides/devise_token_auth/confirmations_controller_override.rb +14 -0
- data/app/overrides/devise_token_auth/omniauth_callbacks_controller_override.rb +45 -0
- data/app/overrides/devise_token_auth/passwords_controller_override.rb +9 -0
- data/app/overrides/devise_token_auth/registrations_controller_override.rb +20 -0
- data/app/overrides/devise_token_auth/sessions_controller_override.rb +26 -0
- data/app/overrides/devise_token_auth/token_validations_controller_override.rb +18 -0
- data/app/policies/rhino/account_policy.rb +27 -0
- data/app/policies/rhino/active_storage_attachment_policy.rb +16 -0
- data/app/policies/rhino/admin_policy.rb +20 -0
- data/app/policies/rhino/base_policy.rb +72 -0
- data/app/policies/rhino/crud_policy.rb +109 -0
- data/app/policies/rhino/editor_policy.rb +12 -0
- data/app/policies/rhino/global_policy.rb +8 -0
- data/app/policies/rhino/resource_info_policy.rb +9 -0
- data/app/policies/rhino/user_policy.rb +20 -0
- data/app/policies/rhino/viewer_policy.rb +19 -0
- data/app/resources/rhino/info_graph.rb +41 -0
- data/app/resources/rhino/open_api_info.rb +108 -0
- data/config/routes.rb +19 -0
- data/db/migrate/20180101000000_devise_token_auth_create_users.rb +54 -0
- data/db/migrate/20180622142754_add_allow_change_password_to_users.rb +5 -0
- data/db/migrate/20191217010224_add_approved_to_users.rb +7 -0
- data/db/migrate/20200503182019_change_tokens_to_json_b.rb +9 -0
- data/lib/commands/rhino/module/coverage_command.rb +44 -0
- data/lib/commands/rhino/module/dummy_command.rb +43 -0
- data/lib/commands/rhino/module/new_command.rb +34 -0
- data/lib/commands/rhino/module/rails_command.rb +43 -0
- data/lib/commands/rhino/module/test_command.rb +43 -0
- data/lib/generators/rhino/dev/setup/setup_generator.rb +199 -0
- data/lib/generators/rhino/dev/setup/templates/env.client.tt +4 -0
- data/lib/generators/rhino/dev/setup/templates/env.root.tt +1 -0
- data/lib/generators/rhino/dev/setup/templates/env.server.tt +35 -0
- data/lib/generators/rhino/dev/setup/templates/prepare-commit-msg +17 -0
- data/lib/generators/rhino/install/install_generator.rb +24 -0
- data/lib/generators/rhino/install/templates/account.rb +4 -0
- data/lib/generators/rhino/install/templates/rhino.rb +24 -0
- data/lib/generators/rhino/install/templates/user.rb +4 -0
- data/lib/generators/rhino/model/model_generator.rb +96 -0
- data/lib/generators/rhino/module/USAGE +6 -0
- data/lib/generators/rhino/module/module_generator.rb +92 -0
- data/lib/generators/rhino/module/templates/%name%.gemspec.tt +24 -0
- data/lib/generators/rhino/module/templates/lib/%namespaced_name%/engine.rb.tt +18 -0
- data/lib/generators/rhino/module/templates/lib/generators/%namespaced_name%/install/install_generator.rb.tt +12 -0
- data/lib/generators/rhino/module/templates/lib/tasks/%namespaced_name%_tasks.rake.tt +13 -0
- data/lib/generators/rhino/module/templates/test/dummy/app/models/user.rb +4 -0
- data/lib/generators/rhino/module/templates/test/dummy/config/database.yml +25 -0
- data/lib/generators/rhino/module/templates/test/dummy/config/initializers/devise.rb +311 -0
- data/lib/generators/rhino/module/templates/test/dummy/config/initializers/devise_token_auth.rb +71 -0
- data/lib/generators/rhino/module/templates/test/test_helper.rb +54 -0
- data/lib/generators/rhino/policy/policy_generator.rb +33 -0
- data/lib/generators/rhino/policy/templates/policy.rb.tt +46 -0
- data/lib/generators/test_unit/rhino_policy_generator.rb +13 -0
- data/lib/generators/test_unit/templates/policy_test.rb.tt +57 -0
- data/lib/rhino/engine.rb +166 -0
- data/lib/rhino/omniauth/strategies/azure_o_auth2.rb +16 -0
- data/lib/rhino/resource/active_model_extension/backing_store/google_sheet.rb +89 -0
- data/lib/rhino/resource/active_model_extension/backing_store.rb +33 -0
- data/lib/rhino/resource/active_model_extension/describe.rb +38 -0
- data/lib/rhino/resource/active_model_extension/params.rb +70 -0
- data/lib/rhino/resource/active_model_extension/properties.rb +231 -0
- data/lib/rhino/resource/active_model_extension/reference.rb +50 -0
- data/lib/rhino/resource/active_model_extension/routing.rb +15 -0
- data/lib/rhino/resource/active_model_extension/serialization.rb +16 -0
- data/lib/rhino/resource/active_model_extension.rb +38 -0
- data/lib/rhino/resource/active_record_extension/describe.rb +44 -0
- data/lib/rhino/resource/active_record_extension/params.rb +213 -0
- data/lib/rhino/resource/active_record_extension/properties.rb +85 -0
- data/lib/rhino/resource/active_record_extension/properties_describe.rb +228 -0
- data/lib/rhino/resource/active_record_extension/reference.rb +50 -0
- data/lib/rhino/resource/active_record_extension/routing.rb +21 -0
- data/lib/rhino/resource/active_record_extension/search.rb +23 -0
- data/lib/rhino/resource/active_record_extension/serialization.rb +16 -0
- data/lib/rhino/resource/active_record_extension/super_admin.rb +25 -0
- data/lib/rhino/resource/active_record_extension.rb +32 -0
- data/lib/rhino/resource/active_record_tree.rb +50 -0
- data/lib/rhino/resource/active_storage_extension.rb +41 -0
- data/lib/rhino/resource/describe.rb +19 -0
- data/lib/rhino/resource/owner.rb +172 -0
- data/lib/rhino/resource/params.rb +31 -0
- data/lib/rhino/resource/properties.rb +192 -0
- data/lib/rhino/resource/reference.rb +29 -0
- data/lib/rhino/resource/routing.rb +107 -0
- data/lib/rhino/resource/serialization.rb +13 -0
- data/lib/rhino/resource/sieves.rb +36 -0
- data/lib/rhino/resource.rb +55 -0
- data/lib/rhino/sieve/filter.rb +149 -0
- data/lib/rhino/sieve/geospatial.rb +45 -0
- data/lib/rhino/sieve/helpers.rb +11 -0
- data/lib/rhino/sieve/limit.rb +20 -0
- data/lib/rhino/sieve/offset.rb +16 -0
- data/lib/rhino/sieve/order.rb +143 -0
- data/lib/rhino/sieve/search.rb +20 -0
- data/lib/rhino/sieve.rb +159 -0
- data/lib/rhino/test_case/controller.rb +145 -0
- data/lib/rhino/test_case/model.rb +86 -0
- data/lib/rhino/test_case/override.rb +19 -0
- data/lib/rhino/test_case/policy.rb +76 -0
- data/lib/rhino/test_case.rb +11 -0
- data/lib/rhino/version.rb +17 -0
- data/lib/rhino_project_core.rb +131 -0
- data/lib/tasks/rhino.rake +24 -0
- data/lib/tasks/rhino_dev.rake +17 -0
- data/lib/validators/country_validator.rb +11 -0
- data/lib/validators/email_validator.rb +8 -0
- data/lib/validators/ipv4_validator.rb +10 -0
- data/lib/validators/mac_address_validator.rb +9 -0
- metadata +531 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 0f2e6d0cf9a761ebe9b75b3cacf2b6345a2b91df4d9d3f456a9108492aac0988
|
|
4
|
+
data.tar.gz: 45365e2f7d1efe25bbfc153a497804758a43bc160c2d13a22918af94390d0790
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: bdb873a1c1e4717d74dc52dda547b573a921e730023fa6ea307a2df0b776fd579832aba12da869d1c33e2edc2d2a39f7a5bc33a501e177ccb7cf0db71c7d6908
|
|
7
|
+
data.tar.gz: 7375ef91e536a7a77b1a39e85ee5c501b035b6bd4127ad824064424ef78ae64b6114600dd88c6eade02ce7350fa1a3ffe5787a80ac07db4e625cf823c90c5444
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020-present Codalio Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Rhino
|
|
2
|
+
Short description and motivation.
|
|
3
|
+
|
|
4
|
+
## Usage
|
|
5
|
+
How to use my plugin.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
Add this line to your application's Gemfile:
|
|
9
|
+
|
|
10
|
+
```ruby
|
|
11
|
+
gem 'rhino'
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
And then execute:
|
|
15
|
+
```bash
|
|
16
|
+
$ bundle
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install it yourself as:
|
|
20
|
+
```bash
|
|
21
|
+
$ gem install rhino
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Contributing
|
|
25
|
+
Contribution directions go here.
|
|
26
|
+
|
|
27
|
+
## License
|
|
28
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'bundler/setup'
|
|
3
|
+
rescue LoadError
|
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
require 'rdoc/task'
|
|
8
|
+
|
|
9
|
+
task :package
|
|
10
|
+
|
|
11
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
12
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
13
|
+
rdoc.title = 'Rhino'
|
|
14
|
+
rdoc.options << '--line-numbers'
|
|
15
|
+
rdoc.rdoc_files.include('README.md')
|
|
16
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
|
20
|
+
load 'rails/tasks/engine.rake'
|
|
21
|
+
|
|
22
|
+
load 'rails/tasks/statistics.rake'
|
|
23
|
+
|
|
24
|
+
require 'bundler/gem_tasks'
|
|
25
|
+
|
|
26
|
+
require 'rake/testtask'
|
|
27
|
+
|
|
28
|
+
Rake::TestTask.new do |t|
|
|
29
|
+
t.libs << "test"
|
|
30
|
+
t.test_files = FileList["test/**/*_test.rb"].exclude("test/system/**/*", "test/dummy/**/*")
|
|
31
|
+
t.verbose = true
|
|
32
|
+
t.warning = false
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
task default: :test
|
|
Binary file
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rhino
|
|
4
|
+
module Authenticated
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
prepend_before_action :authenticate_user!
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Overrides the default devise_token_auth handler
|
|
12
|
+
def render_authenticate_error
|
|
13
|
+
cookies.delete(DeviseTokenAuth.cookie_name, domain: DeviseTokenAuth.cookie_attributes[:domain])
|
|
14
|
+
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_record/associations"
|
|
4
|
+
|
|
5
|
+
module Rhino
|
|
6
|
+
module ErrorHandling
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do # rubocop:disable Metrics/BlockLength
|
|
10
|
+
rescue_from Exception, with: :handle_uncaught_error
|
|
11
|
+
|
|
12
|
+
# ActiveRecord::DeleteRestrictionError is for dependent: :restrict_with_exception
|
|
13
|
+
rescue_from ActionController::ParameterMissing, ActiveRecord::DeleteRestrictionError do |e|
|
|
14
|
+
render json: { errors: [e.message] }, status: :bad_request
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
rescue_from Pundit::NotAuthorizedError, with: :forbidden
|
|
18
|
+
|
|
19
|
+
# ActiveRecord::RecordNotDestroyed is for dependent: :restrict_with_error
|
|
20
|
+
rescue_from ActiveRecord::RecordInvalid, ActiveRecord::RecordNotDestroyed do |e|
|
|
21
|
+
unprocessable e.record.errors
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
rescue_from ActiveRecord::RecordNotFound, with: :not_found
|
|
25
|
+
|
|
26
|
+
def handle_uncaught_error(exception)
|
|
27
|
+
# Send to rollbar if available
|
|
28
|
+
Rollbar.error(exception) if defined? Rollbar
|
|
29
|
+
|
|
30
|
+
logger.error("Internal server error#{exception.class} #{exception.message} #{exception.backtrace.join("\n")}")
|
|
31
|
+
|
|
32
|
+
render json: { errors: ['Internal server error.'] },
|
|
33
|
+
status: :internal_server_error
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def not_found
|
|
37
|
+
render json: { errors: ['Not found.'] }, status: :not_found
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def bad_request(errors = nil)
|
|
41
|
+
render json: { errors: (errors || ['Bad request.']) },
|
|
42
|
+
status: :bad_request
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def forbidden
|
|
46
|
+
render json: { errors: ['Access denied.'] }, status: :forbidden
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def unprocessable(errors = nil)
|
|
50
|
+
render json: {
|
|
51
|
+
errors: (errors&.to_hash(full_messages: true) || ['Unprocessable request.'])
|
|
52
|
+
}, status: :unprocessable_entity
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def cors
|
|
56
|
+
render json: {}
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rhino
|
|
4
|
+
module Permit
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
protected
|
|
8
|
+
# The params are wrapped with 'crud' if the crud_controller is called directly
|
|
9
|
+
# instead of via a customization class
|
|
10
|
+
#
|
|
11
|
+
# Protected to prevent polluting the action method
|
|
12
|
+
#
|
|
13
|
+
# FIXME: Couldn't find a way to call wrap_parameters appropriately
|
|
14
|
+
# FIXME: Will some sort of namespacing break this?
|
|
15
|
+
# _wrapper_options is semi-private but we can get at it
|
|
16
|
+
def pundit_params_for(_record)
|
|
17
|
+
params.require(_wrapper_options.name)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
def permit_and_transform(model = @model)
|
|
22
|
+
klass.transform_params(permitted_attributes(model))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def permit_model(model = @model)
|
|
26
|
+
model_policy = Pundit.policy(current_user, model)
|
|
27
|
+
|
|
28
|
+
model_params = ActionController::Parameters.new(model.to_caching_json)
|
|
29
|
+
model_params[:can_current_user_edit] = model_policy.update?
|
|
30
|
+
model_params[:can_current_user_destroy] = model_policy.destroy?
|
|
31
|
+
model_params.permit(model_policy.permitted_attributes_for_show + %i[can_current_user_edit can_current_user_destroy])
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def permit_and_render
|
|
35
|
+
render json: permit_model
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rhino
|
|
4
|
+
class AccountController < BaseController
|
|
5
|
+
include Rhino::Authenticated
|
|
6
|
+
include Rhino::Permit
|
|
7
|
+
|
|
8
|
+
# Confirm we are calling authorize and scope correctly
|
|
9
|
+
after_action :verify_authorized
|
|
10
|
+
after_action :verify_policy_scoped
|
|
11
|
+
|
|
12
|
+
def show
|
|
13
|
+
@model = authorize_resource
|
|
14
|
+
|
|
15
|
+
permit_and_render
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def update
|
|
19
|
+
@model = authorize_resource
|
|
20
|
+
@model.update!(permit_and_transform)
|
|
21
|
+
|
|
22
|
+
permit_and_render
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
def find_resource
|
|
27
|
+
policy_scope(klass).first
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def authorize_resource
|
|
31
|
+
authorize find_resource
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rhino
|
|
4
|
+
class ActiveModelExtensionController < BaseController
|
|
5
|
+
include Rhino::Authenticated
|
|
6
|
+
include Rhino::Permit
|
|
7
|
+
|
|
8
|
+
# Confirm we are calling authorize and scope correctly
|
|
9
|
+
after_action :verify_authorized
|
|
10
|
+
# after_action :verify_policy_scoped, only: %i[index show]
|
|
11
|
+
|
|
12
|
+
def create
|
|
13
|
+
@model = authorize klass.new(permit_and_transform(klass.new))
|
|
14
|
+
klass.backing_store_create(@model)
|
|
15
|
+
|
|
16
|
+
permit_and_render
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def index
|
|
20
|
+
authorize klass
|
|
21
|
+
@models = klass.backing_store_index
|
|
22
|
+
|
|
23
|
+
# FIXME: - policy and sieve scopings
|
|
24
|
+
# @models = klass.sieves.resolve(policy_scope(klass), params)
|
|
25
|
+
render json: {
|
|
26
|
+
results: @models.map { |m| permit_model(m) },
|
|
27
|
+
total: @models.count
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def show
|
|
32
|
+
@model = authorize(klass.backing_store_show(params[:id]))
|
|
33
|
+
|
|
34
|
+
permit_and_render
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def update
|
|
38
|
+
@model = authorize klass.new(permit_and_transform(klass.new))
|
|
39
|
+
# FIXME: Return updated model
|
|
40
|
+
@model.backing_store_update
|
|
41
|
+
|
|
42
|
+
permit_and_render
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def destroy
|
|
46
|
+
@model = authorize klass.backing_store_show(params[:id])
|
|
47
|
+
@model.backing_store_destroy
|
|
48
|
+
|
|
49
|
+
permit_and_render
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rhino
|
|
4
|
+
class BaseController < ActionController::API
|
|
5
|
+
include ActionController::Cookies
|
|
6
|
+
include DeviseTokenAuth::Concerns::SetUserByToken
|
|
7
|
+
include Pundit
|
|
8
|
+
|
|
9
|
+
include Rhino::ErrorHandling
|
|
10
|
+
include Rhino::SetCurrentUser
|
|
11
|
+
include Rhino::PaperTrailWhodunnit
|
|
12
|
+
|
|
13
|
+
# https://api.rubyonrails.org/classes/AbstractController/Base.html#method-c-abstract-21
|
|
14
|
+
# Prevents the utility methods below from showing up as actions in CrudController
|
|
15
|
+
abstract!
|
|
16
|
+
|
|
17
|
+
respond_to :json
|
|
18
|
+
|
|
19
|
+
def klass
|
|
20
|
+
@klass ||= params.delete(:rhino_resource).constantize
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rhino
|
|
4
|
+
class CrudController < BaseController
|
|
5
|
+
include Rhino::Authenticated
|
|
6
|
+
include Rhino::Permit
|
|
7
|
+
|
|
8
|
+
# Confirm we are calling authorize and scope correctly
|
|
9
|
+
after_action :verify_authorized
|
|
10
|
+
after_action :verify_policy_scoped, only: %i[index show]
|
|
11
|
+
|
|
12
|
+
wrap_parameters :crud, format: [:json]
|
|
13
|
+
|
|
14
|
+
def create
|
|
15
|
+
@model = authorize klass.new(permit_and_transform(klass))
|
|
16
|
+
@model.save!
|
|
17
|
+
|
|
18
|
+
permit_and_render
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def index
|
|
22
|
+
authorize klass
|
|
23
|
+
|
|
24
|
+
@models = klass.sieves.resolve(policy_scope(klass), params)
|
|
25
|
+
render json: {
|
|
26
|
+
results: @models.eager_load_refs.map { |m| permit_model(m) },
|
|
27
|
+
total: @models.unscope(:limit, :offset).reselect(:id).count
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def show
|
|
32
|
+
@model = authorize find_resource(policy_scope(klass).eager_load_refs)
|
|
33
|
+
|
|
34
|
+
permit_and_render
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def update
|
|
38
|
+
@model = authorize find_resource
|
|
39
|
+
@model.update!(permit_and_transform)
|
|
40
|
+
|
|
41
|
+
permit_and_render
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def destroy
|
|
45
|
+
@model = authorize find_resource
|
|
46
|
+
@model.destroy!
|
|
47
|
+
|
|
48
|
+
permit_and_render
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
def find_resource(scope = klass.all)
|
|
53
|
+
scope = scope.friendly if scope.respond_to? :friendly
|
|
54
|
+
scope.find(params[:id])
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rhino
|
|
4
|
+
class SimpleController < BaseController
|
|
5
|
+
def action_missing(action)
|
|
6
|
+
authorize klass, "#{action}?".to_sym
|
|
7
|
+
|
|
8
|
+
method = klass.method(action)
|
|
9
|
+
|
|
10
|
+
render json: method.parameters.any? ? klass.send(action, params) : klass.send(action)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rhino
|
|
4
|
+
class SimpleStreamController < BaseController
|
|
5
|
+
def action_missing(action)
|
|
6
|
+
authorize klass, "#{action}?".to_sym
|
|
7
|
+
|
|
8
|
+
info = klass.send(action)
|
|
9
|
+
send_file(info[:file], info.except(:file)) if info.key?(:file)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rhino
|
|
4
|
+
module OmniauthHelper
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
def strategies_metadata
|
|
8
|
+
params = { resource_class: "User" }
|
|
9
|
+
|
|
10
|
+
strategies.each_with_object([]) do |strategy, array|
|
|
11
|
+
array << {
|
|
12
|
+
name: strategy,
|
|
13
|
+
path: "#{::OmniAuth.config.path_prefix}/#{strategy}?#{params.to_param}"
|
|
14
|
+
}
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def strategies
|
|
19
|
+
strategies = ENV.keys.filter_map do |env|
|
|
20
|
+
match = /AUTH_(.*)_CLIENT_ID/.match(env)
|
|
21
|
+
next unless match
|
|
22
|
+
|
|
23
|
+
match[1].downcase.to_sym
|
|
24
|
+
end.uniq
|
|
25
|
+
|
|
26
|
+
strategies += [:developer] if Rails.env.development? && !Rake.try(:application)
|
|
27
|
+
|
|
28
|
+
strategies
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def app_info(strategy)
|
|
32
|
+
case strategy
|
|
33
|
+
when :developer
|
|
34
|
+
[]
|
|
35
|
+
when :azure_oauth2
|
|
36
|
+
azure_info
|
|
37
|
+
when :auth0
|
|
38
|
+
auth0_info
|
|
39
|
+
else
|
|
40
|
+
env_keys(strategy)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def env_keys(strategy)
|
|
45
|
+
ups = strategy.to_s.upcase
|
|
46
|
+
[ENV["AUTH_#{ups}_CLIENT_ID"], ENV["AUTH_#{ups}_SECRET_KEY"]]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def azure_info
|
|
50
|
+
[
|
|
51
|
+
client_id: ENV["AUTH_AZURE_OAUTH2_CLIENT_ID"],
|
|
52
|
+
client_secret: ENV["AUTH_AZURE_OAUTH2_SECRET_KEY"],
|
|
53
|
+
tenant_id: ENV["AUTH_AZURE_OAUTH2_TENANT_ID"]
|
|
54
|
+
]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def auth0_info
|
|
58
|
+
[client_id: ENV["AUTH_AUTH0_CLIENT_ID"],
|
|
59
|
+
client_secret: ENV["AUTH_AUTH0_SECRET_KEY"],
|
|
60
|
+
domain: ENV["AUTH_AUTH0_DOMAIN"],
|
|
61
|
+
callback_path: "/api/auth/omniauth/auth0/callback",
|
|
62
|
+
authorize_params: {
|
|
63
|
+
scope: "openid profile"
|
|
64
|
+
}]
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rhino
|
|
4
|
+
module PolicyHelper
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
# Looks for the policy associated with a role and resource
|
|
8
|
+
#
|
|
9
|
+
# If a policy for a role and resource is not found, looks for the policy
|
|
10
|
+
# associated with the role.
|
|
11
|
+
#
|
|
12
|
+
# === Examples
|
|
13
|
+
#
|
|
14
|
+
# find_policy(:author, Blog)
|
|
15
|
+
#
|
|
16
|
+
def find_policy(role, resource, additional_class = nil)
|
|
17
|
+
role = role.to_s if role.is_a? Symbol
|
|
18
|
+
role = role.classify
|
|
19
|
+
|
|
20
|
+
resource = resource.class if resource.class != Class
|
|
21
|
+
resource = resource.to_s.classify
|
|
22
|
+
|
|
23
|
+
policy_class = 'Policy'
|
|
24
|
+
policy_class = "#{policy_class}::#{additional_class}" if additional_class
|
|
25
|
+
|
|
26
|
+
# Look for role and resource specific policy
|
|
27
|
+
policy_constant = "#{role}#{resource}#{policy_class}".safe_constantize
|
|
28
|
+
return policy_constant if policy_constant.present?
|
|
29
|
+
|
|
30
|
+
# Fall back to just the role specific policy
|
|
31
|
+
policy_constant = "#{role}#{policy_class}".safe_constantize
|
|
32
|
+
return policy_constant if policy_constant.present?
|
|
33
|
+
|
|
34
|
+
# Fall back just to the rhino version
|
|
35
|
+
"Rhino::#{role}#{policy_class}".safe_constantize
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def find_policy_scope(role, resource)
|
|
39
|
+
find_policy(role, resource, 'Scope')
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rhino
|
|
4
|
+
module SegmentHelper
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
def track(type, user)
|
|
8
|
+
segment_track(type, user)
|
|
9
|
+
rescue StandardError => e
|
|
10
|
+
Rollbar.error(e)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def track_account(type, organization, users_role = nil)
|
|
14
|
+
segment_track_account(type, organization, users_role)
|
|
15
|
+
rescue StandardError => e
|
|
16
|
+
Rollbar.error(e)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def track_invite(users_role_invite)
|
|
20
|
+
segment_track_invite(users_role_invite)
|
|
21
|
+
rescue StandardError => e
|
|
22
|
+
Rollbar.error(e)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def segment_track(type, user)
|
|
26
|
+
Analytics.track(
|
|
27
|
+
user_id: user.id,
|
|
28
|
+
event: type,
|
|
29
|
+
properties: {
|
|
30
|
+
name: user.name,
|
|
31
|
+
email: user.email,
|
|
32
|
+
current_sign_in_ip: user.current_sign_in_ip,
|
|
33
|
+
last_sign_in_ip: user.last_sign_in_ip,
|
|
34
|
+
approved: user.approved
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def segment_track_account(type, organization, users_role)
|
|
40
|
+
properties = {}
|
|
41
|
+
properties[:account_name] = organization.name
|
|
42
|
+
properties[:user_email] = users_role.user&.email unless users_role.nil?
|
|
43
|
+
properties[:role] = users_role.role&.name unless users_role.nil?
|
|
44
|
+
Analytics.track(
|
|
45
|
+
user_id: users_role.nil? ? organization.id : users_role.user&.id,
|
|
46
|
+
event: type,
|
|
47
|
+
properties:
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def segment_track_invite(users_role_invite)
|
|
52
|
+
Analytics.track(
|
|
53
|
+
event: "Invite Sent",
|
|
54
|
+
user_id: Rhino::Current.user&.id,
|
|
55
|
+
properties: {
|
|
56
|
+
invitee_email: users_role_invite.email,
|
|
57
|
+
invitee_role: users_role_invite.role&.name
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rhino
|
|
4
|
+
class User < ApplicationRecord
|
|
5
|
+
self.abstract_class = true
|
|
6
|
+
|
|
7
|
+
include DeviseTokenAuth::Concerns::User
|
|
8
|
+
|
|
9
|
+
rhino_properties_read only: %i[id name nickname email image]
|
|
10
|
+
rhino_properties_create only: %i[name nickname email]
|
|
11
|
+
rhino_properties_update only: %i[name nickname]
|
|
12
|
+
|
|
13
|
+
rhino_policy :user
|
|
14
|
+
|
|
15
|
+
def self.devise_modules_to_load
|
|
16
|
+
modules = %i[database_authenticatable registerable recoverable rememberable trackable validatable confirmable omniauthable]
|
|
17
|
+
modules.push :invitable if Rhino.resources.include?("Organization")
|
|
18
|
+
end
|
|
19
|
+
devise(*devise_modules_to_load)
|
|
20
|
+
|
|
21
|
+
validates :email, uniqueness: { case_sensitive: false }
|
|
22
|
+
after_create_commit :track_sign_up
|
|
23
|
+
|
|
24
|
+
def display_name
|
|
25
|
+
name || email
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.roles_for_auth(auth_owner, record = nil)
|
|
29
|
+
return {} unless auth_owner
|
|
30
|
+
|
|
31
|
+
# If user is logged in, but no record, they are still an admin for their data
|
|
32
|
+
# Otherwise owner must match to be an admin
|
|
33
|
+
# A list of roles as hash keys with an array of base_owners for each
|
|
34
|
+
return { admin: [auth_owner] } if !record.respond_to?(:base_owner_ids) || record.base_owner_ids.include?(auth_owner&.id)
|
|
35
|
+
|
|
36
|
+
{}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
def track_sign_up
|
|
41
|
+
Rhino::SegmentHelper.track("Signed Up", self)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|