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.
Files changed (134) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +28 -0
  4. data/Rakefile +35 -0
  5. data/app/assets/stripe_flow.png +0 -0
  6. data/app/controllers/concerns/rhino/authenticated.rb +18 -0
  7. data/app/controllers/concerns/rhino/error_handling.rb +60 -0
  8. data/app/controllers/concerns/rhino/paper_trail_whodunnit.rb +11 -0
  9. data/app/controllers/concerns/rhino/permit.rb +38 -0
  10. data/app/controllers/concerns/rhino/set_current_user.rb +13 -0
  11. data/app/controllers/rhino/account_controller.rb +34 -0
  12. data/app/controllers/rhino/active_model_extension_controller.rb +52 -0
  13. data/app/controllers/rhino/base_controller.rb +23 -0
  14. data/app/controllers/rhino/crud_controller.rb +57 -0
  15. data/app/controllers/rhino/simple_controller.rb +13 -0
  16. data/app/controllers/rhino/simple_stream_controller.rb +12 -0
  17. data/app/helpers/rhino/omniauth_helper.rb +67 -0
  18. data/app/helpers/rhino/policy_helper.rb +42 -0
  19. data/app/helpers/rhino/segment_helper.rb +62 -0
  20. data/app/models/rhino/account.rb +13 -0
  21. data/app/models/rhino/current.rb +7 -0
  22. data/app/models/rhino/user.rb +44 -0
  23. data/app/overrides/active_record/autosave_association_override.rb +18 -0
  24. data/app/overrides/active_record/delegated_type_override.rb +14 -0
  25. data/app/overrides/activestorage/direct_uploads_controller_override.rb +23 -0
  26. data/app/overrides/activestorage/redirect_controller_override.rb +21 -0
  27. data/app/overrides/activestorage/redirect_representation_controller_override.rb +21 -0
  28. data/app/overrides/devise_token_auth/confirmations_controller_override.rb +14 -0
  29. data/app/overrides/devise_token_auth/omniauth_callbacks_controller_override.rb +45 -0
  30. data/app/overrides/devise_token_auth/passwords_controller_override.rb +9 -0
  31. data/app/overrides/devise_token_auth/registrations_controller_override.rb +20 -0
  32. data/app/overrides/devise_token_auth/sessions_controller_override.rb +26 -0
  33. data/app/overrides/devise_token_auth/token_validations_controller_override.rb +18 -0
  34. data/app/policies/rhino/account_policy.rb +27 -0
  35. data/app/policies/rhino/active_storage_attachment_policy.rb +16 -0
  36. data/app/policies/rhino/admin_policy.rb +20 -0
  37. data/app/policies/rhino/base_policy.rb +72 -0
  38. data/app/policies/rhino/crud_policy.rb +109 -0
  39. data/app/policies/rhino/editor_policy.rb +12 -0
  40. data/app/policies/rhino/global_policy.rb +8 -0
  41. data/app/policies/rhino/resource_info_policy.rb +9 -0
  42. data/app/policies/rhino/user_policy.rb +20 -0
  43. data/app/policies/rhino/viewer_policy.rb +19 -0
  44. data/app/resources/rhino/info_graph.rb +41 -0
  45. data/app/resources/rhino/open_api_info.rb +108 -0
  46. data/config/routes.rb +19 -0
  47. data/db/migrate/20180101000000_devise_token_auth_create_users.rb +54 -0
  48. data/db/migrate/20180622142754_add_allow_change_password_to_users.rb +5 -0
  49. data/db/migrate/20191217010224_add_approved_to_users.rb +7 -0
  50. data/db/migrate/20200503182019_change_tokens_to_json_b.rb +9 -0
  51. data/lib/commands/rhino/module/coverage_command.rb +44 -0
  52. data/lib/commands/rhino/module/dummy_command.rb +43 -0
  53. data/lib/commands/rhino/module/new_command.rb +34 -0
  54. data/lib/commands/rhino/module/rails_command.rb +43 -0
  55. data/lib/commands/rhino/module/test_command.rb +43 -0
  56. data/lib/generators/rhino/dev/setup/setup_generator.rb +199 -0
  57. data/lib/generators/rhino/dev/setup/templates/env.client.tt +4 -0
  58. data/lib/generators/rhino/dev/setup/templates/env.root.tt +1 -0
  59. data/lib/generators/rhino/dev/setup/templates/env.server.tt +35 -0
  60. data/lib/generators/rhino/dev/setup/templates/prepare-commit-msg +17 -0
  61. data/lib/generators/rhino/install/install_generator.rb +24 -0
  62. data/lib/generators/rhino/install/templates/account.rb +4 -0
  63. data/lib/generators/rhino/install/templates/rhino.rb +24 -0
  64. data/lib/generators/rhino/install/templates/user.rb +4 -0
  65. data/lib/generators/rhino/model/model_generator.rb +96 -0
  66. data/lib/generators/rhino/module/USAGE +6 -0
  67. data/lib/generators/rhino/module/module_generator.rb +92 -0
  68. data/lib/generators/rhino/module/templates/%name%.gemspec.tt +24 -0
  69. data/lib/generators/rhino/module/templates/lib/%namespaced_name%/engine.rb.tt +18 -0
  70. data/lib/generators/rhino/module/templates/lib/generators/%namespaced_name%/install/install_generator.rb.tt +12 -0
  71. data/lib/generators/rhino/module/templates/lib/tasks/%namespaced_name%_tasks.rake.tt +13 -0
  72. data/lib/generators/rhino/module/templates/test/dummy/app/models/user.rb +4 -0
  73. data/lib/generators/rhino/module/templates/test/dummy/config/database.yml +25 -0
  74. data/lib/generators/rhino/module/templates/test/dummy/config/initializers/devise.rb +311 -0
  75. data/lib/generators/rhino/module/templates/test/dummy/config/initializers/devise_token_auth.rb +71 -0
  76. data/lib/generators/rhino/module/templates/test/test_helper.rb +54 -0
  77. data/lib/generators/rhino/policy/policy_generator.rb +33 -0
  78. data/lib/generators/rhino/policy/templates/policy.rb.tt +46 -0
  79. data/lib/generators/test_unit/rhino_policy_generator.rb +13 -0
  80. data/lib/generators/test_unit/templates/policy_test.rb.tt +57 -0
  81. data/lib/rhino/engine.rb +166 -0
  82. data/lib/rhino/omniauth/strategies/azure_o_auth2.rb +16 -0
  83. data/lib/rhino/resource/active_model_extension/backing_store/google_sheet.rb +89 -0
  84. data/lib/rhino/resource/active_model_extension/backing_store.rb +33 -0
  85. data/lib/rhino/resource/active_model_extension/describe.rb +38 -0
  86. data/lib/rhino/resource/active_model_extension/params.rb +70 -0
  87. data/lib/rhino/resource/active_model_extension/properties.rb +231 -0
  88. data/lib/rhino/resource/active_model_extension/reference.rb +50 -0
  89. data/lib/rhino/resource/active_model_extension/routing.rb +15 -0
  90. data/lib/rhino/resource/active_model_extension/serialization.rb +16 -0
  91. data/lib/rhino/resource/active_model_extension.rb +38 -0
  92. data/lib/rhino/resource/active_record_extension/describe.rb +44 -0
  93. data/lib/rhino/resource/active_record_extension/params.rb +213 -0
  94. data/lib/rhino/resource/active_record_extension/properties.rb +85 -0
  95. data/lib/rhino/resource/active_record_extension/properties_describe.rb +228 -0
  96. data/lib/rhino/resource/active_record_extension/reference.rb +50 -0
  97. data/lib/rhino/resource/active_record_extension/routing.rb +21 -0
  98. data/lib/rhino/resource/active_record_extension/search.rb +23 -0
  99. data/lib/rhino/resource/active_record_extension/serialization.rb +16 -0
  100. data/lib/rhino/resource/active_record_extension/super_admin.rb +25 -0
  101. data/lib/rhino/resource/active_record_extension.rb +32 -0
  102. data/lib/rhino/resource/active_record_tree.rb +50 -0
  103. data/lib/rhino/resource/active_storage_extension.rb +41 -0
  104. data/lib/rhino/resource/describe.rb +19 -0
  105. data/lib/rhino/resource/owner.rb +172 -0
  106. data/lib/rhino/resource/params.rb +31 -0
  107. data/lib/rhino/resource/properties.rb +192 -0
  108. data/lib/rhino/resource/reference.rb +29 -0
  109. data/lib/rhino/resource/routing.rb +107 -0
  110. data/lib/rhino/resource/serialization.rb +13 -0
  111. data/lib/rhino/resource/sieves.rb +36 -0
  112. data/lib/rhino/resource.rb +55 -0
  113. data/lib/rhino/sieve/filter.rb +149 -0
  114. data/lib/rhino/sieve/geospatial.rb +45 -0
  115. data/lib/rhino/sieve/helpers.rb +11 -0
  116. data/lib/rhino/sieve/limit.rb +20 -0
  117. data/lib/rhino/sieve/offset.rb +16 -0
  118. data/lib/rhino/sieve/order.rb +143 -0
  119. data/lib/rhino/sieve/search.rb +20 -0
  120. data/lib/rhino/sieve.rb +159 -0
  121. data/lib/rhino/test_case/controller.rb +145 -0
  122. data/lib/rhino/test_case/model.rb +86 -0
  123. data/lib/rhino/test_case/override.rb +19 -0
  124. data/lib/rhino/test_case/policy.rb +76 -0
  125. data/lib/rhino/test_case.rb +11 -0
  126. data/lib/rhino/version.rb +17 -0
  127. data/lib/rhino_project_core.rb +131 -0
  128. data/lib/tasks/rhino.rake +24 -0
  129. data/lib/tasks/rhino_dev.rake +17 -0
  130. data/lib/validators/country_validator.rb +11 -0
  131. data/lib/validators/email_validator.rb +8 -0
  132. data/lib/validators/ipv4_validator.rb +10 -0
  133. data/lib/validators/mac_address_validator.rb +9 -0
  134. metadata +531 -0
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ DeviseTokenAuth.setup do |config|
4
+ config.cookie_enabled = true
5
+ config.cookie_attributes = {
6
+ secure: true,
7
+ httponly: true,
8
+ expires: 1.day,
9
+ same_site: "None"
10
+ # WARNING: the :domain attribute specifies to whom the cookie will be sent to. It's recommended to leave it empty, so it acts in the most restrictive way as per https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html#domain-and-path-attributes
11
+ }
12
+ # By default the authorization headers will change after each request. The
13
+ # client is responsible for keeping track of the changing tokens. Change
14
+ # this to false to prevent the Authorization header from changing after
15
+ # each request.
16
+ config.change_headers_on_each_request = false
17
+
18
+ # By default, users will need to re-authenticate after 2 weeks. This setting
19
+ # determines how long tokens will remain valid after they are issued.
20
+ # config.token_lifespan = 2.weeks
21
+
22
+ # Limiting the token_cost to just 4 in testing will increase the performance of
23
+ # your test suite dramatically. The possible cost value is within range from 4
24
+ # to 31. It is recommended to not use a value more than 10 in other environments.
25
+ config.token_cost = Rails.env.test? ? 4 : 10
26
+
27
+ # Sets the max number of concurrent devices per user, which is 10 by default.
28
+ # After this limit is reached, the oldest tokens will be removed.
29
+ # config.max_number_of_devices = 10
30
+
31
+ # Sometimes it's necessary to make several requests to the API at the same
32
+ # time. In this case, each request in the batch will need to share the same
33
+ # auth token. This setting determines how far apart the requests can be while
34
+ # still using the same auth token.
35
+ # config.batch_request_buffer_throttle = 5.seconds
36
+
37
+ # This route will be the prefix for all oauth2 redirect callbacks. For
38
+ # example, using the default '/omniauth', the github oauth2 provider will
39
+ # redirect successful authentications to '/omniauth/github/callback'
40
+ config.omniauth_prefix = "/api/auth/omniauth"
41
+
42
+ # By default sending current password is not needed for the password update.
43
+ # Uncomment to enforce current_password param to be checked before all
44
+ # attribute updates. Set it to :password if you want it to be checked only if
45
+ # password is updated.
46
+ # config.check_current_password_before_update = :attributes
47
+
48
+ # By default we will use callbacks for single omniauth.
49
+ # It depends on fields like email, provider and uid.
50
+ # config.default_callbacks = true
51
+
52
+ # Makes it possible to change the headers names
53
+ # config.headers_names = {:'access-token' => 'access-token',
54
+ # :'client' => 'client',
55
+ # :'expiry' => 'expiry',
56
+ # :'uid' => 'uid',
57
+ # :'token-type' => 'token-type' }
58
+
59
+ # By default, only Bearer Token authentication is implemented out of the box.
60
+ # If, however, you wish to integrate with legacy Devise authentication, you can
61
+ # do so by enabling this flag. NOTE: This feature is highly experimental!
62
+ # config.enable_standard_devise_support = false
63
+
64
+ # By default DeviseTokenAuth will not send confirmation email, even when including
65
+ # devise confirmable module. If you want to use devise confirmable module and
66
+ # send email, set it to true. (This is a setting for compatibility)
67
+ # config.send_confirmation_email = true
68
+
69
+ config.default_confirm_success_url = "#{ENV['FRONT_END_URL']}/auth/signin"
70
+ config.default_password_reset_url = "#{ENV['FRONT_END_URL']}/auth/reset-password"
71
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "simplecov" if ENV["COVERAGE"]
4
+
5
+ # Configure Rails Environment
6
+ ENV["RAILS_ENV"] = "test"
7
+
8
+ require_relative "../test/dummy/config/environment"
9
+ ActiveRecord::Migrator.migrations_paths = [File.expand_path("../test/dummy/db/migrate", __dir__)]
10
+ require "rails/test_help"
11
+
12
+ require "rhino/test_case"
13
+
14
+ # Filter out the backtrace from minitest while preserving the one from other libraries.
15
+ Minitest.backtrace_filter = Minitest::BacktraceFilter.new
16
+
17
+ # Load fixtures from the engine
18
+ if ActiveSupport::TestCase.respond_to?(:fixture_path=)
19
+ ActiveSupport::TestCase.fixture_path = File.expand_path("fixtures", __dir__)
20
+ ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path
21
+ ActiveSupport::TestCase.file_fixture_path = File.expand_path("fixtures/files", __dir__)
22
+ ActiveSupport::TestCase.fixtures :all
23
+ end
24
+
25
+ # Load all test helpers
26
+ # https://guides.rubyonrails.org/testing.html#eagerly-requiring-helpers
27
+ # But we are in an engine so use that root - Rails.root would point to dummy
28
+ Dir[Rhino::Engine.root.join("test", "test_helpers", "**", "*.rb")].each { |file| require file }
29
+
30
+ class ActiveSupport::TestCase
31
+ # Run tests in parallel with specified workers
32
+ parallelize(workers: :number_of_processors)
33
+
34
+ # make each process running tests a separate command for simple cov.
35
+ # in the end, they will all get merged and not overwrite each other's results
36
+ # https://github.com/simplecov-ruby/simplecov/issues/718#issuecomment-538201587
37
+
38
+ parallelize_setup do |worker|
39
+ SimpleCov.command_name "#{SimpleCov.command_name}-#{worker}" if ENV["COVERAGE"]
40
+
41
+ # https://guides.rubyonrails.org/active_storage_overview.html#discarding-files-created-during-tests
42
+ ActiveStorage::Blob.service.root = "#{ActiveStorage::Blob.service.root}-#{worker}"
43
+ end
44
+
45
+ parallelize_teardown do |_worker|
46
+ SimpleCov.result if ENV["COVERAGE"]
47
+
48
+ # https://guides.rubyonrails.org/active_storage_overview.html#cleaning-up-fixtures
49
+ FileUtils.rm_rf(ActiveStorage::Blob.services.fetch(:test).root)
50
+ end
51
+
52
+ # Add more helper methods to be used by all tests here...
53
+ include FactoryBot::Syntax::Methods
54
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rhino
4
+ class PolicyGenerator < ::Rails::Generators::NamedBase
5
+ source_root File.expand_path("templates", __dir__)
6
+
7
+ class_option :parent, type: :string, default: "Rhino::ViewerPolicy", desc: "The parent class for the policy"
8
+
9
+ check_class_collision suffix: "Policy"
10
+
11
+ hook_for :test_framework, as: :rhino_policy
12
+
13
+ def create_policy_file
14
+ template "policy.rb", File.join("app/policies", class_path, "#{file_name}_policy.rb")
15
+ end
16
+
17
+ private
18
+ def parent_class_name
19
+ # FIXME: Does this really need to be top level namespaced?
20
+ return options[:parent] if options[:parent].starts_with?("::")
21
+
22
+ "::#{options[:parent]}"
23
+ end
24
+
25
+ def parent_scope_class_name
26
+ "#{parent_class_name}::Scope"
27
+ end
28
+
29
+ def file_name
30
+ @file_name ||= super.sub(/_policy\z/i, "")
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ <% module_namespacing do -%>
4
+ class <%= class_name %>Policy < <%= parent_class_name %>
5
+ # NOTE: Be explicit about which action you allow!
6
+ # def index?
7
+ # authorize_action(false)
8
+ # end
9
+
10
+ # def show?
11
+ # authorize_action(false)
12
+ # end
13
+
14
+ # def create?
15
+ # authorize_action(false)
16
+ # end
17
+
18
+ # def update?
19
+ # authorize_action(false)
20
+ # end
21
+
22
+ # def destroy?
23
+ # authorize_action(false)
24
+ # end
25
+
26
+ # def permitted_attributes_for_create
27
+ # super
28
+ # super - [:name] # Remove the name attribute from the permitted attributes for create
29
+ # end
30
+
31
+ # def permitted_attributes_for_show
32
+ # super
33
+ # end
34
+
35
+ # def permitted_attributes_for_update
36
+ # super
37
+ # end
38
+
39
+ class Scope < <%= parent_scope_class_name %>
40
+ # NOTE: Be explicit about which records you allow access to!
41
+ # def resolve
42
+ # scope.all
43
+ # end
44
+ end
45
+ end
46
+ <% end -%>
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TestUnit
4
+ module Generators
5
+ class RhinoPolicyGenerator < ::Rails::Generators::NamedBase
6
+ source_root File.expand_path("templates", __dir__)
7
+
8
+ def create_policy_test
9
+ template "policy_test.rb", File.join("test/policies", class_path, "#{file_name}_policy_test.rb")
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class <%= class_name %>PolicyTest < Rhino::TestCase::Policy
6
+ # Testing for a policy where users can view any blog, create, update and destroy their own
7
+ # but cannot update or destroy another users blog.
8
+
9
+ # def setup
10
+ # @current_user = create :user
11
+ # @another_user = create :user
12
+
13
+ # @blog = Blog.create(title: "Test Blog", author: @current_user)
14
+ # @another_user_blog = Blog.create(title: "Other Blog", author: @another_user)
15
+ # end
16
+
17
+ # test "allows create for user" do
18
+ # assert_permit @current_user, Blog, :create
19
+ # end
20
+
21
+ # test "allows update for user" do
22
+ # assert_permit @current_user, @blog, :update
23
+ # end
24
+
25
+ # test "does not allow update for another users blog" do
26
+ # assert_not_permit @current_user, @another_user_blog, :update
27
+ # end
28
+
29
+ # test "allows destroy for user" do
30
+ # assert_permit @current_user, @blog, :destroy
31
+ # end
32
+
33
+ # test "does not allow destroy for another users blog" do
34
+ # assert_not_permit @current_user, @another_user_blog, :destroy
35
+ # end
36
+
37
+ # test "allows index for user and returns correct blogs" do
38
+ # assert_permit @current_user, Blog, :index
39
+ # assert_scope_only @current_user, Blog, [@blog, @another_user_blog]
40
+ # end
41
+
42
+ # test "allows show for user and returns correct blog" do
43
+ # assert_permit @current_user, @blog, :show
44
+ # assert_scope_only @current_user, @blog, [@blog]
45
+ # end
46
+
47
+ # test "allows show for another users blog" do
48
+ # assert_permit @current_user, @another_user_blog, :show
49
+ # assert_scope_only @current_user, [@another_user_blog]
50
+ # end
51
+
52
+ # If the user could instead not show another users blog, the following test could be used instead
53
+ ## test "does not allow show for another users blog" do
54
+ ## assert_not_permit @current_user, @another_user_blog, :show
55
+ ## assert_scope_empty @current_user, @another_user_blog
56
+ ## end
57
+ end
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rhino/version"
4
+
5
+ # https://guides.rubyonrails.org/engines.html#other-gem-dependencies
6
+ require "activeadmin"
7
+ require "acts-as-taggable-on"
8
+ require "ancestry"
9
+ require "arel-helpers"
10
+ require "countries"
11
+ require "devise"
12
+ require "devise_invitable"
13
+ require "devise_token_auth"
14
+ require "friendly_id"
15
+ require "geocoder"
16
+ require "omniauth"
17
+ require "omniauth-azure-activedirectory-v2"
18
+ require "omniauth-auth0"
19
+ require "omniauth-facebook"
20
+ require "omniauth-github"
21
+ require "omniauth-google-oauth2"
22
+ require "omniauth/rails_csrf_protection"
23
+ require "rack/cors"
24
+ require "pg_search"
25
+ require "phonelib"
26
+ require "pundit"
27
+ require "segment/analytics"
28
+
29
+ module Rhino
30
+ class Engine < ::Rails::Engine
31
+ config.before_configuration do
32
+ # When running the dummy apps through rails commands the .env file won't exist in the root
33
+ # but the DB_NAME variable will have been set by rhino_command; the rake task name will be
34
+ # set for rhino:dev:setup
35
+ if Rails.env.development? && (!File.exist?(Rails.root.join(".env")) && (!run_from_dummy? && !run_from_dev_setup? && !run_from_package?))
36
+ raise ".env file must exist in development - see README.md"
37
+ end
38
+ end
39
+
40
+ initializer "rhino.active_record_extension" do
41
+ ActiveSupport.on_load(:active_record) do
42
+ require_relative "resource/active_record_extension"
43
+ require_relative "resource/active_record_tree"
44
+ require_relative "resource/active_model_extension"
45
+
46
+ include Rhino::Resource::ActiveRecordExtension if Rhino.auto_include_active_record
47
+ end
48
+ end
49
+
50
+ initializer "rhino.active_storage_extension" do
51
+ ActiveSupport.on_load(:active_storage_attachment) do
52
+ require_relative "resource/active_storage_extension"
53
+
54
+ include Rhino::Resource::ActiveStorageExtension
55
+ end
56
+ end
57
+
58
+ # https://guides.rubyonrails.org/engines.html#overriding-models-and-controllers
59
+ # Use root instead of Rails.root to scope for this engine
60
+ initializer "rhino.overrides" do
61
+ overrides = "#{root}/app/overrides"
62
+ Rails.autoloaders.main.ignore(overrides)
63
+
64
+ config.to_prepare do
65
+ Dir.glob("#{overrides}/**/*_override.rb").each do |override|
66
+ load override
67
+ end
68
+ end
69
+ end
70
+
71
+ initializer "rhino.check_resources" do
72
+ config.after_initialize do
73
+ check_resources
74
+ end
75
+ end
76
+
77
+ initializer "rhino.resource_reloader" do
78
+ config.after_initialize do
79
+ Rails.application.reloader.to_prepare do
80
+ Rhino.resource_classes = nil
81
+ end
82
+ end
83
+ end
84
+
85
+ initializer "rhino.register_module" do
86
+ require "rhino/omniauth/strategies/azure_o_auth2"
87
+
88
+ config.after_initialize do
89
+ Rhino.registered_modules[:rhino] = {
90
+ version: Rhino::VERSION::STRING,
91
+ authOwner: Rhino.auth_owner.model_name.singular,
92
+ baseOwner: Rhino.base_owner.model_name.singular,
93
+ oauth: Rhino::OmniauthHelper.strategies_metadata,
94
+ allow_signup: Rhino.allow_signup
95
+ }
96
+ end
97
+ end
98
+
99
+ def top_level_references(resource)
100
+ # Handle things like rhino_references [{ blog_post: [:blog] }]
101
+ # Just check the top level ones for now
102
+ resource.references.flat_map { |ref| ref.is_a?(Hash) ? ref.keys : ref }
103
+ end
104
+
105
+ def unowned?(resource)
106
+ # Special case
107
+ return true if resource.ancestors.include?(Rhino::Resource::ActiveStorageExtension)
108
+
109
+ # Owners are not themselves owned
110
+ resource.auth_owner? || resource.base_owner? || resource.global_owner?
111
+ end
112
+
113
+ def check_owner_reflections
114
+ raise "#{Rhino.base_owner} must have reflection for #{Rhino.auth_owner}" if Rhino.base_to_auth.nil?
115
+
116
+ raise "#{Rhino.auth_owner} must have reflection for #{Rhino.base_owner}" if Rhino.auth_to_base.nil?
117
+ end
118
+
119
+ def check_ownership(resource)
120
+ return if unowned?(resource)
121
+
122
+ raise "#{resource} does not have rhino ownership set" unless resource.resource_owned_by.present?
123
+ end
124
+
125
+ def check_references(resource)
126
+ # Some resource types don't have reflections
127
+ top_level_reflections = resource.try(:reflections)&.keys&.map(&:to_sym) || []
128
+
129
+ # All references should have a reflection
130
+ delta = top_level_references(resource) - top_level_reflections
131
+
132
+ raise "#{resource} has references #{delta} that do not exist as associations" if delta.present?
133
+ end
134
+
135
+ def check_owner_reference(resource)
136
+ return if unowned?(resource)
137
+
138
+ # If its in the list, we're good
139
+ return if top_level_references(resource).include?(resource.resource_owned_by)
140
+
141
+ raise "#{resource} does not have a reference to its owner #{resource.resource_owned_by}"
142
+ end
143
+
144
+ def check_resources
145
+ check_owner_reflections
146
+
147
+ Rhino.resource_classes.each do |resource|
148
+ check_ownership(resource)
149
+ check_references(resource)
150
+ check_owner_reference(resource) if resource.ancestors.include?(Rhino::Resource::ActiveRecordExtension)
151
+ end
152
+ end
153
+
154
+ def self.run_from_dummy?
155
+ ENV["DB_NAME"].present?
156
+ end
157
+
158
+ def self.run_from_dev_setup?
159
+ defined?(Rake.application) && Rake.application.top_level_tasks == ["rhino:dev:setup"]
160
+ end
161
+
162
+ def self.run_from_package?
163
+ defined?(Rake.application) && Rake.application.top_level_tasks == ["package"]
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ return unless defined?(::OmniAuth::Strategies::AzureActivedirectoryV2)
4
+
5
+ module Rhino
6
+ module Omniauth
7
+ module Strategies
8
+ class AzureOAuth2 < ::OmniAuth::Strategies::AzureActivedirectoryV2
9
+ option :name, "azure_oauth2"
10
+ option :callback_path, "/api/auth/omniauth/azure_oauth2/callback"
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ ::OmniAuth::Strategies::AzureOauth2 = Rhino::Omniauth::Strategies::AzureOAuth2
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rhino
4
+ module Resource
5
+ module ActiveModelExtension
6
+ module BackingStore
7
+ module GoogleSheet
8
+ extend ActiveSupport::Concern
9
+
10
+ def backing_store_update
11
+ attr_map = sheet.list.keys.index_by(&:downcase)
12
+
13
+ # ID won't be mapped
14
+ attrs = serializable_hash(except: :id).transform_keys { |k| attr_map[k] }
15
+
16
+ sheet.list[id - 1].update(attrs)
17
+
18
+ sheet.synchronize
19
+ end
20
+
21
+ def backing_store_destroy
22
+ sheet.delete_rows(id + 1, 1)
23
+
24
+ sheet.synchronize
25
+ end
26
+
27
+ included do
28
+ thread_mattr_accessor :google_client
29
+ thread_mattr_accessor :google_sheet
30
+ thread_mattr_accessor :google_worksheet
31
+
32
+ class_attribute :sheet_id, default: nil
33
+ class_attribute :work_sheet_title, default: nil
34
+
35
+ delegate :sheet, to: :class
36
+ end
37
+
38
+ class_methods do # rubocop:todo Metrics/BlockLength
39
+ def backing_store_index
40
+ sheet.reload
41
+
42
+ idx = 0
43
+ sheet.list.map do |row|
44
+ idx += 1
45
+ row_to_instance(row, idx)
46
+ end
47
+ end
48
+
49
+ def backing_store_create(model)
50
+ attr_map = sheet.list.keys.index_by(&:downcase)
51
+
52
+ # ID won't be mapped
53
+ attrs = model.serializable_hash(except: :id).transform_keys { |k| attr_map[k] }
54
+
55
+ sheet.list.push(attrs)
56
+
57
+ sheet.synchronize
58
+ end
59
+
60
+ def backing_store_show(id)
61
+ sheet.reload
62
+
63
+ row_to_instance(sheet.list[id.to_i - 1], id)
64
+ end
65
+
66
+ def sheet
67
+ return @google_worksheet if @google_worksheet
68
+
69
+ @google_client = GoogleDrive::Session.from_service_account_key(nil)
70
+
71
+ # Pass the sheet id
72
+ @google_sheet = @google_client.spreadsheet_by_key(sheet_id)
73
+
74
+ return @google_worksheet = @google_sheet.worksheet_by_title(work_sheet_title) if work_sheet_title
75
+
76
+ @google_worksheet = @google_sheet.worksheets[0]
77
+ end
78
+
79
+ def row_to_instance(row, id)
80
+ attrs = row.to_hash
81
+ attrs = attrs.transform_keys(&:downcase).transform_keys(&:to_sym)
82
+ new(attrs.merge(id:))
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rhino
4
+ module Resource
5
+ module ActiveModelExtension
6
+ module BackingStore
7
+ extend ActiveSupport::Concern
8
+
9
+ def backing_store_update
10
+ raise NotImplementedError, "#update is not implemented for BackingStore"
11
+ end
12
+
13
+ def backing_store_destroy
14
+ raise NotImplementedError, "#destroy is not implemented for BackingStore"
15
+ end
16
+
17
+ class_methods do
18
+ def backing_store_create
19
+ raise NotImplementedError, "#create is not implemented for BackingStore"
20
+ end
21
+
22
+ def backing_store_index
23
+ raise NotImplementedError, "#index is not implemented for BackingStore"
24
+ end
25
+
26
+ def backing_store_show
27
+ raise NotImplementedError, "#show is not implemented for BackingStore"
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rhino
4
+ module Resource
5
+ module ActiveModelExtension
6
+ module Describe
7
+ extend ActiveSupport::Concern
8
+
9
+ class_methods do
10
+ def describe # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
11
+ properties = all_properties.index_with { |p| describe_property(p) }
12
+
13
+ required = properties.reject { |_p, d| d[:nullable] || d[:readOnly] }.keys
14
+ # required: [] is not valid and will be compacted away
15
+ required = nil unless required.present?
16
+
17
+ {
18
+ "x-rhino-model": {
19
+ model: model_name.singular,
20
+ modelPlural: model_name.collection,
21
+ name: model_name.name.camelize(:lower),
22
+ pluralName: model_name.name.camelize(:lower).pluralize,
23
+ readableName: model_name.human,
24
+ pluralReadableName: model_name.human.pluralize,
25
+ ownedBy: resource_owned_by,
26
+ singular: route_singular?,
27
+ path: "#{Rhino.namespace}/#{route_path}"
28
+ },
29
+ type: :object,
30
+ properties:,
31
+ required:
32
+ }.compact
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end