diff --git a/__openerp__.py b/__openerp__.py index 24a71db8c2652ac8562b55144018d423f6972774_X19vcGVuZXJwX18ucHk=..cd10d1d08dc34e11d2722d692f095c9f6a5155bf_X19vcGVuZXJwX18ucHk= 100644 --- a/__openerp__.py +++ b/__openerp__.py @@ -31,7 +31,11 @@ 'author': 'XCG Consulting s.a.s.', 'maintainer': 'XCG Consulting s.a.s.', 'website': 'http://www.xcg-consulting.fr', - 'depends': ['base', 'web', 'base_setup'], + 'depends': [ + 'base', + 'base_setup', + 'web', + ], 'data': [ 'data/auth_saml.xml', @@ -49,7 +53,6 @@ 'static/lib/zocial/css/zocial.css', 'static/src/css/auth_saml.css', ], - 'qweb': ['static/src/xml/auth_saml.xml'], 'installable': True, 'auto_install': False, } diff --git a/controllers/main.py b/controllers/main.py index 24a71db8c2652ac8562b55144018d423f6972774_Y29udHJvbGxlcnMvbWFpbi5weQ==..cd10d1d08dc34e11d2722d692f095c9f6a5155bf_Y29udHJvbGxlcnMvbWFpbi5weQ== 100644 --- a/controllers/main.py +++ b/controllers/main.py @@ -5,4 +5,6 @@ import werkzeug.utils import openerp +from openerp import http +from openerp.http import request from openerp import SUPERUSER_ID @@ -8,3 +10,3 @@ from openerp import SUPERUSER_ID -import openerp.addons.web.http as oeweb +# import openerp.addons.web.http as oeweb from openerp.addons.web.controllers.main import set_cookie_and_redirect @@ -10,2 +12,3 @@ from openerp.addons.web.controllers.main import set_cookie_and_redirect +from openerp.addons.web.controllers.main import ensure_db from openerp.addons.web.controllers.main import login_and_redirect @@ -11,5 +14,4 @@ from openerp.addons.web.controllers.main import login_and_redirect -from openerp.modules.registry import RegistryManager _logger = logging.getLogger(__name__) @@ -40,6 +42,31 @@ # ---------------------------------------------------------- # Controller # ---------------------------------------------------------- -class SAMLController(oeweb.Controller): - _cp_path = '/auth_saml' +class SAMLLogin(openerp.addons.web.controllers.main.Home): + + def list_providers(self): + try: + provider_obj = request.registry.get('auth.saml.provider') + providers = provider_obj.search_read( + request.cr, SUPERUSER_ID, [('enabled', '=', True)] + ) + except Exception, e: + _logger.exception("SAML2: %s" % str(e)) + providers = [] + + return providers + + @http.route() + def web_login(self, *args, **kw): + ensure_db() + if ( + request.httprequest.method == 'GET' and + request.session.uid and + request.params.get('redirect') + ): + + # Redirect if already logged in and redirect param is present + return http.redirect_with_hash(request.params.get('redirect')) + + providers = self.list_providers() @@ -45,6 +72,32 @@ - @oeweb.jsonrequest - def get_auth_request(self, req, relaystate): + response = super(SAMLLogin, self).web_login(*args, **kw) + if response.is_qweb: + error = request.params.get('saml_error') + if error == '1': + error = _("Sign up is not allowed on this database.") + elif error == '2': + error = _("Access Denied") + elif error == '3': + error = _( + "You do not have access to this database or your " + "invitation has expired. Please ask for an invitation " + "and be sure to follow the link in your invitation email." + ) + else: + error = None + + response.qcontext['providers'] = providers + + if error: + response.qcontext['error'] = error + + return response + + +class AuthSAMLController(http.Controller): + + @http.route('/auth_saml/get_auth_request', type='http', auth='none') + def get_auth_request(self, pid): """state is the JSONified state object and we need to pass it inside our request as the RelayState argument """ @@ -48,5 +101,4 @@ """state is the JSONified state object and we need to pass it inside our request as the RelayState argument """ - state = simplejson.loads(relaystate) @@ -52,11 +104,7 @@ - dbname = state['d'] - provider_id = state['p'] - context = state.get('c', {}) - - registry = RegistryManager.get(dbname) - provider_osv = registry.get('auth.saml.provider') + provider_id = int(pid) + provider_osv = request.registry.get('auth.saml.provider') auth_request = None try: @@ -59,6 +107,6 @@ auth_request = None try: - with registry.cursor() as cr: + with request.registry.cursor() as cr: auth_request = provider_osv._get_auth_request( @@ -64,7 +112,7 @@ auth_request = provider_osv._get_auth_request( - cr, SUPERUSER_ID, provider_id, state, context=context + cr, SUPERUSER_ID, provider_id, pid ) except Exception, e: _logger.exception("SAML2: %s" % str(e)) @@ -66,7 +114,21 @@ ) except Exception, e: _logger.exception("SAML2: %s" % str(e)) - return {'auth_request': auth_request} + # store a RelayState on the request to our IDP so that the IDP + # can send us back this info alongside the obtained token + params = { + "RelayState": simplejson.dumps({ + "d": request.session.db, + "p": pid, + }), + } + url = auth_request + "&" + werkzeug.url_encode(params) + print "*"*35 + print url + print "*"*35 + redirect = werkzeug.utils.redirect(url, 303) + redirect.autocorrect_location_header = True + return redirect @@ -72,33 +134,4 @@ - @oeweb.jsonrequest - def list_providers(self, req, dbname): - l = [] - try: - registry = RegistryManager.get(dbname) - with registry.cursor() as cr: - providers = registry.get('auth.saml.provider') - if providers: - l = providers.read( - cr, SUPERUSER_ID, providers.search( - cr, SUPERUSER_ID, [('enabled', '=', True)] - ), - [ - "id", - "name", - "enabled", - "css_class", - "body", - "sequence", - ], - ) - else: - l = [] - - except Exception, e: - _logger.exception("SAML2: %s" % str(e)) - - return l - - @oeweb.httprequest + @http.route('/auth_saml/signin', type='http', auth='none') @fragment_to_query_string def signin(self, req, **kw): @@ -103,7 +136,7 @@ @fragment_to_query_string def signin(self, req, **kw): - """JS client obtained a saml token and passed it back + """client obtained a saml token and passed it back to us... we need to validate it """ saml_response = kw.get('SAMLResponse', None) @@ -106,8 +139,8 @@ to us... we need to validate it """ saml_response = kw.get('SAMLResponse', None) - if not kw.get('RelayState', None): + if kw.get('RelayState', None) is None: # here we are in front of a client that went through # some routes that "lost" its relaystate... this can happen # if the client visited his IDP and successfully logged in @@ -119,8 +152,9 @@ return redirect state = simplejson.loads(kw['RelayState']) - dbname = state['d'] - provider = state['p'] - context = state.get('c', {}) - registry = RegistryManager.get(dbname) + print "*"*35 + print state + print "*"*35 + # THIS IS FALSE TODO TODO + provider = state @@ -126,3 +160,3 @@ - with registry.cursor() as cr: + with request.registry.cursor() as cr: try: @@ -128,3 +162,3 @@ try: - u = registry.get('res.users') + u = request.registry.get('res.users') credentials = u.auth_saml( @@ -130,5 +164,5 @@ credentials = u.auth_saml( - cr, SUPERUSER_ID, provider, saml_response, context=context + cr, SUPERUSER_ID, provider, saml_response ) cr.commit() action = state.get('a') @@ -141,6 +175,6 @@ return login_and_redirect(req, *credentials, redirect_url=url) except AttributeError, e: - print e + # print e # auth_signup is not installed _logger.error("auth_signup not installed on database " @@ -145,6 +179,6 @@ # auth_signup is not installed _logger.error("auth_signup not installed on database " - "%s: saml sign up cancelled." % (dbname,)) + "saml sign up cancelled.") url = "/#action=login&saml_error=1" except openerp.exceptions.AccessDenied: @@ -164,6 +198,6 @@ _logger.exception("SAML2: %s" % str(e)) url = "/#action=login&saml_error=2" - return set_cookie_and_redirect(req, url) + return set_cookie_and_redirect(url) # vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/model/res_users.py b/model/res_users.py index 24a71db8c2652ac8562b55144018d423f6972774_bW9kZWwvcmVzX3VzZXJzLnB5..cd10d1d08dc34e11d2722d692f095c9f6a5155bf_bW9kZWwvcmVzX3VzZXJzLnB5 100644 --- a/model/res_users.py +++ b/model/res_users.py @@ -62,7 +62,11 @@ """ return the validation data corresponding to the access token """ p = self.pool.get('auth.saml.provider') - login = p._get_lasso_for_provider(cr, uid, provider, context=context) + # we are not yet logged in, so the userid cannot have access to the + # fields we need yet + login = p._get_lasso_for_provider( + cr, SUPERUSER_ID, provider, context=context + ) try: login.processAuthnResponseMsg(token) diff --git a/static/src/xml/auth_saml.xml b/static/src/xml/auth_saml.xml deleted file mode 100644 index 24a71db8c2652ac8562b55144018d423f6972774_c3RhdGljL3NyYy94bWwvYXV0aF9zYW1sLnhtbA==..0000000000000000000000000000000000000000 --- a/static/src/xml/auth_saml.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<templates id="template" xml:space="preserve"> -<t t-name="auth_saml.Login.button"> - <t t-foreach="widget.saml_providers" t-as="p"> - <a href="#" t-attf-class="oe_saml_provider_login_button #{p.css_class}" t-att-data-index="p_index"><t t-esc="p.body"/></a> - </t> -</t> -</templates> diff --git a/views/auth_saml.xml b/views/auth_saml.xml index 24a71db8c2652ac8562b55144018d423f6972774_dmlld3MvYXV0aF9zYW1sLnhtbA==..cd10d1d08dc34e11d2722d692f095c9f6a5155bf_dmlld3MvYXV0aF9zYW1sLnhtbA== 100644 --- a/views/auth_saml.xml +++ b/views/auth_saml.xml @@ -2,6 +2,24 @@ <openerp> <data> + <!-- login form button, new in Odoo8, Odoo7 automatically adds the button --> + <template id="auth_saml.providers" name="Auth SAML Providers"> + <div t-foreach="providers" t-as="p"> + <a t-att-href="'/auth_saml/get_auth_request?pid=%s'%p['id']" class="btn btn-link"> + <i t-att-class="p['css_class']"/> + <t t-esc="p['body']"/> + </a> + </div> + </template> + + <template id="auth_saml.login" inherit_id="web.login" name="Samlv2 Login buttons"> + <xpath expr="//button[@type='submit']" position="before"> + <div class="pull-right"> + <t t-call="auth_saml.providers"/> + </div> + </xpath> + </template> + <!-- Views for the auth.saml.provider model. --> <record model="ir.ui.view" id="view_saml_provider_list">