def _search(self, args, offset=0, limit=None, order=None, count=False, access_rights_uid=None): if self._uid != SUPERUSER_ID and args: domain_fields = {term[0] for term in args if isinstance(term, (tuple, list))} if domain_fields.intersection(USER_PRIVATE_FIELDS): raise AccessError(_('Invalid search criterion')) return super(Users, self)._search(args, offset=offset, limit=limit, order=order, count=count, access_rights_uid=access_rights_uid)
def cancel(self): """ cancel one or more order.line, update order status and unlink existing cashmoves """ if self.user_has_groups("lunch.group_lunch_manager"): self.state = 'cancelled' self.cashmove.unlink() else: raise AccessError(_("Only your lunch manager cancels the orders."))
def order(self): """ The order_line is ordered to the vendor but isn't received yet """ if self.user_has_groups("lunch.group_lunch_manager"): self.state = 'ordered' else: raise AccessError( _("Only your lunch manager processes the orders."))
def write(self, values): employee_id = values.get('employee_id', False) if not self._check_state_access_right(values): raise AccessError( _('You cannot set a leave request as \'%s\'. Contact a human resource manager.' ) % values.get('state')) result = super(Holidays, self).write(values) self.add_follower(employee_id) if 'employee_id' in values: self._onchange_employee_id() return result
def check(self, mode, values=None): """Restricts the access to an ir.attachment, according to referred model In the 'document' module, it is overriden to relax this hard rule, since more complex ones apply there. """ # collect the records to check (by model) model_ids = defaultdict(set) # {model_name: set(ids)} require_employee = False if self: self._cr.execute( 'SELECT res_model, res_id, create_uid, public FROM ir_attachment WHERE id IN %s', [tuple(self.ids)]) for res_model, res_id, create_uid, public in self._cr.fetchall(): if public and mode == 'read': continue if not (res_model and res_id): if create_uid != self._uid: require_employee = True continue model_ids[res_model].add(res_id) if values and values.get('res_model') and values.get('res_id'): model_ids[values['res_model']].add(values['res_id']) # check access rights on the records for res_model, res_ids in model_ids.items(): # ignore attachments that are not attached to a resource anymore # when checking access rights (resource was deleted but attachment # was not) if res_model not in self.env: require_employee = True continue elif res_model == 'res.users' and len( res_ids) == 1 and self._uid == list(res_ids)[0]: # by default a user cannot write on itself, despite the list of writeable fields # e.g. in the case of a user inserting an image into his image signature # we need to bypass this check which would needlessly throw us away continue records = self.env[res_model].browse(res_ids).exists() if len(records) < len(res_ids): require_employee = True # For related models, check if we can write to the model, as unlinking # and creating attachments can be seen as an update to the model records.check_access_rights('write' if mode in ( 'create', 'unlink') else mode) records.check_access_rule(mode) if require_employee: if not (self.env.user._is_admin() or self.env.user.has_group('base.group_user')): raise AccessError( _("Sorry, you are not allowed to access this document."))
def check_access_rule(self, operation): """ Add Access rules of mail.message for non-employee user: - read: - raise if the type is comment and subtype NULL (internal note) """ if self.user_has_groups('base.group_public'): self.env.cr.execute( 'SELECT id FROM "%s" WHERE website_published IS FALSE AND id = ANY (%%s)' % (self._table), (self.ids, )) if self.env.cr.fetchall(): raise AccessError( _('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)' ) % (self._description, operation)) return super(MailMessage, self).check_access_rule(operation=operation)
def force_storage(self): """Force all attachments to be stored in the currently configured storage""" if not self.env.user._is_admin(): raise AccessError( _('Only administrators can execute this action.')) # domain to retrieve the attachments to migrate domain = { 'db': [('store_fname', '!=', False)], 'file': [('db_datas', '!=', False)], }[self._storage()] for attach in self.search(domain): attach.write({'datas': attach.datas}) return True
def confirm(self): """ confirm one or more order line, update order status and create new cashmove """ if self.user_has_groups("lunch.group_lunch_manager"): if self.state != 'confirmed': values = { 'user_id': self.user_id.id, 'amount': -self.price, 'description': self.product_id.name, 'order_id': self.id, 'state': 'order', 'date': self.date, } self.env['lunch.cashmove'].create(values) self.state = 'confirmed' else: raise AccessError( _("Only your lunch manager sets the orders as received."))
def create(self, values): """ Override to avoid automatic logging of creation """ employee_id = values.get('employee_id', False) if not self._check_state_access_right(values): raise AccessError( _('You cannot set a leave request as \'%s\'. Contact a human resource manager.' ) % values.get('state')) if not values.get('department_id'): values.update({ 'department_id': self.env['hr.employee'].browse(employee_id).department_id.id }) holiday = super( Holidays, self.with_context(mail_create_nolog=True, mail_create_nosubscribe=True)).create(values) holiday.add_follower(employee_id) if 'employee_id' in values: holiday._onchange_employee_id() return holiday
def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True): groupby_fields = set([groupby] if isinstance(groupby, pycompat.string_types) else groupby) if groupby_fields.intersection(USER_PRIVATE_FIELDS): raise AccessError(_("Invalid 'group by' parameter")) return super(Users, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
def execute(self): self.ensure_one() if not self.env.user._is_superuser() and not self.env.user.has_group( 'base.group_system'): raise AccessError(_("Only administrators can change the settings")) self = self.with_context(active_test=False) classified = self._get_classified_fields() # default values fields IrDefault = self.env['ir.default'].sudo() for name, model, field in classified['default']: if isinstance(self[name], models.BaseModel): if self._fields[name].type == 'many2one': value = self[name].id else: value = self[name].ids else: value = self[name] IrDefault.set(model, field, value) # group fields: modify group / implied groups current_settings = self.default_get(list(self.fields_get())) with self.env.norecompute(): for name, groups, implied_group in classified['group']: if self[name] == current_settings[name]: continue if self[name]: groups.write({'implied_ids': [(4, implied_group.id)]}) else: groups.write({'implied_ids': [(3, implied_group.id)]}) implied_group.write({ 'users': [(3, user.id) for user in groups.mapped('users')] }) self.recompute() # other fields: execute method 'set_values' # Methods that start with `set_` are now deprecated for method in dir(self): if method.startswith('set_') and method is not 'set_values': _logger.warning( _('Methods that start with `set_` are deprecated. Override `set_values` instead (Method %s)' ) % method) self.set_values() # module fields: install/uninstall the selected modules to_install = [] to_uninstall_modules = self.env['ir.module.module'] lm = len('module_') for name, module in classified['module']: if self[name]: to_install.append((name[lm:], module)) else: if module and module.state in ('installed', 'to upgrade'): to_uninstall_modules += module if to_uninstall_modules: to_uninstall_modules.button_immediate_uninstall() self._install_modules(to_install) if to_install or to_uninstall_modules: # After the uninstall/install calls, the registry and environments # are no longer valid. So we reset the environment. self.env.reset() self = self.env()[self._name] # pylint: disable=next-method-called config = self.env['res.config'].next() or {} if config.get('type') not in ('ir.actions.act_window_close', ): return config # force client-side reload (update user menu and current view) return { 'type': 'ir.actions.client', 'tag': 'reload', }
def check_access_rule(self, operation): """ Access rules of mail.message: - read: if - author_id == pid, uid is the author OR - uid is in the recipients (partner_ids) OR - uid has been notified (needaction) OR - uid is member of a listern channel (channel_ids.partner_ids) OR - uid have read access to the related document if model, res_id - otherwise: raise - create: if - no model, no res_id (private message) OR - pid in message_follower_ids if model, res_id OR - uid can read the parent OR - uid have write or create access on the related document if model, res_id, OR - otherwise: raise - write: if - author_id == pid, uid is the author, OR - uid is in the recipients (partner_ids) OR - uid has write or create access on the related document if model, res_id - otherwise: raise - unlink: if - uid has write or create access on the related document if model, res_id - otherwise: raise Specific case: non employee users see only messages with subtype (aka do not see internal logs). """ def _generate_model_record_ids(msg_val, msg_ids): """ :param model_record_ids: {'model': {'res_id': (msg_id, msg_id)}, ... } :param message_values: {'msg_id': {'model': .., 'res_id': .., 'author_id': ..}} """ model_record_ids = {} for id in msg_ids: vals = msg_val.get(id, {}) if vals.get('model') and vals.get('res_id'): model_record_ids.setdefault(vals['model'], set()).add(vals['res_id']) return model_record_ids if self._uid == SUPERUSER_ID: return # Non employees see only messages with a subtype (aka, not internal logs) if not self.env['res.users'].has_group('base.group_user'): self._cr.execute('''SELECT DISTINCT message.id, message.subtype_id, subtype.internal FROM "%s" AS message LEFT JOIN "mail_message_subtype" as subtype ON message.subtype_id = subtype.id WHERE message.message_type = %%s AND (message.subtype_id IS NULL OR subtype.internal IS TRUE) AND message.id = ANY (%%s)''' % (self._table), ('comment', self.ids,)) if self._cr.fetchall(): raise AccessError( _('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % (self._description, operation)) # Read mail_message.ids to have their values message_values = dict((res_id, {}) for res_id in self.ids) if operation in ['read', 'write']: self._cr.execute(""" SELECT DISTINCT m.id, m.model, m.res_id, m.author_id, m.parent_id, COALESCE(partner_rel.res_partner_id, needaction_rel.res_partner_id), channel_partner.channel_id as channel_id FROM "%s" m LEFT JOIN "mail_message_res_partner_rel" partner_rel ON partner_rel.mail_message_id = m.id AND partner_rel.res_partner_id = %%(pid)s LEFT JOIN "mail_message_res_partner_needaction_rel" needaction_rel ON needaction_rel.mail_message_id = m.id AND needaction_rel.res_partner_id = %%(pid)s LEFT JOIN "mail_message_mail_channel_rel" channel_rel ON channel_rel.mail_message_id = m.id LEFT JOIN "mail_channel" channel ON channel.id = channel_rel.mail_channel_id LEFT JOIN "mail_channel_partner" channel_partner ON channel_partner.channel_id = channel.id AND channel_partner.partner_id = %%(pid)s WHERE m.id = ANY (%%(ids)s)""" % self._table, dict(pid=self.env.user.partner_id.id, ids=self.ids)) for mid, rmod, rid, author_id, parent_id, partner_id, channel_id in self._cr.fetchall(): message_values[mid] = { 'model': rmod, 'res_id': rid, 'author_id': author_id, 'parent_id': parent_id, 'notified': any((message_values[mid].get('notified'), partner_id, channel_id)) } else: self._cr.execute("""SELECT DISTINCT id, model, res_id, author_id, parent_id FROM "%s" WHERE id = ANY (%%s)""" % self._table, (self.ids,)) for mid, rmod, rid, author_id, parent_id in self._cr.fetchall(): message_values[mid] = {'model': rmod, 'res_id': rid, 'author_id': author_id, 'parent_id': parent_id} # Author condition (READ, WRITE, CREATE (private)) author_ids = [] if operation == 'read' or operation == 'write': author_ids = [mid for mid, message in message_values.items() if message.get('author_id') and message.get('author_id') == self.env.user.partner_id.id] elif operation == 'create': author_ids = [mid for mid, message in message_values.items() if not message.get('model') and not message.get('res_id')] # Parent condition, for create (check for received notifications for the created message parent) notified_ids = [] if operation == 'create': # TDE: probably clean me parent_ids = [message.get('parent_id') for message in message_values.values() if message.get('parent_id')] self._cr.execute("""SELECT DISTINCT m.id, partner_rel.res_partner_id, channel_partner.partner_id FROM "%s" m LEFT JOIN "mail_message_res_partner_rel" partner_rel ON partner_rel.mail_message_id = m.id AND partner_rel.res_partner_id = (%%s) LEFT JOIN "mail_message_mail_channel_rel" channel_rel ON channel_rel.mail_message_id = m.id LEFT JOIN "mail_channel" channel ON channel.id = channel_rel.mail_channel_id LEFT JOIN "mail_channel_partner" channel_partner ON channel_partner.channel_id = channel.id AND channel_partner.partner_id = (%%s) WHERE m.id = ANY (%%s)""" % self._table, (self.env.user.partner_id.id, self.env.user.partner_id.id, parent_ids,)) not_parent_ids = [mid[0] for mid in self._cr.fetchall() if any([mid[1], mid[2]])] notified_ids += [mid for mid, message in message_values.items() if message.get('parent_id') in not_parent_ids] # Recipients condition, for read and write (partner_ids) and create (message_follower_ids) other_ids = set(self.ids).difference(set(author_ids), set(notified_ids)) model_record_ids = _generate_model_record_ids(message_values, other_ids) if operation in ['read', 'write']: notified_ids = [mid for mid, message in message_values.items() if message.get('notified')] elif operation == 'create': for doc_model, doc_ids in model_record_ids.items(): followers = self.env['mail.followers'].sudo().search([ ('res_model', '=', doc_model), ('res_id', 'in', list(doc_ids)), ('partner_id', '=', self.env.user.partner_id.id), ]) fol_mids = [follower.res_id for follower in followers] notified_ids += [mid for mid, message in message_values.items() if message.get('model') == doc_model and message.get('res_id') in fol_mids] # CRUD: Access rights related to the document other_ids = other_ids.difference(set(notified_ids)) model_record_ids = _generate_model_record_ids(message_values, other_ids) document_related_ids = [] for model, doc_ids in model_record_ids.items(): DocumentModel = self.env[model] mids = DocumentModel.browse(doc_ids).exists() if hasattr(DocumentModel, 'check_mail_message_access'): DocumentModel.check_mail_message_access(mids.ids, operation) # ?? mids ? else: self.env['mail.thread'].check_mail_message_access(mids.ids, operation, model_name=model) document_related_ids += [mid for mid, message in message_values.items() if message.get('model') == model and message.get('res_id') in mids.ids] # Calculate remaining ids: if not void, raise an error other_ids = other_ids.difference(set(document_related_ids)) if not (other_ids and self.browse(other_ids).exists()): return raise AccessError( _('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % (self._description, operation))
def read(self, fields=None, load='_classic_read'): if self.search_count([('id', 'in', self._ids), ('name', '=', 'NOACCESS')]): raise AccessError('Sorry') return super(Category, self).read(fields=fields, load=load)
def web_settings_dashboard_data(self, **kw): if not request.env.user.has_group('base.group_erp_manager'): raise AccessError("Access Denied") installed_apps = request.env['ir.module.module'].search_count([ ('application', '=', True), ('state', 'in', ['installed', 'to upgrade', 'to remove']) ]) cr = request.cr cr.execute(""" SELECT count(*) FROM res_users WHERE active=true AND share=false """) active_count = cr.dictfetchall()[0].get('count') cr.execute(""" SELECT count(u.*) FROM res_users u WHERE active=true AND NOT exists(SELECT 1 FROM res_users_log WHERE create_uid=u.id) """) pending_count = cr.dictfetchall()[0].get('count') cr.execute(""" SELECT id, login FROM res_users u WHERE active=true AND NOT exists(SELECT 1 FROM res_users_log WHERE create_uid=u.id) ORDER BY id desc LIMIT 10 """) pending_users = cr.fetchall() # See update.py for this computation limit_date = datetime.now() - timedelta(15) enterprise_users = request.env['res.users'].search_count([ ("login_date", ">=", fields.Datetime.to_string(limit_date)), ('share', '=', False) ]) expiration_date = request.env['ir.config_parameter'].sudo().get_param( 'database.expiration_date') return { 'apps': { 'installed_apps': installed_apps, 'enterprise_users': enterprise_users, }, 'users_info': { 'active_users': active_count, 'pending_count': pending_count, 'pending_users': pending_users, 'user_form_view_id': request.env['ir.model.data'].xmlid_to_res_id( "base.view_users_form"), }, 'share': { 'server_version': release.version, 'expiration_date': expiration_date, 'debug': request.debug, }, 'company': { 'company_id': request.env.user.company_id.id, 'company_name': request.env.user.company_id.name } }
def check_user(self, uid=None): if uid is None: uid = request.uid is_admin = request.env['res.users'].browse(uid)._is_admin() if not is_admin: raise AccessError(_("Only administrators can upload a module"))