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
Labels
Merge Requests
7
Merge Requests
7
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Jobs
Commits
Open sidebar
Jérome Perrin
erp5
Commits
a11b313c
Commit
a11b313c
authored
Oct 19, 2022
by
Kazuhiko Shiozaki
Committed by
Jérome Perrin
May 29, 2024
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
py2/py3: import from six.moves.
parent
515a2a4e
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
52 additions
and
54 deletions
+52
-54
bt5/erp5_oauth2_authorisation/DocumentTemplateItem/portal_components/document.erp5.OAuth2AuthorisationServerConnector.py
...nents/document.erp5.OAuth2AuthorisationServerConnector.py
+16
-17
bt5/erp5_oauth2_authorisation/SkinTemplateItem/portal_skins/erp5_oauth2_authorisation/ERP5Site_getClientIdFromLoginOnceCameFrom.py
...uthorisation/ERP5Site_getClientIdFromLoginOnceCameFrom.py
+2
-2
bt5/erp5_oauth2_authorisation/SkinTemplateItem/portal_skins/erp5_oauth2_authorisation/ERP5Site_isOAuth2CameFrom.py
...ns/erp5_oauth2_authorisation/ERP5Site_isOAuth2CameFrom.py
+2
-2
bt5/erp5_oauth2_authorisation/SkinTemplateItem/portal_skins/erp5_oauth2_authorisation/ERP5Site_retryOAuth2Authorisation.py
...oauth2_authorisation/ERP5Site_retryOAuth2Authorisation.py
+3
-3
bt5/erp5_oauth2_authorisation/SkinTemplateItem/portal_skins/erp5_oauth2_authorisation/logged_in_once.py
.../portal_skins/erp5_oauth2_authorisation/logged_in_once.py
+3
-3
bt5/erp5_oauth2_resource/DocumentTemplateItem/portal_components/document.erp5.OAuth2AuthorisationClientConnector.py
...nents/document.erp5.OAuth2AuthorisationClientConnector.py
+13
-14
bt5/erp5_oauth2_resource/SkinTemplateItem/portal_skins/erp5_oauth2_resource/ERP5Site_preventLoginAttemptRetry.py
...erp5_oauth2_resource/ERP5Site_preventLoginAttemptRetry.py
+5
-6
bt5/erp5_web_renderjs_ui/SkinTemplateItem/portal_skins/erp5_web_renderjs_ui/login_form.py
...plateItem/portal_skins/erp5_web_renderjs_ui/login_form.py
+2
-2
bt5/erp5_web_service/MixinTemplateItem/portal_components/mixin.erp5.RESTAPIClientConnectorMixin.py
...rtal_components/mixin.erp5.RESTAPIClientConnectorMixin.py
+6
-5
No files found.
bt5/erp5_oauth2_authorisation/DocumentTemplateItem/portal_components/document.erp5.OAuth2AuthorisationServerConnector.py
View file @
a11b313c
...
...
@@ -31,8 +31,7 @@ from io import BytesIO
import
json
from
os
import
urandom
from
time
import
time
import
urllib
import
urlparse
from
six.moves.urllib.parse
import
parse_qsl
,
urlencode
,
urlsplit
,
urlunsplit
import
uuid
from
cryptography.hazmat.backends
import
default_backend
from
cryptography
import
fernet
...
...
@@ -145,7 +144,7 @@ def substituteRequest(
environ
=
request
.
environ
inner_environ_dict
=
environ
.
copy
()
inner_environ_dict
[
'REQUEST_METHOD'
]
=
method
inner_environ_dict
[
'QUERY_STRING'
]
=
url
lib
.
url
encode
(
query_list
)
inner_environ_dict
[
'QUERY_STRING'
]
=
urlencode
(
query_list
)
if
request
.
_auth
:
inner_environ_dict
[
'HTTP_AUTHORIZATION'
]
=
request
.
_auth
...
...
@@ -256,18 +255,18 @@ class _ERP5AuthorisationEndpoint(AuthorizationEndpoint):
if
is_local_client
and
self
.
__login_retry_url
:
# ...with a local resource server, redirect user agent to
# the provided login URL.
split_login_retry_url
=
url
parse
.
url
split
(
self
.
__login_retry_url
)
split_login_retry_url
=
urlsplit
(
self
.
__login_retry_url
)
return
(
(
(
'Location'
,
url
parse
.
url
unsplit
((
urlunsplit
((
split_login_retry_url
.
scheme
,
split_login_retry_url
.
netloc
,
split_login_retry_url
.
path
,
url
lib
.
url
encode
([
urlencode
([
(
x
,
y
)
for
x
,
y
in
urlparse
.
parse_qsl
(
split_login_retry_url
.
query
)
for
x
,
y
in
parse_qsl
(
split_login_retry_url
.
query
)
if
x
!=
'portal_status_message'
]
+
[(
'portal_status_message'
,
...
...
@@ -299,7 +298,7 @@ class _ERP5AuthorisationEndpoint(AuthorizationEndpoint):
credentials
=
credentials
,
)
if
authorization_status
==
302
and
is_local_client
:
split_location
=
url
parse
.
url
split
(
authorization_header_dict
[
'Location'
])
split_location
=
urlsplit
(
authorization_header_dict
[
'Location'
])
# XXX: to cut down on code complexity, this code has strong expectations on what location is.
_
,
client_connector_id
,
method_id
=
split_location
.
path
.
rsplit
(
'/'
,
2
)
if
method_id
!=
'loggedIn'
:
...
...
@@ -307,7 +306,7 @@ class _ERP5AuthorisationEndpoint(AuthorizationEndpoint):
client_connector_value
=
client_value
.
getParentValue
().
getParentValue
()[
client_connector_id
]
if
client_connector_value
.
getPortalType
()
!=
'OAuth2 Authorisation Client Connector'
:
raise
ValueError
(
split_location
.
path
)
query_list
=
urlparse
.
parse_qsl
(
split_location
.
query
)
query_list
=
parse_qsl
(
split_location
.
query
)
# Note: query string generation should not have produce any duplicate
# entries, so convert into a dict for code simplicity.
query_dict
=
{
...
...
@@ -361,7 +360,7 @@ class _ERP5AuthorisationEndpoint(AuthorizationEndpoint):
for
key
,
value
in
six
.
iteritems
(
request_info_dict
):
if
value
is
None
:
continue
if
not
isinstance
(
value
,
basestring
):
if
not
isinstance
(
value
,
six
.
text_type
):
raise
TypeError
((
key
,
repr
(
value
)))
new_request_info_dict
[
key
]
=
value
inner_response
=
HTTPResponse
(
stdout
=
None
,
stderr
=
None
)
...
...
@@ -385,7 +384,7 @@ class _ERP5AuthorisationEndpoint(AuthorizationEndpoint):
# Use the internal path back to us so it can be traversed to while
# still in the just-authenticated request.
(
self
.
__server_connector_path
+
'?'
+
url
parse
.
url
split
(
uri
).
query
self
.
__server_connector_path
+
'?'
+
urlsplit
(
uri
).
query
)
if
is_local_client
else
# Use the external URL back to us so user can be redirected to it,
# as they are then authenticated over multiple requests.
...
...
@@ -407,8 +406,8 @@ class _ERP5AuthorisationEndpoint(AuthorizationEndpoint):
login_form
=
neutral_context_value
.
login_form
portal_status_message_list
=
[
value
for
name
,
value
in
urlparse
.
parse_qsl
(
url
parse
.
url
split
(
came_from
).
query
,
for
name
,
value
in
parse_qsl
(
urlsplit
(
came_from
).
query
,
)
if
name
==
'portal_status_message'
]
...
...
@@ -763,8 +762,8 @@ class _ERP5RequestValidator(RequestValidator):
# redirect_uri path, but it may be under an extra layer of VirtualHost Monster
# magic.
# Client is declared local, accept any redirect URI on our scheme and netloc.
split_my_url
=
url
parse
.
url
split
(
client_value
.
absolute_url
())
split_redirect_uri
=
url
parse
.
url
split
(
redirect_uri
)
split_my_url
=
urlsplit
(
client_value
.
absolute_url
())
split_redirect_uri
=
urlsplit
(
redirect_uri
)
return
(
split_my_url
.
scheme
==
split_redirect_uri
.
scheme
and
split_my_url
.
netloc
==
split_redirect_uri
.
netloc
...
...
@@ -854,10 +853,10 @@ def _callEndpoint(endpoint, self, REQUEST):
if
request_body
is
None
and
content_type
==
'application/x-www-form-urlencoded'
:
# XXX: very imperfect, but should be good enough for OAuth2 usage:
# no standard OAuth2 POST field should be marshalled by Zope.
request_body
=
url
lib
.
url
encode
([
request_body
=
urlencode
([
(
x
,
y
)
for
x
,
y
in
six
.
iteritems
(
REQUEST
.
form
)
if
isinstance
(
y
,
basestring
)
if
isinstance
(
y
,
six
.
text_type
)
])
uri
=
other
.
get
(
'URL'
,
''
)
query_string
=
environ
.
get
(
'QUERY_STRING'
)
...
...
bt5/erp5_oauth2_authorisation/SkinTemplateItem/portal_skins/erp5_oauth2_authorisation/ERP5Site_getClientIdFromLoginOnceCameFrom.py
View file @
a11b313c
...
...
@@ -6,14 +6,14 @@ Once the user is authenticated, the same value can be accessed with:
from AccessControl import getSecurityManager
getSecurityManager().getUser().getClientId()
"""
import
urlparse
from
six.moves.urllib.parse
import
parse_qsl
,
urlsplit
# The came_from for login_once_form is special: it has no scheme, no netloc, a path and a query.
# Verify this so caller knows if they are providing the wrong value.
if
not
context
.
ERP5Site_isOAuth2CameFrom
(
came_from
=
came_from
):
raise
ValueError
result
,
=
[
value
for
name
,
value
in
urlparse
.
parse_qsl
(
urlparse
.
urlsplit
(
came_from
).
query
)
for
name
,
value
in
parse_qsl
(
urlsplit
(
came_from
).
query
)
if
name
==
'client_id'
]
return
result
bt5/erp5_oauth2_authorisation/SkinTemplateItem/portal_skins/erp5_oauth2_authorisation/ERP5Site_isOAuth2CameFrom.py
View file @
a11b313c
...
...
@@ -2,8 +2,8 @@
OAuth2's /authorize endpoint produces a very specific format of came_from, with very specific meaning (not a real URL).
This script returns True value if given such came_from, and False otherwise.
"""
import
urlparse
parsed_came_from
=
url
parse
.
url
split
(
came_from
)
from
six.moves.urllib.parse
import
urlsplit
parsed_came_from
=
urlsplit
(
came_from
)
return
bool
(
not
parsed_came_from
.
scheme
and
not
parsed_came_from
.
netloc
and
...
...
bt5/erp5_oauth2_authorisation/SkinTemplateItem/portal_skins/erp5_oauth2_authorisation/ERP5Site_retryOAuth2Authorisation.py
View file @
a11b313c
...
...
@@ -3,16 +3,16 @@
Retry calling /authorize using the values in came_from
(which a previous call to /authorize generated, and is not a traditional came_from).
"""
import
urlparse
from
six.moves.urllib.parse
import
parse_qsl
,
urlsplit
from
erp5.component.document.OAuth2AuthorisationServerConnector
import
substituteRequest
if
not
context
.
ERP5Site_isOAuth2CameFrom
(
came_from
):
# came_from is broken, there is no way to call /authorize , so escape to wherever.
context
.
Base_redirect
()
return
parsed_came_from
=
url
parse
.
url
split
(
came_from
)
parsed_came_from
=
urlsplit
(
came_from
)
query_list
=
[
(
key
,
value
)
for
key
,
value
in
urlparse
.
parse_qsl
(
parsed_came_from
.
query
)
for
key
,
value
in
parse_qsl
(
parsed_came_from
.
query
)
if
key
!=
'portal_status_message'
]
if
portal_status_message
is
not
None
:
...
...
bt5/erp5_oauth2_authorisation/SkinTemplateItem/portal_skins/erp5_oauth2_authorisation/logged_in_once.py
View file @
a11b313c
...
...
@@ -3,7 +3,7 @@
Similar to logged_in, but user authentication will only last for current request if nothing else is done.
So came_from must be honoured within the current request, and not redirected to.
"""
import
urlparse
from
six.moves.urllib.parse
import
parse_qsl
,
urlsplit
from
erp5.component.document.OAuth2AuthorisationServerConnector
import
substituteRequest
portal
=
context
.
getPortalObject
()
if
portal
.
portal_skins
.
updateSkinCookie
():
...
...
@@ -28,7 +28,7 @@ if not came_from or not context.ERP5Site_isOAuth2CameFrom(came_from):
# came_from is broken, there is no way to call authorize, so escape to wherever.
context
.
Base_redirect
()
return
parsed_came_from
=
url
parse
.
url
split
(
came_from
)
parsed_came_from
=
urlsplit
(
came_from
)
# Turn the ZODB path from came_from into a relative URL and base it on context (and not portal) to
# work as expected from within Web Sites without Virtual Host Monster relocating them above portal.
connector_value
=
context
.
restrictedTraverse
(
parsed_came_from
.
path
.
lstrip
(
'/'
))
...
...
@@ -40,7 +40,7 @@ if (
return
# Note: query string generation should not have produce any duplicate
# entries, so directly use to update form dict for code simplicity.
form
=
dict
(
urlparse
.
parse_qsl
(
parsed_came_from
.
query
))
form
=
dict
(
parse_qsl
(
parsed_came_from
.
query
))
login_retry_url
=
REQUEST
.
form
.
get
(
'login_retry_url'
)
if
login_retry_url
is
not
None
:
form
[
'login_retry_url'
]
=
login_retry_url
...
...
bt5/erp5_oauth2_resource/DocumentTemplateItem/portal_components/document.erp5.OAuth2AuthorisationClientConnector.py
View file @
a11b313c
...
...
@@ -31,13 +31,12 @@ import email.utils
import
functools
import
hashlib
import
hmac
import
httplib
from
six.moves.http_client
import
HTTPConnection
,
HTTPSConnection
import
json
from
os
import
urandom
import
random
from
time
import
time
import
urllib
import
urlparse
from
six.moves.urllib.parse
import
urlencode
,
urljoin
,
urlparse
import
ssl
from
AccessControl
import
(
ClassSecurityInfo
,
...
...
@@ -191,7 +190,7 @@ class _OAuth2AuthorisationServerProxy(object):
ca_certificate_pem
,
insecure
,
):
scheme
=
url
parse
.
url
split
(
authorisation_server_url
).
scheme
scheme
=
urlsplit
(
authorisation_server_url
).
scheme
if
scheme
!=
'https'
and
not
insecure
:
raise
ValueError
(
'Only https access to Authorisation Server is allowed'
)
self
.
_scheme
=
scheme
...
...
@@ -210,7 +209,7 @@ class _OAuth2AuthorisationServerProxy(object):
def
_query
(
self
,
method_id
,
body
,
header_dict
=
()):
plain_url
=
self
.
_authorisation_server_url
+
'/'
+
method_id
parsed_url
=
urlparse
.
urlparse
(
plain_url
)
parsed_url
=
urlparse
(
plain_url
)
if
self
.
_scheme
==
'https'
:
ssl_context
=
ssl
.
create_default_context
(
cadata
=
self
.
_ca_certificate_pem
,
...
...
@@ -222,11 +221,11 @@ class _OAuth2AuthorisationServerProxy(object):
ssl_context
.
verify_mode
=
ssl
.
CERT_REQUIRED
ssl_context
.
check_hostname
=
True
Connection
=
functools
.
partial
(
httplib
.
HTTPSConnection
,
HTTPSConnection
,
context
=
ssl_context
,
)
else
:
Connection
=
httplib
.
HTTPConnection
Connection
=
HTTPConnection
timeout
=
getTimeLeft
()
if
timeout
is
None
or
timeout
>
self
.
_timeout
:
timeout
=
self
.
_timeout
...
...
@@ -256,7 +255,7 @@ class _OAuth2AuthorisationServerProxy(object):
def
_queryERP5
(
self
,
method_id
,
kw
=
()):
header_dict
,
body
,
status
=
self
.
_query
(
method_id
=
method_id
,
body
=
url
lib
.
url
encode
(
kw
),
body
=
urlencode
(
kw
),
header_dict
=
{
'Accept'
:
'application/json;charset=UTF-8'
,
'Content-Type'
:
'application/x-www-form-urlencoded'
,
...
...
@@ -274,7 +273,7 @@ class _OAuth2AuthorisationServerProxy(object):
def
_queryOAuth2
(
self
,
method
,
REQUEST
,
RESPONSE
):
header_dict
,
body
,
status
=
self
.
_query
(
method
,
body
=
url
lib
.
url
encode
(
REQUEST
.
form
.
items
()),
body
=
urlencode
(
REQUEST
.
form
.
items
()),
header_dict
=
{
'CONTENT_TYPE'
:
REQUEST
.
environ
[
'CONTENT_TYPE'
],
},
...
...
@@ -377,7 +376,7 @@ class OAuth2AuthorisationClientConnector(
if
'/'
in
authorisation_server_url
:
# Remote Authorisation Server
return
_OAuth2AuthorisationServerProxy
(
authorisation_server_url
=
url
parse
.
url
join
(
authorisation_server_url
=
urljoin
(
# In case authorisation_server_url contains slashes but is still
# relative (to the scheme or to the netloc - path-relative is not
# supported by urljoin)
...
...
@@ -474,7 +473,7 @@ class OAuth2AuthorisationClientConnector(
assert
inner_response
.
status
==
200
access_token
=
oauth2_response
[
'access_token'
]
refresh_token
=
oauth2_response
.
get
(
'refresh_token'
)
parsed_actual_url
=
urlparse
.
urlparse
(
request
.
other
.
get
(
'ACTUAL_URL'
))
parsed_actual_url
=
urlparse
(
request
.
other
.
get
(
'ACTUAL_URL'
))
same_site
=
self
.
ERP5Site_getAuthCookieSameSite
(
scheme
=
parsed_actual_url
.
scheme
,
hostname
=
parsed_actual_url
.
hostname
,
...
...
@@ -712,8 +711,8 @@ class OAuth2AuthorisationClientConnector(
# came_from is what the user was trying to do just before they ended up
# here, so we can redirect them there once they are authenticated.
if
came_from
:
parsed_came_from
=
urlparse
.
urlparse
(
came_from
)
parsed_redirect_uri
=
urlparse
.
urlparse
(
redirect_uri
)
parsed_came_from
=
urlparse
(
came_from
)
parsed_redirect_uri
=
urlparse
(
redirect_uri
)
if
(
parsed_came_from
.
scheme
!=
parsed_redirect_uri
.
scheme
or
parsed_came_from
.
netloc
!=
parsed_redirect_uri
.
netloc
...
...
@@ -829,7 +828,7 @@ class OAuth2AuthorisationClientConnector(
'Location'
,
self
.
_getAuthorisationServerValue
(
REQUEST
=
REQUEST
,
).
absolute_url
()
+
'/authorize?'
+
url
lib
.
url
encode
(
query_list
),
).
absolute_url
()
+
'/authorize?'
+
urlencode
(
query_list
),
)
else
:
# Provide the current URL to authorize, so that it can redirect the
...
...
bt5/erp5_oauth2_resource/SkinTemplateItem/portal_skins/erp5_oauth2_resource/ERP5Site_preventLoginAttemptRetry.py
View file @
a11b313c
...
...
@@ -3,17 +3,16 @@ Modify given URL so that the resulting one prevents further login attempts when
Useful to break redirection loops.
"""
import
urllib
import
urlparse
from
six.moves.urllib.parse
import
parse_qsl
,
urlencode
,
urlsplit
,
urlunsplit
PARAMETER_NAME
=
'disable_cookie_login__'
parsed_url
=
url
parse
.
url
split
(
url
)
return
url
parse
.
url
unsplit
((
parsed_url
=
urlsplit
(
url
)
return
urlunsplit
((
parsed_url
.
scheme
,
parsed_url
.
netloc
,
parsed_url
.
path
,
url
lib
.
url
encode
([
urlencode
([
(
x
,
y
)
for
x
,
y
in
urlparse
.
parse_qsl
(
parsed_url
.
query
)
for
x
,
y
in
parse_qsl
(
parsed_url
.
query
)
if
x
!=
PARAMETER_NAME
]
+
[
(
PARAMETER_NAME
,
'1'
),
...
...
bt5/erp5_web_renderjs_ui/SkinTemplateItem/portal_skins/erp5_web_renderjs_ui/login_form.py
View file @
a11b313c
# Short-circuit old (pre-oauth2) web-mode "login_form"s
import
urllib
from
six.moves.urllib.parse
import
urlencode
web_section_value
=
context
.
getWebSectionValue
()
client_id
=
context
.
getPortalObject
().
ERP5Site_getOAuth2ClientConnectorClientId
(
connector_id
=
(
...
...
@@ -13,7 +13,7 @@ if client_id is None:
return
context
.
login_once_form
(
has_oauth2
=
False
)
if
came_from
:
# Make the user go through WebSite_login after authentication, so it does its url de-templatification magic
came_from
=
context
.
absolute_url
()
+
'/WebSite_login?'
+
url
lib
.
url
encode
(((
'came_from'
,
came_from
),
))
came_from
=
context
.
absolute_url
()
+
'/WebSite_login?'
+
urlencode
(((
'came_from'
,
came_from
),
))
return
context
.
skinSuper
(
'erp5_web_renderjs_ui'
,
script
.
id
)(
REQUEST
=
REQUEST
,
RESPONSE
=
RESPONSE
,
...
...
bt5/erp5_web_service/MixinTemplateItem/portal_components/mixin.erp5.RESTAPIClientConnectorMixin.py
View file @
a11b313c
...
...
@@ -26,10 +26,11 @@
#
##############################################################################
import
time
import
urlparse
import
ssl
import
httplib
import
json
from
six.moves.http_client
import
HTTPSConnection
from
six.moves.urllib.parse
import
urlparse
from
six
import
string_types
as
basestring
from
Products.ERP5Type.Timeout
import
getTimeLeft
from
contextlib
import
contextmanager
from
Products.ERP5Type.XMLObject
import
XMLObject
...
...
@@ -105,7 +106,7 @@ class RESTAPIClientConnectorMixin(XMLObject):
header_dict
[
'content-type'
]
=
'application/json'
body
=
json
.
dumps
(
body
)
plain_url
=
self
.
getBaseUrl
().
rstrip
(
'/'
)
+
'/'
+
path
.
lstrip
(
'/'
)
parsed_url
=
urlparse
.
urlparse
(
plain_url
)
parsed_url
=
urlparse
(
plain_url
)
ssl_context
=
ssl
.
create_default_context
(
cadata
=
self
.
getCaCertificatePem
(),
)
...
...
@@ -115,7 +116,7 @@ class RESTAPIClientConnectorMixin(XMLObject):
if
bind_address
:
bind_address
=
(
bind_address
,
0
)
time_left_before_timeout
=
getTimeLeft
()
http_connection
=
httplib
.
HTTPSConnection
(
http_connection
=
HTTPSConnection
(
host
=
parsed_url
.
hostname
,
port
=
parsed_url
.
port
,
strict
=
True
,
...
...
@@ -184,7 +185,7 @@ class RESTAPIClientConnectorMixin(XMLObject):
with
time_tracker
(
'call'
),
Deadline
(
timeout
):
# Limit numbers of retries, in case the authentication API succeeds
# but the token is not usable.
for
_
in
x
range
(
2
):
for
_
in
range
(
2
):
with
time_tracker
(
'token'
):
access_token
=
self
.
_getAccessToken
()
if
access_token
is
not
None
:
...
...
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