forest_liana 5.3.3 → 5.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 52b354dcc7c4a05c8303bffa7c1deca674900d7b693f37e816d2bafe665c4b12
4
- data.tar.gz: 358a702306af12e805a6fe954c2e0fbb1d1ccc3ebe8fe8d8b73d6ebd8f101bb5
3
+ metadata.gz: 4e1a93b16b8c079a92000891ac73f8794fa87c2b6411e99a6e160d7f5e88e64d
4
+ data.tar.gz: 0b6853a6eda1fef9f395fa9f247b90273fef74fe8eb9e621ed4647d43832dc03
5
5
  SHA512:
6
- metadata.gz: 68dac9b8f4e7f427d1c9f4bf885d2412cf740b8e6038a6e83e919c967e9fbe9369fddc753992b7d80a191a04688366c06cc028a2afe309a476686e993603e8a6
7
- data.tar.gz: fcdb37f174ec29d416bfe1ce668608baa9e1924ec6ea2b51e61dcc74f1976e11fdf6eb41eb5503a22ed352d4e0ad772bc1498f2f31f958a467ac54dcf6e56a75
6
+ metadata.gz: 3e7aa35686573cce22819ea583a20c9d93afd39b72187aae0c618fd0e67e1494475435ae0ca9c8c892b7bbe3015c0e6ac0f220ca75b2526f66883f5a491f54f7
7
+ data.tar.gz: ffdcfcf09b90001e66bff1910a86d635def03cf718a9e13282061d4f9d5c4b0780058ba7462106ea6c4deb299e388bb3c32a3eec624d51bfd7b0420a85a60167
@@ -16,18 +16,15 @@ module ForestLiana
16
16
  def index
17
17
  begin
18
18
  if request.format == 'csv'
19
- checker = ForestLiana::PermissionsChecker.new(@resource, 'export', @rendering_id)
20
- return head :forbidden unless checker.is_authorized?
21
- elsif params.has_key?(:searchToEdit)
22
- checker = ForestLiana::PermissionsChecker.new(@resource, 'searchToEdit', @rendering_id)
19
+ checker = ForestLiana::PermissionsChecker.new(@resource, 'exportEnabled', @rendering_id, user_id: forest_user['id'])
23
20
  return head :forbidden unless checker.is_authorized?
24
21
  else
25
22
  checker = ForestLiana::PermissionsChecker.new(
26
23
  @resource,
27
- 'list',
24
+ 'browseEnabled',
28
25
  @rendering_id,
29
- nil,
30
- get_collection_list_permission_info(forest_user, request)
26
+ user_id: forest_user['id'],
27
+ collection_list_parameters: get_collection_list_permission_info(forest_user, request)
31
28
  )
32
29
  return head :forbidden unless checker.is_authorized?
33
30
  end
@@ -59,10 +56,10 @@ module ForestLiana
59
56
  begin
60
57
  checker = ForestLiana::PermissionsChecker.new(
61
58
  @resource,
62
- 'list',
59
+ 'browseEnabled',
63
60
  @rendering_id,
64
- nil,
65
- get_collection_list_permission_info(forest_user, request)
61
+ user_id: forest_user['id'],
62
+ collection_list_parameters: get_collection_list_permission_info(forest_user, request)
66
63
  )
67
64
  return head :forbidden unless checker.is_authorized?
68
65
 
@@ -89,7 +86,7 @@ module ForestLiana
89
86
 
90
87
  def show
91
88
  begin
92
- checker = ForestLiana::PermissionsChecker.new(@resource, 'show', @rendering_id)
89
+ checker = ForestLiana::PermissionsChecker.new(@resource, 'readEnabled', @rendering_id, user_id: forest_user['id'])
93
90
  return head :forbidden unless checker.is_authorized?
94
91
 
95
92
  getter = ForestLiana::ResourceGetter.new(@resource, params)
@@ -104,7 +101,7 @@ module ForestLiana
104
101
 
