Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
5880d7e
chore: update default setting for permission_expiration
matthv Nov 22, 2023
26b33c9
chore: add new http exception forbidden
matthv Nov 22, 2023
a3b6d20
chore: add new method handle error on requester
matthv Nov 22, 2023
bdc660b
fix: requester handling error
matthv Nov 22, 2023
a4ca26a
chore: add remaining test on api requester
matthv Nov 22, 2023
186791f
feat(permissions): add new permission service
matthv Nov 22, 2023
cb8f50d
chore: add utils context variable
matthv Nov 23, 2023
b27bdd9
chore: rewrite context variable injector
matthv Nov 24, 2023
4aae1bd
chore: add test on context variable injector
matthv Nov 28, 2023
9e91124
feat(permissions): add new http exceptions
matthv Nov 28, 2023
a643af4
feat(permissions): add smart_action_checker class
matthv Nov 28, 2023
5ac18a0
refactor: smart_action_checker
matthv Nov 28, 2023
f89cefa
fix(permissions): smart_action_checker
matthv Nov 29, 2023
3c3a5af
fix(exception): typo on conflict error class
matthv Nov 29, 2023
f742585
chore: add getter on smart_action_checker
matthv Nov 29, 2023
77da7d2
fix(typo): smart_action
matthv Nov 29, 2023
d7a2222
chore: add tests on smart_action_checker
matthv Nov 29, 2023
d4e6565
refactor: remove double call of caller into list, show, update & count
matthv Nov 30, 2023
93d49da
refactor: api requester remove not nil params attributes
matthv Nov 30, 2023
6d646c7
feat(permissions): add new permission attributes into authenticated r…
matthv Nov 30, 2023
fb33320
refactor: smart action checker
matthv Dec 1, 2023
0695d51
refactor: context variable
matthv Dec 1, 2023
30e158a
feat(permission): set invalide cache to class method
matthv Dec 1, 2023
44c47d9
fix(permissions): remove all string to sym
matthv Dec 1, 2023
f931b99
chore: add method add_action to base collection
matthv Dec 4, 2023
029565e
chore: test add fake schema
matthv Dec 4, 2023
c2d8f5f
fix(permission): setup method find_action_from_endpoint
matthv Dec 4, 2023
2970a91
fix(permissions): typo
matthv Dec 5, 2023
8a32623
chore: add remaining test on permissions
matthv Dec 5, 2023
678944b
chore: add package file-cache
matthv Dec 5, 2023
05e592f
feat(permissions): replace cache with file-cache + update tests
matthv Dec 5, 2023
12d730b
chore: add package ld-eventsource
matthv Dec 6, 2023
a0b749c
chore: add new settings agent config instant_cache_refresh for SSE
matthv Dec 6, 2023
310d230
feat(permission): add new service sse_cache_invalidation
matthv Dec 6, 2023
5c3c0f8
chore: add call of sse_cache_invalidation into rails engine
matthv Dec 6, 2023
7a963e0
chore: remove useless comment on permisions_spec
matthv Dec 6, 2023
ac145f6
refactor: sse_cache_invalidation
matthv Dec 6, 2023
383ef71
chore: add test on sse_cache_invalidation
matthv Dec 6, 2023
739008f
feat(permissions): add permission on routes
matthv Dec 7, 2023
b7be343
refactor: set run method to method class for sse_invalidation_cache
matthv Dec 7, 2023
e55d48f
chore: update rubocop + lint
matthv Dec 7, 2023
4a63cf8
fix(test): forest_admin_api_requester
matthv Dec 7, 2023
242bcec
fix: default value of instant_cache_refresh
matthv Dec 7, 2023
5f60d9f
fix: tests
matthv Dec 7, 2023
12cf097
fix(permissions): remove useless comment
matthv Dec 7, 2023
2675a19
fix(context_variables): force symbolize keys fro user & team
matthv Dec 7, 2023
6e561fc
chore: remove lightly package & replace cache by file-cache package
matthv Dec 8, 2023
fc1eb14
fix: test schema_emitter
matthv Dec 8, 2023
5e1ab07
refactor: count routes
matthv Dec 8, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ Style/RedundantConstantBase:
Exclude:
- 'packages/forest_admin_rails/spec/rails_helper.rb'

Style/HashEachMethods:
Exclude:
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/components/query/condition_tree/condition_tree_factory.rb'

Lint/NestedMethodDefinition:
Exclude:
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/components/query/filter_factory.rb'
Expand All @@ -161,13 +165,14 @@ Naming/PredicateName:

