Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
prozorro-sale
Cors
Commits
22b79524
Commit
22b79524
authored
Feb 01, 2022
by
Viacheslav Sukhovieiev
Browse files
feat: add handler decorator logic
parent
0225f1f8
Pipeline
#35932
passed with stages
in 1 minute and 50 seconds
Changes
4
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
src/prozorro_sale/cors/__init__.py
View file @
22b79524
...
...
@@ -15,6 +15,7 @@
"""CORS support for aiohttp.
"""
import
functools
from
typing
import
Mapping
,
Union
,
Any
from
aiohttp
import
web
...
...
@@ -65,3 +66,39 @@ def setup(app: web.Application, *,
cors
=
CorsConfig
(
app
,
defaults
=
defaults
)
app
[
APP_CONFIG_KEY
]
=
cors
return
cors
def
get_base_config
()
->
dict
:
"""
returns base CORS config
{
"*": ResourceOptions(
allow_credentials=True,
expose_headers="*",
allow_headers="*",
allow_methods="*",
)
}
"""
return
{
"*"
:
ResourceOptions
(
allow_credentials
=
True
,
expose_headers
=
"*"
,
allow_headers
=
"*"
,
allow_methods
=
"*"
,
)
}
def
custom_handler
(
config
=
None
,
defaults
=
False
):
if
not
config
and
defaults
is
True
:
config
=
get_base_config
()
def
wrapper
(
func
):
@
functools
.
wraps
(
func
)
def
handler
(
*
args
,
**
kwargs
):
return
func
(
*
args
,
**
kwargs
)
handler
.
__apply_cors
=
True
handler
.
__cors_config
=
config
return
handler
return
wrapper
src/prozorro_sale/cors/cors_config.py
View file @
22b79524
...
...
@@ -261,3 +261,12 @@ class CorsConfig:
stacklevel
=
2
)
return
self
.
_cors_impl
.
add
(
routing_entity
,
config
)
def
apply
(
self
,
app
:
web
.
Application
):
for
route
in
list
(
app
.
router
.
routes
()):
if
hasattr
(
route
.
handler
,
"__apply_cors"
):
if
hasattr
(
route
.
handler
,
"__cors_config"
):
config
=
getattr
(
route
.
handler
,
"__cors_config"
)
else
:
config
=
None
self
.
add
(
route
,
config
)
src/prozorro_sale/cors/urldispatcher_router_adapter.py
View file @
22b79524
...
...
@@ -279,9 +279,10 @@ class ResourcesUrlDispatcherRouterAdapter(AbstractRouterAdapter):
resource
=
self
.
_request_resource
(
preflight_request
)
resource_config
=
self
.
_resource_config
[
resource
]
defaulted_config
=
collections
.
ChainMap
(
resource_config
.
default_config
,
self
.
_default_config
)
if
resource_config
.
default_config
:
defaulted_config
=
resource_config
.
default_config
else
:
defaulted_config
=
self
.
_default_config
options
=
defaulted_config
.
get
(
origin
,
defaulted_config
.
get
(
"*"
))
if
options
is
not
None
and
options
.
is_method_allowed
(
requested_method
):
...
...
test/integration/test_main.py
View file @
22b79524
...
...
@@ -22,7 +22,7 @@ import pytest
from
aiohttp
import
web
from
aiohttp
import
hdrs
from
prozorro_sale.cors
import
setup
as
_setup
,
ResourceOptions
,
CorsViewMixin
from
prozorro_sale.cors
import
setup
as
_setup
,
ResourceOptions
,
CorsViewMixin
,
custom_handler
TEST_BODY
=
"Hello, world"
...
...
@@ -40,6 +40,15 @@ async def handler(request: web.Request) -> web.StreamResponse:
return
response
def
decorated_handler
(
config
=
None
,
defaults
=
False
):
@
custom_handler
(
config
,
defaults
)
async
def
handler
(
request
:
web
.
Request
)
->
web
.
StreamResponse
:
response
=
web
.
Response
(
text
=
TEST_BODY
)
response
.
headers
[
SERVER_CUSTOM_HEADER_NAME
]
=
SERVER_CUSTOM_HEADER_VALUE
return
response
return
handler
class
WebViewHandler
(
web
.
View
,
CorsViewMixin
):
async
def
get
(
self
)
->
web
.
StreamResponse
:
...
...
@@ -912,6 +921,180 @@ async def test_static_route(aiohttp_client):
assert
data
==
''
@
pytest
.
mark
.
parametrize
(
'config, defaults, status, message'
,
[
pytest
.
param
(
{
"*"
:
ResourceOptions
(
allow_credentials
=
True
,
expose_headers
=
"*"
,
allow_headers
=
"*"
,
)
},
False
,
200
,
''
,
id
=
'with config without defaults'
),
pytest
.
param
(
None
,
True
,
200
,
''
,
id
=
'without config with defaults'
),
pytest
.
param
(
None
,
False
,
403
,
'CORS preflight request failed: no origins are allowed'
,
id
=
'without config and without defaults'
)
])
async
def
test_custom_handler
(
aiohttp_client
,
config
,
defaults
,
status
,
message
):
app
=
web
.
Application
()
app
.
router
.
add_route
(
"PUT"
,
"/{name}"
,
decorated_handler
(
config
,
defaults
))
cors
=
_setup
(
app
)
cors
.
apply
(
app
)
client
=
await
aiohttp_client
(
app
)
resp
=
await
client
.
options
(
"/user"
,
headers
=
{
hdrs
.
ORIGIN
:
"http://example.org"
,
hdrs
.
ACCESS_CONTROL_REQUEST_METHOD
:
"PUT"
}
)
assert
resp
.
status
==
status
data
=
await
resp
.
text
()
assert
data
==
message
async
def
test_cors_apply_only_on_custom_handler
(
aiohttp_client
):
app
=
web
.
Application
()
config
=
{
"*"
:
ResourceOptions
(
allow_credentials
=
True
,
expose_headers
=
"*"
,
allow_headers
=
"*"
,
)
}
app
.
router
.
add_route
(
"PUT"
,
"/{name}/decorated"
,
decorated_handler
(
config
))
app
.
router
.
add_route
(
"PUT"
,
"/{name}/non_decorated"
,
handler
)
cors
=
_setup
(
app
)
cors
.
apply
(
app
)
client
=
await
aiohttp_client
(
app
)
resp
=
await
client
.
options
(
"/user/non_decorated"
,
headers
=
{
hdrs
.
ORIGIN
:
"http://example.org"
,
hdrs
.
ACCESS_CONTROL_REQUEST_METHOD
:
"PUT"
}
)
assert
resp
.
status
==
405
data
=
await
resp
.
text
()
assert
data
==
"405: Method Not Allowed"
resp
=
await
client
.
options
(
"/user/decorated"
,
headers
=
{
hdrs
.
ORIGIN
:
"http://example.org"
,
hdrs
.
ACCESS_CONTROL_REQUEST_METHOD
:
"PUT"
}
)
assert
resp
.
status
==
200
data
=
await
resp
.
text
()
assert
data
==
""
async
def
test_custom_handler_with_defaults
(
aiohttp_client
):
app
=
web
.
Application
()
config
=
{
"*"
:
ResourceOptions
(
allow_credentials
=
True
,
expose_headers
=
"*"
,
allow_headers
=
"*"
,
)
}
app
.
router
.
add_route
(
"PUT"
,
"/{name}"
,
decorated_handler
())
cors
=
_setup
(
app
,
defaults
=
config
)
cors
.
apply
(
app
)
client
=
await
aiohttp_client
(
app
)
resp
=
await
client
.
options
(
"/user"
,
headers
=
{
hdrs
.
ORIGIN
:
"http://example.org"
,
hdrs
.
ACCESS_CONTROL_REQUEST_METHOD
:
"PUT"
}
)
assert
resp
.
status
==
200
data
=
await
resp
.
text
()
assert
data
==
""
async
def
test_custom_handler_config_rewrite_defaults
(
aiohttp_client
):
app
=
web
.
Application
()
options
=
ResourceOptions
(
allow_credentials
=
True
,
expose_headers
=
"*"
,
allow_headers
=
"*"
,
)
default_origin_url
=
"http://example.org"
handler_origin_url
=
"http://example2.org"
config
=
{
default_origin_url
:
options
}
handler_config
=
{
handler_origin_url
:
options
}
app
.
router
.
add_route
(
"PUT"
,
"/{name}"
,
decorated_handler
(
handler_config
))
cors
=
_setup
(
app
,
defaults
=
config
)
cors
.
apply
(
app
)
client
=
await
aiohttp_client
(
app
)
resp
=
await
client
.
options
(
"/user"
,
headers
=
{
hdrs
.
ORIGIN
:
default_origin_url
,
hdrs
.
ACCESS_CONTROL_REQUEST_METHOD
:
"PUT"
}
)
assert
resp
.
status
==
403
data
=
await
resp
.
text
()
assert
data
==
f
"CORS preflight request failed: origin '
{
default_origin_url
}
' is not allowed"
resp
=
await
client
.
options
(
"/user"
,
headers
=
{
hdrs
.
ORIGIN
:
handler_origin_url
,
hdrs
.
ACCESS_CONTROL_REQUEST_METHOD
:
"PUT"
}
)
assert
resp
.
status
==
200
data
=
await
resp
.
text
()
assert
data
==
""
# TODO: test requesting resources with not configured CORS.
# TODO: test wildcard origin in default config.
# TODO: test different combinations of ResourceOptions options.
...
...
Write
Preview
Supports
Markdown
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