forest_liana 6.0.0.pre.beta.4 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/forest_liana/actions_controller.rb +12 -2
  3. data/app/controllers/forest_liana/authentication_controller.rb +2 -2
  4. data/app/serializers/forest_liana/stripe_invoice_serializer.rb +5 -5
  5. data/app/services/forest_liana/authentication.rb +0 -2
  6. data/app/services/forest_liana/authorization_getter.rb +23 -21
  7. data/app/services/forest_liana/resource_creator.rb +1 -1
  8. data/app/services/forest_liana/resource_updater.rb +3 -3
  9. data/app/services/forest_liana/schema_utils.rb +8 -3
  10. data/app/services/forest_liana/stripe_invoice_getter.rb +1 -1
  11. data/app/services/forest_liana/stripe_invoices_getter.rb +1 -1
  12. data/app/services/forest_liana/stripe_source_getter.rb +1 -1
  13. data/app/services/forest_liana/stripe_sources_getter.rb +1 -1
  14. data/config/initializers/error-messages.rb +3 -0
  15. data/config/initializers/errors.rb +21 -2
  16. data/config/routes.rb +0 -4
  17. data/lib/forest_liana/bootstrapper.rb +5 -5
  18. data/lib/forest_liana/version.rb +1 -1
  19. data/spec/requests/actions_controller_spec.rb +49 -1
  20. data/test/routing/route_test.rb +0 -12
  21. data/test/services/forest_liana/resources_getter_test.rb +3 -3
  22. metadata +113 -148
  23. data/app/controllers/forest_liana/sessions_controller.rb +0 -95
  24. data/app/serializers/forest_liana/session_serializer.rb +0 -33
  25. data/app/services/forest_liana/login_handler.rb +0 -99
  26. data/app/services/forest_liana/two_factor_registration_confirmer.rb +0 -36
  27. data/app/services/forest_liana/user_secret_creator.rb +0 -26
  28. data/spec/requests/sessions_spec.rb +0 -53
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3d299c9968621d0592da4e25c67969073237e6cf0eac69d0d898e6db6027ad14
4
- data.tar.gz: bbb59dfd951086e62df9829124fcf57ef0b71d40550597abeaff3de07450c946
3
+ metadata.gz: 407abbfa6e112f59b3e951e0b29d9660fa3845606cbaff9db24238e09bde8f08
4
+ data.tar.gz: 1778138ca8558ea3c66c5acc86171493af707948eacc61b400ebcec31966b6ee
5
5
  SHA512:
6
- metadata.gz: 9b3dc3c43d682e085dd95066648471f779bd03da28b935eb645532e04da42ab75651d679291d03118355cba35379655878fa9a3a36a2fb945fbdb1ac35099748
7
- data.tar.gz: 2bd81a3eb3aa40c9129b1afd5fc86004eb10bbe6115f422537f82ffbeec0d94be77c542f68ed5494688464f1857bee5b691187a95d86a45357ca6d3e07b7c400
6
+ metadata.gz: 6b71a26a73298e82533e6a25282e0964214360d8c4c6b7774919b4421c778eef9ab28d31a260d85a83706367a3fd85b6be4169b84f28cc3e8a45b88c79d78cbc
7
+ data.tar.gz: efcde59108053f294bb2128205d347af6261f42bcaec0df8c4336631cbb3f7af34fa8940556863421f79c57b0c1ee88336c0586f4f06360ecc42d59310954ac0
@@ -55,10 +55,20 @@ module ForestLiana
55
55
  # Apply result on fields (transform the object back to an array), preserve order.
56
56
  fields = action.fields.map do |field|
57
57
  updated_field = result[field[:field]]
58
+
58
59
  # Reset `value` when not present in `enums` (which means `enums` has changed).
59
- if updated_field[:enums].is_a?(Array) && !updated_field[:enums].include?(updated_field[:value])
60
- updated_field[:value] = nil
60
+ if updated_field[:enums].is_a?(Array)
61
+ # `value` can be an array if the type of fields is `[x]`
62
+ if updated_field[:type].is_a?(Array) && updated_field[:value].is_a?(Array) && !(updated_field[:value] - updated_field[:enums]).empty?
63
+ updated_field[:value] = nil
64
+ end
65
+
66
+ # `value` can be any other value
67
+ if !updated_field[:type].is_a?(Array) && !updated_field[:enums].include?(updated_field[:value])
68
+ updated_field[:value] = nil
69
+ end
61
70
  end
71
+
62
72
  updated_field
63
73
  end
64
74
 
@@ -86,8 +86,8 @@ module ForestLiana
86
86
  render json: response_body, status: 200
87
87
 
88
88
  rescue => error
89
- render json: { errors: [{ status: 500, detail: error.message }] },
90
- status: :internal_server_error, serializer: nil
89
+ render json: { errors: [{ status: error.error_code || 500, detail: error.message }] },
90
+ status: error.status || :internal_server_error, serializer: nil
91
91
  end
92
92
  end
93
93
 
@@ -3,20 +3,20 @@ module ForestLiana
3
3
  include JSONAPI::Serializer
4
4
 
5
5
  attribute :amount_due
6
+ attribute :amount_paid
7
+ attribute :amount_remaining
8
+ attribute :application_fee_amount
6
9
  attribute :attempt_count
7
10
  attribute :attempted
8
- attribute :closed
9
11
  attribute :currency
10
- attribute :date
11
- attribute :forgiven
12
+ attribute :due_date
12
13
  attribute :paid
13
14
  attribute :period_end
14
15
  attribute :period_start
16
+ attribute :status
15
17
  attribute :subtotal
16
18
  attribute :total
17
- attribute :application_fee
18
19
  attribute :tax
19
- attribute :tax_percent
20
20
 
21
21
  has_one :customer
22
22
 
@@ -26,9 +26,7 @@ module ForestLiana
26
26
 
27
27
  user = ForestLiana::AuthorizationGetter.authenticate(
28
28
  rendering_id,
29
- true,
30
29
  { :forest_token => access_token_instance.instance_variable_get(:@access_token) },
31
- nil,
32
30
  )
33
31
 
34
32
  return ForestLiana::Token.create_token(user, rendering_id)
@@ -1,23 +1,12 @@
1
1
  module ForestLiana
2
2
  class AuthorizationGetter
3
- def self.authenticate(rendering_id, use_google_authentication, auth_data, two_factor_registration)
3
+ def self.authenticate(rendering_id, auth_data)
4
4
  begin
5
5
  route = "/liana/v2/renderings/#{rendering_id.to_s}/authorization"
6
-
7
- if !use_google_authentication.nil?
8
- headers = { 'forest-token' => auth_data[:forest_token] }
9
- elsif !auth_data[:email].nil?
10
- headers = { 'email' => auth_data[:email], 'password' => auth_data[:password] }
11
- end
12
-
13
- query_parameters = {}
14
-
15
- unless two_factor_registration.nil?
16
- query_parameters['two-factor-registration'] = true
17
- end
6
+ headers = { 'forest-token' => auth_data[:forest_token] }
18
7
 
