cnfs-cognito 0.0.1.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +24 -0
- data/app/controllers/chown_requests_controller.rb +4 -0
- data/app/controllers/chown_results_controller.rb +4 -0
- data/app/controllers/cognito/application_controller.rb +6 -0
- data/app/controllers/endpoints_controller.rb +4 -0
- data/app/controllers/identifiers_controller.rb +4 -0
- data/app/controllers/login_controller.rb +45 -0
- data/app/controllers/metabase_cards_controller.rb +4 -0
- data/app/controllers/metabase_token_controller.rb +46 -0
- data/app/controllers/pools_controller.rb +21 -0
- data/app/controllers/users_controller.rb +4 -0
- data/app/models/chown_request.rb +13 -0
- data/app/models/chown_result.rb +14 -0
- data/app/models/cognito/application_record.rb +7 -0
- data/app/models/endpoint.rb +6 -0
- data/app/models/identifier.rb +5 -0
- data/app/models/metabase_card.rb +6 -0
- data/app/models/platform_event_consumer.rb +8 -0
- data/app/models/pool.rb +6 -0
- data/app/models/tenant.rb +5 -0
- data/app/models/user.rb +50 -0
- data/app/models/user_pool.rb +6 -0
- data/app/operations/chown_request_process.rb +27 -0
- data/app/operations/pool_create.rb +65 -0
- data/app/operations/segments/age.rb +65 -0
- data/app/operations/segments/birthday.rb +49 -0
- data/app/operations/segments/except_ids.rb +28 -0
- data/app/operations/segments/gender.rb +35 -0
- data/app/operations/segments_apply.rb +40 -0
- data/app/policies/chown_request_policy.rb +4 -0
- data/app/policies/chown_result_policy.rb +4 -0
- data/app/policies/cognito/application_policy.rb +6 -0
- data/app/policies/endpoint_policy.rb +4 -0
- data/app/policies/identifier_policy.rb +4 -0
- data/app/policies/metabase_card_policy.rb +4 -0
- data/app/policies/pool_policy.rb +4 -0
- data/app/policies/tenant_policy.rb +5 -0
- data/app/policies/user_policy.rb +20 -0
- data/app/resources/chown_request_resource.rb +5 -0
- data/app/resources/chown_result_resource.rb +8 -0
- data/app/resources/cognito/application_resource.rb +7 -0
- data/app/resources/endpoint_resource.rb +49 -0
- data/app/resources/identifier_resource.rb +6 -0
- data/app/resources/metabase_card_resource.rb +5 -0
- data/app/resources/pool_resource.rb +30 -0
- data/app/resources/user_resource.rb +27 -0
- data/app/services/metabase/config.rb +16 -0
- data/app/services/metabase/token_generator.rb +36 -0
- data/config/environment.rb +0 -0
- data/config/routes.rb +14 -0
- data/config/sidekiq.yml +3 -0
- data/config/spring.rb +3 -0
- data/db/migrate/20190310013542_create_endpoints.rb +13 -0
- data/db/migrate/20190317072927_create_pools.rb +12 -0
- data/db/migrate/20190317073055_create_users.rb +18 -0
- data/db/migrate/20190317073139_create_identifiers.rb +14 -0
- data/db/migrate/20190317073328_create_user_pools.rb +12 -0
- data/db/migrate/20191105100356_create_chown_requests.rb +9 -0
- data/db/migrate/20191106023315_create_chown_results.rb +12 -0
- data/db/migrate/20191126011832_create_metabase_cards.rb +11 -0
- data/db/migrate/20191129055605_add_birthday_to_user.rb +5 -0
- data/db/migrate/20191205023159_add_gender_to_user.rb +5 -0
- data/db/migrate/20191210050613_add_system_generated_flag_to_pools.rb +5 -0
- data/db/seeds/development/data.seeds.rb +0 -0
- data/db/seeds/development/tenants.seeds.rb +7 -0
- data/db/seeds/development/users.seeds.rb +29 -0
- data/lib/ros/cognito.rb +16 -0
- data/lib/ros/cognito/engine.rb +53 -0
- data/lib/ros/cognito/version.rb +7 -0
- data/lib/tasks/ros/cognito_tasks.rake +16 -0
- metadata +162 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e2380c60fb607f73af8375a2c5d68f28ce9760e423bfdc52143931342696c29d
|
4
|
+
data.tar.gz: 1af8136e3a110ec001a98437257acdb3be0a2cadda59befa99f95992ccf6f609
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ee571566b474f16df473fe00029d019a29e30b0da691524199aec6c84db9b50b29c460402636838c7e59415bde774827adce192810c253c5c4aa887b5a6d715e
|
7
|
+
data.tar.gz: e1375e02d3f5f77b01eac33b6ecc89c34df931c9bc504dda3fe117dbe4ca5b3e9189f42b842031f5940d888c18cf1879ca36fe6379aa8ec844666e0081658564
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2019 Robert Roach
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Ros::Cognito
|
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 'ros-cognito'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
```bash
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
```bash
|
21
|
+
$ gem install ros-cognito
|
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,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'bundler/setup'
|
5
|
+
rescue LoadError
|
6
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rdoc/task'
|
10
|
+
|
11
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
12
|
+
rdoc.rdoc_dir = 'rdoc'
|
13
|
+
rdoc.title = 'Ros::Cognito'
|
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('spec/dummy/Rakefile', __dir__)
|
20
|
+
load 'rails/tasks/engine.rake'
|
21
|
+
|
22
|
+
load 'rails/tasks/statistics.rake'
|
23
|
+
|
24
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class LoginController < ApplicationController
|
4
|
+
# TODO: Move payload to LoginController Spec
|
5
|
+
# {
|
6
|
+
# "data": {
|
7
|
+
# "type": "login",
|
8
|
+
# "attributes": {
|
9
|
+
# "primary_identifier": "f92a8503-7b33-42b0-939d-5f948d5422ac"
|
10
|
+
# }
|
11
|
+
# }
|
12
|
+
# }
|
13
|
+
|
14
|
+
def create
|
15
|
+
# Get the user from the payload
|
16
|
+
json = request.body.read
|
17
|
+
# TODO: use controller concern jsonapi_params
|
18
|
+
pi = JSON.parse(json)['data']['attributes']['primary_identifier']
|
19
|
+
|
20
|
+
unless (user = User.find_by(primary_identifier: pi))
|
21
|
+
# TODO: Create a controller concern method and use that to render
|
22
|
+
# It should take the status (401), code (:unauthorized) and the title as params
|
23
|
+
# title param if nil defaults to code.to_s.capitalize
|
24
|
+
render(status: :unauthorized, json: { errors: [{ status: '401', code: :unauthorized, title: 'Unauthorized' }] })
|
25
|
+
return
|
26
|
+
end
|
27
|
+
|
28
|
+
# Add the 'sub_cognito' claim to the JWT and set the header
|
29
|
+
current_jwt.add_claims('sub_cognito' => user.to_urn)
|
30
|
+
current_jwt.add_claims('act_cognito' => user)
|
31
|
+
|
32
|
+
# Render some body back to the client
|
33
|
+
# TODO: Use controller concern serialize_resource to render
|
34
|
+
render(status: :ok, json: { 'data': [{
|
35
|
+
'type': 'login',
|
36
|
+
'id': user.id,
|
37
|
+
'attributes': {
|
38
|
+
'jwt': current_jwt.encode
|
39
|
+
},
|
40
|
+
'links': {
|
41
|
+
'self': 'http://example.com/things/1'
|
42
|
+
}
|
43
|
+
}] })
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MetabaseTokenController < Cognito::ApplicationController
|
4
|
+
DEFAULT_TYPE = 'question'
|
5
|
+
|
6
|
+
before_action :identify_resource, only: :show
|
7
|
+
|
8
|
+
def show
|
9
|
+
tokenizer = Metabase::TokenGenerator.new(payload: payload, expiry: params[:expiry])
|
10
|
+
if tokenizer.valid?
|
11
|
+
render json: { token: tokenizer.token }, root: :data
|
12
|
+
else
|
13
|
+
render json: { errors: tokenizer.errors.messages }, status: :unprocessable_entity
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def payload
|
20
|
+
type = params.delete(:type) || DEFAULT_TYPE
|
21
|
+
|
22
|
+
{
|
23
|
+
resource: { type => params[:id].to_i },
|
24
|
+
params: payload_params
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def payload_params
|
29
|
+
options = { tenant: Apartment::Tenant.current }
|
30
|
+
return options if params[:params].blank?
|
31
|
+
|
32
|
+
options.merge(params[:params].to_unsafe_h.deep_symbolize_keys)
|
33
|
+
end
|
34
|
+
|
35
|
+
def identify_resource
|
36
|
+
return params if /^\d+$/.match?(params[:id])
|
37
|
+
|
38
|
+
card = MetabaseCard.find_by(identifier: params[:id])
|
39
|
+
if card.nil?
|
40
|
+
render json: { errors: 'Card not found' }, status: :not_found
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
params[:id] = card.card_id
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class PoolsController < Cognito::ApplicationController
|
4
|
+
def create
|
5
|
+
res = PoolCreate.call(params: create_params, user: context[:user])
|
6
|
+
if res.success?
|
7
|
+
render json: json_resource(resource_class: PoolResource, record: res.model, context: context), status: :created
|
8
|
+
else
|
9
|
+
resource = ApplicationResource.new(res, nil)
|
10
|
+
handle_exceptions JSONAPI::Exceptions::ValidationErrors.new(resource)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def create_params
|
17
|
+
# TODO: Think of the proper pattern for this
|
18
|
+
creatable_fields = PoolResource.creatable_fields(context) + [:base_pool_id, { segments: {} }]
|
19
|
+
jsonapi_params.permit(creatable_fields)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ChownRequest < Cognito::ApplicationRecord
|
4
|
+
has_many :chown_results
|
5
|
+
|
6
|
+
after_commit :create_chown_results, on: :create
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def create_chown_results
|
11
|
+
ChownRequestProcess.call(id: id, from_ids: from_ids, to_id: to_id)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ChownResult < Cognito::ApplicationRecord
|
4
|
+
belongs_to :chown_request
|
5
|
+
|
6
|
+
after_commit :spawn_chown_jobs, on: :create
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def spawn_chown_jobs
|
11
|
+
Ros::ChownJob.set(queue: "#{service_name}_default")
|
12
|
+
.perform_later(from_id: from_id, to_id: to_id, chown_result_id: id)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class PlatformEventConsumer
|
4
|
+
# Includes cross service event consumer functionality, e.g. 'process_document'
|
5
|
+
# include Ros::PlatformEventConsumerConcern
|
6
|
+
|
7
|
+
# Here include event consumer functionality specific to this service
|
8
|
+
end
|
data/app/models/pool.rb
ADDED
data/app/models/user.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class User < Cognito::ApplicationRecord
|
4
|
+
attribute :anonymous, :boolean, default: false
|
5
|
+
|
6
|
+
enum gender: { male: 'm', female: 'f', other: 'o' }
|
7
|
+
|
8
|
+
has_many :user_pools
|
9
|
+
has_many :pools, through: :user_pools
|
10
|
+
|
11
|
+
scope :birth_day, ->(date) { where("TO_CHAR(birthday, 'DD-MM') = TO_CHAR(DATE(?), 'DD-MM')", date) }
|
12
|
+
scope :birth_month, ->(date) { where("TO_CHAR(birthday, 'MM') = TO_CHAR(DATE(?), 'MM')", date) }
|
13
|
+
|
14
|
+
def self.reset
|
15
|
+
UserPool.delete_all
|
16
|
+
Pool.delete_all
|
17
|
+
User.delete_all
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.file_fingerprint_attributes
|
21
|
+
column_names + %i[pool_name]
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.load_document(file_name, column_map = nil, create = false)
|
25
|
+
processed_count = 0
|
26
|
+
column_map ||= default_headers
|
27
|
+
column_map = column_map.invert.symbolize_keys.invert
|
28
|
+
CSV.foreach(file_name, headers: true, header_converters: ->(name) { column_map[name] }) do |row|
|
29
|
+
if create
|
30
|
+
pool = Pool.find_or_create_by(name: row[:pool_name])
|
31
|
+
# row[:phone_number] = "+#{row[:phone_number]}"
|
32
|
+
row = row.to_h.except(:pool_name)
|
33
|
+
user = User.find_or_create_by(primary_identifier: row[:primary_identifier])
|
34
|
+
user.update(row.slice(:title, :last_name, :phone_number, :birthday, :gender))
|
35
|
+
processed_count += 1
|
36
|
+
pool.users << user
|
37
|
+
else
|
38
|
+
Rails.logger.debug "title: #{row[:title]} phone_number: #{row[:phone_number]} " \
|
39
|
+
"last_name: #{row[:last_name]} id: #{row[:primary_identifier]} pool: #{row[:pool_name]}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
processed_count
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.default_headers
|
46
|
+
{ 'Salutation' => :title, 'Last Name' => :last_name, 'Mobile' => :phone_number,
|
47
|
+
'Unique Number' => :primary_identifier, 'Campaign Code' => :pool_name,
|
48
|
+
'Birthday' => :birthday, 'Gender' => :gender }
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ChownRequestProcess < Ros::ActivityBase
|
4
|
+
# - {id} ChownRequest id
|
5
|
+
# - {from_ids} list of user ids to merge
|
6
|
+
# - {to_id} Final user id to receive all the data
|
7
|
+
|
8
|
+
# TODO: Needs improvement
|
9
|
+
# - Ensure that user id is confirmed while all the other users are not
|
10
|
+
# confirmed
|
11
|
+
# - Which permissions should this require?
|
12
|
+
# - For now, requesting user (identified via token), has to match the
|
13
|
+
# id passed in the params
|
14
|
+
|
15
|
+
step :create_chown_results
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def create_chown_results(_ctx, id:, from_ids:, to_id:, **)
|
20
|
+
from_ids.each do |from_id|
|
21
|
+
%w[game instant-outcome voucher-service outcome].each do |service|
|
22
|
+
ChownResult.create(chown_request_id: id, service_name: service,
|
23
|
+
from_id: from_id, to_id: to_id, status: 'pending')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class PoolCreate < Ros::ActivityBase
|
4
|
+
step :check_permission
|
5
|
+
failed :not_permitted, Output(:success) => End(:failure)
|
6
|
+
step :should_be_segmented?, Output(:failure) => Id(:create_regular_pool), Output(:success) => Id(:find_base_pool)
|
7
|
+
step :create_regular_pool, Output(:success) => End(:success), Output(:failure) => End(:failure)
|
8
|
+
|
9
|
+
step :find_base_pool
|
10
|
+
failed :base_pool_not_found, Output(:success) => End(:failure)
|
11
|
+
step :apply_segment
|
12
|
+
failed :fail_to_apply_segment, Output(:success) => End(:failure)
|
13
|
+
step :create_pool
|
14
|
+
failed :pool_not_created, Output(:success) => End(:failure)
|
15
|
+
step :add_users_to_pool
|
16
|
+
|
17
|
+
def check_permission(_ctx, user:, **)
|
18
|
+
PoolPolicy.new(user, Pool.new).create?
|
19
|
+
end
|
20
|
+
|
21
|
+
def not_permitted(_ctx, errors:, **)
|
22
|
+
errors.add(:user, 'not permitted to create a pool')
|
23
|
+
end
|
24
|
+
|
25
|
+
def should_be_segmented?(_ctx, params:, **)
|
26
|
+
params.key?(:base_pool_id) && params.key?(:segments)
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_regular_pool(ctx, params:, **)
|
30
|
+
ctx[:model] = Pool.create(params)
|
31
|
+
end
|
32
|
+
|
33
|
+
def find_base_pool(ctx, params:, **)
|
34
|
+
ctx[:base_pool] = Pool.find(params[:base_pool_id])
|
35
|
+
end
|
36
|
+
|
37
|
+
def base_pool_not_found(_ctx, errors:, params:, **)
|
38
|
+
errors.add(:base_pool, "Can't find base pool with id: #{params[:base_pool_id]}")
|
39
|
+
end
|
40
|
+
|
41
|
+
def apply_segment(ctx, base_pool:, params:, **)
|
42
|
+
segment_result = SegmentsApply.call(users: base_pool.users, segments: params[:segments])
|
43
|
+
return false unless segment_result.success?
|
44
|
+
|
45
|
+
ctx[:users] = segment_result.model
|
46
|
+
end
|
47
|
+
|
48
|
+
def fail_to_apply_segment(_ctx, errors:, **)
|
49
|
+
errors.add(:users, 'Failed to apply segment')
|
50
|
+
end
|
51
|
+
|
52
|
+
def create_pool(ctx, **)
|
53
|
+
ctx[:model] = Pool.create(name: "temporary_pool_#{SecureRandom.hex}", system_generated: true)
|
54
|
+
end
|
55
|
+
|
56
|
+
def pool_not_created(_ctx, errors:, **)
|
57
|
+
errors.add(:pool, "Can't create temporary pool")
|
58
|
+
end
|
59
|
+
|
60
|
+
def add_users_to_pool(_ctx, model:, users:, **)
|
61
|
+
return true if users.empty?
|
62
|
+
|
63
|
+
model.users << users
|
64
|
+
end
|
65
|
+
end
|