Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
erp5
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Titouan Soulard
erp5
Commits
ce262b93
Commit
ce262b93
authored
Nov 26, 2024
by
Titouan Soulard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
erp5_json_form: SPLIT also validate data to output schema
ALSO: add security on methods
parent
8233b311
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
70 additions
and
18 deletions
+70
-18
bt5/erp5_json_form/ModuleComponentTemplateItem/portal_components/document.erp5.JSONForm.py
...tTemplateItem/portal_components/document.erp5.JSONForm.py
+32
-17
bt5/erp5_json_form/TestTemplateItem/portal_components/test.erp5.testJSONForm.py
...tTemplateItem/portal_components/test.erp5.testJSONForm.py
+38
-1
No files found.
bt5/erp5_json_form/ModuleComponentTemplateItem/portal_components/document.erp5.JSONForm.py
View file @
ce262b93
...
@@ -30,6 +30,8 @@ import jsonschema
...
@@ -30,6 +30,8 @@ import jsonschema
from
collections
import
OrderedDict
from
collections
import
OrderedDict
from
zExceptions
import
InternalError
from
erp5.component.document.JSONType
import
JSONType
from
erp5.component.document.JSONType
import
JSONType
from
erp5.component.document.TextDocument
import
TextDocument
from
erp5.component.document.TextDocument
import
TextDocument
...
@@ -64,26 +66,33 @@ class JSONForm(JSONType, TextDocument):
...
@@ -64,26 +66,33 @@ class JSONForm(JSONType, TextDocument):
def
__call__
(
self
,
json_data
,
list_error
=
False
):
#pylint:disable=arguments-differ
def
__call__
(
self
,
json_data
,
list_error
=
False
):
#pylint:disable=arguments-differ
data_dict
=
json
.
loads
(
json_data
)
data_dict
=
json
.
loads
(
json_data
)
validation_result
=
self
.
validateJSON
(
data_dict
,
list_error
)
validation_result
=
self
.
_validateJSON
(
data_dict
,
self
.
getInputJSONSchema
()
,
list_error
)
if
validation_result
is
not
True
:
if
validation_result
is
not
True
:
if
not
list_error
:
if
not
list_error
:
raise
jsonschema
.
exceptions
.
ValidationError
(
validation_result
.
message
)
raise
jsonschema
.
exceptions
.
ValidationError
(
validation_result
.
message
)
else
:
else
:
raise
ValueError
(
json
.
dumps
(
validation_result
))
raise
ValueError
(
json
.
dumps
(
validation_result
))
if
self
.
getAfterMethodId
():
if
not
self
.
getAfterMethodId
():
after_method
=
getattr
(
getattr
(
self
,
"aq_parent"
,
None
),
self
.
getAfterMethodId
())
raise
InternalError
(
"No after method defined in JSON Form"
)
mapped_data_dict
=
self
.
_mapArguments
(
data_dict
,
"input"
)
# XXX: argument name is wrong
after_method
=
getattr
(
getattr
(
self
,
"aq_parent"
,
None
),
self
.
getAfterMethodId
())
mapped_data_dict
[
"form_reference"
]
=
self
mapped_data_dict
=
self
.
_mapArguments
(
data_dict
,
"input"
)
output_dict
=
after_method
(
**
mapped_data_dict
)
# XXX: argument name is wrong
if
not
isinstance
(
output_dict
,
dict
):
mapped_data_dict
[
"form_reference"
]
=
self
output_dict
=
{}
output_dict
=
after_method
(
**
mapped_data_dict
)
mapped_output_dict
=
self
.
_mapArguments
(
output_dict
,
"output"
)
if
not
isinstance
(
output_dict
,
dict
):
return
json
.
dumps
(
mapped_output_dict
)
output_dict
=
{}
mapped_output_dict
=
self
.
_mapArguments
(
output_dict
,
"output"
)
raise
NotImplementedError
(
"No after method"
)
# Also check output to match expected output schema if defined
validation_result
=
self
.
_validateJSON
(
mapped_output_dict
,
self
.
getOutputJSONSchema
())
if
validation_result
is
not
True
:
raise
InternalError
(
"Returned data do not match expected output schema"
)
return
json
.
dumps
(
mapped_output_dict
)
security
.
declarePrivate
(
"_mapArguments"
)
def
_mapArguments
(
self
,
arguments
,
mapping_type
):
def
_mapArguments
(
self
,
arguments
,
mapping_type
):
mappings
=
{
x
.
getSource
():
x
.
getDestination
()
for
x
in
self
.
objectValues
(
portal_type
=
"Argument Mapping"
)
if
x
.
getMappingType
()
==
mapping_type
}
mappings
=
{
x
.
getSource
():
x
.
getDestination
()
for
x
in
self
.
objectValues
(
portal_type
=
"Argument Mapping"
)
if
x
.
getMappingType
()
==
mapping_type
}
mapped_arguments
=
{}
mapped_arguments
=
{}
...
@@ -96,11 +105,12 @@ class JSONForm(JSONType, TextDocument):
...
@@ -96,11 +105,12 @@ class JSONForm(JSONType, TextDocument):
return
mapped_arguments
return
mapped_arguments
def
validateJSON
(
self
,
json_data
,
list_error
=
False
):
security
.
declarePrivate
(
"_validateJSON"
)
def
_validateJSON
(
self
,
json_data
,
schema
,
list_error
=
False
):
"""
"""
Validate contained JSON with the Schema defined in the Portal Type.
Validate contained JSON with the Schema defined in the Portal Type.
"""
"""
defined_schema
=
json
.
loads
(
s
elf
.
getInputJSONSchema
()
or
""
)
defined_schema
=
json
.
loads
(
s
chema
)
try
:
try
:
jsonschema
.
validate
(
json_data
,
defined_schema
,
format_checker
=
jsonschema
.
FormatChecker
())
jsonschema
.
validate
(
json_data
,
defined_schema
,
format_checker
=
jsonschema
.
FormatChecker
())
except
jsonschema
.
exceptions
.
ValidationError
as
err
:
except
jsonschema
.
exceptions
.
ValidationError
as
err
:
...
@@ -114,7 +124,12 @@ class JSONForm(JSONType, TextDocument):
...
@@ -114,7 +124,12 @@ class JSONForm(JSONType, TextDocument):
return
err
return
err
return
True
return
True
def
returnSchema
(
self
,
schema
,
path
,
REQUEST
):
security
.
declarePrivate
(
"_returnSchema"
)
def
_returnSchema
(
self
,
schema
,
path
,
REQUEST
):
# Handle empty schemas (especially output)
if
not
schema
:
schema
=
"{}"
schema
=
json
.
loads
(
schema
,
object_pairs_hook
=
OrderedDict
)
schema
=
json
.
loads
(
schema
,
object_pairs_hook
=
OrderedDict
)
# Replace user URL by absolute URL
# Replace user URL by absolute URL
if
"$id"
in
schema
:
if
"$id"
in
schema
:
...
@@ -135,7 +150,7 @@ class JSONForm(JSONType, TextDocument):
...
@@ -135,7 +150,7 @@ class JSONForm(JSONType, TextDocument):
"""
"""
Method to retrieve the expected JSON Schema for JSON input
Method to retrieve the expected JSON Schema for JSON input
"""
"""
return
self
.
returnSchema
(
return
self
.
_
returnSchema
(
self
.
getOutputSchema
(),
self
.
getOutputSchema
(),
"/getOutputJSONSchema"
,
"/getOutputJSONSchema"
,
self
.
REQUEST
self
.
REQUEST
...
@@ -147,7 +162,7 @@ class JSONForm(JSONType, TextDocument):
...
@@ -147,7 +162,7 @@ class JSONForm(JSONType, TextDocument):
"""
"""
Method to retrieve the expected JSON Schema for JSON output
Method to retrieve the expected JSON Schema for JSON output
"""
"""
return
self
.
returnSchema
(
return
self
.
_
returnSchema
(
self
.
getTextContent
(),
self
.
getTextContent
(),
"/getInputJSONSchema"
,
"/getInputJSONSchema"
,
self
.
REQUEST
self
.
REQUEST
...
...
bt5/erp5_json_form/TestTemplateItem/portal_components/test.erp5.testJSONForm.py
View file @
ce262b93
...
@@ -29,6 +29,8 @@ import json
...
@@ -29,6 +29,8 @@ import json
import
re
import
re
from
DateTime
import
DateTime
from
DateTime
import
DateTime
from
zExceptions
import
InternalError
from
Products.ERP5Type.tests.ERP5TypeTestCase
import
ERP5TypeTestCase
from
Products.ERP5Type.tests.ERP5TypeTestCase
import
ERP5TypeTestCase
from
Products.ERP5Type.tests.utils
import
createZODBPythonScript
from
Products.ERP5Type.tests.utils
import
createZODBPythonScript
...
@@ -71,6 +73,7 @@ class TestJSONForm(ERP5TypeTestCase):
...
@@ -71,6 +73,7 @@ class TestJSONForm(ERP5TypeTestCase):
json_form
.
edit
(
json_form
.
edit
(
text_content
=
text_content
,
text_content
=
text_content
,
after_method_id
=
after_method_id
,
after_method_id
=
after_method_id
,
output_schema
=
""
)
)
if
self
.
portal
.
portal_workflow
.
isTransitionPossible
(
json_form
,
'validate'
):
if
self
.
portal
.
portal_workflow
.
isTransitionPossible
(
json_form
,
'validate'
):
json_form
.
validate
()
json_form
.
validate
()
...
@@ -123,7 +126,7 @@ class TestJSONForm(ERP5TypeTestCase):
...
@@ -123,7 +126,7 @@ class TestJSONForm(ERP5TypeTestCase):
json_form
=
self
.
fixJSONForm
(
method_id
,
schema
,
""
)
json_form
=
self
.
fixJSONForm
(
method_id
,
schema
,
""
)
self
.
tic
()
self
.
tic
()
self
.
assertRaisesRegexp
(
NotImplemented
Error
,
"No after method"
,
json_form
,
json
.
dumps
(
data
))
self
.
assertRaisesRegexp
(
Internal
Error
,
"No after method"
,
json_form
,
json
.
dumps
(
data
))
def
test_call_invalid_json_list_errors
(
self
):
def
test_call_invalid_json_list_errors
(
self
):
"""
"""
...
@@ -232,6 +235,40 @@ class TestJSONForm(ERP5TypeTestCase):
...
@@ -232,6 +235,40 @@ class TestJSONForm(ERP5TypeTestCase):
error
[
json_form
.
absolute_url
()
+
"/getInputJSONSchema"
]
=
[[
'Validation Error'
,
u"u'title' is a required property"
]]
error
[
json_form
.
absolute_url
()
+
"/getInputJSONSchema"
]
=
[[
'Validation Error'
,
u"u'title' is a required property"
]]
self
.
assertRaisesRegexp
(
ValueError
,
re
.
escape
(
json
.
dumps
(
error
)),
json_form
,
json
.
dumps
(
data
),
list_error
=
True
)
self
.
assertRaisesRegexp
(
ValueError
,
re
.
escape
(
json
.
dumps
(
error
)),
json_form
,
json
.
dumps
(
data
),
list_error
=
True
)
def
test_raises_on_invalid_output
(
self
):
"""
Raises when returned dictionary does not match output schema.
"""
input_schema
=
"""{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "my-schema.json",
"properties":{
"title": {
"type": "string"
}
}
}"""
output_schema
=
"""{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "my-schema.json",
"properties":{
"invalid": {
"type": "string"
}
},
"required": ["invalid"]
}"""
data
=
{
"title"
:
"foo"
}
method_id
=
"test_ERP5Site_processSimpleStringAsJSON"
after_method
=
self
.
createBasicScriptreturnJSONWithTimestamp
()
json_form
=
self
.
fixJSONForm
(
method_id
,
input_schema
,
after_method
)
json_form
.
setOutputSchema
(
output_schema
)
self
.
tic
()
self
.
assertRaisesRegexp
(
InternalError
,
"do not match"
,
json_form
,
json
.
dumps
(
data
))
def
test_supports_argument_mappings
(
self
):
def
test_supports_argument_mappings
(
self
):
"""
"""
Ensures arguments mappings can be used properly.
Ensures arguments mappings can be used properly.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment