forest_liana 6.0.0.pre.beta.4 → 6.0.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.
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