def _admin_password_warn(uid): """ Admin still has `admin` password, flash a message via chatter. Uses a private mail.channel from the system (/ flectrabot) to the user, as using a more generic mail.thread could send an email which is undesirable Uses mail.channel directly because using mail.thread might send an email instead. """ if request.params['password'] != 'admin': return if ipaddress.ip_address(request.httprequest.remote_addr).is_private: return env = request.env(user=SUPERUSER_ID, su=True) admin = env.ref('base.partner_admin') if uid not in admin.user_ids.ids: return has_demo = bool(env['ir.module.module'].search_count([('demo', '=', True)])) if has_demo: return user = request.env(user=uid)['res.users'] MailChannel = env(context=user.context_get())['mail.channel'] MailChannel.browse(MailChannel.channel_get([admin.id])['id'])\ .message_post( body=_("Your password is the default (admin)! If this system is exposed to untrusted users it is important to change it immediately for security reasons. I will keep nagging you about it!"), message_type='comment', subtype_xmlid='mail.mt_comment' )
def oauth2callback(self, **kw): """ This route/function is called by Google when user Accept/Refuse the consent of Google """ state = json.loads(kw['state']) dbname = state.get('d') service = state.get('s') url_return = state.get('f') with registry(dbname).cursor() as cr: if kw.get('code'): request.env(cr, request.session.uid)['google.%s' % service].set_all_tokens(kw['code']) return redirect(url_return) elif kw.get('error'): return redirect("%s%s%s" % (url_return, "?error=", kw['error'])) else: return redirect("%s%s" % (url_return, "?error=Unknown_error"))
def avatar(self, res_model, res_id, partner_id): headers = [('Content-Type', 'image/png')] status = 200 content = 'R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' # default image is one white pixel if res_model in request.env: try: # if the current user has access to the document, get the partner avatar as sudo() request.env[res_model].browse(res_id).check_access_rule('read') if partner_id in request.env[res_model].browse(res_id).sudo( ).exists().message_ids.mapped('author_id').ids: status, headers, _content = binary_content( model='res.partner', id=partner_id, field='image_medium', default_mimetype='image/png', env=request.env(user=SUPERUSER_ID)) # binary content return an empty string and not a placeholder if obj[field] is False if _content != '': content = _content if status == 304: return werkzeug.wrappers.Response(status=304) except AccessError: pass image_base64 = base64.b64decode(content) headers.append(('Content-Length', len(image_base64))) response = request.make_response(image_base64, headers) response.status = str(status) return response
def get_model_for_read(model, ENV=False): """Fetch a model object from the environment optimized for read. Postgres serialization levels are changed to allow parallel read queries. To increase the overall efficiency, as it is unlikely this API will be used as a mass transactional interface. Rather we assume sequential and structured integration workflows. :param str model: The model to retrieve from the environment. :param object env: Environment :returns: the framework model if exist, otherwise raises. :rtype: flectra.models.Model :raise: werkzeug.exceptions.HTTPException if the model not found in env. """ if ENV: return ENV[model] cr, uid = request.cr, request.session.uid test_mode = request.registry.test_cr if not test_mode: # Permit parallel query execution on read # Contrary to ISOLATION_LEVEL_SERIALIZABLE as per Flectra Standard cr._cnx.set_isolation_level(ISOLATION_LEVEL_READ_COMMITTED) try: return request.env(cr, uid)[model] except KeyError: err = list(CODE__obj_not_found) err[2] = 'The "%s" model is not available on this instance.' % model raise werkzeug.exceptions.HTTPException(response=error_response(*err))
def jobs(self, country=None, department=None, office_id=None, **kwargs): env = request.env(context=dict(request.env.context, show_address=True, no_tag_br=True)) Country = env['res.country'] Jobs = env['hr.job'] # List jobs available to current UID job_ids = Jobs.search([], order="website_published desc,no_of_recruitment desc").ids if not request.env['res.users'].has_group('website.group_website_publisher'): job_ids = Jobs.search([('website_ids', 'in', request.website.id)], order="website_published desc, no_of_recruitment desc").ids # Browse jobs as superuser, because address is restricted jobs = Jobs.sudo().browse(job_ids) # Default search by user country if not (country or department or office_id or kwargs.get('all_countries')): country_code = request.session['geoip'].get('country_code') if country_code: countries_ = Country.search([('code', '=', country_code)]) country = countries_[0] if countries_ else None if not any(j for j in jobs if j.address_id and j.address_id.country_id == country): country = False # Filter job / office for country if country and not kwargs.get('all_countries'): jobs = [j for j in jobs if j.address_id is None or j.address_id.country_id and j.address_id.country_id.id == country.id] offices = set(j.address_id for j in jobs if j.address_id is None or j.address_id.country_id and j.address_id.country_id.id == country.id) else: offices = set(j.address_id for j in jobs if j.address_id) # Deduce departments and countries offices of those jobs departments = set(j.department_id for j in jobs if j.department_id) countries = set(o.country_id for o in offices if o.country_id) if department: jobs = [j for j in jobs if j.department_id and j.department_id.id == department.id] if office_id and office_id in [x.id for x in offices]: jobs = [j for j in jobs if j.address_id and j.address_id.id == office_id] else: office_id = False # Render page return request.render("website_hr_recruitment.index", { 'jobs': jobs, 'countries': countries, 'departments': departments, 'offices': offices, 'country_id': country, 'department_id': department, 'office_id': office_id, })
def _generate_auth_code(self, scope, name): auth_dict = { 'scope': scope, 'name': name, 'timestamp': int(datetime.datetime.utcnow().timestamp( )), # <- elapsed time should be < 3 mins when verifying 'uid': request.uid, } auth_message = json.dumps(auth_dict, sort_keys=True).encode() signature = flectra.tools.misc.hmac(request.env(su=True), 'mail_client_extension', auth_message).encode() auth_code = "%s.%s" % (base64.b64encode(auth_message).decode(), base64.b64encode(signature).decode()) _logger.info('Auth code created - user %s, scope %s', request.env.user, scope) return auth_code
def _get_auth_code_data(self, auth_code): data, auth_code_signature = auth_code.split('.') data = base64.b64decode(data) auth_code_signature = base64.b64decode(auth_code_signature) signature = flectra.tools.misc.hmac(request.env(su=True), 'mail_client_extension', data).encode() if not hmac.compare_digest(auth_code_signature, signature): return None auth_message = json.loads(data) # Check the expiration if datetime.datetime.utcnow() - datetime.datetime.fromtimestamp( auth_message['timestamp']) > datetime.timedelta(minutes=3): return None return auth_message
def wrap__resource__unlink_one(modelname, id, success_code): """Function to delete one record. :param str modelname: The name of the model. :param int id: The record id of which we want to delete. :param int success_code: The success code. :returns: successful response if the delete operation is performed otherwise error response :rtype: werkzeug.wrappers.Response """ cr, uid = request.cr, request.session.uid record = request.env(cr, uid)[modelname].browse([id]) if not record.exists(): return error_response(*CODE__obj_not_found) record.unlink() return successful_response(success_code)
def user_avatar(self, user_id=0, **post): status, headers, content = binary_content( model='res.users', id=user_id, field='image_medium', default_mimetype='image/png', env=request.env(user=SUPERUSER_ID)) if not content: img_path = modules.get_module_resource('web', 'static/src/img', 'placeholder.png') with open(img_path, 'rb') as f: image = f.read() content = base64.b64encode(image) if status == 304: return werkzeug.wrappers.Response(status=304) image_base64 = base64.b64decode(content) headers.append(('Content-Length', len(image_base64))) response = request.make_response(image_base64, headers) response.status = str(status) return response
def wrap__resource__update_one(modelname, id, success_code, data): """Function to update one record. :param str modelname: The name of the model. :param int id: The record id of which we want to update. :param int success_code: The success code. :param dict data: The data for update. :returns: successful response if the update operation is performed otherwise error response :rtype: werkzeug.wrappers.Response """ cr, uid = request.cr, request.session.uid record = request.env(cr, uid)[modelname].browse(id) if not record.exists(): return error_response(*CODE__obj_not_found) try: record.write(data) except Exception as e: return error_response(400, type(e).__name__, str(e)) return successful_response(success_code)
def get_create_context(namespace, model, canned_context): """Get the requested preconfigured context of the model specification. The canned context is used to preconfigure default values or context flags. That are used in a repetitive way in namespace for specific model. As this should, for performance reasons, not repeatedly result in calls to the persistence layer, this method is cached in memory. :param str namespace: The namespace to also validate against. :param str model: The model, for which we retrieve the configuration. :param str canned_context: The preconfigured context, which we request. :returns: A dictionary containing the requested context. :rtype: dict :raise: werkzeug.exceptions.HTTPException TODO: add description in which case """ cr, uid = request.cr, request.session.uid # Singleton by construction (_sql_constraints) openapi_access = request.env(cr, uid)["openapi.access"].search([ ("model_id", "=", model), ("namespace_id.name", "=", namespace) ]) assert (len(openapi_access) == 1 ), "'openapi_access' is not a singleton, bad construction." # Singleton by construction (_sql_constraints) context = openapi_access.create_context_ids.filtered( lambda r: r["name"] == canned_context) assert len(context) == 1, "'context' is not a singleton, bad construction." if not context: raise werkzeug.exceptions.HTTPException(response=error_response( *CODE__canned_ctx_not_found)) return context
def bundle(self): env = request.env(user=SUPERUSER_ID) bundle = env.ref('test_assetsbundle.bundle1') views = env['ir.ui.view'].search([('inherit_id', '=', bundle.id)]) return views.with_context(check_view_ids=views.ids)._render_template( 'test_assetsbundle.template1')
def get_model_openapi_access(namespace, model): """Get the model configuration and validate the requested namespace against the session. The namespace is a lightweight ACL + default implementation to integrate with various integration consumer, such as webstore, provisioning platform, etc. We validate the namespace at this latter stage, because it forms part of the http route. The token has been related to a namespace already previously This is a double purpose method. As this should, for performance reasons, not repeatedly result in calls to the persistence layer, this method is cached in memory. :param str namespace: The namespace to also validate against. :param str model: The model, for which we retrieve the configuration. :returns: The error response object if namespace validation failed. A dictionary containing the model API configuration for this namespace. The layout of the dict is as follows: ```python {'context': (Dict) flectra context (default values through context), 'out_fields_read_multi': (Tuple) field spec, 'out_fields_read_one': (Tuple) field spec, 'out_fields_create_one': (Tuple) field spec, 'method' : { 'public' : { 'mode': (String) one of 'all', 'none', 'custom', 'whitelist': (List) of method strings, }, 'private' : { 'mode': (String) one of 'none', 'custom', 'whitelist': (List) of method strings, }, 'main' : { 'mode': (String) one of 'none', 'custom', 'whitelist': (List) of method strings, }, } ``` :rtype: dict :raise: werkzeug.exceptions.HTTPException if the namespace has no accesses. """ # TODO: this method has code duplicates with openapi specification code (e.g. get_OAS_definitions_part) cr, uid = request.cr, request.session.uid # Singleton by construction (_sql_constraints) openapi_access = (request.env(cr, uid)["openapi.access"].sudo().search([ ("model_id", "=", model), ("namespace_id.name", "=", namespace) ])) if not openapi_access.exists(): raise werkzeug.exceptions.HTTPException(response=error_response( *CODE__canned_ctx_not_found)) res = { "context": {}, # Take ot here FIXME: make sure it is for create_context "out_fields_read_multi": (), "out_fields_read_one": (), "out_fields_create_one": (), # FIXME: for what? "method": { "public": { "mode": "", "whitelist": [] }, "private": { "mode": "", "whitelist": [] }, "main": { "mode": "", "whitelist": [] }, }, } # Infer public method mode if openapi_access.api_public_methods and openapi_access.public_methods: res["method"]["public"]["mode"] = "custom" res["method"]["public"][ "whitelist"] = openapi_access.public_methods.split() elif openapi_access.api_public_methods: res["method"]["public"]["mode"] = "all" else: res["method"]["public"]["mode"] = "none" # Infer private method mode if openapi_access.private_methods: res["method"]["private"]["mode"] = "custom" res["method"]["private"][ "whitelist"] = openapi_access.private_methods.split() else: res["method"]["private"]["mode"] = "none" for c in openapi_access.create_context_ids.mapped("context"): res["context"].update(json.loads(c)) res["out_fields_read_multi"] = openapi_access.read_many_id.export_fields.mapped( "name") or ("id", ) res["out_fields_read_one"] = openapi_access.read_one_id.export_fields.mapped( "name") or ("id", ) if openapi_access.public_methods: res["method"]["public"][ "whitelist"] = openapi_access.public_methods.split() if openapi_access.private_methods: res["method"]["private"][ "whitelist"] = openapi_access.private_methods.split() main_methods = ["api_create", "api_read", "api_update", "api_delete"] for method in main_methods: if openapi_access[method]: res["method"]["main"]["whitelist"].append(method) if len(res["method"]["main"]["whitelist"]) == len(main_methods): res["method"]["main"]["mode"] = "all" elif not res["method"]["main"]["whitelist"]: res["method"]["main"]["mode"] = "none" else: res["method"]["main"]["mode"] = "custom" return res