ros-core 0.1.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.
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../migrations'
4
+ require_relative 'console'
5
+
6
+ module Ros
7
+ module Core
8
+ class Engine < ::Rails::Engine
9
+ config.generators.api_only = true
10
+ config.generators do |g|
11
+ g.test_framework :rspec, fixture: true
12
+ g.fixture_replacement :factory_bot, dir: 'spec/factories'
13
+ end
14
+
15
+ # Adds this gem's db/migrations path to the enclosing application's migraations_path array
16
+ # https://github.com/rails/rails/issues/22261
17
+ initializer :append_migrations do |app|
18
+ config.paths['db/migrate'].expanded.each do |expanded_path|
19
+ app.config.paths['db/migrate'] << expanded_path
20
+ ActiveRecord::Migrator.migrations_paths << expanded_path
21
+ end
22
+ end
23
+
24
+ initializer :jsonapi_authorization do |app|
25
+ JSONAPI.configure do |config|
26
+ config.default_processor_klass = JSONAPI::Authorization::AuthorizingProcessor
27
+ config.exception_class_whitelist = [Pundit::NotAuthorizedError]
28
+ end
29
+ end
30
+
31
+ # NOTE: ENV vars indicate hierarchy with two underscores '__'
32
+ # export PLATFORM__CREDENTIALS__JWT_ENCRYPTION_KEY='test'
33
+ initializer :platform_settings do |app|
34
+ settings_path = root.join('config/settings')
35
+ # NOTE: Sources are prepended in reverse order, meaning the first prepend is loaded last
36
+ Settings.prepend_source!({ credentials: Rails.application.credentials.config })
37
+ Settings.prepend_source!("#{settings_path}.yml")
38
+ Settings.reload!
39
+ end
40
+
41
+ initializer :platform_services_connections do |app|
42
+ connection_type = Settings.dig(:services, :connection, :type)
43
+ client_config = Settings.dig(:services, :connection, connection_type).to_h
44
+ Ros::Platform::Client.configure(client_config.merge(connection_type: connection_type))
45
+ end
46
+
47
+ initializer :load_middleware do |app|
48
+ Warden::Strategies.add(:api_token, Ros::ApiTokenStrategy)
49
+ app.config.middleware.use Warden::Manager do |manager|
50
+ manager.default_strategies :api_token
51
+ manager.failure_app = Ros::FailureApp
52
+ end
53
+ app.config.middleware.use Ros::TenantMiddleware
54
+ end
55
+
56
+ initializer :documentation do |app|
57
+ require 'open_api'
58
+ OpenApi::Config.class_eval do
59
+ # Part 1: configs of this gem
60
+ self.file_output_path = 'public/open_api'
61
+
62
+ # Part 2: config (DSL) for generating OpenApi info
63
+ open_api :doc_name, base_doc_classes: [ApplicationDoc]
64
+ info version: '1.0.0', title: 'Platform APIs'#, description: ..
65
+ # server 'http://localhost:3000', desc: 'Internal staging server for testing'
66
+ bearer_auth :Authorization
67
+ end
68
+ end
69
+
70
+ # Configure any error reporting services if their credential has been set
71
+ # For now, only sentry.io is supported
72
+ initializer :error_reporting_services do |app|
73
+ # export PLATFORM__CREDENTIALS__SENTRY_DSN=url
74
+ if Settings.credentials.sentry_dsn
75
+ require 'sentry-raven'
76
+ Raven.configure do |config|
77
+ config.dsn = Settings.credentials.sentry_dsn
78
+ end
79
+ end
80
+ end
81
+
82
+ # Add console methods from ./console.rb
83
+ config.after_initialize do
84
+ TOPLEVEL_BINDING.eval('self').extend Ros::Console::Methods
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,5 @@
1
+ module Ros
2
+ module Core
3
+ VERSION = '0.1.0'
4
+ end
5
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apartment/elevators/generic'
4
+
5
+ module Ros
6
+ class TenantMiddleware < Apartment::Elevators::Generic
7
+ attr_accessor :auth_string, :auth_type, :token, :access_key_id
8
+
9
+ # Returns the schema_name for Apartment to switch to for this request
10
+ def parse_tenant_name(request)
11
+ @auth_string = request.env['HTTP_AUTHORIZATION']
12
+ return 'public' unless auth_string.present?
13
+ @auth_type, @token = auth_string.split(' ')
14
+ @auth_type.downcase!
15
+ Rails.logger.info("Invalid auth type #{auth_type}") and return 'public' unless auth_type.in? %w(basic bearer)
16
+ Rails.logger.info('Invalid token') and return 'public' if token.nil?
17
+ schema_name = send("tenant_name_from_#{auth_type}")
18
+ Rails.logger.info('Invalid credentials') if schema_name.eql?('public')
19
+ schema_name
20
+ end
21
+
22
+ def tenant_name_from_basic
23
+ return 'public' unless @access_key_id = token.split(':').first
24
+ credential.try(:schema_name) || 'public'
25
+ end
26
+
27
+ def credential
28
+ # TODO: Credential.authorization must be an instance variable
29
+ Ros::Sdk::Credential.authorization = auth_string
30
+ Ros::IAM::Credential.where(access_key_id: access_key_id).first
31
+ # rescue JsonApiClient::Errors::ServerError => e
32
+ # NOTE: Swallow the auth error and return nil which causes tenant to be 'public'
33
+ rescue JsonApiClient::Errors::NotAuthorized => e
34
+ end
35
+
36
+ def tenant_name_from_bearer
37
+ return 'public' unless account_id = urn.try(:account_id)
38
+ Tenant.account_id_to_schema(account_id)
39
+ end
40
+
41
+ def urn; Urn.from_jwt(token) end
42
+
43
+ # elsif Settings.service.auth_type
44
+ # Ros::Sdk::Credential.authorization = "#{Settings.service.auth_type} #{token}"
45
+ # Receiving request from another service
46
+ end
47
+ end
48
+
49
+ =begin
50
+ module Ros
51
+ class SomeTenantFromJWT < Apartment::Elevators::Generic
52
+ def some_other_thing(request)
53
+ # TODO: This only applies to IAM service so code goes there
54
+ # Then only service agnostic code goes here
55
+ return Tenant.public_schema if request.path.eql?('/roots/sign_in')
56
+ if request.path.eql?('/users/sign_in')
57
+ params = JSON.parse request.body.read
58
+ request.body.rewind
59
+ return Tenant.schema_name_from(account_id: params['account_id'])
60
+ end
61
+ # This code is for all services including IAM except for the endpoints above
62
+ token = nil
63
+ if request.env['HTTP_AUTHORIZATION']
64
+ encryption_key = Rails.application.credentials.dig(:jwt, :encryption_key) || ENV['DEVISE_JWT_SECRET_KEY'] || 'test1234'
65
+ # token = JWT.decode(request.env['HTTP_AUTHORIZATION'].split.last, encryption_key, 'HS256')
66
+ token = JWT.decode(request.env['HTTP_AUTHORIZATION'].split.last, 'test1234', 'HS256')
67
+ end
68
+ return unless token
69
+ urn_text = token[0]['urn']
70
+ urn = Ros::Urn.new(urn_text)
71
+ # TODO: Add the user's auth stuff here?
72
+ # NOTE: It seems that the devise-jwt should take it from here and checks that the token is valid
73
+ # All we are doing here is selecting the correct tenant based on the values in the token
74
+ # Another question is: how is the current_user set
75
+ # binding.pry
76
+ Tenant.schema_name_from(account_id: urn.account_id)
77
+
78
+ # return
79
+ # return 'public' if request.path.starts_with?('/core')
80
+ # return 'public' if request.path.starts_with?('/tenants')
81
+ # return 'public' if request.path.starts_with?('/policies')
82
+ # if request.path.eql?('/users/sign_in')
83
+ # body = request.body.read
84
+ # request.body.rewind
85
+ # # rescue JSON::ParserError => e
86
+ # binding.pry
87
+ # if access_key_id = JSON.parse(body)['access_key_id']
88
+ # aki = AccessKeyId.find_by(identifier: access_key_id).tenant.schema_name
89
+ # return aki
90
+ # end
91
+ # else
92
+ # return 'test2'
93
+ # # rb = JSON.parse request.body.read
94
+ # # request.body.rewind
95
+ # # return rb['tenant']
96
+ # end
97
+ # Tenant.public_schema_endpoints.each do |path|
98
+ # return Tenant.public_schema if request.path.eql?(path)
99
+ # end
100
+ # token = JWT.decode(request.env['HTTP_AUTHORIZATION'].split.last, 'test1234', 'HS256')
101
+ # token.first['tenant']
102
+ # end
103
+ # Tenant.set_request_store(request.env)
104
+ # RequestStore.store[:tenant_request].schema_name
105
+ end
106
+ end
107
+ end
108
+ =end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :ros do
4
+ namespace :db do
5
+ # desc 'Remove all SQLite3 database files'
6
+ # task :rm do
7
+ # FileUtils.rm Dir.glob('db/*.sqlite3')
8
+ # FileUtils.rm_rf('spec/dummy/db')
9
+ # FileUtils.mkdir_p('spec/dummy/db')
10
+ # FileUtils.touch('spec/dummy/db/seeds.rb')
11
+ # end
12
+
13
+ desc 'Clean a database (removes all existing data in all schemas)'
14
+ task clean: [:environment] do
15
+ DatabaseCleaner.strategy = :truncation, { cache_tables: false }
16
+ (Tenant.all.pluck(:schema_name) << 'public').each do |schema_name|
17
+ Apartment::Tenant.switch!(schema_name)
18
+ DatabaseCleaner.clean
19
+ end
20
+ Rake::Task['app:db:environment:set'].invoke
21
+ end
22
+
23
+ namespace :clean do
24
+ desc 'Clean a database and seed it'
25
+ task seed: ['app:ros:db:clean'] do
26
+ Rake::Task['app:db:seed'].invoke
27
+ end
28
+ end
29
+
30
+ desc 'Reset a database (drop, create and run migrations)'
31
+ task reset: ['app:db:drop', 'app:db:create'] do
32
+ Rake::Task['app:db:migrate'].invoke
33
+ end
34
+
35
+ namespace :reset do
36
+ desc 'Reset a database and seed it'
37
+ task seed: ['app:ros:db:reset'] do
38
+ Rake::Task['app:db:seed'].invoke
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :ros_core do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,313 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ros-core
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Robert Roach
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-03-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 6.0.0.beta1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 6.0.0.beta1
27
+ - !ruby/object:Gem::Dependency
28
+ name: jsonapi-resources
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.9.5
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 0.9.5
41
+ - !ruby/object:Gem::Dependency
42
+ name: jsonapi-authorization
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 1.0.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: warden
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.2.8
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 1.2.8
69
+ - !ruby/object:Gem::Dependency
70
+ name: jwt
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: 2.1.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 2.1.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry-rails
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 0.3.9
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 0.3.9
97
+ - !ruby/object:Gem::Dependency
98
+ name: sneakers
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '='
102
+ - !ruby/object:Gem::Version
103
+ version: 2.11.0
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '='
109
+ - !ruby/object:Gem::Version
110
+ version: 2.11.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: apartment
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '='
116
+ - !ruby/object:Gem::Version
117
+ version: 2.2.0
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '='
123
+ - !ruby/object:Gem::Version
124
+ version: 2.2.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: grpc
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '='
130
+ - !ruby/object:Gem::Version
131
+ version: 1.18.0
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '='
137
+ - !ruby/object:Gem::Version
138
+ version: 1.18.0
139
+ - !ruby/object:Gem::Dependency
140
+ name: prometheus_exporter
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '='
144
+ - !ruby/object:Gem::Version
145
+ version: 0.4.5
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '='
151
+ - !ruby/object:Gem::Version
152
+ version: 0.4.5
153
+ - !ruby/object:Gem::Dependency
154
+ name: sidekiq
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - '='
158
+ - !ruby/object:Gem::Version
159
+ version: 5.2.5
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - '='
165
+ - !ruby/object:Gem::Version
166
+ version: 5.2.5
167
+ - !ruby/object:Gem::Dependency
168
+ name: seedbank
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - '='
172
+ - !ruby/object:Gem::Version
173
+ version: 0.5.0
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - '='
179
+ - !ruby/object:Gem::Version
180
+ version: 0.5.0
181
+ - !ruby/object:Gem::Dependency
182
+ name: sentry-raven
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - '='
186
+ - !ruby/object:Gem::Version
187
+ version: 2.9.0
188
+ type: :runtime
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - '='
193
+ - !ruby/object:Gem::Version
194
+ version: 2.9.0
195
+ - !ruby/object:Gem::Dependency
196
+ name: attr_encrypted
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: 3.1.0
202
+ type: :runtime
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: 3.1.0
209
+ - !ruby/object:Gem::Dependency
210
+ name: zero-rails_openapi
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - '='
214
+ - !ruby/object:Gem::Version
215
+ version: 2.1.0
216
+ type: :runtime
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - '='
221
+ - !ruby/object:Gem::Version
222
+ version: 2.1.0
223
+ - !ruby/object:Gem::Dependency
224
+ name: config
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - '='
228
+ - !ruby/object:Gem::Version
229
+ version: 1.7.1
230
+ type: :runtime
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - '='
235
+ - !ruby/object:Gem::Version
236
+ version: 1.7.1
237
+ - !ruby/object:Gem::Dependency
238
+ name: sqlite3
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - "~>"
242
+ - !ruby/object:Gem::Version
243
+ version: '1.3'
244
+ type: :development
245
+ prerelease: false
246
+ version_requirements: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - "~>"
249
+ - !ruby/object:Gem::Version
250
+ version: '1.3'
251
+ description: Base controller, model, resource and policy classes; authentication with
252
+ JWT, per request tenant selection, exception reporting
253
+ email:
254
+ - rjayroach@gmail.com
255
+ executables: []
256
+ extensions: []
257
+ extra_rdoc_files: []
258
+ files:
259
+ - MIT-LICENSE
260
+ - README.md
261
+ - Rakefile
262
+ - app/controllers/ros/application_controller.rb
263
+ - app/controllers/tenants_controller.rb
264
+ - app/docs/application_doc.rb
265
+ - app/docs/tenants_doc.rb
266
+ - app/models/concerns/api_belongs_to.rb
267
+ - app/models/concerns/ros/tenant_concern.rb
268
+ - app/models/ros/application_record.rb
269
+ - app/policies/ros/application_policy.rb
270
+ - app/policies/tenant_policy.rb
271
+ - app/resources/ros/application_resource.rb
272
+ - app/resources/tenant_resource.rb
273
+ - config/environment.rb
274
+ - config/initializers/apartment.rb
275
+ - config/initializers/config.rb
276
+ - config/initializers/jsonapi_resources.rb
277
+ - config/routes.rb
278
+ - config/settings.yml
279
+ - lib/generators/endpoint/USAGE
280
+ - lib/generators/endpoint/endpoint_generator.rb
281
+ - lib/migrations.rb
282
+ - lib/ros/api_token_strategy.rb
283
+ - lib/ros/core.rb
284
+ - lib/ros/core/console.rb
285
+ - lib/ros/core/engine.rb
286
+ - lib/ros/core/version.rb
287
+ - lib/ros/tenant_middleware.rb
288
+ - lib/tasks/db.rake
289
+ - lib/tasks/ros/core_tasks.rake
290
+ homepage: https://github.com/rails-on-services
291
+ licenses:
292
+ - MIT
293
+ metadata: {}
294
+ post_install_message:
295
+ rdoc_options: []
296
+ require_paths:
297
+ - lib
298
+ required_ruby_version: !ruby/object:Gem::Requirement
299
+ requirements:
300
+ - - ">="
301
+ - !ruby/object:Gem::Version
302
+ version: '0'
303
+ required_rubygems_version: !ruby/object:Gem::Requirement
304
+ requirements:
305
+ - - ">="
306
+ - !ruby/object:Gem::Version
307
+ version: '0'
308
+ requirements: []
309
+ rubygems_version: 3.0.2
310
+ signing_key:
311
+ specification_version: 4
312
+ summary: Provides common support services to Rails on Services based Projects
313
+ test_files: []