105
102
  def create
106
103
  begin
107
- checker = ForestLiana::PermissionsChecker.new(@resource, 'create', @rendering_id)
104
+ checker = ForestLiana::PermissionsChecker.new(@resource, 'addEnabled', @rendering_id, user_id: forest_user['id'])
108
105
  return head :forbidden unless checker.is_authorized?
109
106
 
110
107
  creator = ForestLiana::ResourceCreator.new(@resource, params)
@@ -127,7 +124,7 @@ module ForestLiana
127
124
 
128
125
  def update
129
126
  begin
130
- checker = ForestLiana::PermissionsChecker.new(@resource, 'update', @rendering_id)
127
+ checker = ForestLiana::PermissionsChecker.new(@resource, 'editEnabled', @rendering_id, user_id: forest_user['id'])
131
128
  return head :forbidden unless checker.is_authorized?
132
129
 
133
130
  updater = ForestLiana::ResourceUpdater.new(@resource, params)
@@ -149,7 +146,7 @@ module ForestLiana
149
146
  end
150
147
 
151
148
  def destroy
152
- checker = ForestLiana::PermissionsChecker.new(@resource, 'delete', @rendering_id)
149
+ checker = ForestLiana::PermissionsChecker.new(@resource, 'deleteEnabled', @rendering_id, user_id: forest_user['id'])
153
150
  return head :forbidden unless checker.is_authorized?
154
151
 
155
152
  @resource.destroy(params[:id]) if @resource.exists?(params[:id])
@@ -161,7 +158,7 @@ module ForestLiana
161
158
  end
162
159
 
163
160
  def destroy_bulk
164
- checker = ForestLiana::PermissionsChecker.new(@resource, 'delete', @rendering_id)
161
+ checker = ForestLiana::PermissionsChecker.new(@resource, 'deleteEnabled', @rendering_id, user_id: forest_user['id'])
165
162
  return head :forbidden unless checker.is_authorized?
166
163
 
167
164
  ids = ForestLiana::ResourcesGetter.get_ids_from_request(params)
@@ -245,8 +242,8 @@ module ForestLiana
245
242
  @collection ||= ForestLiana.apimap.find { |collection| collection.name.to_s == collection_name }
246
243
  end
247
244
 
248
- # NOTICE: Return a formatted object containing the request condition filters and
249
- # the user id used by the scope validator class to validate if scope is
245
+ # NOTICE: Return a formatted object containing the request condition filters and
246
+ # the user id used by the scope validator class to validate if scope is
250
247
  # in request
251
248
  def get_collection_list_permission_info(user, collection_list_request)