19
8
  response = ForestLiana::ForestApiRequester
20
- .get(route, query: query_parameters, headers: headers)
9
+ .get(route, query: {}, headers: headers)
21
10
 
22
11
  if response.code.to_i == 200
23
12
  body = JSON.parse(response.body, :symbolize_names => false)
@@ -25,15 +14,28 @@ module ForestLiana
25
14
  user['id'] = body['data']['id']
26
15
  user
27
16
  else
28
- unless use_google_authentication.nil?
29
- raise "Cannot authorize the user using this google account. Forest API returned an #{Errors::HTTPErrorHelper.format(response)}"
30
- else
31
- raise "Cannot authorize the user using this email/password. Forest API returned an #{Errors::HTTPErrorHelper.format(response)}"
32
- end
17
+ raise generate_authentication_error response
33
18
  end
34
- rescue
35
- raise ForestLiana::Errors::HTTP401Error
36
19
  end
37
20
  end
21
+
22
+ private
23
+ def self.generate_authentication_error(error)
24
+ case error[:message]
25
+ when ForestLiana::MESSAGES[:SERVER_TRANSACTION][:SECRET_AND_RENDERINGID_INCONSISTENT]
26
+ return ForestLiana::Errors::InconsistentSecretAndRenderingError.new()
27
+ when ForestLiana::MESSAGES[:SERVER_TRANSACTION][:SECRET_NOT_FOUND]
28
+ return ForestLiana::Errors::SecretNotFoundError.new()
29
+ else
30
+ end
31
+
32
+ serverError = error[:jse_cause][:response][:body][:errors][0] || nil
33
+
34
+ if !serverError.nil? && serverError[:name] == ForestLiana::MESSAGES[:SERVER_TRANSACTION][:names][:TWO_FACTOR_AUTHENTICATION_REQUIRED]
35
+ return ForestLiana::Errors::TwoFactorAuthenticationRequiredError.new()
36
+ end
37
+
38
+ return StandardError.new(error)
39
+ end
38
40
  end
39
41
  end
@@ -53,7 +53,7 @@ module ForestLiana
53
53
  end
54
54
 
55
55
  def has_strong_parameter
56
- Rails::VERSION::MAJOR > 5 || @resource.instance_method(:update_attributes!).arity == 1
56
+ Rails::VERSION::MAJOR > 5 || @resource.instance_method(:update!).arity == 1
57
57
  end
58
58
  end
59
59
  end
@@ -14,9 +14,9 @@ module ForestLiana
14
14
  @record = @resource.find(@params[:id])
15
15
 
16
16
  if has_strong_parameter
17
- @record.update_attributes(resource_params)
17
+ @record.update(resource_params)
18
18
  else
19
- @record.update_attributes(resource_params, without_protection: true)
19
+ @record.update(resource_params, without_protection: true)
20
20
  end
21
21
  rescue ActiveRecord::StatementInvalid => exception
22
22
  # NOTICE: SQL request cannot be executed properly
@@ -33,7 +33,7 @@ module ForestLiana
33
33
  end
34
34
 
35
35
  def has_strong_parameter
36
- Rails::VERSION::MAJOR > 5 || @resource.instance_method(:update_attributes!).arity == 1
36
+ Rails::VERSION::MAJOR > 5 || @resource.instance_method(:update!).arity == 1
37
37
  end
38
38
  end
39
39
  end
@@ -2,9 +2,14 @@ module ForestLiana
2
2
  class SchemaUtils
3
3
 
4
4
  def self.associations(active_record_class)
5
- active_record_class
6
- .reflect_on_all_associations
7
- .select { |association| !polymorphic?(association) && !is_active_type?(association.klass) }
5
+ active_record_class.reflect_on_all_associations.select do |association|
6
+ begin
7
+ !polymorphic?(association) && !is_active_type?(association.klass)
8
+ rescue
9
+ FOREST_LOGGER.warn "Unknown association #{association.name} on class #{active_record_class.name}"
10
+ false
11
+ end
12
+ end
8
13
  end
9
14
 
10
15
  def self.one_associations(active_record_class)
@@ -11,7 +11,7 @@ module ForestLiana
11
11
  query = {}
12
12
  @record = ::Stripe::Invoice.retrieve(@params[:invoice_id])
13
13
 
14
- @record.date = Time.at(@record.date).to_datetime
14
+ @record.due_date = Time.at(@record.due_date).to_datetime unless @record.due_date.nil?
15
15
  @record.period_start = Time.at(@record.period_start).to_datetime
16
16
  @record.period_end = Time.at(@record.period_end).to_datetime
17
17
  @record.subtotal /= 100.00
@@ -32,7 +32,7 @@ module ForestLiana
32
32
  end
33
33
 
34
34
  @records = @invoices.data.map do |d|
35
- d.date = Time.at(d.date).to_datetime
35
+ d.date = Time.at(d.created).to_datetime
36
36
  d.period_start = Time.at(d.period_start).to_datetime
37
37
  d.period_end = Time.at(d.period_end).to_datetime
38
38
  d.subtotal /= 100.00
@@ -12,7 +12,7 @@ module ForestLiana
12
12
  customer = resource[field]
13
13
 
14
14
  @record = ::Stripe::Customer
15
- .retrieve(customer)
15
+ .retrieve({ id: customer, expand: ['sources'] })
16
16
  .sources.retrieve(@params[:objectId])
17
17
 
18
18
  query = {}
@@ -32,7 +32,7 @@ module ForestLiana
32
32
 
33
33
  def fetch_bank_accounts(customer, params)
34
34
  begin
35
- @cards = ::Stripe::Customer.retrieve(customer).sources.list(params)
35
+ @cards = ::Stripe::Customer.retrieve({ id: customer, expand: ['sources'] }).sources.list(params)
36
36
  if @cards.blank?
37
37
  @records = []
38
38
  return
@@ -15,6 +15,9 @@ module ForestLiana
15
15
  INVALID_RENDERING_ID: "The parameter renderingId is not valid",
16
16
  REGISTRATION_FAILED: "The registration to the authentication API failed, response: ",
17
17
  OIDC_CONFIGURATION_RETRIEVAL_FAILED: "Failed to retrieve the provider's configuration.",
18
+ names: {
19
+ TWO_FACTOR_AUTHENTICATION_REQUIRED: 'TwoFactorAuthenticationRequiredForbiddenError',
20
+ }
18
21
  }
19
22
  }
20
23
  end
@@ -14,12 +14,13 @@ module ForestLiana
14
14
  end
15
15
 
16
16
  class ExpectedError < StandardError
17
- attr_reader :error_code, :status, :message
17
+ attr_reader :error_code, :status, :message, :name
18
18
 
19
- def initialize(error_code, status, message)
19
+ def initialize(error_code, status, message, name = nil)
20
20
  @error_code = error_code
21
21
  @status = status
22
22
  @message = message
23
+ @name = name
23
24
  end
24
25
 
25
26
  def display_error