Metrics/ParameterLists:
Exclude:
- 'packages/forest_admin_agent/lib/forest_admin_agent/services/smart_action_checker.rb'
- 'packages/forest_admin_agent/spec/lib/forest_admin_agent/routes/resources/related/list_related_spec.rb'
- 'packages/forest_admin_agent/spec/lib/forest_admin_agent/routes/resources/related/count_related_spec.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/schema/relations/many_to_many_schema.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/schema/column_schema.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/components/caller.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/components/query/filter.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/utils/collection.rb'
- 'packages/forest_admin_agent/spec/lib/forest_admin_agent/routes/resources/related/list_related_spec.rb'
- 'packages/forest_admin_agent/spec/lib/forest_admin_agent/routes/resources/related/count_related_spec.rb'

Metrics/ModuleLength:
CountAsOne: [ 'array', 'hash', 'method_call' ]
Expand Down Expand Up @@ -201,6 +206,7 @@ Metrics/BlockLength:
Metrics/ClassLength:
Exclude:
- 'packages/forest_admin_agent/lib/forest_admin_agent/utils/schema/generator_field.rb'
- 'packages/forest_admin_agent/lib/forest_admin_agent/services/permissions.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/utils/collection.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/components/query/filter_factory.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/components/query/condition_tree/nodes/condition_tree_leaf.rb'
Expand All @@ -222,6 +228,12 @@ Layout/LineLength:
Max: 120
Exclude:
- 'packages/forest_admin_agent/spec/shared/caller.rb'
- 'packages/forest_admin_agent/lib/forest_admin_agent/http/forest_admin_api_requester.rb'
- 'packages/forest_admin_agent/spec/lib/forest_admin_agent/http/forest_admin_api_requester_spec.rb'
- 'packages/forest_admin_agent/lib/forest_admin_agent/routes/resources/list.rb'
- 'packages/forest_admin_agent/lib/forest_admin_agent/services/permissions.rb'
- 'packages/forest_admin_agent/spec/lib/forest_admin_agent/utils/context_variables_injector_spec.rb'
- 'packages/forest_admin_agent/spec/lib/forest_admin_agent/services/smart_action_checker_spec.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/components/query/condition_tree/condition_tree_factory.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/utils/collection.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/components/query/filter_factory.rb'
Expand Down
3 changes: 2 additions & 1 deletion packages/forest_admin_agent/forest_admin_agent.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ admin work on any Ruby application."
spec.add_dependency "activesupport", ">= 6.1"
spec.add_dependency "dry-container", "~> 0.11"
spec.add_dependency "faraday", "~> 2.7"
spec.add_dependency "filecache", "~> 1.0"
spec.add_dependency "ipaddress", "~> 0.8.3"
spec.add_dependency "jsonapi-serializers", "~> 1.0"
spec.add_dependency "jwt", "~> 2.7"
spec.add_dependency "lightly", "~> 0.4.0"
spec.add_dependency "ld-eventsource", "~> 2.2"
spec.add_dependency "mono_logger", "~> 1.1"
spec.add_dependency "openid_connect", "~> 2.2"
spec.add_dependency "rake", "~> 13.0"
Expand Down
1 change: 1 addition & 0 deletions packages/forest_admin_agent/lib/forest_admin_agent.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

loader = Zeitwerk::Loader.for_gem
loader.inflector.inflect('oauth2' => 'OAuth2')
loader.inflector.inflect('sse_cache_invalidation' => 'SSECacheInvalidation')
loader.setup

module ForestAdminAgent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class ForestProvider < OpenIDConnect::Client
attr_reader :rendering_id

def initialize(rendering_id, attributes = {})
super attributes
super(attributes)
@rendering_id = rendering_id
@authorization_endpoint = '/oidc/auth'
@token_endpoint = '/oidc/token'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require 'filecache'
require 'openid_connect'
require_relative 'oauth2/oidc_config'
require_relative 'oauth2/forest_provider'
Expand All @@ -18,8 +19,8 @@ def make_forest_provider(rendering_id)
private

