def index(self, req, s_action=None, db=None, **kw): """ If LemonLDAP module is activated, auto login the user and keep him connected automatically Warning: Security is not managed on OpenERP anymore, OpenERP has to be behind a proxy and not accessible from outside ! """ # handle 500 error manually, to retrieve error as a string, not a json object try: lemon_params = self.get_lemon_params(req) log.debug('Lemon > db: %s, user_id: %s, username: %s, secret: %s', lemon_params['db'], lemon_params['user_id'], lemon_params['username'], lemon_params['secret']) db = self.get_current_db(req, lemon_params['db'] or db) config = self.get_sso_config(req, db) # only process if the module is activated if config['enabled']: log.debug('forwarded > header: %s, config: %s', lemon_params['forwarded'], config['forwarded']) # some basic security check to identify LemonLDAP and the proxy if lemon_params['forwarded'] not in config['forwarded']: raise Exception('OpenERP is not behind a proxy or the forwarded domain is wrong') if config['lemon_secret'] != lemon_params['secret']: raise Exception('LemonLDAP secret is not the same than secret configured on OpenERP !') log.debug("db_monodb(req): %s, db: %s", db_monodb(req), db) session = self.get_current_session(req) if db_monodb(req) != db: log.info('force re authenticate user %s', lemon_params['username']) # force the db retrieved from request header req.params.update({'db': db }) url = '/?db=%s' % db # login with a fake password, the security check has been disabled on res.users model return login_and_redirect(req, db, lemon_params['username'], 'nopassword', redirect_url=url) if not session or str(session._uid) != lemon_params['user_id']: log.info('auto-authenticate user %s', lemon_params['username']) # login with a fake password, the security check has been disabled on res.users model return login_and_redirect(req, db, lemon_params['username'], 'nopassword') log.info('user %s already authenticated', lemon_params['username']) except Exception as e: body = "<h1>OpenERP - LemonLDAP Authorization Error</h1><p>%s</p>" % str(e) return Response(body, status=500, headers=[('Content-Type', 'text/html'), ('Content-Length', len(body))]) return super(SSO, self).index(req, s_action, db, **kw)
def oea(self, **kw): """login user via Odoo Account provider""" dbname = kw.pop('db', None) if not dbname: dbname = db_monodb() if not dbname: return BadRequest() registry = RegistryManager.get(dbname) with registry.cursor() as cr: IMD = registry['ir.model.data'] try: model, provider_id = IMD.get_object_reference( cr, SUPERUSER_ID, 'auth_oauth', 'provider_openerp') except ValueError: return set_cookie_and_redirect('/web?db=%s' % dbname) assert model == 'auth.oauth.provider' state = { 'd': dbname, 'p': provider_id, 'c': { 'no_user_creation': True }, } kw['state'] = simplejson.dumps(state) return self.signin(**kw)
def oea(self, req, **kw): """login user via OpenERP Account provider""" dbname = kw.pop('db', None) if not dbname: dbname = db_monodb(req) if not dbname: return BadRequest() registry = RegistryManager.get(dbname) with registry.cursor() as cr: IMD = registry['ir.model.data'] model, provider_id = IMD.get_object_reference( cr, SUPERUSER_ID, 'auth_oauth', 'provider_openerp') assert model == 'auth.oauth.provider' state = { 'd': dbname, 'p': provider_id, 'c': { 'no_user_creation': True }, } kw['state'] = simplejson.dumps(state) return self.signin(req, **kw)
def oea(self, **kw): """login user via OpenERP Account provider""" dbname = kw.pop('db', None) if not dbname: dbname = db_monodb() if not dbname: return BadRequest() registry = RegistryManager.get(dbname) with registry.cursor() as cr: IMD = registry['ir.model.data'] try: model, provider_id = IMD.get_object_reference(cr, SUPERUSER_ID, 'auth_oauth', 'provider_openerp') except ValueError: return set_cookie_and_redirect('/web?db=%s' % dbname) assert model == 'auth.oauth.provider' state = { 'd': dbname, 'p': provider_id, 'c': {'no_user_creation': True}, } kw['state'] = simplejson.dumps(state) return self.signin(**kw)
def get_current_db(self, req, db): """ Retrieve the database to use, from Lemon header, URI parameter or the default one """ db = db if db and len(db) > 0 else db_monodb(req) if not db: raise Exception('Can not found a database to use...') return db
def oea(self, **kw): """login user via OpenERP Account provider""" dbname = kw.pop("db", None) if not dbname: dbname = db_monodb() if not dbname: return BadRequest() registry = RegistryManager.get(dbname) with registry.cursor() as cr: IMD = registry["ir.model.data"] try: model, provider_id = IMD.get_object_reference(cr, SUPERUSER_ID, "auth_oauth", "provider_openerp") except ValueError: return set_cookie_and_redirect("/web?db=%s" % dbname) assert model == "auth.oauth.provider" state = {"d": dbname, "p": provider_id, "c": {"no_user_creation": True}} kw["state"] = simplejson.dumps(state) return self.signin(**kw)
def signin(self, **kw): # TODO faltaria implementar el redirect y no hardcodear el my/home ensure_db() partner_uuid = kw['partner_uuid'] dbname = kw.pop('db', None) if not dbname: dbname = db_monodb() if not dbname: return werkzeug.exceptions.BadRequest() registry = RegistryManager.get(dbname) with registry.cursor() as cr: url = '/my/home' try: u = registry.get('res.users') credentials = u.partner_uuid_login( cr, SUPERUSER_ID, partner_uuid) cr.commit() return login_and_redirect(*credentials, redirect_url=url) except: pass return set_cookie_and_redirect(url)
def stats(self, **post): # TODO auth server_db = db_monodb() res = request.registry['saas_server.client'].update_all( request.cr, SUPERUSER_ID, server_db) return simplejson.dumps(res)
def doorkeeper_cb(self, **kw): if kw.get('state', False): state = simplejson.loads(kw['state']) master_db = db_monodb() proto, root_url = request.httprequest.url_root.split("://") if not master_db: return BadRequest() if state.get('login', False): login = state['login'] db_prefix = state['login'].split('@')[0] if state.get('demo', False): db_prefix = "%s-%s" % (db_prefix, 'demo') dbname = "%s_%s" % (db_prefix, master_db) redirect = "%s://%s.%s" % (proto, db_prefix, root_url) if not redirect.endswith("/"): redirect += "/" state.update({'d': dbname}) kw['state'] = simplejson.dumps(state) if openerp.service.db.exp_db_exist(dbname): registry = RegistryManager.get(dbname) with registry.cursor() as cr: IMD = registry['ir.model.data'] try: model, provider_id = IMD.get_object_reference( cr, SUPERUSER_ID, 'cenit_saas_server', 'saas_oauth_provider') except ValueError: return set_cookie_and_redirect('/web?db=%s' % dbname) assert model == 'auth.oauth.provider' params = { 'access_token': kw['access_token'], 'state': simplejson.dumps({ 'd': dbname, 'p': provider_id, }), } return werkzeug.utils.redirect( '{host}{controller}?{params}'.format( host=redirect, controller='auth_oauth/signin', params=werkzeug.url_encode(params))) else: _logger.info("\n\nNo existing tenant\n") registry = RegistryManager.get(master_db) if not state.get('name', False): state.update({'name': db_prefix.capitalize()}) if not state.get('organization', False): state.update({'organization': db_prefix.capitalize()}) if state.get('demo', False): plan = self.get_demo_plan() else: if state.get('plan', False): plan = self.get_plan(state.get('plan')) else: plan = self.get_default_plan() state.update({ 'db_template': plan['template'], 'plan': plan['id'] }) kw['state'] = simplejson.dumps(state) try: provider = self.__create_app_for_db(state['d']) partner_id, credentials = self.__signup_user(provider, kw) request.cr.commit() except Exception, e: _logger.exception(e) url = "/web/login?oauth_error=2" return set_cookie_and_redirect(url) url = "/saas_server/new_database" kw['admin_data'] = simplejson.dumps({ 'user_id': partner_id, 'client_id': provider.client_id, 'email': login, 'name': state['name'] }) full_url = '%s?%s' % (url, werkzeug.url_encode(kw)) _logger.info("\n\nFullURL: %s\n", full_url) return login_and_redirect(*credentials, redirect_url=full_url)
def doorkeeper_cb (self, **kw): if kw.get('state', False): state = simplejson.loads(kw['state']) master_db = db_monodb () proto, root_url = request.httprequest.url_root.split ("://") if not master_db: return BadRequest() if state.get ('login', False): login = state['login'] db_prefix = state['login'].split ('@')[0] if state.get ('demo', False): db_prefix = "%s-%s" % (db_prefix, 'demo') dbname = "%s_%s" %(db_prefix, master_db) redirect = "%s://%s.%s" %(proto, db_prefix, root_url) if not redirect.endswith ("/"): redirect += "/" state.update ({'d': dbname}) kw['state'] = simplejson.dumps (state) if openerp.service.db.exp_db_exist (dbname): registry = RegistryManager.get (dbname) with registry.cursor() as cr: IMD = registry['ir.model.data'] try: model, provider_id = IMD.get_object_reference( cr, SUPERUSER_ID, 'cenit_saas_server', 'saas_oauth_provider' ) except ValueError: return set_cookie_and_redirect('/web?db=%s' % dbname) assert model == 'auth.oauth.provider' params = { 'access_token': kw['access_token'], 'state': simplejson.dumps({ 'd': dbname, 'p': provider_id, }), } return werkzeug.utils.redirect('{host}{controller}?{params}'.format( host = redirect, controller = 'auth_oauth/signin', params = werkzeug.url_encode(params) ) ) else: _logger.info ("\n\nNo existing tenant\n") registry = RegistryManager.get (master_db) if not state.get ('name', False): state.update ({ 'name': db_prefix.capitalize () }) if not state.get ('organization', False): state.update ({ 'organization': db_prefix.capitalize () }) if state.get ('demo', False): plan = self.get_demo_plan () else: if state.get ('plan', False): plan = self.get_plan (state.get ('plan')) else: plan = self.get_default_plan () state.update ({ 'db_template': plan['template'], 'plan': plan['id'] }) kw['state'] = simplejson.dumps (state) try: provider = self.__create_app_for_db (state['d']) partner_id, credentials = self.__signup_user (provider, kw) request.cr.commit () except Exception, e: _logger.exception (e) url = "/web/login?oauth_error=2" return set_cookie_and_redirect (url) url = "/saas_server/new_database" kw['admin_data'] = simplejson.dumps ({ 'user_id': partner_id, 'client_id': provider.client_id, 'email': login, 'name': state['name'] }) full_url = '%s?%s' % (url, werkzeug.url_encode(kw)) _logger.info ("\n\nFullURL: %s\n", full_url) return login_and_redirect (*credentials, redirect_url=full_url)
def verify(self, **kw): # if select database db= kw.pop('db', None) # login, password,.. login= kw.pop('login', None) password = kw.pop('password', None) redirect = kw.pop('redirect', None) pincode= kw.pop('pincode', None) numpin= kw.pop('numpin', None) if not db: db=db_monodb() if not numpin: numpin=0 registry = RegistryManager.get(db) ipuser=request.httprequest.remote_addr with registry.cursor() as cr: res_users = registry.get('res.users') #check password/login for user => return l'id or false iduser=res_users.authenticate(db, login, password,user_agent_env=None) cr.commit() if iduser<>False : user=res_users.browse(cr,SUPERUSER_ID,iduser,context=None) if int(numpin)>0 : #verify pin, if error return login/password auth_log = registry.get('pincode.log') auth_log=user.pincode_log_id[0] if pincode<>auth_log.pincode: vals=dict() vals['states']="wrong" vals['ipuser']=ipuser authen_log = registry.get('pincode.log') idlog=authen_log.write(cr,SUPERUSER_ID,user.pincode_log_id[0].id,vals) return http.request.render('web.login', {'error':'Wrong pincode '}) else: # if ok pincode # is outdated ?? interval=str(datetime.now()-datetime.strptime(auth_log.date+".000000","%Y-%m-%d %H:%M:%S.%f")).split(".") interval_sec=sum(int(x) * 60 ** i for i,x in enumerate(reversed(interval[0].split(":")))) time_validity_sec=(user.pincode_id[int(numpin)-1].type_id.time_validity)*60*60 # in seconde user.pincode_id[int(numpin)-1].type_id.time_validity if interval_sec>time_validity_sec: vals=dict() vals['states']='outdated' vals['ipuser']=ipuser authen_log = registry.get('pincode.log') idlog=authen_log.write(cr,SUPERUSER_ID,user.pincode_log_id[0].id,vals) return http.request.render('web.login', {'error':'pincode outdated'}) else: vals=dict() vals['states']="used" vals['ipuser']=ipuser authen_log = registry.get('pincode.log') idlog=authen_log.write(cr,SUPERUSER_ID,user.pincode_log_id[0].id,vals) if user.pincode_id and int(numpin)==0: self.create_pincode(cr,registry,user.pincode_id[0],user.id) return http.request.render('pincode.pincode', { 'login': login, 'password':password, 'db':db, 'redirect':redirect, 'numpin':1, }) # Login ok , pin ok => menu ! url="/web" credentials=(cr.dbname, login, password) return login_and_redirect(*credentials, redirect_url=url) #if error return login return http.request.render('web.login', {'error':'Wrong login/password '})
def stats(self, **post): # TODO auth server_db = db_monodb() res = request.registry['saas_server.client'].update_all(request.cr, SUPERUSER_ID, server_db) return simplejson.dumps(res)
def xml(self, **kwargs): database = kwargs.get('database', None) if not database: database = db_monodb() req = openerp.http.request language = kwargs.get('language', None) if req.httprequest.method == 'GET': # Login database = kwargs.get('database', None) req.session.db = database try: self.authenticate(req, database, language) except Exception as e: logger.warning("Failed login attempt: %s" % e) return Response( 'Login with Odoo user name and password', 401, headers=[('WWW-Authenticate', 'Basic realm="odoo"')] ) # As an optional extra security check we can validate a web token attached # to the request. It allows use to verify that the request is generated # from frePPLe and not from somebody else. # Generate data try: xp = exporter( req, database = database, company = kwargs.get('company', None), mode = int(kwargs.get('mode', 1)) ) # TODO Returning an iterator to stream the response back to the client and # to save memory on the server side return req.make_response( ''.join([i for i in xp.run()]), headers=[ ('Content-Type', 'application/xml;charset=utf8'), ('Cache-Control', 'no-cache, no-store, must-revalidate'), ('Pragma', 'no-cache'), ('Expires', '0') ]) except Exception as e: logger.exception('Error generating frePPLe XML data') raise InternalServerError(description='Error generating frePPLe XML data: check the Odoo log file for more details') elif req.httprequest.method == 'POST': # Authenticate the user database = req.httprequest.form.get('database', None) req.session.db = database try: self.authenticate(req, database, language) except Exception as e: logger.warning("Failed login attempt %s" % e) return Response( 'Login with Odoo user name and password', 401, headers=[('WWW-Authenticate', 'Basic realm="odoo"')] ) # Validate the company argument company_name = req.httprequest.form.get('company', None) company = None m = req.session.model('res.company') m_search = m.search([('name', '=', company_name)]) for i in m.browse(m_search): company = i if not company: return Response('Invalid company name argument', 401) # Verify that the data was posted from frePPLe and nobody else try: webtoken = req.httprequest.form.get('webtoken', None) decoded = jwt.decode( webtoken, company.webtoken_key, algorithms=['HS256'] ) if self.user != decoded.get('user', None): return Response('Incorrect or missing webtoken', 401) except: return Response('Incorrect or missing webtoken', 401) # Import the data try: ip = importer( req, database=database, company=company, mode=req.httprequest.form.get('mode', 1) ) return req.make_response( ip.run(), [ ('Content-Type', 'text/plain'), ('Cache-Control', 'no-cache, no-store, must-revalidate'), ('Pragma', 'no-cache'), ('Expires', '0') ]) except Exception as e: logger.exception('Error processing data posted by frePPLe') raise InternalServerError(description='Error processing data posted by frePPLe: check the Odoo log file for more details') else: raise MethodNotAllowed('Only GET and POST requests are accepted')
def xml(self, **kwargs): req = openerp.http.request language = kwargs.get("language", None) if req.httprequest.method == "GET": # Login database = kwargs.get("database", None) if not database: database = db_monodb() req.session.db = database try: self.authenticate(req, database, language) except Exception as e: logger.warning("Failed login attempt: %s" % e) return Response( "Login with Odoo user name and password", 401, headers=[("WWW-Authenticate", 'Basic realm="odoo"')], ) # As an optional extra security check we can validate a web token attached # to the request. It allows use to verify that the request is generated # from frePPLe and not from somebody else. # Generate data try: xp = exporter( req, database=database, company=kwargs.get("company", None), mode=int(kwargs.get("mode", 1)), ) # TODO Returning an iterator to stream the response back to the client and # to save memory on the server side return req.make_response( "".join([i for i in xp.run()]), headers=[ ("Content-Type", "application/xml;charset=utf8"), ("Cache-Control", "no-cache, no-store, must-revalidate"), ("Pragma", "no-cache"), ("Expires", "0"), ], ) except Exception as e: logger.exception("Error generating frePPLe XML data") raise InternalServerError( description= "Error generating frePPLe XML data: check the Odoo log file for more details" ) elif req.httprequest.method == "POST": # Authenticate the user database = req.httprequest.form.get("database", None) if not database: database = db_monodb() req.session.db = database try: self.authenticate(req, database, language) except Exception as e: logger.warning("Failed login attempt %s" % e) return Response( "Login with Odoo user name and password", 401, headers=[("WWW-Authenticate", 'Basic realm="odoo"')], ) # Validate the company argument company_name = req.httprequest.form.get("company", None) company = None m = req.session.model("res.company") m_search = m.search([("name", "=", company_name)]) for i in m.browse(m_search): company = i if not company: return Response("Invalid company name argument", 401) # Verify that the data was posted from frePPLe and nobody else try: webtoken = req.httprequest.form.get("webtoken", None) decoded = jwt.decode(webtoken, company.webtoken_key, algorithms=["HS256"]) if self.user != decoded.get("user", None): return Response("Incorrect or missing webtoken", 401) except Exception: return Response("Incorrect or missing webtoken", 401) # Import the data try: ip = importer( req, database=database, company=company, mode=req.httprequest.form.get("mode", 1), ) return req.make_response( ip.run(), [ ("Content-Type", "text/plain"), ("Cache-Control", "no-cache, no-store, must-revalidate"), ("Pragma", "no-cache"), ("Expires", "0"), ], ) except Exception as e: logger.exception("Error processing data posted by frePPLe") raise InternalServerError( description= "Error processing data posted by frePPLe: check the Odoo log file for more details" ) else: raise MethodNotAllowed("Only GET and POST requests are accepted")
def xml(self, **kwargs): database = kwargs.get('database', None) if not database: database = db_monodb() req = openerp.http.request language = kwargs.get('language', None) if req.httprequest.method == 'GET': # Login database = kwargs.get('database', None) req.session.db = database try: self.authenticate(req, database, language) except Exception as e: logger.warning("Failed login attempt: %s" % e) return Response('Login with Odoo user name and password', 401, headers=[('WWW-Authenticate', 'Basic realm="odoo"')]) # As an optional extra security check we can validate a web token attached # to the request. It allows use to verify that the request is generated # from frePPLe and not from somebody else. # Generate data try: xp = exporter(req, database=database, company=kwargs.get('company', None), mode=int(kwargs.get('mode', 1))) # TODO Returning an iterator to stream the response back to the client and # to save memory on the server side return req.make_response( ''.join([i for i in xp.run()]), headers=[('Content-Type', 'application/xml;charset=utf8'), ('Cache-Control', 'no-cache, no-store, must-revalidate'), ('Pragma', 'no-cache'), ('Expires', '0')]) except Exception as e: logger.exception('Error generating frePPLe XML data') raise InternalServerError( description= 'Error generating frePPLe XML data: check the Odoo log file for more details' ) elif req.httprequest.method == 'POST': # Authenticate the user database = req.httprequest.form.get('database', None) req.session.db = database try: self.authenticate(req, database, language) except Exception as e: logger.warning("Failed login attempt %s" % e) return Response('Login with Odoo user name and password', 401, headers=[('WWW-Authenticate', 'Basic realm="odoo"')]) # Validate the company argument company_name = req.httprequest.form.get('company', None) company = None m = req.session.model('res.company') m_search = m.search([('name', '=', company_name)]) for i in m.browse(m_search): company = i if not company: return Response('Invalid company name argument', 401) # Verify that the data was posted from frePPLe and nobody else try: webtoken = req.httprequest.form.get('webtoken', None) decoded = jwt.decode(webtoken, company.webtoken_key, algorithms=['HS256']) if self.user != decoded.get('user', None): return Response('Incorrect or missing webtoken', 401) except: return Response('Incorrect or missing webtoken', 401) # Import the data try: ip = importer(req, database=database, company=company, mode=req.httprequest.form.get('mode', 1)) return req.make_response( ip.run(), [('Content-Type', 'text/plain'), ('Cache-Control', 'no-cache, no-store, must-revalidate'), ('Pragma', 'no-cache'), ('Expires', '0')]) except Exception as e: logger.exception('Error processing data posted by frePPLe') raise InternalServerError( description= 'Error processing data posted by frePPLe: check the Odoo log file for more details' ) else: raise MethodNotAllowed('Only GET and POST requests are accepted')