@@ -45,6 +46,24 @@ module ForestLiana
45
46
  end
46
47
  end
47
48
 
49
+ class InconsistentSecretAndRenderingError < ExpectedError
50
+ def initialize(message=ForestLiana::MESSAGES[:SERVER_TRANSACTION][:SECRET_AND_RENDERINGID_INCONSISTENT])
51
+ super(500, :internal_server_error, message, 'InconsistentSecretAndRenderingError')
52
+ end
53
+ end
54
+
55
+ class SecretNotFoundError < ExpectedError
56
+ def initialize(message=ForestLiana::MESSAGES[:SERVER_TRANSACTION][:SECRET_NOT_FOUND])
57
+ super(500, :internal_server_error, message, 'SecretNotFoundError')
58
+ end
59
+ end
60
+
61
+ class TwoFactorAuthenticationRequiredError < ExpectedError
62
+ def initialize(message='Two factor authentication required')
63
+ super(403, :forbidden, message, 'TwoFactorAuthenticationRequiredError')
64
+ end
65
+ end
66
+
48
67
  class ExceptionHelper
49
68
  def self.recursively_print(error, margin: '', is_error: false)
50
69
  logger = is_error ?
data/config/routes.rb CHANGED
@@ -9,10 +9,6 @@ ForestLiana::Engine.routes.draw do
9
9
  get 'authentication/callback' => 'authentication#authentication_callback'
10
10
  post 'authentication/logout' => 'authentication#logout'
11
11
 
12
- # Session
13
- post 'sessions' => 'sessions#create_with_password'
14
- post 'sessions-google' => 'sessions#create_with_google'
15
-
16
12
  # Associations
17
13
  get ':collection/:id/relationships/:association_name' => 'associations#index'
18
14
  get ':collection/:id/relationships/:association_name/count' => 'associations#count'
@@ -430,19 +430,19 @@ module ForestLiana
430
430
  fields: [
431
431
  { field: :id, type: 'String', is_filterable: false },
432
432
  { field: :amount_due, type: 'Number', is_filterable: false },
433
+ { field: :amount_paid, type: 'Number', is_filterable: false },
434
+ { field: :amount_remaining, type: 'Number', is_filterable: false },
435
+ { field: :application_fee_amount, type: 'Number', is_filterable: false },
433
436
  { field: :attempt_count, type: 'Number', is_filterable: false },
434
437
  { field: :attempted, type: 'Boolean', is_filterable: false },
435
- { field: :closed, type: 'Boolean', is_filterable: false },
436
438
  { field: :currency, type: 'String', is_filterable: false },
437
- { field: :date, type: 'Date', is_filterable: false },
438
- { field: :forgiven, type: 'Boolean', is_filterable: false },
439
+ { field: :due_date, type: 'Date', is_filterable: false },
439
440
  { field: :period_start, type: 'Date', is_filterable: false },
440
441
  { field: :period_end, type: 'Date', is_filterable: false },
442
+ { field: :status, type: 'String', enums: ['draft', 'open', 'paid', 'uncollectible', 'void'], is_filterable: false },
441
443
  { field: :subtotal, type: 'Number', is_filterable: false },
442
444
  { field: :total, type: 'Number', is_filterable: false },
443
- { field: :application_fee, type: 'Number', is_filterable: false },
444
445
  { field: :tax, type: 'Number', is_filterable: false },
445
- { field: :tax_percent, type: 'Number', is_filterable: false },
446
446
  {
447
447
  field: :customer,
448
448
  type: 'String',
@@ -1,3 +1,3 @@
1
1
  module ForestLiana
2
- VERSION = "6.0.0-beta.4"
2
+ VERSION = "6.0.0"
3
3
  end
@@ -35,6 +35,11 @@ describe 'Requesting Actions routes', :type => :request do
35
35
  type: 'Enum',
36
36
  enums: %w[a b c],
37
37
  }
38
+ multiple_enum = {
39
+ field: 'multipleEnum',
40
+ type: ['Enum'],
41
+ enums: %w[a b c],
42
+ }
38
43
 
39
44
  action_definition = {
40
45
  name: 'my_action',
@@ -95,12 +100,28 @@ describe 'Requesting Actions routes', :type => :request do
95
100
  }
96
101
  }
97
102
  }
103
+
104
+ multiple_enums_action_definition = {
105
+ name: 'multiple_enums_action',
106
+ fields: [foo, multiple_enum],
107
+ hooks: {
108
+ :change => {
109
+ 'foo' => -> (context) {
110
+ fields = context[:fields]
111
+ fields['multipleEnum'][:enums] = %w[c d z]
112
+ return fields
113
+ }
114
+ }
115
+ }
116
+ }
117
+
98
118
  action = ForestLiana::Model::Action.new(action_definition)
99
119
  fail_action = ForestLiana::Model::Action.new(fail_action_definition)
100
120
  cheat_action = ForestLiana::Model::Action.new(cheat_action_definition)
101
121
  enums_action = ForestLiana::Model::Action.new(enums_action_definition)
122
+ multiple_enums_action = ForestLiana::Model::Action.new(multiple_enums_action_definition)
102
123
  island = ForestLiana.apimap.find {|collection| collection.name.to_s == ForestLiana.name_for(Island)}
103
- island.actions = [action, fail_action, cheat_action, enums_action]
124
+ island.actions = [action, fail_action, cheat_action, enums_action, multiple_enums_action]
104
125
 
105
126
  describe 'call /load' do
106
127
  params = {recordIds: [1], collectionName: 'Island'}
@@ -169,6 +190,33 @@ describe 'Requesting Actions routes', :type => :request do
169
190
  expect(JSON.parse(response.body)).to eq({'fields' => [expected_foo.stringify_keys, expected_enum.stringify_keys]})
170
191
  end
171
192
 
193
+ it 'should not reset value when every enum values are in the enums definition' do
194
+ updated_multiple_enum = multiple_enum.clone.merge({:previousValue => nil, :value => %w[c]})
195
+ p = {recordIds: [1], fields: [foo, updated_multiple_enum], collectionName: 'Island', changedField: 'foo'}
196
+ post '/forest/actions/multiple_enums_action/hooks/change', params: JSON.dump(p), headers: { 'CONTENT_TYPE' => 'application/json' }
197
+ expect(response.status).to eq(200)
198
+
199
+ expected_multiple_enum = updated_multiple_enum.clone.merge({ :enums => %w[c d z], :widgetEdit => nil, :value => %w[c]})
200
+ expected_multiple_enum.delete(:widget)
201
+ expected_foo = foo.clone.merge({ :widgetEdit => nil})
202
+ expected_foo.delete(:widget)
203
+
204
+ expect(JSON.parse(response.body)).to eq({'fields' => [expected_foo.stringify_keys, expected_multiple_enum.stringify_keys]})
205
+ end
206
+
207
+ it 'should reset value when one of the enum values is not in the enums definition' do
208
+ wrongly_updated_multiple_enum = multiple_enum.clone.merge({:previousValue => nil, :value => %w[a b]})
209
+ p = {recordIds: [1], fields: [foo, wrongly_updated_multiple_enum], collectionName: 'Island', changedField: 'foo'}
210
+ post '/forest/actions/multiple_enums_action/hooks/change', params: JSON.dump(p), headers: { 'CONTENT_TYPE' => 'application/json' }
211
+ expect(response.status).to eq(200)
212
+
213
+ expected_multiple_enum = wrongly_updated_multiple_enum.clone.merge({ :enums => %w[c d z], :widgetEdit => nil, :value => nil })
214
+ expected_multiple_enum.delete(:widget)
215
+ expected_foo = foo.clone.merge({ :widgetEdit => nil})
216
+ expected_foo.delete(:widget)
217
+
218
+ expect(JSON.parse(response.body)).to eq({'fields' => [expected_foo.stringify_keys, expected_multiple_enum.stringify_keys]})
219
+ end
172
220
  end