def setup_cache(env_secret, config_agent)
lightly = Lightly.new(life: TTL, dir: "#{config_agent[:cache_dir]}/issuer")
lightly.get env_secret do
cache = FileCache.new('auth_issuer', (config_agent[:cache_dir]).to_s, TTL)
cache.get_or_set env_secret do
oidc_config = retrieve_config(config_agent[:forest_server_url])
credentials = register(
config_agent[:env_secret],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require 'dry-container'
require 'lightly'
require 'filecache'

module ForestAdminAgent
module Builder
Expand All @@ -21,7 +21,7 @@ def setup(options)
end

def add_datasource(datasource)
datasource.collections.each { |_name, collection| @customizer.add_collection(collection) }
datasource.collections.each_value { |collection| @customizer.add_collection(collection) }
self
end

Expand All @@ -40,9 +40,9 @@ def send_schema(force: false)
if !schema_is_know || force
# Logger::log('Info', 'schema was updated, sending new version');
client = ForestAdminAgent::Http::ForestAdminApiRequester.new
client.post('/forest/apimaps', schema)
schema_file_hash_cache = Lightly.new(life: TTL_SCHEMA, dir: @options[:cache_dir].to_s)
schema_file_hash_cache.get 'value' do
client.post('/forest/apimaps', schema.to_json)
schema_file_hash_cache = FileCache.new('app', @options[:cache_dir].to_s, TTL_SCHEMA)
schema_file_hash_cache.get_or_set 'value' do
schema[:meta][:schemaFileHash]
end
@container.register(:schema_file_hash, schema_file_hash_cache)
Expand All @@ -59,11 +59,11 @@ def build_container
end

def build_cache
@container.register(:cache, Lightly.new(life: TTL_CONFIG, dir: @options[:cache_dir].to_s))
@container.register(:cache, FileCache.new('app', @options[:cache_dir].to_s, TTL_SCHEMA))
return unless @has_env_secret

cache = @container.resolve(:cache)
cache.get 'config' do
cache.get_or_set 'config' do
@options.to_h
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class AuthenticationOpenIdClient < HttpException
attr_reader :error, :error_description, :state

def initialize(error, error_description, state)
super error, 401, error_description
super(error, 401, error_description)
@error = error
@error_description = error_description
@state = state
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module ForestAdminAgent
module Http
module Exceptions
class ConflictError < HttpException
attr_reader :name

def initialize(message, name = 'ConflictError')
@name = name
super(429, 'Conflict', message)
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module ForestAdminAgent
module Http
module Exceptions
class ForbiddenError < HttpException
attr_reader :name

def initialize(message, name = 'ForbiddenError')
@name = name
super(403, 'Forbidden', message)
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class NotFoundError < StandardError
attr_reader :name, :status

def initialize(msg, name = 'NotFoundError')
super msg
super(msg)
@name = name
@status = 404
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module ForestAdminAgent
module Http
module Exceptions
class RequireApproval < HttpException
attr_reader :name, :data

def initialize(message, name = 'RequireApproval', data = [])
@name = name
@data = data
super(403, 'Forbidden', message)
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
module ForestAdminAgent
module Http
class ForestAdminApiRequester
include ForestAdminDatasourceToolkit::Exceptions

def initialize
@headers = {
'Content-Type' => 'application/json',
Expand All @@ -16,12 +18,38 @@ def initialize
)
end

def get(url, params)
@client.get(url, params.to_json)
def get(url, params = nil)
@client.get(url, params)
end

def post(url, params = nil)
@client.post(url, params)
end

def post(url, params)
@client.post(url, params.to_json)
def handle_response_error(error)
raise error if error.is_a?(ForestException)

if error.response[:message]&.include?('certificate')
raise ForestException,
'ForestAdmin server TLS certificate cannot be verified. Please check that your system time is set properly.'
end

if error.response[:status].zero? || error.response[:status] == 502
raise ForestException, 'Failed to reach ForestAdmin server. Are you online?'
end

if error.response[:status] == 404
raise ForestException,
'ForestAdmin server failed to find the project related to the envSecret you configured. Can you check that you copied it properly in the Forest initialization?'
end

if error.response[:status] == 503
raise ForestException,
'Forest is in maintenance for a few minutes. We are upgrading your experience in the forest. We just need a few more minutes to get it right.'
end

raise ForestException,
'An unexpected error occurred while contacting the ForestAdmin server. Please contact support@forestadmin.com for further investigations.'
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ def build(args = {})
Facades::Whitelist.check_ip(args[:headers]['action_dispatch.remote_ip'].to_s)
end
@caller = Utils::QueryStringParser.parse_caller(args)
@permissions = ForestAdminAgent::Services::Permissions.new(@caller)
super
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
module ForestAdminAgent
module Routes
module Resources
class Count < AbstractRoute
class Count < AbstractAuthenticatedRoute
include ForestAdminAgent::Builder
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
def setup_routes
add_route('forest_count', 'get', '/:collection_name/count', ->(args) { handle_request(args) })

Expand All @@ -13,12 +14,14 @@ def setup_routes

def handle_request(args = {})
build(args)
@permissions.can?(:browse, @collection)

if @collection.is_countable?
caller = ForestAdminAgent::Utils::QueryStringParser.parse_caller(args)
filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new
filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
condition_tree: @permissions.get_scope(@collection)
)
aggregation = ForestAdminDatasourceToolkit::Components::Query::Aggregation.new(operation: 'Count')
result = @collection.aggregate(caller, filter, aggregation)
result = @collection.aggregate(@caller, filter, aggregation)

