forest_liana 2.12.0 → 2.13.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|