173
221
  end
174
222
  end
@@ -12,18 +12,6 @@ module ForestLiana
12
12
  controller: 'forest_liana/apimaps', action: 'index'
13
13
  })
14
14
 
15
- # Session
16
- assert_routing({
17
- method: 'post', path: 'sessions'
18
- }, {
19
- controller: 'forest_liana/sessions', action: 'create_with_password'
20
- })
21
- assert_routing({
22
- method: 'post', path: 'sessions-google'
23
- }, {
24
- controller: 'forest_liana/sessions', action: 'create_with_google'
25
- })
26
-
27
15
  # Associations
28
16
  assert_routing({
29
17
  method: 'get', path: ':collection/:id/relationships/:association_name'
@@ -106,7 +106,7 @@ module ForestLiana
106
106
  conditions: [{
107
107
  field: 'created_at',
108
108
  operator: 'after',
109
- value: '2015-06-18 08:00:00',
109
+ value: "#{Time.now.year - 5}-06-18 08:00:00",
110
110
  }, {
111
111
  field: 'owner:name',
112
112
  operator: 'equal',
@@ -119,8 +119,8 @@ module ForestLiana
119
119
  records = getter.records
120
120
  count = getter.count
121
121
 
122
- assert records.count == 2
123
- assert count = 2
122
+ assert records.count == 1
123
+ assert count = 1
124
124
  assert records.first.id == 4
125
125
  assert records.first.name == 'Oak'
126
126
  assert records.first.owner.name == 'Arnaud Besnier'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forest_liana
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0.pre.beta.4
4
+ version: 6.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sandro Munda
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-15 00:00:00.000000000 Z
11
+ date: 2021-02-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -122,34 +122,6 @@ dependencies:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
- - !ruby/object:Gem::Dependency
126
- name: rotp
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - ">="
130
- - !ruby/object:Gem::Version
131
- version: '0'
132
- type: :runtime
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - ">="
137
- - !ruby/object:Gem::Version
138
- version: '0'
139
- - !ruby/object:Gem::Dependency
140
- name: base32
141
- requirement: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - ">="
144
- - !ruby/object:Gem::Version
145
- version: '0'
146
- type: :runtime
147
- prerelease: false
148
- version_requirements: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - ">="
151
- - !ruby/object:Gem::Version
152
- version: '0'
153
125
  - !ruby/object:Gem::Dependency
154
126
  name: httparty
155
127
  requirement: !ruby/object:Gem::Requirement
@@ -246,7 +218,6 @@ files:
246
218
  - app/controllers/forest_liana/mixpanel_controller.rb
247
219
  - app/controllers/forest_liana/resources_controller.rb
248
220
  - app/controllers/forest_liana/router.rb
249
- - app/controllers/forest_liana/sessions_controller.rb
250
221
  - app/controllers/forest_liana/smart_actions_controller.rb
251
222
  - app/controllers/forest_liana/stats_controller.rb
252
223
  - app/controllers/forest_liana/stripe_controller.rb
@@ -267,7 +238,6 @@ files:
267
238
  - app/serializers/forest_liana/mixpanel_event_serializer.rb
268
239
  - app/serializers/forest_liana/schema_serializer.rb
269
240
  - app/serializers/forest_liana/serializer_factory.rb
270
- - app/serializers/forest_liana/session_serializer.rb
271
241
  - app/serializers/forest_liana/stat_serializer.rb
272
242
  - app/serializers/forest_liana/stripe_bank_account_serializer.rb
273
243
  - app/serializers/forest_liana/stripe_card_serializer.rb
@@ -294,7 +264,6 @@ files:
294
264
  - app/services/forest_liana/leaderboard_stat_getter.rb
295
265
  - app/services/forest_liana/line_stat_getter.rb
296
266
  - app/services/forest_liana/live_query_checker.rb
297
- - app/services/forest_liana/login_handler.rb
298
267
  - app/services/forest_liana/mixpanel_last_events_getter.rb
299
268
  - app/services/forest_liana/objective_stat_getter.rb
300
269
  - app/services/forest_liana/oidc_client_manager.rb
@@ -326,8 +295,6 @@ files:
326
295
  - app/services/forest_liana/stripe_subscription_getter.rb
327
296
  - app/services/forest_liana/stripe_subscriptions_getter.rb
328
297
  - app/services/forest_liana/token.rb
329
- - app/services/forest_liana/two_factor_registration_confirmer.rb
330
- - app/services/forest_liana/user_secret_creator.rb
331
298
  - app/services/forest_liana/utils/beta_schema_utils.rb
332
299
  - app/services/forest_liana/value_stat_getter.rb
333
300
  - app/views/layouts/forest_liana/application.html.erb
@@ -397,7 +364,6 @@ files:
397
364
  - spec/requests/actions_controller_spec.rb
398
365
  - spec/requests/authentications_spec.rb
399
366
  - spec/requests/resources_spec.rb
400
- - spec/requests/sessions_spec.rb
401
367
  - spec/services/forest_liana/apimap_sorter_spec.rb
402
368
  - spec/services/forest_liana/filters_parser_spec.rb
403
369
  - spec/services/forest_liana/ip_whitelist_checker_spec.rb
@@ -514,162 +480,161 @@ required_ruby_version: !ruby/object:Gem::Requirement
514
480
  version: '0'
515
481
  required_rubygems_version: !ruby/object:Gem::Requirement
516
482
  requirements:
517
- - - ">"
483
+ - - ">="
518
484
  - !ruby/object:Gem::Version
519
- version: 1.3.1
485
+ version: '0'
520
486
  requirements: []
521
- rubygems_version: 3.0.8
487
+ rubygems_version: 3.1.2
522
488
  signing_key:
523
489
  specification_version: 4
524
490
  summary: Official Rails Liana for Forest
525
491
  test_files:
526
- - test/fixtures/owner.yml
527
- - test/fixtures/belongs_to_field.yml
528
- - test/fixtures/has_many_through_field.yml
529
- - test/fixtures/tree.yml
530
- - test/fixtures/has_one_field.yml
531
- - test/fixtures/reference.yml
532
- - test/fixtures/serialize_field.yml
533
- - test/fixtures/has_many_field.yml
534
- - test/fixtures/string_field.yml
535
- - test/services/forest_liana/resources_getter_test.rb
536
- - test/services/forest_liana/scope_validator_test.rb
537
- - test/services/forest_liana/schema_adapter_test.rb
538
- - test/services/forest_liana/has_many_getter_test.rb
539
- - test/services/forest_liana/value_stat_getter_test.rb
540
- - test/services/forest_liana/resource_updater_test.rb
541
- - test/services/forest_liana/pie_stat_getter_test.rb
542
- - test/test_helper.rb
543
- - test/dummy/README.rdoc
492
+ - test/dummy/app/assets/stylesheets/application.css
493
+ - test/dummy/app/assets/config/manifest.js
494
+ - test/dummy/app/assets/javascripts/application.js
544
495
  - test/dummy/app/views/layouts/application.html.erb
496
+ - test/dummy/app/controllers/application_controller.rb
497
+ - test/dummy/app/helpers/application_helper.rb
498
+ - test/dummy/app/models/reference.rb
499
+ - test/dummy/app/models/float_field.rb
545
500
  - test/dummy/app/models/has_many_field.rb
501
+ - test/dummy/app/models/polymorphic_field.rb
502
+ - test/dummy/app/models/decimal_field.rb
546
503
  - test/dummy/app/models/string_field.rb
547
- - test/dummy/app/models/has_and_belongs_to_many_field.rb
504
+ - test/dummy/app/models/serialize_field.rb
505
+ - test/dummy/app/models/tree.rb
506
+ - test/dummy/app/models/boolean_field.rb
507
+ - test/dummy/app/models/date_field.rb
548
508
  - test/dummy/app/models/has_many_through_field.rb
549
- - test/dummy/app/models/has_many_class_name_field.rb
509
+ - test/dummy/app/models/owner.rb
550
510
  - test/dummy/app/models/has_one_field.rb
551
- - test/dummy/app/models/date_field.rb
552
- - test/dummy/app/models/boolean_field.rb
553
- - test/dummy/app/models/float_field.rb
554
- - test/dummy/app/models/reference.rb
511
+ - test/dummy/app/models/has_many_class_name_field.rb
555
512
  - test/dummy/app/models/belongs_to_class_name_field.rb
556
- - test/dummy/app/models/polymorphic_field.rb
557
- - test/dummy/app/models/tree.rb
558
- - test/dummy/app/models/decimal_field.rb
559
- - test/dummy/app/models/serialize_field.rb
560
513
  - test/dummy/app/models/integer_field.rb
561
514
  - test/dummy/app/models/belongs_to_field.rb
562
- - test/dummy/app/models/owner.rb
563
- - test/dummy/app/helpers/application_helper.rb
564
- - test/dummy/app/assets/javascripts/application.js
565
- - test/dummy/app/assets/stylesheets/application.css
566
- - test/dummy/app/assets/config/manifest.js
567
- - test/dummy/app/controllers/application_controller.rb
568
- - test/dummy/Rakefile
569
- - test/dummy/bin/rake
570
- - test/dummy/bin/bundle
571
- - test/dummy/bin/rails
572
- - test/dummy/bin/setup
573
- - test/dummy/public/404.html
574
- - test/dummy/public/500.html
575
- - test/dummy/public/422.html
576
- - test/dummy/public/favicon.ico
577
- - test/dummy/config.ru
578
- - test/dummy/db/migrate/20181111162121_create_references_table.rb
579
- - test/dummy/db/migrate/20150608131430_create_integer_field.rb
580
- - test/dummy/db/migrate/20160628173505_add_timestamps.rb
581
- - test/dummy/db/migrate/20160627172810_create_owner.rb
582
- - test/dummy/db/migrate/20150814081918_create_has_many_through_field.rb
515
+ - test/dummy/app/models/has_and_belongs_to_many_field.rb
516
+ - test/dummy/db/migrate/20160627172951_create_tree.rb
583
517
  - test/dummy/db/migrate/20150608132159_create_boolean_field.rb
584
- - test/dummy/db/migrate/20150608131603_create_decimal_field.rb
585
- - test/dummy/db/migrate/20150616150629_create_polymorphic_field.rb
586
518
  - test/dummy/db/migrate/20150612112520_create_has_and_belongs_to_many_field.rb
587
- - test/dummy/db/migrate/20150608131610_create_float_field.rb
588
- - test/dummy/db/migrate/20150608133038_create_belongs_to_field.rb
589
- - test/dummy/db/migrate/20150608150016_create_has_many_field.rb
519
+ - test/dummy/db/migrate/20160627172810_create_owner.rb
590
520
  - test/dummy/db/migrate/20150609114636_create_belongs_to_class_name_field.rb
521
+ - test/dummy/db/migrate/20150608130516_create_date_field.rb
522
+ - test/dummy/db/migrate/20160628173505_add_timestamps.rb
523
+ - test/dummy/db/migrate/20150608131610_create_float_field.rb
591
524
  - test/dummy/db/migrate/20170614141921_create_serialize_field.rb
525
+ - test/dummy/db/migrate/20150608150016_create_has_many_field.rb
526
+ - test/dummy/db/migrate/20150608131430_create_integer_field.rb
527
+ - test/dummy/db/migrate/20150608133038_create_belongs_to_field.rb
528
+ - test/dummy/db/migrate/20150616150629_create_polymorphic_field.rb
592
529
  - test/dummy/db/migrate/20150608132621_create_string_field.rb
593
- - test/dummy/db/migrate/20150608133044_create_has_one_field.rb
530
+ - test/dummy/db/migrate/20181111162121_create_references_table.rb
594
531
  - test/dummy/db/migrate/20150623115554_create_has_many_class_name_field.rb
595
- - test/dummy/db/migrate/20150608130516_create_date_field.rb
596
- - test/dummy/db/migrate/20160627172951_create_tree.rb
532
+ - test/dummy/db/migrate/20150608133044_create_has_one_field.rb
533
+ - test/dummy/db/migrate/20150814081918_create_has_many_through_field.rb
534
+ - test/dummy/db/migrate/20150608131603_create_decimal_field.rb
597
535
  - test/dummy/db/schema.rb
598
- - test/dummy/config/routes.rb
599
- - test/dummy/config/environment.rb
536
+ - test/dummy/config.ru
537
+ - test/dummy/README.rdoc
600
538
  - test/dummy/config/secrets.yml
601
- - test/dummy/config/application.rb
602
- - test/dummy/config/boot.rb
603
- - test/dummy/config/environments/test.rb
604
- - test/dummy/config/environments/development.rb
605
- - test/dummy/config/environments/production.rb
606
539
  - test/dummy/config/locales/en.yml
540
+ - test/dummy/config/routes.rb
541
+ - test/dummy/config/database.yml
607
542
  - test/dummy/config/initializers/wrap_parameters.rb
608
- - test/dummy/config/initializers/mime_types.rb
609
- - test/dummy/config/initializers/filter_parameter_logging.rb
610
543
  - test/dummy/config/initializers/assets.rb
544
+ - test/dummy/config/initializers/filter_parameter_logging.rb
611
545
  - test/dummy/config/initializers/session_store.rb
546
+ - test/dummy/config/initializers/backtrace_silencers.rb
612
547
  - test/dummy/config/initializers/inflections.rb
613
548
  - test/dummy/config/initializers/cookies_serializer.rb
614
- - test/dummy/config/initializers/backtrace_silencers.rb
615
- - test/dummy/config/database.yml
549
+ - test/dummy/config/initializers/mime_types.rb
550
+ - test/dummy/config/application.rb
551
+ - test/dummy/config/environments/development.rb
552
+ - test/dummy/config/environments/production.rb
553
+ - test/dummy/config/environments/test.rb
554
+ - test/dummy/config/environment.rb
555
+ - test/dummy/config/boot.rb
556
+ - test/dummy/public/500.html
557
+ - test/dummy/public/favicon.ico
558
+ - test/dummy/public/404.html
559
+ - test/dummy/public/422.html
560
+ - test/dummy/Rakefile
561
+ - test/dummy/bin/rake
562
+ - test/dummy/bin/setup
563
+ - test/dummy/bin/rails
564
+ - test/dummy/bin/bundle
616
565
  - test/forest_liana_test.rb
566
+ - test/services/forest_liana/schema_adapter_test.rb
567
+ - test/services/forest_liana/pie_stat_getter_test.rb
568
+ - test/services/forest_liana/value_stat_getter_test.rb
569
+ - test/services/forest_liana/has_many_getter_test.rb
570
+ - test/services/forest_liana/resource_updater_test.rb
571
+ - test/services/forest_liana/scope_validator_test.rb
572
+ - test/services/forest_liana/resources_getter_test.rb
573
+ - test/test_helper.rb
617
574
  - test/routing/route_test.rb
618
- - spec/services/forest_liana/permissions_formatter_spec.rb
619
- - spec/services/forest_liana/permissions_checker_acl_disabled_spec.rb
620
- - spec/services/forest_liana/ip_whitelist_checker_spec.rb
621
- - spec/services/forest_liana/permissions_checker_acl_enabled_spec.rb
622
- - spec/services/forest_liana/schema_adapter_spec.rb
623
- - spec/services/forest_liana/apimap_sorter_spec.rb
624
- - spec/services/forest_liana/filters_parser_spec.rb
625
- - spec/services/forest_liana/permissions_getter_spec.rb
626
- - spec/spec_helper.rb
627
- - spec/requests/actions_controller_spec.rb
628
- - spec/requests/sessions_spec.rb
629
- - spec/requests/authentications_spec.rb
630
- - spec/requests/resources_spec.rb
631
- - spec/dummy/README.rdoc
632
- - spec/dummy/app/views/layouts/application.html.erb
633
- - spec/dummy/app/models/island.rb
634
- - spec/dummy/app/models/user.rb
635
- - spec/dummy/app/models/tree.rb
636
- - spec/dummy/app/helpers/application_helper.rb
637
- - spec/dummy/app/assets/javascripts/application.js
575
+ - test/fixtures/reference.yml
576
+ - test/fixtures/has_many_through_field.yml
577
+ - test/fixtures/has_many_field.yml
578
+ - test/fixtures/tree.yml
579
+ - test/fixtures/belongs_to_field.yml
580
+ - test/fixtures/owner.yml
581
+ - test/fixtures/serialize_field.yml
582
+ - test/fixtures/string_field.yml
583
+ - test/fixtures/has_one_field.yml
584
+ - spec/rails_helper.rb
638
585
  - spec/dummy/app/assets/stylesheets/application.css
639
586
  - spec/dummy/app/assets/config/manifest.js
587
+ - spec/dummy/app/assets/javascripts/application.js
588
+ - spec/dummy/app/views/layouts/application.html.erb
640
589
  - spec/dummy/app/config/routes.rb
641
590
  - spec/dummy/app/controllers/application_controller.rb
642
- - spec/dummy/Rakefile
643
- - spec/dummy/bin/rake
644
- - spec/dummy/bin/bundle
645
- - spec/dummy/bin/rails
646
- - spec/dummy/bin/setup
647
- - spec/dummy/config.ru
648
- - spec/dummy/db/migrate/20190226174951_create_tree.rb
649
- - spec/dummy/db/migrate/20190716135241_add_type_to_user.rb
591
+ - spec/dummy/app/helpers/application_helper.rb
592
+ - spec/dummy/app/models/user.rb
593
+ - spec/dummy/app/models/island.rb
594
+ - spec/dummy/app/models/tree.rb
650
595
  - spec/dummy/db/migrate/20190226173051_create_isle.rb
651
596
  - spec/dummy/db/migrate/20190716130830_add_age_to_tree.rb
597
+ - spec/dummy/db/migrate/20190226174951_create_tree.rb
598
+ - spec/dummy/db/migrate/20190716135241_add_type_to_user.rb
652
599
  - spec/dummy/db/migrate/20190226172951_create_user.rb
653
600
  - spec/dummy/db/schema.rb
654
- - spec/dummy/config/routes.rb
655
- - spec/dummy/config/environment.rb
601
+ - spec/dummy/config.ru
602
+ - spec/dummy/README.rdoc
656
603
  - spec/dummy/config/secrets.yml
657
- - spec/dummy/config/application.rb
658
- - spec/dummy/config/boot.rb
659
- - spec/dummy/config/environments/test.rb
660
- - spec/dummy/config/environments/development.rb
661
- - spec/dummy/config/environments/production.rb
604
+ - spec/dummy/config/routes.rb
605
+ - spec/dummy/config/database.yml
662
606
  - spec/dummy/config/initializers/wrap_parameters.rb
607
+ - spec/dummy/config/initializers/assets.rb
663
608
  - spec/dummy/config/initializers/forest_liana.rb
664
- - spec/dummy/config/initializers/mime_types.rb
665
609
  - spec/dummy/config/initializers/filter_parameter_logging.rb
666
- - spec/dummy/config/initializers/assets.rb
667
610
  - spec/dummy/config/initializers/session_store.rb
611
+ - spec/dummy/config/initializers/backtrace_silencers.rb
668
612
  - spec/dummy/config/initializers/inflections.rb
669
613
  - spec/dummy/config/initializers/cookies_serializer.rb
670
- - spec/dummy/config/initializers/backtrace_silencers.rb
671
- - spec/dummy/config/database.yml
614
+ - spec/dummy/config/initializers/mime_types.rb
615
+ - spec/dummy/config/application.rb
616
+ - spec/dummy/config/environments/development.rb
617
+ - spec/dummy/config/environments/production.rb
618
+ - spec/dummy/config/environments/test.rb
619
+ - spec/dummy/config/environment.rb
620
+ - spec/dummy/config/boot.rb
621
+ - spec/dummy/Rakefile
622
+ - spec/dummy/bin/rake
623
+ - spec/dummy/bin/setup
624
+ - spec/dummy/bin/rails
625
+ - spec/dummy/bin/bundle
626
+ - spec/services/forest_liana/schema_adapter_spec.rb
627
+ - spec/services/forest_liana/permissions_checker_acl_disabled_spec.rb
628
+ - spec/services/forest_liana/permissions_checker_acl_enabled_spec.rb
629
+ - spec/services/forest_liana/permissions_formatter_spec.rb
630
+ - spec/services/forest_liana/apimap_sorter_spec.rb
631
+ - spec/services/forest_liana/permissions_getter_spec.rb
632
+ - spec/services/forest_liana/ip_whitelist_checker_spec.rb
633
+ - spec/services/forest_liana/filters_parser_spec.rb
634
+ - spec/helpers/forest_liana/query_helper_spec.rb
672
635
  - spec/helpers/forest_liana/schema_helper_spec.rb
673
636
  - spec/helpers/forest_liana/is_same_data_structure_helper_spec.rb
674
- - spec/helpers/forest_liana/query_helper_spec.rb
675
- - spec/rails_helper.rb
637
+ - spec/requests/actions_controller_spec.rb
638
+ - spec/requests/authentications_spec.rb
639
+ - spec/requests/resources_spec.rb
640
+ - spec/spec_helper.rb
@@ -1,95 +0,0 @@
1
- module ForestLiana
2
- class SessionsController < ForestLiana::BaseController
3
- def create_with_password
4
- @error_message = nil
5
- rendering_id = params['renderingId']
6
- project_id = params['projectId']
7
- email = params['email']
8
- password = params['password']
9
- two_factor_token = params['token']
10
- two_factor_registration = params['twoFactorRegistration']
11
-
12
- process_login(
13
- use_google_authentication: false,
14
- rendering_id: rendering_id,
15
- project_id: project_id,
16
- auth_data: { email: email, password: password },
17
- two_factor_registration: two_factor_registration,
18
- two_factor_token: two_factor_token,
19
- )
20
- end
21
-
22
- def create_with_google
23
- @error_message = nil
24
-
25
- forest_token = params['forestToken']
26
- rendering_id = params['renderingId']
27
- project_id = params['projectId']
28
- two_factor_token = params['token']
29
- two_factor_registration = params['twoFactorRegistration']
30
-
31
- process_login(
32
- use_google_authentication: true,
33
- rendering_id: rendering_id,
34
- project_id: project_id,
35
- auth_data: { forest_token: forest_token },
36
- two_factor_registration: two_factor_registration,
37
- two_factor_token: two_factor_token,
38
- )
39
- end
40
-
41
- private
42
-
43
- def process_login(
44
- use_google_authentication:,
45
- rendering_id:,
46
- project_id:,
47
- auth_data:,
48
- two_factor_registration:,
49
- two_factor_token:
50
- )
51
- begin
52
- if two_factor_registration && two_factor_token.nil?
53
- raise ForestLiana::Errors::HTTP401Error
54
- end
55
-
56
- # NOTICE: The IP Whitelist is retrieved on any request if it was not retrieved yet, or when
57
- # an IP is rejected, to ensure the IP is still rejected (meaning the configuration
58
- # on the projects has not changed). To handle the last case, which is rejecting an
59
- # IP which was not initaliy rejected, we need periodically refresh the whitelist.
60
- # This is done here on the login of any user.
61
- ForestLiana::IpWhitelist.retrieve
62
-
63
- reponse_data = ForestLiana::LoginHandler.new(
64
- rendering_id,
65
- auth_data,
66
- use_google_authentication,
67
- two_factor_registration,
68
- project_id,
69
- two_factor_token
70
- ).perform
71
-
72
- rescue ForestLiana::Errors::ExpectedError => error
73
- error.display_error
74
- error_data = JSONAPI::Serializer.serialize_errors([{
75
- status: error.error_code,
76
- detail: error.message
77
- }])
78
- render(serializer: nil, json: error_data, status: error.status)
79
- rescue StandardError => error
80
- FOREST_LOGGER.error error
81
- FOREST_LOGGER.error error.backtrace.join("\n")
82
-
83
- render(serializer: nil, json: nil, status: :internal_server_error)
84
- else
85
- # NOTICE: Set a cookie to ensure secure authentication using export feature.
86
- # NOTICE: The token is empty at first authentication step if the 2FA option is active.
87
- if reponse_data[:token]
88
- response.set_cookie("forest_session_token", { value: reponse_data[:token], expires: (ForestLiana::Token.expiration_in_days) })
89
- end
90
-
91
- render(json: reponse_data, serializer: nil)
92
- end
93
- end
94
- end
95
- end
@@ -1,33 +0,0 @@
1
- module ForestLiana
2
- class SessionSerializer
3
- include JSONAPI::Serializer
4
-
5
- attribute :first_name
6
- attribute :last_name
7
- attribute :email
8
-
9
- def type
10
- 'users'
11
- end
12
-
13
- def format_name(attribute_name)
14
- attribute_name.to_s
15
- end
16
-
17
- def unformat_name(attribute_name)
18
- attribute_name.to_s.underscore
19
- end
20
-
21
- def self_link
22
- nil
23
- end
24
-
25
- def relationship_self_link(attribute_name)
26
- nil
27
- end
28
-
29
- def relationship_related_link(attribute_name)
30
- nil
31
- end
32
- end
33
- end
@@ -1,99 +0,0 @@
1
- require 'rotp'
2
-
3
- module ForestLiana
4
- class LoginHandler
5
- def initialize(
6
- rendering_id,
7
- auth_data,
8
- use_google_authentication,
9
- two_factor_registration,
10
- project_id,
11
- two_factor_token
12
- )
13
- @rendering_id = rendering_id
14
- @auth_data = auth_data
15
- @use_google_authentication = use_google_authentication
16
- @two_factor_registration = two_factor_registration
17
- @project_id = project_id
18
- @two_factor_token = two_factor_token
19
- end
20
-
21
- def perform
22
- user = ForestLiana::AuthorizationGetter.authenticate(
23
- @rendering_id,
24
- @use_google_authentication,
25
- @auth_data,
26
- @two_factor_registration
27
- )
28
-
29
- if user['two_factor_authentication_enabled']
30
- if !@two_factor_token.nil?
31
- if is_two_factor_token_valid(user, @two_factor_token)
32
- ForestLiana::TwoFactorRegistrationConfirmer
33
- .new(@project_id, @use_google_authentication, @auth_data)
34
- .perform
35
-
36
- return { 'token' => create_token(user, @rendering_id) }
37
- else
38
- raise ForestLiana::Errors::HTTP401Error.new('Your token is invalid, please try again.')
39
- end
40
- else
41
- return get_two_factor_response(user)
42
- end
43
- end
44
-
45
- return { token: create_token(user, @rendering_id) }
46
- end
47
-
48
- private
49
-
50
- def check_two_factor_secret_salt
51
- if ENV['FOREST_2FA_SECRET_SALT'].nil?
52
- FOREST_LOGGER.error 'Cannot use the two factor authentication because the environment variable "FOREST_2FA_SECRET_SALT" is not set.'
53
- FOREST_LOGGER.error 'You can generate it using this command: `$ openssl rand -hex 10`'
54
-
55
- raise Errors::HTTP401Error.new('Invalid 2FA configuration, please ask more information to your admin')
56
- end
57
-
58
- if ENV['FOREST_2FA_SECRET_SALT'].length != 20
59
- FOREST_LOGGER.error 'The FOREST_2FA_SECRET_SALT environment variable must be 20 characters long.'
60
- FOREST_LOGGER.error 'You can generate it using this command: `$ openssl rand -hex 10`'
61
-
62
- raise ForestLiana::Errors::HTTP401Error.new('Invalid 2FA configuration, please ask more information to your admin')
63
- end
64
- end
65
-
66
- def get_two_factor_response(user)
67
- check_two_factor_secret_salt
68
-
69
- return { 'twoFactorAuthenticationEnabled' => true } if user['two_factor_authentication_active']
70
-
71
- two_factor_authentication_secret = user['two_factor_authentication_secret']
72
- user_secret = ForestLiana::UserSecretCreator
73
- .new(two_factor_authentication_secret, ENV['FOREST_2FA_SECRET_SALT'])
74
- .perform
75
-
76
- {
77
- twoFactorAuthenticationEnabled: true,
78
- userSecret: user_secret,
79
- }
80
- end
81
-
82
- def is_two_factor_token_valid(user, two_factor_token)
83
- check_two_factor_secret_salt
84
-
85
- two_factor_authentication_secret = user['two_factor_authentication_secret']
86
-
87
- user_secret = ForestLiana::UserSecretCreator
88
- .new(two_factor_authentication_secret, ENV['FOREST_2FA_SECRET_SALT'])
89
- .perform
90
-
91
- totp = ROTP::TOTP.new(user_secret)
92
- totp.verify(two_factor_token)
93
- end
94
-
95
- def create_token(user, rendering_id)
96
- ForestLiana::Token.create_token(user, rendering_id)
97
- end
98
- end
99
- end
@@ -1,36 +0,0 @@
1
- module ForestLiana
2
- class TwoFactorRegistrationConfirmer
3
- def initialize(
4
- project_id,
5
- use_google_authentication,
6
- auth_data
7
- )
8
- @project_id = project_id
9
- @use_google_authentication = use_google_authentication
10
- @auth_data = auth_data
11
- end
12
-
13
- def perform
14
- begin
15
- body_data = { 'useGoogleAuthentication' => @use_google_authentication }
16
-
17
- if @use_google_authentication
18
- body_data['forestToken'] = @auth_data[:forest_token]
19
- else
20
- body_data['email'] = @auth_data[:email]
21
- end
22
-
23
- response = ForestLiana::ForestApiRequester.post(
24
- "/liana/v2/projects/#{@project_id}/two-factor-registration-confirm",
25
- body: body_data,
26
- )
27
-
28
- unless response.is_a?(Net::HTTPOK)
29
- raise "Cannot retrieve the data from the Forest server. Forest API returned an #{ForestLiana::Errors::HTTPErrorHelper.format(response)}"
30
- end
31
- rescue
32
- raise ForestLiana::Errors::HTTP401Error
33
- end
34
- end
35
- end
36
- end
@@ -1,26 +0,0 @@
1
- require 'base32'
2
-
3
- module ForestLiana
4
- # NOTICE: This service combines the 2FA secret stored on the forest server to the local secret
5
- # salt. This guarantees that only the owner of the server and the concerned end user can
6
- # know the final key.
7
- # This is done by using a bitwise exclusive or operation, which guarantees the key to stay
8
- # unique, so it is impossible for two users to have the same key.
9
- class UserSecretCreator
10
- def initialize(two_factor_authentication_secret, two_factor_secret_salt)
11
- @two_factor_authentication_secret = two_factor_authentication_secret
12
- @two_factor_secret_salt = two_factor_secret_salt
13
- end
14
-
15
- def perform
16
- hash = (@two_factor_authentication_secret.to_i(16) ^ @two_factor_secret_salt.to_i(16)).to_s(16)
17
- bin_hash = hex_to_bin(hash)
18
-
19
- Base32.encode(bin_hash).tr('=', '')
20
- end
21
-
22
- def hex_to_bin(hex_string)
23
- hex_string.scan(/../).map { |x| x.hex.chr }.join
24
- end
25
- end
26
- end
@@ -1,53 +0,0 @@
1
- require 'rails_helper'
2
- require 'openid_connect'
3
- require 'json'
4
-
5
- RSpec.describe "Authentications", type: :request do
6
- before() do
7
- allow(ForestLiana::IpWhitelist).to receive(:retrieve) { true }
8
- allow(ForestLiana::IpWhitelist).to receive(:is_ip_whitelist_retrieved) { true }
9
- allow(ForestLiana::IpWhitelist).to receive(:is_ip_valid) { true }
10
-
11
- body = '{"data":{"id":"654","type":"users","attributes":{"email":"user@email.com","first_name":"FirstName","last_name":"LastName","teams":["Operations"]}},"relationships":{"renderings":{"data":[{"id":1,"type":"renderings"}]}}}'
12
- allow(ForestLiana::ForestApiRequester).to receive(:get).with(
13
- "/liana/v2/renderings/42/authorization", { :headers => { "forest-token" => "google-access-token" }, :query=> {} }
14
- ).and_return(
15
- instance_double(HTTParty::Response, :body => body, :code => 200)
16
- )
17
- end
18
-
19
- after() do
20
- Rails.cache.delete(URI.join(ForestLiana.application_url, ForestLiana::Engine.routes.url_helpers.authentication_callback_path).to_s)
21
- end
22
-
23
- describe "POST /forest/sessions-google" do
24
- before() do
25
- post ForestLiana::Engine.routes.url_helpers.sessions_google_path, params: '{ "renderingId": "42", "forestToken": "google-access-token" }', headers: {
26
- 'Accept' => 'application/json',
27
- 'Content-Type' => 'application/json',
28
- }
29
- end
30
-
31
- it "should respond with a 200 code" do
32
- expect(response).to have_http_status(200)
33
- end
34
-
35
- it "should return a valid authentication token" do
36
- response_body = JSON.parse(response.body, :symbolize_names => true)
37
- expect(response_body).to have_key(:token)
38
-
39
- token = response_body[:token]
40
- decoded = JWT.decode(token, ForestLiana.auth_secret, true, { algorithm: 'HS256' })[0]
41
-
42
- expected_token_data = {
43
- "id" => '654',
44
- "email" => 'user@email.com',
45
- "first_name" => 'FirstName',
46
- "last_name" => 'LastName',
47
- "rendering_id" => "42",
48
- "team" => 'Operations'
49
- }
50
- expect(decoded).to include(expected_token_data);
51
- end
52
- end
53
- end