return {
name: args[:params]['collection_name'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module Resources
class Delete < AbstractAuthenticatedRoute
include ForestAdminAgent::Builder
include ForestAdminDatasourceToolkit::Components::Query
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree

def setup_routes
add_route('forest_delete_bulk', 'delete', '/:collection_name', ->(args) { handle_request_bulk(args) })
Expand All @@ -17,6 +18,7 @@ def setup_routes

def handle_request(args = {})
build(args)
@permissions.can?(:delete, @collection)
id = Utils::Id.unpack_id(@collection, args[:params]['id'], with_key: true)
delete_records(args, { ids: [id], are_excluded: false })

Expand All @@ -25,6 +27,7 @@ def handle_request(args = {})

def handle_request_bulk(args = {})
build(args)
@permissions.can?(:delete, @collection)
selection_ids = Utils::Id.parse_selection_ids(@collection, args[:params], with_key: true)
delete_records(args, selection_ids)

Expand All @@ -38,7 +41,8 @@ def delete_records(args, selection_ids)
condition_tree: ConditionTree::ConditionTreeFactory.intersect(
[
Utils::QueryStringParser.parse_condition_tree(@collection, args),
condition_tree_ids
condition_tree_ids,
@permissions.get_scope(@collection)
]
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module Routes
module Resources
class List < AbstractAuthenticatedRoute
include ForestAdminAgent::Builder
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
def setup_routes
add_route('forest_list', 'get', '/:collection_name', ->(args) { handle_request(args) })

Expand All @@ -13,14 +14,19 @@ def setup_routes

def handle_request(args = {})
build(args)
caller = ForestAdminAgent::Utils::QueryStringParser.parse_caller(args)
@permissions.can?(:browse, @collection)

filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
condition_tree: ForestAdminAgent::Utils::QueryStringParser.parse_condition_tree(@collection, args),
condition_tree: ConditionTreeFactory.intersect([
@permissions.get_scope(@collection),
ForestAdminAgent::Utils::QueryStringParser.parse_condition_tree(
@collection, args
)
]),
page: ForestAdminAgent::Utils::QueryStringParser.parse_pagination(args)
)
projection = ForestAdminAgent::Utils::QueryStringParser.parse_projection_with_pks(@collection, args)
records = @collection.list(caller, filter, projection)
records = @collection.list(@caller, filter, projection)

{
name: args[:params]['collection_name'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class AssociateRelated < AbstractRelatedRoute
include ForestAdminAgent::Builder
include ForestAdminDatasourceToolkit::Utils
include ForestAdminDatasourceToolkit::Components::Query

def setup_routes
add_route(
'forest_related_associate',
Expand All @@ -21,6 +22,7 @@ def setup_routes

def handle_request(args = {})
build(args)
@permissions.can?(:edit, @collection)

parent_id = Utils::Id.unpack_id(@collection, args[:params]['id'], with_key: true)
target_relation_id = Utils::Id.unpack_id(@child_collection, args[:params]['data'][0]['id'], with_key: true)
Expand All @@ -40,7 +42,14 @@ def handle_request(args = {})
def associate_one_to_many(relation, parent_id, target_relation_id)
id = Schema.primary_keys(@child_collection)[0]
value = Collection.get_value(@child_collection, @caller, target_relation_id, id)
filter = Filter.new(condition_tree: ConditionTree::Nodes::ConditionTreeLeaf.new(id, 'Equal', value))
filter = Filter.new(
condition_tree: ConditionTree::ConditionTreeFactory.intersect(
[
ConditionTree::Nodes::ConditionTreeLeaf.new(id, 'Equal', value),
@permissions.get_scope(@collection)
]
)
)
value = Collection.get_value(@collection, @caller, parent_id, relation.origin_key_target)

@child_collection.update(@caller, filter, { relation.origin_key => value })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ def setup_routes

def handle_request(args = {})
build(args)
@permissions.can?(:browse, @collection)

if @child_collection.is_countable?
filter = Filter.new
filter = Filter.new(condition_tree: @permissions.get_scope(@collection))
id = Utils::Id.unpack_id(@collection, args[:params]['id'], with_key: true)
result = Collection.aggregate_relation(
@collection,
Expand Down
Loading