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
Carlos Ramos Carreño
erp5
Commits
0eade2ca
Commit
0eade2ca
authored
Apr 01, 2024
by
Jérome Perrin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
oauth2_authorisation: py3
parent
c1159e94
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
66 additions
and
49 deletions
+66
-49
bt5/erp5_oauth2_authorisation/DocumentTemplateItem/portal_components/document.erp5.OAuth2AuthorisationServerConnector.py
...nents/document.erp5.OAuth2AuthorisationServerConnector.py
+14
-13
bt5/erp5_oauth2_authorisation/SkinTemplateItem/portal_skins/erp5_oauth2_authorisation/ERP5Site_authoriseOAuth2Client.py
...p5_oauth2_authorisation/ERP5Site_authoriseOAuth2Client.py
+4
-1
bt5/erp5_oauth2_authorisation/SkinTemplateItem/portal_skins/erp5_oauth2_authorisation/logged_in_once.py
.../portal_skins/erp5_oauth2_authorisation/logged_in_once.py
+2
-0
bt5/erp5_oauth2_authorisation/TestTemplateItem/portal_components/test.erp5.testOAuth2Server.py
...plateItem/portal_components/test.erp5.testOAuth2Server.py
+19
-13
bt5/erp5_oauth2_resource/DocumentTemplateItem/portal_components/document.erp5.OAuth2AuthorisationClientConnector.py
...nents/document.erp5.OAuth2AuthorisationClientConnector.py
+11
-8
product/ERP5/ERP5Site.py
product/ERP5/ERP5Site.py
+3
-2
product/ERP5Security/ERP5OAuth2ResourceServerPlugin.py
product/ERP5Security/ERP5OAuth2ResourceServerPlugin.py
+13
-12
No files found.
bt5/erp5_oauth2_authorisation/DocumentTemplateItem/portal_components/document.erp5.OAuth2AuthorisationServerConnector.py
View file @
0eade2ca
...
@@ -70,7 +70,7 @@ from DateTime import DateTime
...
@@ -70,7 +70,7 @@ from DateTime import DateTime
from
Products.ERP5Type
import
Permissions
from
Products.ERP5Type
import
Permissions
from
Products.ERP5Type.Message
import
translateString
from
Products.ERP5Type.Message
import
translateString
from
Products.ERP5Type.UnrestrictedMethod
import
super_user
from
Products.ERP5Type.UnrestrictedMethod
import
super_user
from
Products.ERP5Type.Utils
import
bytes2str
,
str2bytes
,
unicode2str
from
Products.ERP5Type.Utils
import
bytes2str
,
str2bytes
,
unicode2str
,
str2unicode
from
Products.ERP5Type.XMLObject
import
XMLObject
from
Products.ERP5Type.XMLObject
import
XMLObject
from
Products.ERP5Security.ERP5GroupManager
import
(
from
Products.ERP5Security.ERP5GroupManager
import
(
disableCache
as
ERP5GroupManager_disableCache
,
disableCache
as
ERP5GroupManager_disableCache
,
...
@@ -196,6 +196,7 @@ def substituteRequest(
...
@@ -196,6 +196,7 @@ def substituteRequest(
request_container
.
REQUEST
=
inner_request
request_container
.
REQUEST
=
inner_request
try
:
try
:
__traceback_info__
=
inner_request
__traceback_info__
=
inner_request
print
(
'inner_request =>'
,
inner_request
.
text
())
yield
inner_request
yield
inner_request
finally
:
finally
:
request_container
.
REQUEST
=
request_from_container
request_container
.
REQUEST
=
request_from_container
...
@@ -448,7 +449,7 @@ class _ERP5AuthorisationEndpoint(AuthorizationEndpoint):
...
@@ -448,7 +449,7 @@ class _ERP5AuthorisationEndpoint(AuthorizationEndpoint):
}
}
for
x
in
(
for
x
in
(
portal
.
portal_categories
.
resolveCategory
(
portal
.
portal_categories
.
resolveCategory
(
'oauth2_scope/'
+
y
.
encode
(
'utf-8'
),
'oauth2_scope/'
+
unicode2str
(
y
),
)
)
for
y
in
scope_list
for
y
in
scope_list
)
)
...
@@ -553,7 +554,7 @@ class _ERP5RequestValidator(RequestValidator):
...
@@ -553,7 +554,7 @@ class _ERP5RequestValidator(RequestValidator):
return
token_callable
(
**
kw
)
return
token_callable
(
**
kw
)
except
jwt
.
InvalidTokenError
:
except
jwt
.
InvalidTokenError
:
pass
pass
raise
raise
# pylint:disable=misplaced-bare-raise
def
client_authentication_required
(
self
,
request
,
*
args
,
**
kwargs
):
def
client_authentication_required
(
self
,
request
,
*
args
,
**
kwargs
):
# Use this method, which is called early on most endpoints, to setup request.client .
# Use this method, which is called early on most endpoints, to setup request.client .
...
@@ -699,7 +700,7 @@ class _ERP5RequestValidator(RequestValidator):
...
@@ -699,7 +700,7 @@ class _ERP5RequestValidator(RequestValidator):
client_value
=
request
.
client
.
erp5_client_value
,
client_value
=
request
.
client
.
erp5_client_value
,
redirect_uri
=
request
.
redirect_uri
,
redirect_uri
=
request
.
redirect_uri
,
scope_list
=
[
scope_list
=
[
x
.
encode
(
'utf-8'
)
unicode2str
(
x
)
for
x
in
request
.
scopes
for
x
in
request
.
scopes
],
],
code_challenge
=
request
.
code_challenge
,
code_challenge
=
request
.
code_challenge
,
...
@@ -859,13 +860,13 @@ def _callEndpoint(endpoint, self, REQUEST):
...
@@ -859,13 +860,13 @@ def _callEndpoint(endpoint, self, REQUEST):
# not have to care about intermediate proxies).
# not have to care about intermediate proxies).
request_header_dict
[
'X_FORWARDED_FOR'
]
=
REQUEST
.
getClientAddr
()
request_header_dict
[
'X_FORWARDED_FOR'
]
=
REQUEST
.
getClientAddr
()
request_body
=
REQUEST
.
get
(
'BODY'
)
request_body
=
REQUEST
.
get
(
'BODY'
)
if
request_body
is
None
and
content_type
==
'application/x-www-form-urlencoded'
:
if
(
not
request_body
)
and
content_type
==
'application/x-www-form-urlencoded'
:
# XXX: very imperfect, but should be good enough for OAuth2 usage:
# XXX: very imperfect, but should be good enough for OAuth2 usage:
# no standard OAuth2 POST field should be marshalled by Zope.
# no standard OAuth2 POST field should be marshalled by Zope.
request_body
=
urlencode
([
request_body
=
urlencode
([
(
x
,
y
)
(
x
,
y
)
for
x
,
y
in
six
.
iteritems
(
REQUEST
.
form
)
for
x
,
y
in
six
.
iteritems
(
REQUEST
.
form
)
if
isinstance
(
y
,
six
.
text_type
)
if
isinstance
(
y
,
six
.
string_types
)
])
])
uri
=
other
.
get
(
'URL'
,
''
)
uri
=
other
.
get
(
'URL'
,
''
)
query_string
=
environ
.
get
(
'QUERY_STRING'
)
query_string
=
environ
.
get
(
'QUERY_STRING'
)
...
@@ -1287,7 +1288,7 @@ class OAuth2AuthorisationServerConnector(XMLObject):
...
@@ -1287,7 +1288,7 @@ class OAuth2AuthorisationServerConnector(XMLObject):
ensure_ascii
(
token_dict
[
JWT_PAYLOAD_KEY
]),
ensure_ascii
(
token_dict
[
JWT_PAYLOAD_KEY
]),
)
)
return
token_dict
return
token_dict
raise
raise
# pylint:disable=misplaced-bare-raise
def
_getRefreshTokenDict
(
self
,
value
,
request
):
def
_getRefreshTokenDict
(
self
,
value
,
request
):
for
_
,
algorithm
,
symetric_key
in
self
.
__getRefreshTokenKeyList
():
for
_
,
algorithm
,
symetric_key
in
self
.
__getRefreshTokenKeyList
():
...
@@ -1309,14 +1310,14 @@ class OAuth2AuthorisationServerConnector(XMLObject):
...
@@ -1309,14 +1310,14 @@ class OAuth2AuthorisationServerConnector(XMLObject):
continue
continue
else
:
else
:
return
token_dict
return
token_dict
raise
raise
# pylint:disable=misplaced-bare-raise
def
_checkCustomTokenPolicy
(
self
,
token
,
request
):
def
_checkCustomTokenPolicy
(
self
,
token
,
request
):
"""
"""
Validate non-standard jwt claims against request.
Validate non-standard jwt claims against request.
"""
"""
if
not
isAddressInNetworkList
(
if
not
isAddressInNetworkList
(
address
=
request
.
headers
[
'X_FORWARDED_FOR'
].
decode
(
'utf-8'
),
address
=
str2unicode
(
request
.
headers
[
'X_FORWARDED_FOR'
]
),
network_list
=
token
[
JWT_CLAIM_NETWORK_LIST_KEY
],
network_list
=
token
[
JWT_CLAIM_NETWORK_LIST_KEY
],
):
):
raise
jwt
.
InvalidTokenError
raise
jwt
.
InvalidTokenError
...
@@ -1369,7 +1370,7 @@ class OAuth2AuthorisationServerConnector(XMLObject):
...
@@ -1369,7 +1370,7 @@ class OAuth2AuthorisationServerConnector(XMLObject):
continue
continue
else
:
else
:
return
token_dict
[
'iss'
]
return
token_dict
[
'iss'
]
raise
raise
# pylint:disable=misplaced-bare-raise
security
.
declarePrivate
(
'getRefreshTokenClientId'
)
security
.
declarePrivate
(
'getRefreshTokenClientId'
)
def
getRefreshTokenClientId
(
self
,
value
,
request
):
def
getRefreshTokenClientId
(
self
,
value
,
request
):
...
@@ -1395,13 +1396,13 @@ class OAuth2AuthorisationServerConnector(XMLObject):
...
@@ -1395,13 +1396,13 @@ class OAuth2AuthorisationServerConnector(XMLObject):
continue
continue
else
:
else
:
return
token_dict
[
'iss'
]
return
token_dict
[
'iss'
]
raise
raise
# pylint:disable=misplaced-bare-raise
def
_getSessionValueFromTokenDict
(
self
,
token_dict
):
def
_getSessionValueFromTokenDict
(
self
,
token_dict
):
session_value
=
self
.
_getSessionValue
(
session_value
=
self
.
_getSessionValue
(
token_dict
[
JWT_PAYLOAD_KEY
][
unicode2str
(
token_dict
[
JWT_PAYLOAD_KEY
][
JWT_PAYLOAD_AUTHORISATION_SESSION_ID_KEY
JWT_PAYLOAD_AUTHORISATION_SESSION_ID_KEY
]
.
encode
(
'utf-8'
),
]),
'validated'
,
'validated'
,
)
)
if
session_value
is
not
None
:
if
session_value
is
not
None
:
...
...
bt5/erp5_oauth2_authorisation/SkinTemplateItem/portal_skins/erp5_oauth2_authorisation/ERP5Site_authoriseOAuth2Client.py
View file @
0eade2ca
...
@@ -5,17 +5,20 @@ Mutate REQUEST to call standard OAuth2 /authorize endpoint from an ERP5 Form in
...
@@ -5,17 +5,20 @@ Mutate REQUEST to call standard OAuth2 /authorize endpoint from an ERP5 Form in
import
json
import
json
import
six
import
six
from
erp5.component.document.OAuth2AuthorisationServerConnector
import
substituteRequest
from
erp5.component.document.OAuth2AuthorisationServerConnector
import
substituteRequest
from
Products.ERP5Type.Utils
import
unicode2str
# XXX: Accessing REQUEST from acquisition is bad. But Base_callDialogMethod
# XXX: Accessing REQUEST from acquisition is bad. But Base_callDialogMethod
# does not propagate the request cleanly, so no other way so far.
# does not propagate the request cleanly, so no other way so far.
REQUEST
=
context
.
REQUEST
REQUEST
=
context
.
REQUEST
form
=
{
form
=
{
key
.
encode
(
'utf-8'
):
value
.
encode
(
'utf-8'
)
unicode2str
(
key
):
unicode2str
(
value
)
for
key
,
value
in
six
.
iteritems
(
json
.
loads
(
request_info_json
))
for
key
,
value
in
six
.
iteritems
(
json
.
loads
(
request_info_json
))
}
}
if
scope_list
:
if
scope_list
:
form
[
'scopes'
]
=
' '
.
join
(
scope_list
)
form
[
'scopes'
]
=
' '
.
join
(
scope_list
)
portal
=
context
.
getPortalObject
()
portal
=
context
.
getPortalObject
()
from
pprint
import
pprint
pprint
((
'substituteRequest form'
,
substituteRequest
))
with
substituteRequest
(
with
substituteRequest
(
context
=
portal
,
context
=
portal
,
request
=
REQUEST
,
request
=
REQUEST
,
...
...
bt5/erp5_oauth2_authorisation/SkinTemplateItem/portal_skins/erp5_oauth2_authorisation/logged_in_once.py
View file @
0eade2ca
...
@@ -44,6 +44,8 @@ form = dict(parse_qsl(parsed_came_from.query))
...
@@ -44,6 +44,8 @@ form = dict(parse_qsl(parsed_came_from.query))
login_retry_url
=
REQUEST
.
form
.
get
(
'login_retry_url'
)
login_retry_url
=
REQUEST
.
form
.
get
(
'login_retry_url'
)
if
login_retry_url
is
not
None
:
if
login_retry_url
is
not
None
:
form
[
'login_retry_url'
]
=
login_retry_url
form
[
'login_retry_url'
]
=
login_retry_url
from
pprint
import
pprint
pprint
((
'logged_in_once substituteRequest'
,
form
))
with
substituteRequest
(
with
substituteRequest
(
context
=
portal
,
context
=
portal
,
request
=
REQUEST
,
request
=
REQUEST
,
...
...
bt5/erp5_oauth2_authorisation/TestTemplateItem/portal_components/test.erp5.testOAuth2Server.py
View file @
0eade2ca
...
@@ -43,11 +43,12 @@ import random
...
@@ -43,11 +43,12 @@ import random
import
pprint
import
pprint
from
time
import
time
from
time
import
time
import
unittest
import
unittest
from
six.moves.urllib.parse
import
parse_qsl
,
quote
,
unquote
,
urlencode
,
urlsplit
,
urlunsplit
import
six.moves.urllib
as
urllib
from
six.moves.urllib.parse
import
parse_qsl
,
quote
,
urlencode
,
urlsplit
,
urlunsplit
from
AccessControl.SecurityManagement
import
getSecurityManager
,
setSecurityManager
from
AccessControl.SecurityManagement
import
getSecurityManager
,
setSecurityManager
from
DateTime
import
DateTime
from
DateTime
import
DateTime
from
Products.ERP5Type.tests.ERP5TypeTestCase
import
ERP5TypeTestCase
from
Products.ERP5Type.tests.ERP5TypeTestCase
import
ERP5TypeTestCase
from
Products.ERP5Type.Utils
import
bytes2str
,
str2bytes
from
Products.ERP5Type.Utils
import
bytes2str
,
str2bytes
,
unicode2str
from
Products.ERP5.ERP5Site
import
(
from
Products.ERP5.ERP5Site
import
(
ERP5_AUTHORISATION_EXTRACTOR_USERNAME_NAME
,
ERP5_AUTHORISATION_EXTRACTOR_USERNAME_NAME
,
ERP5_AUTHORISATION_EXTRACTOR_PASSWORD_NAME
,
ERP5_AUTHORISATION_EXTRACTOR_PASSWORD_NAME
,
...
@@ -83,14 +84,19 @@ class FormExtractor(HTMLParser):
...
@@ -83,14 +84,19 @@ class FormExtractor(HTMLParser):
elif
self
.
__in_form
and
tag
in
_HTML_FIELD_TAG_SET
:
elif
self
.
__in_form
and
tag
in
_HTML_FIELD_TAG_SET
:
self
.
form_list
[
-
1
][
1
].
append
((
self
.
form_list
[
-
1
][
1
].
append
((
attr_dict
[
'name'
],
attr_dict
[
'name'
],
attr_dict
.
get
(
'value'
,
''
).
encode
(
'utf-8'
),
unicode2str
(
attr_dict
.
get
(
'value'
,
''
))
))
))
def
handle_endtag
(
self
,
tag
):
def
handle_endtag
(
self
,
tag
):
if
tag
==
'form'
:
if
tag
==
'form'
:
self
.
__in_form
=
False
self
.
__in_form
=
False
def
error
(
self
,
message
):
raise
ValueError
(
message
)
class
TestOAuth2
(
ERP5TypeTestCase
):
class
TestOAuth2
(
ERP5TypeTestCase
):
# pylint:disable=unused-private-member
__cleanup_list
=
None
__cleanup_list
=
None
__port
=
None
__port
=
None
__query_trace
=
None
__query_trace
=
None
...
@@ -428,7 +434,7 @@ class TestOAuth2(ERP5TypeTestCase):
...
@@ -428,7 +434,7 @@ class TestOAuth2(ERP5TypeTestCase):
cookie_value
,
cookie_attributes
=
cookie_body
.
split
(
';'
,
1
)
cookie_value
,
cookie_attributes
=
cookie_body
.
split
(
';'
,
1
)
cookie_value
=
cookie_value
.
strip
(
'"'
)
cookie_value
=
cookie_value
.
strip
(
'"'
)
cookie_value_dict
=
{
cookie_value_dict
=
{
'value'
:
six
.
moves
.
urllib
.
parse
.
unquote
(
cookie_value
),
'value'
:
urllib
.
parse
.
unquote
(
cookie_value
),
}
}
for
cookie_attribute
in
cookie_attributes
.
split
(
';'
):
for
cookie_attribute
in
cookie_attributes
.
split
(
';'
):
cookie_attribute
=
cookie_attribute
.
lstrip
()
cookie_attribute
=
cookie_attribute
.
lstrip
()
...
@@ -497,7 +503,7 @@ class TestOAuth2(ERP5TypeTestCase):
...
@@ -497,7 +503,7 @@ class TestOAuth2(ERP5TypeTestCase):
b''
,
b''
,
# XXX: Tolerate the redirect URL being returned in the body.
# XXX: Tolerate the redirect URL being returned in the body.
# This is a bug, body should really be empty.
# This is a bug, body should really be empty.
header_dict
.
get
(
'location'
,
b''
),
str2bytes
(
header_dict
.
get
(
'location'
,
''
)
),
),
),
)
)
parsed_location
=
urlsplit
(
header_dict
.
get
(
'location'
,
''
))
parsed_location
=
urlsplit
(
header_dict
.
get
(
'location'
,
''
))
...
@@ -855,13 +861,13 @@ class TestOAuth2(ERP5TypeTestCase):
...
@@ -855,13 +861,13 @@ class TestOAuth2(ERP5TypeTestCase):
path
=
oauth2_server_connector
+
'/token'
,
path
=
oauth2_server_connector
+
'/token'
,
method
=
'POST'
,
method
=
'POST'
,
content_type
=
'application/x-www-form-urlencoded'
,
content_type
=
'application/x-www-form-urlencoded'
,
body
=
urlencode
({
body
=
str2bytes
(
urlencode
({
'grant_type'
:
'authorization_code'
,
'grant_type'
:
'authorization_code'
,
'code'
:
authorisation_code
,
'code'
:
authorisation_code
,
'client_id'
:
client_id
,
'client_id'
:
client_id
,
'code_verifier'
:
code_verifier
,
'code_verifier'
:
code_verifier
,
'redirect_uri'
:
_EXTERNAL_CLIENT_REDIRECT_URI
,
'redirect_uri'
:
_EXTERNAL_CLIENT_REDIRECT_URI
,
}),
})
)
,
)
)
time_after
=
int
(
time
())
time_after
=
int
(
time
())
self
.
assertEqual
(
status
,
200
,
response
)
self
.
assertEqual
(
status
,
200
,
response
)
...
@@ -883,10 +889,10 @@ class TestOAuth2(ERP5TypeTestCase):
...
@@ -883,10 +889,10 @@ class TestOAuth2(ERP5TypeTestCase):
path
=
oauth2_server_connector
+
'/token'
,
path
=
oauth2_server_connector
+
'/token'
,
method
=
'POST'
,
method
=
'POST'
,
content_type
=
'application/x-www-form-urlencoded'
,
content_type
=
'application/x-www-form-urlencoded'
,
body
=
urlencode
({
body
=
str2bytes
(
urlencode
({
'grant_type'
:
'refresh_token'
,
'grant_type'
:
'refresh_token'
,
'refresh_token'
:
refresh_token
,
'refresh_token'
:
refresh_token
,
}),
})
)
,
)
)
self
.
assertEqual
(
status
,
200
)
self
.
assertEqual
(
status
,
200
)
self
.
assertEqual
(
cookie_dict
,
{})
self
.
assertEqual
(
cookie_dict
,
{})
...
@@ -900,10 +906,10 @@ class TestOAuth2(ERP5TypeTestCase):
...
@@ -900,10 +906,10 @@ class TestOAuth2(ERP5TypeTestCase):
path
=
oauth2_server_connector
+
'/revoke'
,
path
=
oauth2_server_connector
+
'/revoke'
,
method
=
'POST'
,
method
=
'POST'
,
content_type
=
'application/x-www-form-urlencoded'
,
content_type
=
'application/x-www-form-urlencoded'
,
body
=
urlencode
({
body
=
str2bytes
(
urlencode
({
'token_type_hint'
:
'refresh_token'
,
'token_type_hint'
:
'refresh_token'
,
'token'
:
refresh_token
,
'token'
:
refresh_token
,
}),
})
)
,
)
)
self
.
assertEqual
(
status
,
200
)
self
.
assertEqual
(
status
,
200
)
self
.
assertEqual
(
cookie_dict
,
{})
self
.
assertEqual
(
cookie_dict
,
{})
...
@@ -916,10 +922,10 @@ class TestOAuth2(ERP5TypeTestCase):
...
@@ -916,10 +922,10 @@ class TestOAuth2(ERP5TypeTestCase):
path
=
oauth2_server_connector
+
'/token'
,
path
=
oauth2_server_connector
+
'/token'
,
method
=
'POST'
,
method
=
'POST'
,
content_type
=
'application/x-www-form-urlencoded'
,
content_type
=
'application/x-www-form-urlencoded'
,
body
=
urlencode
({
body
=
str2bytes
(
urlencode
({
'grant_type'
:
'refresh_token'
,
'grant_type'
:
'refresh_token'
,
'refresh_token'
:
refresh_token
,
'refresh_token'
:
refresh_token
,
}),
})
)
,
)
)
self
.
assertEqual
(
status
,
400
)
self
.
assertEqual
(
status
,
400
)
self
.
assertEqual
(
cookie_dict
,
{})
self
.
assertEqual
(
cookie_dict
,
{})
...
...
bt5/erp5_oauth2_resource/DocumentTemplateItem/portal_components/document.erp5.OAuth2AuthorisationClientConnector.py
View file @
0eade2ca
...
@@ -51,7 +51,7 @@ from OFS.Traversable import NotFound
...
@@ -51,7 +51,7 @@ from OFS.Traversable import NotFound
from
Products.ERP5Type
import
Permissions
from
Products.ERP5Type
import
Permissions
from
Products.ERP5Type.XMLObject
import
XMLObject
from
Products.ERP5Type.XMLObject
import
XMLObject
from
Products.ERP5Type.Timeout
import
getTimeLeft
from
Products.ERP5Type.Timeout
import
getTimeLeft
from
Products.ERP5Type.Utils
import
bytes2str
,
str2bytes
,
str2unicode
from
Products.ERP5Type.Utils
import
bytes2str
,
unicode2str
,
str2bytes
,
str2unicode
from
Products.ERP5Security.ERP5OAuth2ResourceServerPlugin
import
(
from
Products.ERP5Security.ERP5OAuth2ResourceServerPlugin
import
(
OAuth2AuthorisationClientConnectorMixIn
,
OAuth2AuthorisationClientConnectorMixIn
,
ERP5OAuth2ResourceServerPlugin
,
ERP5OAuth2ResourceServerPlugin
,
...
@@ -227,13 +227,16 @@ class _OAuth2AuthorisationServerProxy(object):
...
@@ -227,13 +227,16 @@ class _OAuth2AuthorisationServerProxy(object):
)
)
else
:
else
:
Connection
=
HTTPConnection
Connection
=
HTTPConnection
if
six
.
PY2
:
# Changed in version 3.4: The strict parameter was removed.
# HTTP 0.9-style "Simple Responses" are no longer supported.
Connection
=
functools
.
partial
(
Connection
,
strict
=
True
)
timeout
=
getTimeLeft
()
timeout
=
getTimeLeft
()
if
timeout
is
None
or
timeout
>
self
.
_timeout
:
if
timeout
is
None
or
timeout
>
self
.
_timeout
:
timeout
=
self
.
_timeout
timeout
=
self
.
_timeout
http_connection
=
Connection
(
http_connection
=
Connection
(
host
=
parsed_url
.
hostname
,
host
=
parsed_url
.
hostname
,
port
=
parsed_url
.
port
,
port
=
parsed_url
.
port
,
strict
=
True
,
timeout
=
timeout
,
timeout
=
timeout
,
source_address
=
self
.
_bind_address
,
source_address
=
self
.
_bind_address
,
)
)
...
@@ -274,7 +277,7 @@ class _OAuth2AuthorisationServerProxy(object):
...
@@ -274,7 +277,7 @@ class _OAuth2AuthorisationServerProxy(object):
def
_queryOAuth2
(
self
,
method
,
REQUEST
,
RESPONSE
):
def
_queryOAuth2
(
self
,
method
,
REQUEST
,
RESPONSE
):
header_dict
,
body
,
status
=
self
.
_query
(
header_dict
,
body
,
status
=
self
.
_query
(
method
,
method
,
body
=
urlencode
(
REQUEST
.
form
.
items
()
),
body
=
urlencode
(
REQUEST
.
form
),
header_dict
=
{
header_dict
=
{
'CONTENT_TYPE'
:
REQUEST
.
environ
[
'CONTENT_TYPE'
],
'CONTENT_TYPE'
:
REQUEST
.
environ
[
'CONTENT_TYPE'
],
},
},
...
@@ -313,7 +316,7 @@ class _OAuth2AuthorisationServerProxy(object):
...
@@ -313,7 +316,7 @@ class _OAuth2AuthorisationServerProxy(object):
def
getAccessTokenSignatureAlgorithmAndPublicKeyList
(
self
):
def
getAccessTokenSignatureAlgorithmAndPublicKeyList
(
self
):
return
tuple
(
return
tuple
(
(
signature_algorithm
.
encode
(
'ascii'
),
public_key
.
encode
(
'ascii'
))
(
unicode2str
(
signature_algorithm
),
unicode2str
(
public_key
))
for
signature_algorithm
,
public_key
in
self
.
_queryERP5
(
for
signature_algorithm
,
public_key
in
self
.
_queryERP5
(
'getAccessTokenSignatureAlgorithmAndPublicKeyList'
,
'getAccessTokenSignatureAlgorithmAndPublicKeyList'
,
)
)
...
@@ -864,7 +867,7 @@ class OAuth2AuthorisationClientConnector(
...
@@ -864,7 +867,7 @@ class OAuth2AuthorisationClientConnector(
try
:
try
:
state_dict
=
json
.
loads
(
state_dict
=
json
.
loads
(
self
.
__getMultiFernet
().
decrypt
(
self
.
__getMultiFernet
().
decrypt
(
st
ate
,
st
r2bytes
(
state
)
,
ttl
=
self
.
_SESSION_STATE_VALIDITY
,
ttl
=
self
.
_SESSION_STATE_VALIDITY
,
),
),
)
)
...
@@ -882,7 +885,7 @@ class OAuth2AuthorisationClientConnector(
...
@@ -882,7 +885,7 @@ class OAuth2AuthorisationClientConnector(
came_from
=
state_dict
.
get
(
_STATE_CAME_FROM_NAME
)
came_from
=
state_dict
.
get
(
_STATE_CAME_FROM_NAME
)
if
came_from
:
if
came_from
:
context
=
self
# whatever
context
=
self
# whatever
kw
[
'redirect_url'
]
=
came_from
.
encode
(
'utf-8'
)
kw
[
'redirect_url'
]
=
unicode2str
(
came_from
)
else
:
else
:
context
=
self
.
_getNeutralContextValue
()
context
=
self
.
_getNeutralContextValue
()
context
.
Base_redirect
(
**
kw
)
context
.
Base_redirect
(
**
kw
)
...
@@ -930,7 +933,7 @@ class OAuth2AuthorisationClientConnector(
...
@@ -930,7 +933,7 @@ class OAuth2AuthorisationClientConnector(
REQUEST
=
REQUEST
,
REQUEST
=
REQUEST
,
RESPONSE
=
RESPONSE
,
RESPONSE
=
RESPONSE
,
)
)
identifier_from_state
=
state_dict
[
_STATE_IDENTIFIER_NAME
].
encode
(
'ascii'
)
identifier_from_state
=
unicode2str
(
state_dict
[
_STATE_IDENTIFIER_NAME
]
)
for
(
for
(
state_cookie_name
,
state_cookie_name
,
identifier_from_cookie
,
identifier_from_cookie
,
...
@@ -965,7 +968,7 @@ class OAuth2AuthorisationClientConnector(
...
@@ -965,7 +968,7 @@ class OAuth2AuthorisationClientConnector(
'code'
:
code
,
'code'
:
code
,
'redirect_uri'
:
self
.
getRedirectUri
(),
'redirect_uri'
:
self
.
getRedirectUri
(),
'client_id'
:
self
.
getReference
(),
'client_id'
:
self
.
getReference
(),
'code_verifier'
:
state_dict
[
_STATE_CODE_VERIFIER_NAME
].
encode
(
'ascii'
),
'code_verifier'
:
unicode2str
(
state_dict
[
_STATE_CODE_VERIFIER_NAME
])
},
},
)
)
access_token
,
_
,
error_message
=
self
.
_setCookieFromTokenResponse
(
access_token
,
_
,
error_message
=
self
.
_setCookieFromTokenResponse
(
...
...
product/ERP5/ERP5Site.py
View file @
0eade2ca
...
@@ -46,6 +46,7 @@ from Products.ERP5Type.TransactionalVariable import \
...
@@ -46,6 +46,7 @@ from Products.ERP5Type.TransactionalVariable import \
getTransactionalVariable
,
TransactionalResource
getTransactionalVariable
,
TransactionalResource
from
Products.ERP5Type.dynamic.portal_type_class
import
synchronizeDynamicModules
from
Products.ERP5Type.dynamic.portal_type_class
import
synchronizeDynamicModules
from
Products.ERP5Type.mixin.response_header_generator
import
ResponseHeaderGenerator
from
Products.ERP5Type.mixin.response_header_generator
import
ResponseHeaderGenerator
from
Products.ERP5Type.Utils
import
str2bytes
,
bytes2str
from
zLOG
import
LOG
,
INFO
,
WARNING
,
ERROR
from
zLOG
import
LOG
,
INFO
,
WARNING
,
ERROR
from
zExceptions
import
BadRequest
from
zExceptions
import
BadRequest
...
@@ -248,10 +249,10 @@ class AutorisationExtractorBeforeTraverseHook(object):
...
@@ -248,10 +249,10 @@ class AutorisationExtractorBeforeTraverseHook(object):
ERP5_AUTHORISATION_EXTRACTOR_PASSWORD_NAME
in
form_dict
ERP5_AUTHORISATION_EXTRACTOR_PASSWORD_NAME
in
form_dict
):
):
username
=
form_dict
[
ERP5_AUTHORISATION_EXTRACTOR_USERNAME_NAME
]
username
=
form_dict
[
ERP5_AUTHORISATION_EXTRACTOR_USERNAME_NAME
]
request
.
_auth
=
'Basic '
+
b
ase64
.
b64encode
(
'%s:%s'
%
(
request
.
_auth
=
'Basic '
+
b
ytes2str
(
base64
.
b64encode
(
str2bytes
(
'%s:%s'
%
(
username
,
username
,
form_dict
[
ERP5_AUTHORISATION_EXTRACTOR_PASSWORD_NAME
],
form_dict
[
ERP5_AUTHORISATION_EXTRACTOR_PASSWORD_NAME
],
))
))
))
request
.
response
.
_auth
=
1
request
.
response
.
_auth
=
1
_setUserNameForAccessLog
(
username
,
request
)
_setUserNameForAccessLog
(
username
,
request
)
...
...
product/ERP5Security/ERP5OAuth2ResourceServerPlugin.py
View file @
0eade2ca
...
@@ -48,6 +48,7 @@ from Products.PluggableAuthService.interfaces.plugins import (
...
@@ -48,6 +48,7 @@ from Products.PluggableAuthService.interfaces.plugins import (
)
)
from
Products.ERP5Security
import
_setUserNameForAccessLog
from
Products.ERP5Security
import
_setUserNameForAccessLog
from
Products.ERP5Type.Globals
import
InitializeClass
from
Products.ERP5Type.Globals
import
InitializeClass
from
Products.ERP5Type.Utils
import
bytes2str
,
str2bytes
,
str2unicode
,
unicode2str
# Public constants. Must not change once deployed.
# Public constants. Must not change once deployed.
...
@@ -109,11 +110,11 @@ def encodeAccessTokenPayload(payload):
...
@@ -109,11 +110,11 @@ def encodeAccessTokenPayload(payload):
Encode given json-safe value into a format suitable for
Encode given json-safe value into a format suitable for
decodeAccessTokenPayload.
decodeAccessTokenPayload.
"""
"""
return
base64
.
urlsafe_b64encode
(
return
b
ytes2str
(
b
ase64
.
urlsafe_b64encode
(
zlib
.
compress
(
zlib
.
compress
(
json
.
dumps
(
payload
),
str2bytes
(
json
.
dumps
(
payload
)
),
),
),
)
)
)
def
decodeAccessTokenPayload
(
encoded_payload
):
def
decodeAccessTokenPayload
(
encoded_payload
):
"""
"""
...
@@ -353,7 +354,7 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin):
...
@@ -353,7 +354,7 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin):
if
client_id
is
None
:
if
client_id
is
None
:
# Peek into token (without checking its signature) to guess the client_id
# Peek into token (without checking its signature) to guess the client_id
# to look for.
# to look for.
client_id
=
jwt
.
decode
(
client_id
=
unicode2str
(
jwt
.
decode
(
raw_token
,
raw_token
,
# no key.
# no key.
# any algorithm is fine.
# any algorithm is fine.
...
@@ -361,7 +362,7 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin):
...
@@ -361,7 +362,7 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin):
'verify_signature'
:
False
,
'verify_signature'
:
False
,
'verify_exp'
:
False
,
'verify_exp'
:
False
,
},
},
)[
'iss'
]
.
encode
(
'utf-8'
)
)[
'iss'
])
assert
client_id
is
not
None
assert
client_id
is
not
None
web_service_value_list
=
list
(
self
.
__iterClientConnectorValue
(
web_service_value_list
=
list
(
self
.
__iterClientConnectorValue
(
client_id
=
client_id
,
client_id
=
client_id
,
...
@@ -425,7 +426,7 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin):
...
@@ -425,7 +426,7 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin):
The schema of this dictionary is purely an internal implementation detail
The schema of this dictionary is purely an internal implementation detail
of this plugin.
of this plugin.
"""
"""
client_address
=
request
.
getClientAddr
().
decode
(
'utf-8'
)
client_address
=
str2unicode
(
request
.
getClientAddr
()
)
token
=
self
.
__checkTokenSignature
(
access_token
)
token
=
self
.
__checkTokenSignature
(
access_token
)
if
token
is
None
and
can_update_key
:
if
token
is
None
and
can_update_key
:
self
.
__updateAccessTokenSignatureKeyList
(
request
=
request
)
self
.
__updateAccessTokenSignatureKeyList
(
request
=
request
)
...
@@ -440,9 +441,9 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin):
...
@@ -440,9 +441,9 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin):
return
return
# JWT is known valid. Access its content.
# JWT is known valid. Access its content.
token_payload
=
decodeAccessTokenPayload
(
token_payload
=
decodeAccessTokenPayload
(
token
[
JWT_PAYLOAD_KEY
].
encode
(
'ascii'
),
bytes2str
(
token
[
JWT_PAYLOAD_KEY
].
encode
(
'ascii'
)
),
)
)
client_id
=
token
[
'iss'
].
encode
(
'utf-8'
)
client_id
=
unicode2str
(
token
[
'iss'
]
)
if
self
.
__getWebServiceValue
(
if
self
.
__getWebServiceValue
(
client_id
=
client_id
,
client_id
=
client_id
,
).
getSessionVersion
(
).
getSessionVersion
(
...
@@ -452,8 +453,8 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin):
...
@@ -452,8 +453,8 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin):
return
return
return
{
return
{
_PRIVATE_EXTRACTED_KEY
:
(
_PRIVATE_EXTRACTED_KEY
:
(
token_payload
[
JWT_PAYLOAD_USER_ID_KEY
].
encode
(
'utf-8'
),
unicode2str
(
token_payload
[
JWT_PAYLOAD_USER_ID_KEY
]
),
token_payload
[
JWT_PAYLOAD_USER_CAPTION_KEY
].
encode
(
'utf-8'
),
unicode2str
(
token_payload
[
JWT_PAYLOAD_USER_CAPTION_KEY
]
),
),
),
_PRIVATE_TOKEN_KEY
:
(
access_token
,
refresh_token
),
_PRIVATE_TOKEN_KEY
:
(
access_token
,
refresh_token
),
_PRIVATE_CLIENT_ID
:
client_id
,
_PRIVATE_CLIENT_ID
:
client_id
,
...
@@ -467,10 +468,10 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin):
...
@@ -467,10 +468,10 @@ class ERP5OAuth2ResourceServerPlugin(BasePlugin):
]
or
''
,
]
or
''
,
},
},
_PRIVATE_GROUP_LIST_KEY
:
tuple
(
_PRIVATE_GROUP_LIST_KEY
:
tuple
(
x
.
encode
(
'utf-8'
)
for
x
in
token_payload
[
JWT_PAYLOAD_GROUP_LIST_KEY
]
unicode2str
(
x
)
for
x
in
token_payload
[
JWT_PAYLOAD_GROUP_LIST_KEY
]
),
),
_PRIVATE_ROLE_LIST_KEY
:
tuple
(
_PRIVATE_ROLE_LIST_KEY
:
tuple
(
x
.
encode
(
'utf-8'
)
for
x
in
token_payload
[
JWT_PAYLOAD_ROLE_LIST_KEY
]
unicode2str
(
x
)
for
x
in
token_payload
[
JWT_PAYLOAD_ROLE_LIST_KEY
]
),
),
}
}
...
...
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