forest_liana 2.12.0 → 2.13.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.
- checksums.yaml +4 -4
- data/app/controllers/forest_liana/base_controller.rb +30 -0
- data/app/controllers/forest_liana/sessions_controller.rb +6 -1
- data/app/services/forest_liana/ip_whitelist.rb +40 -0
- data/app/services/forest_liana/ip_whitelist_checker.rb +71 -0
- data/config/initializers/errors.rb +7 -1
- data/lib/forest_liana/version.rb +1 -1
- metadata +17 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90cfcbc6a189d0f813ab9be3b691b651147080b1
|
4
|
+
data.tar.gz: 20a7fb7fe5095658f63cd4e83f926959f90d45c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33199e1098b84e64b5a90fc289b4f4aa67e40d77296a55887d77f8cd15332eac37eac91ec27df47679d97978366047b6ae3a05ad394d74dce26251c00e53071e
|
7
|
+
data.tar.gz: 59668615c97545bd736bff03f24273fd08e37ee89c51d0b3af2ea331776579c5f2deda5686158f58de8652f7f0e320f284dc43eedc6828d9889b6b205f6eba39
|
@@ -2,5 +2,35 @@ module ForestLiana
|
|
2
2
|
class BaseController < ::ActionController::Base
|
3
3
|
skip_before_action :verify_authenticity_token, raise: false
|
4
4
|
wrap_parameters false
|
5
|
+
before_action :reject_unauthorized_ip
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def reject_unauthorized_ip
|
10
|
+
begin
|
11
|
+
ip = request.remote_ip
|
12
|
+
|
13
|
+
if !IpWhitelist.is_ip_whitelist_retrieved || !IpWhitelist.is_ip_valid(ip)
|
14
|
+
unless IpWhitelist.retrieve
|
15
|
+
raise Errors::HTTP403Error.new("IP whitelist not retrieved")
|
16
|
+
end
|
17
|
+
|
18
|
+
unless IpWhitelist.is_ip_valid(ip)
|
19
|
+
raise Errors::HTTP403Error.new("IP address rejected (#{ip})")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
rescue Errors::ExpectedError => exception
|
23
|
+
exception.display_error
|
24
|
+
error_data = JSONAPI::Serializer.serialize_errors([{
|
25
|
+
status: exception.error_code,
|
26
|
+
detail: exception.message
|
27
|
+
}])
|
28
|
+
render(serializer: nil, json: error_data, status: exception.status)
|
29
|
+
rescue => exception
|
30
|
+
FOREST_LOGGER.error(exception)
|
31
|
+
FOREST_LOGGER.error(exception.backtrace.join("\n"))
|
32
|
+
render(serializer: nil, json: nil, status: :internal_server_error)
|
33
|
+
end
|
34
|
+
end
|
5
35
|
end
|
6
36
|
end
|
@@ -53,7 +53,12 @@ module ForestLiana
|
|
53
53
|
raise Errors::HTTP401Error
|
54
54
|
end
|
55
55
|
|
56
|
-
#
|
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
|
+
IpWhitelist.retrieve
|
57
62
|
|
58
63
|
reponse_data = LoginHandler.new(
|
59
64
|
rendering_id,
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module ForestLiana
|
2
|
+
class IpWhitelist
|
3
|
+
@@use_ip_whitelist = true
|
4
|
+
@@ip_whitelist_rules = nil
|
5
|
+
|
6
|
+
def self.retrieve
|
7
|
+
begin
|
8
|
+
response = ForestApiRequester.get('/liana/v1/ip-whitelist-rules')
|
9
|
+
|
10
|
+
if response.is_a?(Net::HTTPOK)
|
11
|
+
body = JSON.parse(response.body)
|
12
|
+
ip_whitelist_data = body['data']['attributes']
|
13
|
+
|
14
|
+
@@use_ip_whitelist = ip_whitelist_data['use_ip_whitelist']
|
15
|
+
@@ip_whitelist_rules = ip_whitelist_data['rules']
|
16
|
+
true
|
17
|
+
else
|
18
|
+
raise "Cannot retrieve the data from the Forest server. Forest API returned an #{Errors::HTTPErrorHelper.format(response)}"
|
19
|
+
end
|
20
|
+
rescue => exception
|
21
|
+
FOREST_LOGGER.error 'Cannot retrieve the IP Whitelist from the Forest server.'
|
22
|
+
FOREST_LOGGER.error 'Which was caused by:'
|
23
|
+
Errors::ExceptionHelper.recursively_print(exception, margin: ' ', is_error: true)
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.is_ip_whitelist_retrieved
|
29
|
+
!@@use_ip_whitelist || !@@ip_whitelist_rules.nil?
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.is_ip_valid(ip)
|
33
|
+
if @@use_ip_whitelist
|
34
|
+
return IpWhitelistChecker.is_ip_matches_any_rule(ip, @@ip_whitelist_rules)
|
35
|
+
end
|
36
|
+
|
37
|
+
true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'ipaddress'
|
2
|
+
|
3
|
+
module ForestLiana
|
4
|
+
class IpWhitelistChecker
|
5
|
+
module RuleType
|
6
|
+
IP = 0
|
7
|
+
RANGE = 1
|
8
|
+
SUBNET = 2
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.is_ip_matches_any_rule(ip, rules)
|
12
|
+
rules.any? { |rule| IpWhitelistChecker.is_ip_matches_rule(ip, rule) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.is_ip_matches_rule(ip, rule)
|
16
|
+
if rule['type'] == RuleType::IP
|
17
|
+
return IpWhitelistChecker.is_ip_match_ip(ip, rule['ip'])
|
18
|
+
elsif rule['type'] == RuleType::RANGE
|
19
|
+
return IpWhitelistChecker.is_ip_match_range(ip, rule)
|
20
|
+
elsif rule['type'] == RuleType::SUBNET
|
21
|
+
return IpWhitelistChecker.is_ip_match_subnet(ip, rule['range'])
|
22
|
+
end
|
23
|
+
|
24
|
+
raise 'Invalid rule type'
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.ip_version(ip)
|
28
|
+
(IPAddress ip).is_a?(IPAddress::IPv4) ? :ip_v4 : :ip_v6
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.is_same_ip_version(ip1, ip2)
|
32
|
+
ip1_version = IpWhitelistChecker.ip_version(ip1)
|
33
|
+
ip2_version = IpWhitelistChecker.ip_version(ip2)
|
34
|
+
|
35
|
+
ip1_version == ip2_version
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.is_both_loopback(ip1, ip2)
|
39
|
+
IPAddress(ip1).loopback? && IPAddress(ip2).loopback?
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.is_ip_match_ip(ip1, ip2)
|
43
|
+
if !IpWhitelistChecker.is_same_ip_version(ip1, ip2)
|
44
|
+
return IpWhitelistChecker.is_both_loopback(ip1, ip2)
|
45
|
+
end
|
46
|
+
|
47
|
+
if IPAddress(ip1) == IPAddress(ip2)
|
48
|
+
true
|
49
|
+
else
|
50
|
+
IpWhitelistChecker.is_both_loopback(ip1, ip2)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.is_ip_match_range(ip, rule)
|
55
|
+
return false if !IpWhitelistChecker.is_same_ip_version(ip, rule['ip_minimum'])
|
56
|
+
|
57
|
+
ip_range_minimum = (IPAddress rule['ip_minimum']).to_i
|
58
|
+
ip_range_maximum = (IPAddress rule['ip_maximum']).to_i
|
59
|
+
ip_value = (IPAddress ip).to_i
|
60
|
+
|
61
|
+
return ip_value >= ip_range_minimum && ip_value <= ip_range_maximum;
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.is_ip_match_subnet(ip, subnet)
|
65
|
+
return false if !IpWhitelistChecker.is_same_ip_version(ip, subnet)
|
66
|
+
|
67
|
+
IPAddress(subnet).include?(IPAddress(ip))
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -22,7 +22,7 @@ module ForestLiana
|
|
22
22
|
@message = message
|
23
23
|
end
|
24
24
|
|
25
|
-
def display_error
|
25
|
+
def display_error
|
26
26
|
ExceptionHelper.recursively_print(self)
|
27
27
|
end
|
28
28
|
end
|
@@ -33,6 +33,12 @@ module ForestLiana
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
+
class HTTP403Error < ExpectedError
|
37
|
+
def initialize(message = "Forbidden")
|
38
|
+
super(403, :forbidden, message)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
36
42
|
class ExceptionHelper
|
37
43
|
def self.recursively_print(error, margin: '', is_error: false)
|
38
44
|
logger = is_error ?
|
data/lib/forest_liana/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: forest_liana
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sandro Munda
|
@@ -164,6 +164,20 @@ dependencies:
|
|
164
164
|
- - ">="
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: ipaddress
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :runtime
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
167
181
|
description: Forest is a modern admin interface that works on all major web frameworks.
|
168
182
|
forest_liana is the gem that makes Forest admin work on any Rails application (Rails
|
169
183
|
>= 4.0).
|
@@ -227,6 +241,8 @@ files:
|
|
227
241
|
- app/services/forest_liana/intercom_attributes_getter.rb
|
228
242
|
- app/services/forest_liana/intercom_conversation_getter.rb
|
229
243
|
- app/services/forest_liana/intercom_conversations_getter.rb
|
244
|
+
- app/services/forest_liana/ip_whitelist.rb
|
245
|
+
- app/services/forest_liana/ip_whitelist_checker.rb
|
230
246
|
- app/services/forest_liana/line_stat_getter.rb
|
231
247
|
- app/services/forest_liana/live_query_checker.rb
|
232
248
|
- app/services/forest_liana/login_handler.rb
|