252
249
  {
@@ -19,14 +19,15 @@ module ForestLiana
19
19
 
20
20
  def check_permission_for_smart_route
21
21
  begin
22
-
22
+
23
23
  smart_action_request = get_smart_action_request
24
24
  if !smart_action_request.nil? && smart_action_request.has_key?(:smart_action_id)
25
25
  checker = ForestLiana::PermissionsChecker.new(
26
26
  find_resource(smart_action_request[:collection_name]),
27
27
  'actions',
28
28
  @rendering_id,
29
- get_smart_action_permission_info(forest_user, smart_action_request)
29
+ user_id: forest_user['id'],
30
+ smart_action_request_info: get_smart_action_request_info
30
31
  )
31
32
  return head :forbidden unless checker.is_authorized?
32
33
  else
@@ -54,10 +55,14 @@ module ForestLiana
54
55
  end
55
56
  end
56
57
 
57
- def get_smart_action_permission_info(user, smart_action_request)
58
+ # smart action permissions are retrieved from the action's endpoint and http_method
59
+ def get_smart_action_request_info
60
+ endpoint = request.fullpath
61
+ # Trim starting '/'
62
+ endpoint[0] = '' if endpoint[0] == '/'
58
63
  {
59
- user_id: user['id'],
60
- action_id: smart_action_request[:smart_action_id],
64
+ endpoint: endpoint,
65
+ http_method: request.request_method
61
66
  }
62
67
  end
63
68
  end
@@ -1,100 +1,162 @@
1
1
  module ForestLiana
2
2
  class PermissionsChecker
3
- @@permissions_per_rendering = Hash.new
3
+ @@permissions_cached = Hash.new
4
+ @@scopes_cached = Hash.new
5
+ @@roles_acl_activated = false
6
+ # TODO: handle cache scopes per rendering
4
7
  @@expiration_in_seconds = (ENV['FOREST_PERMISSIONS_EXPIRATION_IN_SECONDS'] || 3600).to_i
5
8
 
6
- def initialize(resource, permission_name, rendering_id, smart_action_parameters = nil, collection_list_parameters = nil)
9
+ def initialize(resource, permission_name, rendering_id, user_id:, smart_action_request_info: nil, collection_list_parameters: nil)
10
+ @user_id = user_id
7
11
  @collection_name = ForestLiana.name_for(resource)
8
12
  @permission_name = permission_name
9
13
  @rendering_id = rendering_id
10
- @smart_action_parameters = smart_action_parameters
14
+ @smart_action_request_info = smart_action_request_info
11
15
  @collection_list_parameters = collection_list_parameters
12
16
  end
13
17
 
14
18
  def is_authorized?
15
- (is_permission_expired? || !is_allowed?) ? retrieve_permissions_and_check_allowed : true
16
- end
17
-
18
- private
19
+ # User is still authorized if he already was and the permission has not expire
20
+ # if !have_permissions_expired && is_allowed
21
+ return true unless have_permissions_expired? || !is_allowed
19
22
 
20
- def get_permissions
21
- @@permissions_per_rendering &&
22
- @@permissions_per_rendering[@rendering_id] &&
23
- @@permissions_per_rendering[@rendering_id]['data']
23
+ fetch_permissions
24
+ is_allowed
24
25
  end
25
26
 
26
- def get_last_retrieve
27
- @@permissions_per_rendering &&
28
- @@permissions_per_rendering[@rendering_id] &&
29
- @@permissions_per_rendering[@rendering_id]['last_retrieve']
30
- end
27
+ private
31
28
 
32
- def smart_action_allowed?(smart_actions_permissions)
33
- if !@smart_action_parameters||
34
- !@smart_action_parameters[:user_id] ||
35
- !@smart_action_parameters[:action_id] ||
36
- !smart_actions_permissions ||
37
- !smart_actions_permissions[@smart_action_parameters[:action_id]]
38
- return false
29
+ def fetch_permissions
30
+ permissions = ForestLiana::PermissionsGetter::get_permissions_for_rendering(@rendering_id)
31
+ @@roles_acl_activated = permissions['meta']['rolesACLActivated']
32
+ permissions['last_fetch'] = Time.now
33
+ if @@roles_acl_activated
34
+ @@permissions_cached = permissions
35
+ else
36
+ permissions['data'] = ForestLiana::PermissionsFormatter.convert_to_new_format(permissions['data'], @rendering_id)
37
+ @@permissions_cached[@rendering_id] = permissions
39
38
  end
40
-
41
- @user_id = @smart_action_parameters[:user_id]
42
- @action_id = @smart_action_parameters[:action_id]
43
- @smart_action_permissions = smart_actions_permissions[@action_id]
44
- @allowed = @smart_action_permissions['allowed']
45
- @users = @smart_action_permissions['users']
46
-
47
- return @allowed && (@users.nil?|| @users.include?(@user_id.to_i));
39
+ add_scopes_to_cache(permissions)
48
40
  end
49
41
 
50
- def collection_list_allowed?(scope_permissions)
51
- return ForestLiana::ScopeValidator.new(
52
- scope_permissions['filter'],
53
- scope_permissions['dynamicScopesValues']['users']
54
- ).is_scope_in_request?(@collection_list_parameters)
42
+ def add_scopes_to_cache(permissions)
43
+ permissions['data']['renderings'].keys.each { |rendering_id|
44
+ @@scopes_cached[rendering_id] = permissions['data']['renderings'][rendering_id]
45
+ @@scopes_cached[rendering_id]['last_fetch'] = Time.now
46
+ } if permissions['data']['renderings']
55
47
  end
56
48
 
57
- def is_allowed?
58
- permissions = get_permissions
49
+ def is_allowed
50
+ permissions = get_permissions_content
51
+
59
52
  if permissions && permissions[@collection_name] &&
60
53
  permissions[@collection_name]['collection']
61
54
  if @permission_name === 'actions'
62
55
  return smart_action_allowed?(permissions[@collection_name]['actions'])
63
- # NOTICE: Permissions[@collection_name]['scope'] will either contains conditions filter and
64
- # dynamic user values definition, or null for collection that does not use scopes
65
- elsif @permission_name === 'list' and permissions[@collection_name]['scope']
66
- return collection_list_allowed?(permissions[@collection_name]['scope'])
67
56
  else
68
- return permissions[@collection_name]['collection'][@permission_name]
57
+ if @permission_name === 'browseEnabled'
58
+ refresh_scope_cache if scope_cache_expired?
59
+ scope_permissions = get_scope_in_permissions
60
+ if scope_permissions
61
+ # NOTICE: current_scope will either contains conditions filter and
62
+ # dynamic user values definition, or null for collection that does not use scopes
63
+ return false unless are_scopes_valid?(scope_permissions)
64
+ end
65
+ end
66
+ return is_user_allowed(permissions[@collection_name]['collection'][@permission_name])
69
67
  end
70
68
  else
71
69
  false
72
70
  end
73
71
  end
74
72
 
75
- def retrieve_permissions
76
- @@permissions_per_rendering[@rendering_id] = Hash.new
77
- permissions = ForestLiana::PermissionsGetter.new(@rendering_id).perform()
78
- @@permissions_per_rendering[@rendering_id]['data'] = permissions
79
- @@permissions_per_rendering[@rendering_id]['last_retrieve'] = Time.now
73
+ def get_scope_in_permissions
74
+ @@scopes_cached[@rendering_id] &&
75
+ @@scopes_cached[@rendering_id][@collection_name] &&
76
+ @@scopes_cached[@rendering_id][@collection_name]['scope']
77
+ end
78
+
79
+ def scope_cache_expired?
80
+ return true unless @@scopes_cached[@rendering_id] && @@scopes_cached[@rendering_id]['last_fetch']
81
+
82
+ elapsed_seconds = date_difference_in_seconds(Time.now, @@scopes_cached[@rendering_id]['last_fetch'])
83
+ elapsed_seconds >= @@expiration_in_seconds
84
+ end
85
+
86
+ # This will happen only on rolesACLActivated (as scope cache will always be up to date on disabled)
87
+ def refresh_scope_cache
88
+ permissions = ForestLiana::PermissionsGetter::get_permissions_for_rendering(@rendering_id, rendering_specific_only: true)
89
+ add_scopes_to_cache(permissions)
90
+ end
91
+
92
+ # When acl disabled permissions are stored and retrieved by rendering
93
+ def get_permissions
94
+ @@roles_acl_activated ? @@permissions_cached : @@permissions_cached[@rendering_id]
95
+ end
96
+
97
+ def get_permissions_content
98
+ permissions = get_permissions
99
+ permissions && permissions['data'] && permissions['data']['collections']
100
+ end
101
+
102
+ def get_last_fetch
103
+ permissions = get_permissions
104
+ permissions && permissions['last_fetch']
105
+ end
106
+
107
+ def get_smart_action_permissions(smart_actions_permissions)
108
+ endpoint = @smart_action_request_info[:endpoint]
109
+ http_method = @smart_action_request_info[:http_method]
110
+
111
+ return nil unless endpoint && http_method
112
+
113
+ schema_smart_action = ForestLiana::Utils::BetaSchemaUtils.find_action_from_endpoint(@collection_name, endpoint, http_method)
114
+
115
+ schema_smart_action &&
116
+ schema_smart_action.name &&
117
+ smart_actions_permissions &&
118
+ smart_actions_permissions[schema_smart_action.name]
119
+ end
120
+
121
+ def is_user_allowed(permission_value)
122
+ return false if permission_value.nil?
123
+ return permission_value if permission_value.in? [true, false]
124
+ permission_value.include?(@user_id.to_i)
125
+ end
126
+
127
+ def smart_action_allowed?(smart_actions_permissions)
128
+ smart_action_permissions = get_smart_action_permissions(smart_actions_permissions)
129
+
130
+ return false unless smart_action_permissions
131
+
132
+ is_user_allowed(smart_action_permissions['triggerEnabled'])
133
+ end
134
+
135
+ def are_scopes_valid?(scope_permissions)
136
+ return ForestLiana::ScopeValidator.new(
137
+ scope_permissions['filter'],
138
+ scope_permissions['dynamicScopesValues']['users']
139
+ ).is_scope_in_request?(@collection_list_parameters)
80
140
  end
81
141
 
82
142
  def date_difference_in_seconds(date1, date2)
83
143
  (date1 - date2).to_i
84
144
  end
85
145
 
86
- def is_permission_expired?
87
- last_retrieve = get_last_retrieve
88
-
89
- return true if last_retrieve.nil?
146
+ def have_permissions_expired?
147
+ last_fetch = get_last_fetch
148
+ return true unless last_fetch
90
149
 
91
- elapsed_seconds = date_difference_in_seconds(Time.now, last_retrieve)
150
+ elapsed_seconds = date_difference_in_seconds(Time.now, last_fetch)
92
151
  elapsed_seconds >= @@expiration_in_seconds
93
152
  end
94
153
 
95
- def retrieve_permissions_and_check_allowed
96
- retrieve_permissions
97
- is_allowed?
154
+ # Used only for testing purpose
155
+ def self.empty_cache
156
+ @@permissions_cached = Hash.new
157
+ @@scopes_cached = Hash.new
158
+ @@roles_acl_activated = false
159
+ @@expiration_in_seconds = (ENV['FOREST_PERMISSIONS_EXPIRATION_IN_SECONDS'] || 3600).to_i
98
160
  end
99
161
  end
100
162
  end
@@ -0,0 +1,52 @@
1
+ module ForestLiana
2
+ class PermissionsFormatter
3
+ class << PermissionsFormatter
4
+ # Convert old format permissions to unify PermissionsGetter code
5
+ def convert_to_new_format(permissions, rendering_id)
6
+ permissions_new_format = Hash.new
7
+ permissions_new_format['collections'] = Hash.new
8
+ permissions_new_format['renderings'] = Hash.new
9
+ permissions_new_format['renderings'][rendering_id] = Hash.new
10
+ permissions.keys.each { |collection_name|
11
+ permissions_new_format['collections'][collection_name] = {
12
+ 'collection' => convert_collection_permissions_to_new_format(permissions[collection_name]['collection']),
13
+ 'actions' => convert_actions_permissions_to_new_format(permissions[collection_name]['actions'])
14
+ }
15
+
16
+ permissions_new_format['renderings'][rendering_id][collection_name] = { 'scope' => permissions[collection_name]['scope'] }
17
+ }
18
+
19
+ permissions_new_format
20
+ end
21
+
22
+ def convert_collection_permissions_to_new_format(collection_permissions)
23
+ {
24
+ 'browseEnabled' => collection_permissions['list'] || collection_permissions['searchToEdit'],
25
+ 'readEnabled' => collection_permissions['show'],
26
+ 'addEnabled' => collection_permissions['create'],
27
+ 'editEnabled' => collection_permissions['update'],
28
+ 'deleteEnabled' => collection_permissions['delete'],
29
+ 'exportEnabled' => collection_permissions['export']
30
+ }
31
+ end
32
+
33
+ def convert_actions_permissions_to_new_format(actions_permissions)
34
+ return nil unless actions_permissions
35
+
36
+ actions_permissions_new_format = Hash.new
37
+
38
+ actions_permissions.keys.each { |action_name|
39
+ allowed = actions_permissions[action_name]['allowed']
40
+ users = actions_permissions[action_name]['users']
41
+
42
+ actions_permissions_new_format[action_name] = Hash.new
43
+ actions_permissions_new_format[action_name] = {
44
+ 'triggerEnabled' => allowed && (users.nil? || users)
45
+ }
46
+ }
47
+
48
+ actions_permissions_new_format
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,25 +1,60 @@
1
1
  module ForestLiana
2
2
  class PermissionsGetter
3
- def initialize(rendering_id)
4
- @route = "/liana/v2/permissions"
5
- @rendering_id = rendering_id
6
- end
3
+ class << PermissionsGetter
4
+ def get_permissions_api_route
5
+ '/liana/v3/permissions'
6
+ end
7
+
8
+ # Permission format example:
9
+ # collections => {
10
+ # {model_name} => {
11
+ # collection => {
12
+ # browseEnabled => true,
13
+ # readEnabled => true,
14
+ # editEnabled => true,
15
+ # addEnabled => true,
16
+ # deleteEnabled => true,
17
+ # exportEnabled => true,
18
+ # },
19
+ # actions => {
20
+ # {action_name} => {
21
+ # triggerEnabled => true,
22
+ # },
23
+ # },
24
+ # },
25
+ # },
26
+ # rederings => {
27
+ # {rendering_id} => {
28
+ # {collection_id} => {
29
+ # scope => {
30
+ # dynamicScopesValues => {},
31
+ # filter => {}
32
+ # }
33
+ # }
34
+ # }
35
+ # }
36
+ # }
37
+ # With `rendering_specific_only` this returns only the permissions related data specific to the provided rendering
38
+ # For now this only includes scopes
39
+ def get_permissions_for_rendering(rendering_id, rendering_specific_only: false)
40
+ begin
41
+ query_parameters = { 'renderingId' => rendering_id }
42
+ query_parameters['renderingSpecificOnly'] = rendering_specific_only if rendering_specific_only
7
43
 
8
- def perform
9
- begin
10
- query_parameters = { 'renderingId' => @rendering_id }
11
- response = ForestLiana::ForestApiRequester.get(@route, query: query_parameters)
44
+ api_route = get_permissions_api_route
45
+ response = ForestLiana::ForestApiRequester.get(api_route, query: query_parameters)
12
46
 
13
- if response.is_a?(Net::HTTPOK)
14
- JSON.parse(response.body)
15
- else
16
- raise "Forest API returned an #{ForestLiana::Errors::HTTPErrorHelper.format(response)}"
47
+ if response.is_a?(Net::HTTPOK)
48
+ JSON.parse(response.body)
49
+ else
50
+ raise "Forest API returned an #{ForestLiana::Errors::HTTPErrorHelper.format(response)}"
51
+ end
52
+ rescue => exception
53
+ FOREST_LOGGER.error 'Cannot retrieve the permissions from the Forest server.'
54
+ FOREST_LOGGER.error 'Which was caused by:'
55
+ ForestLiana::Errors::ExceptionHelper.recursively_print(exception, margin: ' ', is_error: true)
56
+ nil
17
57
  end
18
- rescue => exception
19
- FOREST_LOGGER.error 'Cannot retrieve the permissions from the Forest server.'
20
- FOREST_LOGGER.error 'Which was caused by:'
21
- ForestLiana::Errors::ExceptionHelper.recursively_print(exception, margin: ' ', is_error: true)
22
- nil
23
58
  end
24
59
  end
25
60
  end