Commit 67c0f5b3 authored by Łukasz Nowak's avatar Łukasz Nowak

Implement BrowserID authentication.

parent 830e432a
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Cache Factory" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>cache_duration</string> </key>
<value> <int>3600</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>browser_id_auth_token_cache_factory</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Cache Factory</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<none/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Distributed Ram Cache" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>specialise/portal_memcached/default_memcached_plugin</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>persistent_cache_plugin</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Distributed Ram Cache</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Web Section" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_folders_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Copy_or_Move_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Delete_objects_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>__translation_dict</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAY=</string> </persistent>
</value>
</item>
<item>
<key> <string>custom_render_method_id</string> </key>
<value> <string>WebSection_browserIdInitiateLogin</string> </value>
</item>
<item>
<key> <string>default_page_displayed</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>login_with_browser_id</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Web Section</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Login with Browser ID</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="6" aka="AAAAAAAAAAY=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
96
\ No newline at end of file
97
\ No newline at end of file
image_module/vifib.cloud
image_module/vifib.kvm
image_module/vifib.rack
portal_caches/browser_id_auth_token_cache_factory
portal_caches/browser_id_auth_token_cache_factory/persistent_cache_plugin
portal_caches/facebook_server_auth_token_cache_factory
portal_caches/facebook_server_auth_token_cache_factory/persistent_cache_plugin
portal_caches/google_server_auth_token_cache_factory
......
......@@ -36,6 +36,8 @@ import apiclient.discovery
import httplib2
import oauth2client.client
import socket
from Products.ERP5Security.ERP5UserManager import getUserByLogin
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
def formatXml(self, xml):
"""Simple way to have nicely formatted XML"""
......@@ -190,3 +192,50 @@ def Google_checkUserExistence(self):
# user exist if server gave some correct response without waiting for user
return response.status in (200, 204)
# Browser ID
def BrowserID_setServerToken(self, key, body):
setServerToken(self, key, body, 'browser_id_auth_token_cache_factory')
def BrowserID_getServerToken(self, key):
return getServerToken(self, key, 'browser_id_auth_token_cache_factory')
def BrowserID_validateAssertion(self, assertion):
connection = httplib.HTTPSConnection(host='browserid.org', timeout=5)
data = urllib.urlencode({'assertion': assertion,
'audience': self.REQUEST.get('SERVER_URL')})
headers = {'Content-type': 'application/x-www-form-urlencoded'}
connection.request('POST', '/verify', data, headers)
response = connection.getresponse()
if response.status != 200:
return None
try:
body = json.loads(response.read())
except Exception:
return None
return body
@UnrestrictedMethod
def BrowserID_checkUserExistence(self):
hash = self.REQUEST.get('__ac_browser_id_hash')
try:
user_dict = BrowserID_getServerToken(self, hash)
except KeyError:
return False
user = user_dict['login']
tag = '%s_user_creation_in_progress' % user
person_list = getUserByLogin(self.getPortalObject(), user)
if len(person_list) == 0:
if self.getPortalObject().portal_activities.countMessageWithTag(tag) == 0:
user_entry = {'reference': user,
'email': user[4:],
'first_name': None,
'last_name': None}
self.Base_createOauth2User(tag, **user_entry)
return False
else:
return True
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>BrowserID_checkUserExistence</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>ViFiBWeb</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>BrowserID_checkUserExistence</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>BrowserID_setServerToken</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>ViFiBWeb</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>BrowserID_setServerToken</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>BrowserID_validateAssertion</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>ViFiBWeb</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>BrowserID_validateAssertion</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_body</string> </key>
<value> <string>def loginFailed():\n
context.getWebSiteValue().login_form.Base_redirect(keep_items={\'portal_status_message\': \'Login with Browser ID failed.\'})\n
assertion = context.REQUEST.get(\'assertion\')\n
data = context.BrowserID_validateAssertion(assertion)\n
\n
if data is None:\n
return loginFailed()\n
\n
if data.get(\'status\', \'failure\') != \'okay\':\n
return loginFailed()\n
\n
login = data.get(\'email\', \'\').encode(\'utf-8\')\n
\n
if login == \'\':\n
return loginFailed()\n
\n
hash = context.Base_getHMAC(assertion, assertion)\n
context.REQUEST.RESPONSE.setCookie(\'__ac_browser_id_hash\', hash, path=\'/\')\n
context.BrowserID_setServerToken(hash, {"login": \'bid_\' + login})\n
return context.REQUEST.RESPONSE.redirect(context.getWebSiteValue().absolute_url())\n
</string> </value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>WebSection_browserIdInitiateLogin</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -106,6 +106,7 @@
<list>
<string>your_user_preparation_facebook</string>
<string>your_user_preparation_google</string>
<string>your_user_preparation_browser_id</string>
</list>
</value>
</item>
......
......@@ -180,6 +180,7 @@ if REQUEST.has_key(\'portal_skin\'):\n
REQUEST.RESPONSE.expireCookie(\'__ac\', path=\'/\')\n
REQUEST.RESPONSE.expireCookie(\'__ac_facebook_hash\', path=\'/\')\n
REQUEST.RESPONSE.expireCookie(\'__ac_google_hash\', path=\'/\')\n
REQUEST.RESPONSE.expireCookie(\'__ac_browser_id_hash\', path=\'/\')\n
msg = context.Base_translateString(\'You have been logged out. Thank you for using this website.\')\n
return website.Base_redirect(form_id, keep_items = {\'portal_status_message\' : msg}, **kw)\n
</string> </value>
......
......@@ -64,10 +64,50 @@
type="hidden" name="came_from"\n
tal:attributes="value request/came_from" />\n
<fieldset>\n
<script src="https://browserid.org/include.js" type="text/javascript"></script> \n
<div class="field nolabel validate widthAuto forgotten_password">\n
You can login with with <a href="./login_with_facebook"><img width="25px" src="./vifib_image/facebook_logo.png" alt="Facebook" title="Facebook"></a>,\n
with <a href="./login_with_google"><img src="./vifib_image/google_logo.png" alt="Google" title="Google"></a>, or use traditional methods below.\n
with <a href="./login_with_google"><img src="./vifib_image/google_logo.png" alt="Google" title="Google"></a>, BrowserID \n
<a href="#" id="browserid" title="Sign-in with BrowserID"> <img src="./vifib_image/browser_id_logo.png" alt="Sign-in with BrowserID" title="Sign-in with BrowserID"></a> or use traditional methods below.\n
</div>\n
<browserid id="browser_id_login" tal:attributes=\'value python: here.getWebSiteValue().login_with_browser_id.absolute_url()\'/>\n
<script type="text/javascript">\n
$(\'#browserid\').click(function() {\n
navigator.id.get(gotAssertion);\n
return false;\n
});\n
\n
function post_to_url(path, params, method) {\n
method = method || "post"; // Set method to post by default, if not specified.\n
\n
// The rest of this code assumes you are not using a library.\n
// It can be made less wordy if you use one.\n
var form = document.createElement("form");\n
form.setAttribute("method", method);\n
form.setAttribute("action", path);\n
\n
for(var key in params) {\n
if(params.hasOwnProperty(key)) {\n
var hiddenField = document.createElement("input");\n
hiddenField.setAttribute("type", "hidden");\n
hiddenField.setAttribute("name", key);\n
hiddenField.setAttribute("value", params[key]);\n
\n
form.appendChild(hiddenField);\n
}\n
}\n
\n
document.body.appendChild(form);\n
form.submit();\n
}\n
\n
function gotAssertion(assertion) { \n
// got an assertion, now send it up to the server for verification \n
if (assertion !== null) { \n
post_to_url($(\'#browser_id_login\').attr(\'value\'), {\'assertion\': assertion})\n
} \n
} \n
</script>\n
<div class="field login_name">\n
<label for="name" class="required" i18n:translate="" i18n:domain="ui">Name</label>\n
<div class="input"><input type="text" name="__ac_name" id="name" tal:attributes="value python: request.get(\'__ac_name\') or \'\'" /></div>\n
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Image" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_EtagSupport__etag</string> </key>
<value> <string>ts42007936.54</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>browser_id_logo.png</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>image/png</string> </value>
</item>
<item>
<key> <string>data</string> </key>
<value> <string encoding="base64">iVBORw0KGgoAAAANSUhEUgAAAE8AAAAWCAYAAACBtcG5AAAGzUlEQVRYw+2ZXUxb5xnHz6R0laqi
Kd2ajRQqpcnFsjZqd1spysWaTruaplWiSzVNUF9Uay92v4tqkxYGXdKElCRrsqwhBmJDCAsJcQDz
jQPEhGAC/sQ29vHHObYpkA+wCfz3Pu/ZOTUYB07Lbmhf6af3fc7zcey/nvec42NB+G588zExMfF8
IpE4lJLlEkYpWxuexv4bccMb/dgWHBhA6QEbjrw+iLdf7cfLuoSTJOmwLMtNSVlOJhMJbJb9rTIO
3MZ2Iv3abdx7dQAV+wawd1MdR8KlUilsloQsa/zYJGO3BduGYiuw1wa8YkN7US8OPVW8WCx2iHVe
Uo94yWQSsiRp/LBewq7WlW9MycDKltTJ5o32FbzVrS+nkOUU93OGiweW3s4rXjweL5HiEtuGyRwC
gQAG+gc0yFZ9CTkBylP5QW0ML9x48lQOtD1BtWsF9qQCrX/Rtcx9R8dXQIOObVRHD9FHK3iQ0V/z
RfZZC/uWsatveWRX79Iv1xUvEomUxmMxsK2bQ19fXw7ZftaxoFyVAmMUBS2ZvNgTy1yguvEZnB1J
wDOThl1e5r6SthTuinP4nUV+ag291I/JDOlr5e60ZPBi7xJ+1JNhAi7mChiNRg3RSJQLsZbe3r4c
1sbEWddRvsqOGhE7mhdz+E13RhFuNIbCmiB+cimMPZ+P41dmH/cXNHyJD29O42f/mdVyDranUWbL
cFSboPVPb6Q5tCa/ul7L4asi3r0R1ezN5qk805rGc+yzP9edHhGsi6u3cDQcNUREkXVOPIee7p4c
1ouLRWOgGirCFyEIVx+voqxnnovXFXyEfdcXtOPPNszxubQ/zf1l/crxv44qYkcfKt2qjouuNN5s
W+Drq4EM5tPKdqeZjq897x1pCXYpw9d68lbBPu+OTiamdWFI6Fg4qIkXDoYNYkhkXRPLQRQj6Orq
1iB7vTgiIkZBdVSEfwUhND7U+L4xDo/8UBNhWHqCv9xNa/4y64wiHpvJptEyIaG4egQfNju5XdHt
x84LPgiXlVh3chF/vObCe6Zxbp90PF51TsIeUeC2jrwcrj2CYGVxHY9bBEuikIsXDAYNoWCIC5NN
mAkwNuZAV2eXBtl0fG2sRlgE1VIRzvkhmOY1Xj/vwGnbNNxZIjZOZbjvD9YUt2kmm0bNmLJ+s1ni
dtXIjFaL31wGIxAuMWplbp/K8qvYxQccvXnrcpXRxoS0PPhIEc8fNAQDQS6KyjT74taOzryQPzte
JTQdBtUirjRegfBP1iX1X35FbQrfO+fFzlN2vHXBjuicso1+3jKP37cl+Jpmij1q9bEttYyh+BKf
3dID7KuZ0mrxjrEn89oqmng689blyiwTjgl4c76Si+f3+g0Bf4B/cZXR0Xvo6LDmhfzZ8QQJSnWI
xoZGCKfdEIyp/FyM4ZQtxD/8kfYZvGdRuoBm8n/cL2N+cQlVtyN4p3YMBSeHIdTIWj6NE8OJvLbK
HXGeozcvhwbWna2znD0nWpXO83q9Br/Pj2BwWqO9rWNDsuMDrNOoBtFgboDwmZOJI69if1MKlXcf
8lm1I3Np3n3CuQCO3IwrQrKZ/EORx9weEh9hMLoAs3dByyVoHB+S8toqd8JzHL15q7jMOvP6DGfP
8dYW9tteueZ53V6Dz+vTuoa4dattQ9RY/1QAlE+YTWYIVZMQ/i3l8EFnCuJcetWd805oFr823uP+
d1uj/BjNZL9y5i7eN4/hT60+nLCJrAufYDKZ1urRODYo5bVVhplwhN48jTom3rUUp/j49SH2aPfV
3dbtdhuYgJjyBTQsllsbQnE+rx+US5hMJgifOthdNrI+1U48W9GDg2dtKDON8ln4x212XZxS/Ge8
KDjaweffWpTtVHJdyROqHPhiWNniaj0eW+XIa2vQOQi9eYQxBqFZ5uw+dm2EPdeufs5zuVylHqeH
CTGlC6/HB8ojLtdfhnCMddDnoY05M6Vsa5rzxOw858H8QoZ3asXwLM46lLtk85i4uXNsBRfZuZri
nN2fcOFyf2FMjk+WuCbdXIzN4mGdRjkEF66SXcxP+7aOag8OVvfgZLcXg6E5tHtTqOpiN6BK29ae
Jx8XAuwGEea8VNG0vnA0HA7nIed9Z9Lt9MLj2hiKc064OPV1TLi/sy3xmXvrOXkfwies9t86IZR3
K+v/x3nWcp6JZ57mFFVeGc7ZqtnDXG1+nnVf0+R9J/RQX1vPrld29vzj35a8VG5up9d1G74QrbtU
d9horGuqNdYm64z12AyFfz6P4nLztqKo3JwuKjfdKzpqqmAdt1fXfxikNHvVVMIoZe/5DN8W/vd9
j9AWZY8im/4P4789+eGvSTTTwgAAAABJRU5ErkJggg==</string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>22</int> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>size</string> </key>
<value> <int>1798</int> </value>