Commit bbf420a5 authored by Jérome Perrin's avatar Jérome Perrin

Google login in ERP5JS

Change login button to comply with Google's branding guidelines.

Add support for google login in ERP5JS.


See merge request nexedi/erp5!1166
parents b1da02b9 64c0bb5d
...@@ -36,6 +36,7 @@ elif code is not None: ...@@ -36,6 +36,7 @@ elif code is not None:
method = getattr(context, "ERP5Site_createGoogleUserToOAuth", None) method = getattr(context, "ERP5Site_createGoogleUserToOAuth", None)
if method is not None: if method is not None:
method(user_reference, user_dict) method(user_reference, user_dict)
return response.redirect(request.get("came_from") or context.absolute_url()) # XXX for ERP5JS web sites without a rewrite rule, we make sure there's a trailing /
return response.redirect(request.get("came_from") or context.absolute_url() + '/')
return handleError('') return handleError('')
...@@ -27,6 +27,9 @@ ...@@ -27,6 +27,9 @@
import uuid import uuid
import mock import mock
import lxml
import urlparse
import httplib
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
...@@ -89,8 +92,7 @@ def getUserEntry(access_token): ...@@ -89,8 +92,7 @@ def getUserEntry(access_token):
} }
class TestGoogleLogin(ERP5TypeTestCase): class GoogleLoginTestCase(ERP5TypeTestCase):
def afterSetUp(self): def afterSetUp(self):
""" """
This is ran before anything, used to set the environment This is ran before anything, used to set the environment
...@@ -123,6 +125,8 @@ class TestGoogleLogin(ERP5TypeTestCase): ...@@ -123,6 +125,8 @@ class TestGoogleLogin(ERP5TypeTestCase):
self.tic() self.tic()
self.logout() self.logout()
class TestGoogleLogin(GoogleLoginTestCase):
def test_redirect(self): def test_redirect(self):
""" """
Check URL generate to redirect to Google Check URL generate to redirect to Google
...@@ -279,7 +283,9 @@ return credential_request ...@@ -279,7 +283,9 @@ return credential_request
google_hash = self.portal.REQUEST.RESPONSE.cookies.get("__ac_google_hash")["value"] google_hash = self.portal.REQUEST.RESPONSE.cookies.get("__ac_google_hash")["value"]
self.assertEqual("b01533abb684a658dc71c81da4e67546", google_hash) self.assertEqual("b01533abb684a658dc71c81da4e67546", google_hash)
self.assertEqual(self.portal.absolute_url(), response) absolute_url = self.portal.absolute_url()
self.assertNotEqual(absolute_url[-1], '/')
self.assertEqual(absolute_url + '/', response)
cache_dict = self.portal.Base_getBearerToken(google_hash, "google_server_auth_token_cache_factory") cache_dict = self.portal.Base_getBearerToken(google_hash, "google_server_auth_token_cache_factory")
self.assertEqual(CLIENT_ID, cache_dict["client_id"]) self.assertEqual(CLIENT_ID, cache_dict["client_id"])
self.assertEqual(ACCESS_TOKEN, cache_dict["access_token"]) self.assertEqual(ACCESS_TOKEN, cache_dict["access_token"])
...@@ -300,3 +306,30 @@ return credential_request ...@@ -300,3 +306,30 @@ return credential_request
person = credential_request.getDestinationDecisionValue() person = credential_request.getDestinationDecisionValue()
google_login = person.objectValues(portal_types="Google Login")[0] google_login = person.objectValues(portal_types="Google Login")[0]
self.assertEqual(getUserId(None), google_login.getReference()) self.assertEqual(getUserId(None), google_login.getReference())
def test_logout(self):
resp = self.publish(self.portal.getId() + '/logout')
self.assertEqual(resp.getCookie("__ac_google_hash")['value'], 'deleted')
class TestERP5JSGoogleLogin(GoogleLoginTestCase):
def _getWebSite(self):
return self.portal.web_site_module.renderjs_runner
def test_login_form(self):
resp = self.publish(self._getWebSite().getPath() + '/login_form')
tree = lxml.etree.fromstring(resp.getBody(), parser=lxml.etree.HTMLParser())
google_login_link, = [
img.getparent().attrib['href']
for img in tree.findall('.//a/img')
if img.attrib['alt'] == 'Sign in with Google'
]
self.assertIn('/ERP5Site_redirectToGoogleLoginPage', google_login_link)
resp = self.publish(urlparse.urlparse(google_login_link).path)
# this request redirects to google
self.assertEqual(resp.getStatus(), httplib.FOUND)
self.assertIn('google.com', resp.getHeader('Location'))
def test_logout(self):
resp = self.publish(self._getWebSite().getPath() + '/WebSite_logout')
self.assertEqual(resp.getCookie("__ac_google_hash")['value'], 'deleted')
erp5_full_text_myisam_catalog erp5_full_text_myisam_catalog
erp5_credential erp5_credential
erp5_web_renderjs_ui
\ No newline at end of file
...@@ -1045,6 +1045,19 @@ div[data-gadget-scope='header'] .ui-header ul { ...@@ -1045,6 +1045,19 @@ div[data-gadget-scope='header'] .ui-header ul {
.gadget-content button[name='action_update']:active { .gadget-content button[name='action_update']:active {
background-color: #a9a9a9; background-color: #a9a9a9;
} }
.gadget-content .sign_in_with_google {
height: 46px;
width: 191px;
display: inline-block;
overflow: hidden;
margin-top: 6pt;
}
.gadget-content .sign_in_with_google img:hover {
margin-top: -46px;
}
.gadget-content .sign_in_with_google img:active {
margin-top: -92px;
}
@media not screen and (max-width: 85em) { @media not screen and (max-width: 85em) {
div[data-role='page']:not(.desktop-panel-hidden) .gadget-content { div[data-role='page']:not(.desktop-panel-hidden) .gadget-content {
margin-left: 180pt; margin-left: 180pt;
......
...@@ -262,8 +262,8 @@ ...@@ -262,8 +262,8 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1579787106.93</float> <float>1593579172.65</float>
<string>UTC</string> <string>GMT+2</string>
</tuple> </tuple>
</state> </state>
</object> </object>
......
""" """
Default logout handler, overwritten to give website specific portal status message. Default logout handler, overwritten to give website specific portal status message.
""" """
portal = context.getPortalObject()
REQUEST = context.REQUEST REQUEST = context.REQUEST
if REQUEST.has_key('portal_skin'): if REQUEST.has_key('portal_skin'):
context.portal_skins.clearSkinCookie() portal.portal_skins.clearSkinCookie()
REQUEST.RESPONSE.expireCookie('__ac', path='/') REQUEST.RESPONSE.expireCookie('__ac', path='/')
if getattr(portal.portal_skins, "erp5_oauth_google_login", None):
REQUEST.RESPONSE.expireCookie('__ac_google_hash', path='/')
if getattr(portal.portal_skins, "erp5_oauth_facebook_login", None):
REQUEST.RESPONSE.expireCookie('__ac_facebook_hash', path='/')
REQUEST.RESPONSE.setHeader('Location', came_from or context.getPermanentURL(context)) REQUEST.RESPONSE.setHeader('Location', came_from or context.getPermanentURL(context))
REQUEST.RESPONSE.setStatus(303) REQUEST.RESPONSE.setStatus(303)
# REQUEST.RESPONSE.redirect(came_from or context.getPermanentURL(context)); # REQUEST.RESPONSE.redirect(came_from or context.getPermanentURL(context));
...@@ -1200,6 +1200,20 @@ div[data-gadget-scope='header'] .ui-header { ...@@ -1200,6 +1200,20 @@ div[data-gadget-scope='header'] .ui-header {
.renderPageSubmitButton(@grey); .renderPageSubmitButton(@grey);
} }
.sign_in_with_google {
height: 46px;
width: 191px;
display: inline-block;
overflow: hidden;
margin-top: @margin-size;
}
.sign_in_with_google img:hover {
margin-top: -46px;
}
.sign_in_with_google img:active {
margin-top: -92px;
}
@media @desktop { @media @desktop {
div[data-role='page']:not(.desktop-panel-hidden) & { div[data-role='page']:not(.desktop-panel-hidden) & {
// Keep the panel always visible // Keep the panel always visible
......
...@@ -423,6 +423,22 @@ fieldset.bottom > .field > label { ...@@ -423,6 +423,22 @@ fieldset.bottom > .field > label {
border-width: 0; border-width: 0;
} }
.login a.google {
height: 46px;
width: 191px;
display: inline-block;
overflow: hidden;
}
.login a.google img:hover {
margin-top: -46px;
}
.login a.google img:active {
margin-top: -92px;
}
.content .field { .content .field {
padding-bottom: 3px; padding-bottom: 3px;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment