[ADD] auth_openid module
bzr revid: chs@openerp.com-20110930132858-jsxqo21l7pn172av
|
@ -0,0 +1,26 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# Copyright (C) 2010-2011 OpenERP s.a. (<http://openerp.com>).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
import res_users
|
||||||
|
import controllers
|
||||||
|
|
||||||
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# Copyright (C) 2010-2011 OpenERP s.a. (<http://openerp.com>).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'OpenID',
|
||||||
|
'version': '2.0',
|
||||||
|
'category': 'Authentification',
|
||||||
|
'description': """Allow users to login through OpenID.""",
|
||||||
|
'author': 'OpenERP s.a.',
|
||||||
|
'maintainer': 'OpenERP s.a.',
|
||||||
|
'website': 'http://www.openerp.com',
|
||||||
|
'depends': ['base'],
|
||||||
|
'data': [
|
||||||
|
'res_users.xml',
|
||||||
|
],
|
||||||
|
'js': [
|
||||||
|
'static/src/js/auth_openid.js',
|
||||||
|
],
|
||||||
|
'css': [
|
||||||
|
'static/src/css/openid.css',
|
||||||
|
],
|
||||||
|
'external_dependencies': {
|
||||||
|
'python' : ['openid'],
|
||||||
|
},
|
||||||
|
'installable': True,
|
||||||
|
'active': False,
|
||||||
|
'web_preload': True,
|
||||||
|
}
|
||||||
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,20 @@
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# Copyright (C) 2011 OpenERP SA (<http://openerp.com>).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
import main
|
|
@ -0,0 +1,225 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# Copyright (C) 2010-2011 OpenERP s.a. (<http://openerp.com>).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
import werkzeug.urls
|
||||||
|
import werkzeug.exceptions
|
||||||
|
|
||||||
|
from openerp.modules.registry import RegistryManager
|
||||||
|
import web.common.dispatch as openerpweb
|
||||||
|
|
||||||
|
from openid import oidutil
|
||||||
|
from openid.store import memstore
|
||||||
|
#from openid.store import filestore
|
||||||
|
from openid.consumer import consumer
|
||||||
|
from openid.cryptutil import randomString
|
||||||
|
from openid.extensions import ax, sreg
|
||||||
|
|
||||||
|
from .. import utils
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_logger = logging.getLogger('web.auth_openid')
|
||||||
|
oidutil.log = logging.getLogger('openid').debug
|
||||||
|
|
||||||
|
|
||||||
|
class GoogleAppsAwareConsumer(consumer.GenericConsumer):
|
||||||
|
def complete(self, message, endpoint, return_to):
|
||||||
|
if message.getOpenIDNamespace() == consumer.OPENID2_NS:
|
||||||
|
server_url = message.getArg(consumer.OPENID2_NS, 'op_endpoint', consumer.no_default)
|
||||||
|
if server_url.startswith('https://www.google.com/a/'):
|
||||||
|
# update fields
|
||||||
|
for attr in ['claimed_id', 'identity']:
|
||||||
|
value = message.getArg(consumer.OPENID2_NS, attr)
|
||||||
|
value = 'https://www.google.com/accounts/o8/user-xrds?uri=%s' % urllib.quote_plus(value)
|
||||||
|
message.setArg(consumer.OPENID2_NS, attr, value)
|
||||||
|
|
||||||
|
# now, resign the message
|
||||||
|
assoc_handle = message.getArg(consumer.OPENID_NS, 'assoc_handle')
|
||||||
|
assoc = self.store.getAssociation(server_url, assoc_handle)
|
||||||
|
message.delArg(consumer.OPENID2_NS, 'sig')
|
||||||
|
message.delArg(consumer.OPENID2_NS, 'signed')
|
||||||
|
message = assoc.signMessage(message)
|
||||||
|
|
||||||
|
return super(GoogleAppsAwareConsumer, self).complete(message, endpoint, return_to)
|
||||||
|
|
||||||
|
|
||||||
|
class OpenIDController(openerpweb.Controller):
|
||||||
|
_cp_path = '/auth_openid/login'
|
||||||
|
|
||||||
|
_store = memstore.MemoryStore() # TODO use a filestore
|
||||||
|
|
||||||
|
_REQUIRED_ATTRIBUTES = ['email']
|
||||||
|
_OPTIONAL_ATTRIBUTES = 'nickname fullname postcode country language timezone'.split()
|
||||||
|
|
||||||
|
|
||||||
|
def _add_extensions(self, request):
|
||||||
|
"""Add extensions to the request"""
|
||||||
|
|
||||||
|
sreg_request = sreg.SRegRequest(required=self._REQUIRED_ATTRIBUTES,
|
||||||
|
optional=self._OPTIONAL_ATTRIBUTES)
|
||||||
|
request.addExtension(sreg_request)
|
||||||
|
|
||||||
|
ax_request = ax.FetchRequest()
|
||||||
|
for alias in self._REQUIRED_ATTRIBUTES:
|
||||||
|
uri = utils.SREG2AX[alias]
|
||||||
|
ax_request.add(ax.AttrInfo(uri, required=True, alias=alias))
|
||||||
|
for alias in self._OPTIONAL_ATTRIBUTES:
|
||||||
|
uri = utils.SREG2AX[alias]
|
||||||
|
ax_request.add(ax.AttrInfo(uri, required=False, alias=alias))
|
||||||
|
|
||||||
|
request.addExtension(ax_request)
|
||||||
|
|
||||||
|
def _get_attributes_from_success_response(self, success_response):
|
||||||
|
attrs = {}
|
||||||
|
|
||||||
|
all_attrs = self._REQUIRED_ATTRIBUTES + self._OPTIONAL_ATTRIBUTES
|
||||||
|
|
||||||
|
sreg_resp = sreg.SRegResponse.fromSuccessResponse(success_response)
|
||||||
|
if sreg_resp:
|
||||||
|
for attr in all_attrs:
|
||||||
|
value = sreg_resp.get(attr)
|
||||||
|
if value is not None:
|
||||||
|
attrs[attr] = value
|
||||||
|
|
||||||
|
ax_resp = ax.FetchResponse.fromSuccessResponse(success_response)
|
||||||
|
if ax_resp:
|
||||||
|
for attr in all_attrs:
|
||||||
|
value = ax_resp.getSingle(utils.SREG2AX[attr])
|
||||||
|
if value is not None:
|
||||||
|
attrs[attr] = value
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def _get_realm(self, req):
|
||||||
|
return req.httprequest.host_url
|
||||||
|
|
||||||
|
@openerpweb.jsonrequest
|
||||||
|
def verify(self, req, db, url):
|
||||||
|
redirect_to = werkzeug.urls.Href(req.httprequest.host_url + 'auth_openid/login/process')(session_id=req.session_id)
|
||||||
|
realm = self._get_realm(req)
|
||||||
|
|
||||||
|
session = dict(dbname=db, openid_url=url) # TODO add origin page ?
|
||||||
|
oidconsumer = consumer.Consumer(session, self._store)
|
||||||
|
|
||||||
|
try:
|
||||||
|
request = oidconsumer.begin(url)
|
||||||
|
except consumer.DiscoveryFailure, exc:
|
||||||
|
fetch_error_string = 'Error in discovery: %s' % (str(exc[0]),)
|
||||||
|
return {'error': fetch_error_string, 'title': 'OpenID Error'}
|
||||||
|
|
||||||
|
if request is None:
|
||||||
|
return {'error': 'No OpenID services found', 'title': 'OpenID Error'}
|
||||||
|
|
||||||
|
req.session.openid_session = session
|
||||||
|
self._add_extensions(request)
|
||||||
|
|
||||||
|
if request.shouldSendRedirect():
|
||||||
|
redirect_url = request.redirectURL(realm, redirect_to)
|
||||||
|
return {'action': 'redirect', 'value': redirect_url, 'session_id': req.session_id}
|
||||||
|
else:
|
||||||
|
form_html = request.htmlMarkup(realm, redirect_to)
|
||||||
|
return {'action': 'post', 'value': form_html, 'session_id': req.session_id}
|
||||||
|
|
||||||
|
|
||||||
|
@openerpweb.httprequest
|
||||||
|
def process(self, req, **kw):
|
||||||
|
session = getattr(req.session, 'openid_session', None)
|
||||||
|
if not session:
|
||||||
|
return werkzeug.utils.redirect('/')
|
||||||
|
|
||||||
|
oidconsumer = consumer.Consumer(session, self._store, consumer_class=GoogleAppsAwareConsumer)
|
||||||
|
|
||||||
|
query = req.httprequest.args
|
||||||
|
info = oidconsumer.complete(query, req.httprequest.base_url)
|
||||||
|
display_identifier = info.getDisplayIdentifier()
|
||||||
|
|
||||||
|
session['status'] = info.status
|
||||||
|
user_id = None
|
||||||
|
|
||||||
|
if info.status == consumer.SUCCESS:
|
||||||
|
dbname = session['dbname']
|
||||||
|
with utils.cursor(dbname) as cr:
|
||||||
|
registry = RegistryManager.get(dbname)
|
||||||
|
Modules = registry.get('ir.module.module')
|
||||||
|
|
||||||
|
installed = Modules.search_count(cr, 1, ['&', ('name', '=', 'auth_openid'), ('state', '=', 'installed')]) == 1
|
||||||
|
if installed:
|
||||||
|
|
||||||
|
Users = registry.get('res.users')
|
||||||
|
|
||||||
|
#openid_url = info.endpoint.canonicalID or display_identifier
|
||||||
|
openid_url = session['openid_url']
|
||||||
|
|
||||||
|
attrs = self._get_attributes_from_success_response(info)
|
||||||
|
attrs['openid_url'] = openid_url
|
||||||
|
session['attributes'] = attrs
|
||||||
|
openid_email = attrs.get('email', False)
|
||||||
|
|
||||||
|
domain = []
|
||||||
|
if openid_email:
|
||||||
|
domain += ['|', ('openid_email', '=', False)]
|
||||||
|
domain += [('openid_email', '=', openid_email)]
|
||||||
|
|
||||||
|
domain += [
|
||||||
|
('openid_url', '=', openid_url),
|
||||||
|
('active', '=', True),
|
||||||
|
]
|
||||||
|
ids = Users.search(cr, 1, domain)
|
||||||
|
assert len(ids) < 2
|
||||||
|
if ids:
|
||||||
|
user_id = ids[0]
|
||||||
|
login = Users.browse(cr, 1, user_id).login
|
||||||
|
key = randomString(utils.KEY_LENGTH, '0123456789abcdef')
|
||||||
|
Users.write(cr, 1, [user_id], {'openid_key': key})
|
||||||
|
# TODO fill empty fields with the ones from sreg/ax
|
||||||
|
cr.commit()
|
||||||
|
|
||||||
|
u = req.session.login(dbname, login, key)
|
||||||
|
|
||||||
|
if not user_id:
|
||||||
|
session['message'] = 'This OpenID identifier is not associated to any active users'
|
||||||
|
|
||||||
|
|
||||||
|
elif info.status == consumer.SETUP_NEEDED:
|
||||||
|
session['message'] = info.setup_url
|
||||||
|
elif info.status == consumer.FAILURE and display_identifier:
|
||||||
|
fmt = "Verification of %s failed: %s"
|
||||||
|
session['message'] = fmt % (display_identifier, info.message)
|
||||||
|
else: # FAILURE
|
||||||
|
# Either we don't understand the code or there is no
|
||||||
|
# openid_url included with the error. Give a generic
|
||||||
|
# failure message. The library should supply debug
|
||||||
|
# information in a log.
|
||||||
|
session['message'] = 'Verification failed.'
|
||||||
|
|
||||||
|
|
||||||
|
fragment = '#loginerror' if not user_id else ''
|
||||||
|
return werkzeug.utils.redirect('/web/webclient/home?debug=1'+fragment)
|
||||||
|
|
||||||
|
@openerpweb.jsonrequest
|
||||||
|
def status(self, req):
|
||||||
|
session = getattr(req.session, 'openid_session', {})
|
||||||
|
return {'status': session.get('status'), 'message': session.get('message')}
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# Copyright (C) 2010-2011 OpenERP s.a. (<http://openerp.com>).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
from openerp.osv import osv, fields
|
||||||
|
import openerp.exceptions
|
||||||
|
import tools
|
||||||
|
|
||||||
|
import utils
|
||||||
|
|
||||||
|
class res_users(osv.osv):
|
||||||
|
_inherit = 'res.users'
|
||||||
|
|
||||||
|
# TODO create helper fields for autofill openid_url and openid_email -> http://pad.openerp.com/web-openid
|
||||||
|
|
||||||
|
_columns = {
|
||||||
|
'openid_url': fields.char('OpenID URL', size=1024),
|
||||||
|
'openid_email': fields.char('OpenID Email', size=256,
|
||||||
|
help="Used for disambiguation in case of a shared OpenID URL"),
|
||||||
|
'openid_key': fields.char('OpenID Key', size=utils.KEY_LENGTH,
|
||||||
|
readonly=True),
|
||||||
|
}
|
||||||
|
|
||||||
|
def _check_openid_url_email(self, cr, uid, ids, context=None):
|
||||||
|
return all(self.search_count(cr, uid, [('active', '=', True), ('openid_url', '=', u.openid_url), ('openid_email', '=', u.openid_email)]) == 1 \
|
||||||
|
for u in self.browse(cr, uid, ids, context) if u.active and u.openid_url)
|
||||||
|
|
||||||
|
def _check_openid_url_email_msg(self, cr, uid, ids, context):
|
||||||
|
return "There is already an active user with this OpenID Email for this OpenID URL"
|
||||||
|
|
||||||
|
_constraints = [
|
||||||
|
(_check_openid_url_email, lambda self, *a, **kw: self._check_openid_url_email_msg(*a, **kw), ['active', 'openid_url', 'openid_email']),
|
||||||
|
]
|
||||||
|
|
||||||
|
def copy(self, cr, uid, rid, defaults=None, context=None):
|
||||||
|
reset_fields = 'openid_url openid_email'.split()
|
||||||
|
reset_values = dict.fromkeys(reset_fields, False)
|
||||||
|
if defaults is None:
|
||||||
|
defaults = reset_values
|
||||||
|
else:
|
||||||
|
defaults = dict(reset_values, **defaults)
|
||||||
|
|
||||||
|
defaults['openid_key'] = False
|
||||||
|
return super(res_users, self).copy(cr, uid, rid, defaults, context)
|
||||||
|
|
||||||
|
def login(self, db, login, password):
|
||||||
|
result = super(res_users, self).login(db, login, password)
|
||||||
|
if result:
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
with utils.cursor(db) as cr:
|
||||||
|
cr.execute('UPDATE res_users SET date=now() WHERE login=%s AND openid_key=%s AND active=%s RETURNING id',
|
||||||
|
(tools.ustr(login), tools.ustr(password), True))
|
||||||
|
res = cr.fetchone()
|
||||||
|
cr.commit()
|
||||||
|
return res[0] if res else False
|
||||||
|
|
||||||
|
|
||||||
|
def check(self, db, uid, passwd):
|
||||||
|
try:
|
||||||
|
return super(res_users, self).check(db, uid, passwd)
|
||||||
|
except openerp.exceptions.AccessDenied:
|
||||||
|
if not passwd:
|
||||||
|
raise
|
||||||
|
with utils.cursor(db) as cr:
|
||||||
|
cr.execute('''SELECT COUNT(1)
|
||||||
|
FROM res_users
|
||||||
|
WHERE id=%s
|
||||||
|
AND openid_key=%s
|
||||||
|
AND active=%s''',
|
||||||
|
(int(uid), passwd, True))
|
||||||
|
if not cr.fetchone()[0]:
|
||||||
|
raise
|
||||||
|
self._uid_cache.setdefault(db, {})[uid] = passwd
|
||||||
|
|
||||||
|
res_users()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<openerp>
|
||||||
|
<data>
|
||||||
|
<record id="view_users_form" model="ir.ui.view">
|
||||||
|
<field name="name">res.users.form</field>
|
||||||
|
<field name="model">res.users</field>
|
||||||
|
<field name="type">form</field>
|
||||||
|
<field name="inherit_id" ref="base.view_users_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<notebook colspan="4" position="inside">
|
||||||
|
<page string="OpenID">
|
||||||
|
<field name="openid_url"/>
|
||||||
|
<field name="openid_email"/>
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</openerp>
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
input[name='openid_url'] {
|
||||||
|
background: #fff url(../img/login-bg.gif) no-repeat 1px;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth_choice {
|
||||||
|
position: static;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openerp .login .oe_forms .oe_box2 td input[name="db"], .oe_forms .oe_box2 td select[name="db"] {
|
||||||
|
width: 50%;
|
||||||
|
float: left;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openerp .login .oe_login_right_pane {
|
||||||
|
margin-left: 525px;
|
||||||
|
}
|
||||||
|
.openerp .login form {
|
||||||
|
width: 475px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openid_providers {
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openid_providers li {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
margin: 0 1px 3px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openid_providers a {
|
||||||
|
display: block;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
background: #fff url(../img/openid_16.png) no-repeat 50%;
|
||||||
|
text-indent: -9999px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openid_providers a.selected {
|
||||||
|
border-color: #9A0404;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openid_providers a[title="Password"] { background-image: url(../img/textfield_key.png); }
|
||||||
|
.openid_providers a[title="AOL"] { background-image: url(../img/aol.png); }
|
||||||
|
.openid_providers a[title="ClaimID"] { background-image: url(../img/claimid.png); }
|
||||||
|
.openid_providers a[title="Google"] { background-image: url(../img/googlefav.png); }
|
||||||
|
.openid_providers a[title="Google Apps"] { background-image: url(../img/marketplace.gif); }
|
||||||
|
.openid_providers a[title="MyOpenID"] { background-image: url(../img/myopenid.png); }
|
||||||
|
.openid_providers a[title="VeriSign"] { background-image: url(../img/verisign.png); }
|
||||||
|
.openid_providers a[title="Yahoo!"] { background-image: url(../img/yahoo.png); }
|
||||||
|
.openid_providers a[title="Launchpad"] { background-image: url(../img/launchpad.png); }
|
||||||
|
|
||||||
|
|
||||||
|
tr.auth_choice.selected {
|
||||||
|
display: table-row;
|
||||||
|
}
|
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 940 B |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 419 B |
After Width: | Height: | Size: 328 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 320 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 671 B |
After Width: | Height: | Size: 455 B |
After Width: | Height: | Size: 520 B |
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,135 @@
|
||||||
|
|
||||||
|
openerp.auth_openid = function(instance) {
|
||||||
|
|
||||||
|
var QWeb = instance.web.qweb;
|
||||||
|
QWeb.add_template('/auth_openid/static/src/xml/auth_openid.xml');
|
||||||
|
|
||||||
|
instance.web.Login = instance.web.Login.extend({
|
||||||
|
start: function() {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.$openid_selected_button = $();
|
||||||
|
this.$openid_selected_input = $();
|
||||||
|
this.$openid_selected_provider = null;
|
||||||
|
|
||||||
|
|
||||||
|
var openIdProvider = null;
|
||||||
|
if (this.has_local_storage && this.remember_creditentials) {
|
||||||
|
openIdProvider = localStorage.getItem('openid-provider');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (openIdProvider) {
|
||||||
|
$openid_selected_provider = openIdProvider;
|
||||||
|
this.do_openid_select('a[href="#' + openIdProvider + '"]', openIdProvider, true);
|
||||||
|
|
||||||
|
if (this.has_local_storage && this.remember_creditentials) {
|
||||||
|
this.$openid_selected_input.find('input').val(localStorage.getItem('openid-login'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.do_openid_select('a[data-url=""]', 'login,password', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$element.find('a[data-url]').click(function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var selected_oidh = $(this).attr('href').substr(1);
|
||||||
|
if (selected_oidh != self.$openid_selected_provider) {
|
||||||
|
self.do_openid_select(this, selected_oidh);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
do_openid_select: function (button, provider, noautosubmit) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self.$openid_selected_button.add(self.$openid_selected_input).removeClass('selected');
|
||||||
|
self.$openid_selected_button = self.$element.find(button).addClass('selected');
|
||||||
|
|
||||||
|
var input = _(provider.split(',')).map(function(p) { return 'tr[data-provider="'+p+'"]'; }).join(',');
|
||||||
|
self.$openid_selected_input = self.$element.find(input).addClass('selected');
|
||||||
|
|
||||||
|
self.$openid_selected_input.find('input:first').focus();
|
||||||
|
self.$openid_selected_provider = (self.$openid_selected_button.attr('href') || '').substr(1);
|
||||||
|
|
||||||
|
if (self.has_local_storage && self.remember_creditentials) {
|
||||||
|
localStorage.setItem('openid-provider', self.$openid_selected_provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!noautosubmit && self.$openid_selected_input.length == 0) {
|
||||||
|
self.$element.find('form').submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
on_login_invalid: function() {
|
||||||
|
var self = this;
|
||||||
|
var fragment = jQuery.deparam.fragment();
|
||||||
|
if (fragment.loginerror != undefined) {
|
||||||
|
this.rpc('/auth_openid/login/status', {}, function(result) {
|
||||||
|
if (_.contains(['success', 'failure'], result.status) && result.message) {
|
||||||
|
self.notification.warn('Invalid OpenID Login', result.message);
|
||||||
|
}
|
||||||
|
if (result.status === 'setup_needed' && result.message) {
|
||||||
|
window.location.replace(result.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this._super();
|
||||||
|
},
|
||||||
|
|
||||||
|
on_submit: function(ev) {
|
||||||
|
|
||||||
|
var dataurl = this.$openid_selected_button.attr('data-url');
|
||||||
|
|
||||||
|
if(!dataurl) {
|
||||||
|
// login-password submitted
|
||||||
|
this._super(ev);
|
||||||
|
} else {
|
||||||
|
ev.preventDefault();
|
||||||
|
|
||||||
|
var id = this.$openid_selected_input.find('input').val();
|
||||||
|
if (this.has_local_storage && this.remember_creditentials) {
|
||||||
|
localStorage.setItem('openid-login', id);
|
||||||
|
}
|
||||||
|
|
||||||
|
var db = this.$element.find("form [name=db]").val();
|
||||||
|
var openid_url = dataurl.replace('{id}', id);
|
||||||
|
|
||||||
|
this.do_openid_login(db, openid_url);
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
do_openid_login: function(db, openid_url) {
|
||||||
|
var self = this;
|
||||||
|
this.rpc('/auth_openid/login/verify', {'db': db, 'url': openid_url}, function(result) {
|
||||||
|
if (result.error) {
|
||||||
|
self.notification.warn(result.title, result.error);
|
||||||
|
self.on_login_invalid();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result.session_id) {
|
||||||
|
self.session.session_id = result.session_id;
|
||||||
|
self.session.session_save();
|
||||||
|
}
|
||||||
|
if (result.action === 'post') {
|
||||||
|
document.open();
|
||||||
|
document.write(result.value);
|
||||||
|
document.close();
|
||||||
|
} else if (result.action === 'redirect') {
|
||||||
|
window.location.replace(result.value);
|
||||||
|
} else {
|
||||||
|
// XXX display error ?
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- vim:fdl=1: -->
|
||||||
|
<templates id="template" xml:space="preserve">
|
||||||
|
|
||||||
|
<t t-extend="Login">
|
||||||
|
<t t-jquery=".oe_box2 tr:first td:nth-child(2)" t-operation="append">
|
||||||
|
<ul class="openid_providers">
|
||||||
|
<li><a href="#login,password" title="Password" data-url="" id="btn_password">Password</a></li>
|
||||||
|
<li><a href="#google" title="Google" data-url="https://www.google.com/accounts/o8/id">Google</a></li>
|
||||||
|
<li><a href="#googleapps" title="Google Apps" data-url="https://www.google.com/accounts/o8/site-xrds?hd={id}">Google</a></li>
|
||||||
|
<li><a href="#launchpad" title="Launchpad" data-url="https://launchpad.net/~{id}">Launchpad</a></li>
|
||||||
|
<li><a href="#openid_url" title="OpenID" data-url="{id}">OpenID</a></li>
|
||||||
|
</ul>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<t t-extend="Login">
|
||||||
|
<t t-jquery=".oe_box2 tr:first" t-operation="after">
|
||||||
|
<tr>
|
||||||
|
<td><label for="googleapps">Google Apps Domain:</label></td>
|
||||||
|
<td><input type="text" name="googleapps" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="launchpad">Username:</label></td>
|
||||||
|
<td><input type="text" name="launchpad" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="openid_url">OpenID URL:</label></td>
|
||||||
|
<td><input type="text" name="openid_url" /></td>
|
||||||
|
</tr>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
<t t-extend="Login">
|
||||||
|
<t t-jquery=".oe_box2 tr:has(input[name!='db'])">
|
||||||
|
//this.addClass('auth_choice'); // XXX for some reason, not all tr tags are HTMLElement's and thus, jQuery decide to not change the class...
|
||||||
|
this.attr('class', 'auth_choice');
|
||||||
|
this.each(function() {
|
||||||
|
var $i = $(this);
|
||||||
|
$i.attr('data-provider', $i.find('input').attr('name'));
|
||||||
|
});
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</templates>
|
|
@ -0,0 +1,46 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# Copyright (C) 2010-2011 OpenERP s.a. (<http://openerp.com>).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from openerp.modules.registry import RegistryManager
|
||||||
|
|
||||||
|
KEY_LENGTH = 16
|
||||||
|
|
||||||
|
SREG2AX = { # from http://www.axschema.org/types/#sreg
|
||||||
|
'nickname': 'http://axschema.org/namePerson/friendly',
|
||||||
|
'email': 'http://axschema.org/contact/email',
|
||||||
|
'fullname': 'http://axschema.org/namePerson',
|
||||||
|
'dob': 'http://axschema.org/birthDate',
|
||||||
|
'gender': 'http://axschema.org/person/gender',
|
||||||
|
'postcode': 'http://axschema.org/contact/postalCode/home',
|
||||||
|
'country': 'http://axschema.org/contact/country/home',
|
||||||
|
'language': 'http://axschema.org/pref/language',
|
||||||
|
'timezone': 'http://axschema.org/pref/timezone',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def cursor(db):
|
||||||
|
cr = RegistryManager.get(db).db.cursor()
|
||||||
|
try:
|
||||||
|
yield cr
|
||||||
|
finally:
|
||||||
|
cr.close()
|
||||||
|
|