rhino_project_core 0.20.0.alpha.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|