Esempio n. 1
0
def _check_special_access(res_model, res_id, token='', _hash='', pid=False):
    record = request.env[res_model].browse(res_id).sudo()
    if token:  # Token Case: token is the global one of the document
        token_field = request.env[res_model]._mail_post_token_field
        return (token and record and consteq(record[token_field], token))
    elif _hash and pid:  # Signed Token Case: hash implies token is signed by partner pid
        return consteq(_hash, record._sign_token(pid))
    else:
        raise Forbidden()
Esempio n. 2
0
 def confirm(self, **kw):
     tx_id = int(kw.get('tx_id', 0))
     access_token = kw.get('access_token')
     if tx_id:
         if access_token:
             tx = request.env['payment.transaction'].sudo().browse(tx_id)
             secret = request.env['ir.config_parameter'].sudo().get_param(
                 'database.secret')
             valid_token_str = '%s%s%s' % (tx.id, tx.reference, tx.amount)
             valid_token = hmac.new(secret.encode('utf-8'),
                                    valid_token_str.encode('utf-8'),
                                    hashlib.sha256).hexdigest()
             if not consteq(ustr(valid_token), access_token):
                 raise werkzeug.exceptions.NotFound
         else:
             tx = request.env['payment.transaction'].browse(tx_id)
         if tx.state in ['done', 'authorized']:
             status = 'success'
             message = tx.acquirer_id.done_msg
         elif tx.state == 'pending':
             status = 'warning'
             message = tx.acquirer_id.pending_msg
         PaymentProcessing.remove_payment_transaction(tx)
         return request.render('payment.confirm', {
             'tx': tx,
             'status': status,
             'message': message
         })
     else:
         return request.redirect('/my/home')
Esempio n. 3
0
 def _check_token(cls, token):
     base_link = request.httprequest.path
     params = dict(request.params)
     params.pop('token', '')
     valid_token = request.env['mail.thread']._notify_encode_link(
         base_link, params)
     return consteq(valid_token, str(token))
Esempio n. 4
0
    def _get_record_and_check(self,
                              xmlid=None,
                              model=None,
                              id=None,
                              field='datas',
                              access_token=None):
        # get object and content
        record = None
        if xmlid:
            record = self._xmlid_to_obj(self.env, xmlid)
        elif id and model in self.env:
            record = self.env[model].browse(int(id))

        # obj exists
        if not record or not record.exists() or field not in record:
            return None, 404

        if model == 'ir.attachment':
            record_sudo = record.sudo()
            if access_token and not consteq(record_sudo.access_token or '',
                                            access_token):
                return None, 403
            elif (access_token
                  and consteq(record_sudo.access_token or '', access_token)):
                record = record_sudo
            elif record_sudo.public:
                record = record_sudo
            elif self.env.user.has_group('base.group_portal'):
                # Check the read access on the record linked to the attachment
                # eg: Allow to download an attachment on a task from /my/task/task_id
                record.check('read')
                record = record_sudo
            # We have prefetched some fields of record, among which the field
            # 'write_date' used by '__last_update' below. In order to check
            # access on record, we have to invalidate its cache first.
            record._cache.clear()

        # check read access
        try:
            record['__last_update']
        except AccessError:
            return None, 403

        return record, 200
Esempio n. 5
0
 def check_token(self, access_token, partner_id, amount, currency_id):
     secret = self.env['ir.config_parameter'].sudo().get_param(
         'database.secret')
     token_str = '%s%s%s' % (partner_id, amount, currency_id)
     correct_token = hmac.new(secret.encode('utf-8'),
                              token_str.encode('utf-8'),
                              hashlib.sha256).hexdigest()
     if consteq(ustr(access_token), correct_token):
         return True
     return False
Esempio n. 6
0
 def _stock_picking_check_access(self, picking_id, access_token=None):
     picking = request.env['stock.picking'].browse([picking_id])
     picking_sudo = picking.sudo()
     try:
         picking.check_access_rights('read')
         picking.check_access_rule('read')
     except exceptions.AccessError:
         if not access_token or not consteq(
                 picking_sudo.sale_id.access_token, access_token):
             raise
     return picking_sudo
Esempio n. 7
0
def _message_post_helper(res_model='',
                         res_id=None,
                         message='',
                         token='',
                         nosubscribe=True,
                         **kw):
    """ Generic chatter function, allowing to write on *any* object that inherits mail.thread.
        If a token is specified, all logged in users will be able to write a message regardless
        of access rights; if the user is the public user, the message will be posted under the name
        of the partner_id of the object (or the public user if there is no partner_id on the object).

        :param string res_model: model name of the object
        :param int res_id: id of the object
        :param string message: content of the message

        optional keywords arguments:
        :param string token: access token if the object's model uses some kind of public access
                             using tokens (usually a uuid4) to bypass access rules
        :param bool nosubscribe: set False if you want the partner to be set as follower of the object when posting (default to True)

        The rest of the kwargs are passed on to message_post()
    """
    record = request.env[res_model].browse(res_id)
    author_id = request.env.user.partner_id.id if request.env.user.partner_id else False
    if token:
        access_as_sudo = _has_token_access(res_model, res_id, token=token)
        if access_as_sudo:
            record = record.sudo()
            if request.env.user._is_public():
                if kw.get('pid') and consteq(
                        kw.get('hash'), record._sign_token(int(
                            kw.get('pid')))):
                    author_id = kw.get('pid')
                else:
                    # TODO : After adding the pid and sign_token in access_url when send invoice by email, remove this line
                    # TODO : Author must be Public User (to rename to 'Anonymous')
                    author_id = record.partner_id.id if hasattr(
                        record,
                        'partner_id') and record.partner_id.id else author_id
            else:
                if not author_id:
                    raise NotFound()
        else:
            raise Forbidden()
    kw.pop('csrf_token', None)
    kw.pop('attachment_ids', None)
    return record.with_context(
        mail_create_nosubscribe=nosubscribe).message_post(
            body=message,
            message_type=kw.pop('message_type', "comment"),
            subtype=kw.pop('subtype', "mt_comment"),
            author_id=author_id,
            **kw)
Esempio n. 8
0
 def _document_check_access(self,
                            model_name,
                            document_id,
                            access_token=None):
     document = request.env[model_name].browse([document_id])
     document_sudo = document.with_user(SUPERUSER_ID).exists()
     if not document_sudo:
         raise MissingError(_("This document does not exist."))
     try:
         document.check_access_rights('read')
         document.check_access_rule('read')
     except AccessError:
         if not access_token or not document_sudo.access_token or not consteq(
                 document_sudo.access_token, access_token):
             raise
     return document_sudo
Esempio n. 9
0
    def _redirect_to_record(cls, model, res_id, access_token=None, **kwargs):
        """ If the current user doesn't have access to the document, but provided
        a valid access token, redirect him to the front-end view.
        If the partner_id and hash parameters are given, add those parameters to the redirect url
        to authentify the recipient in the chatter, if any.

        :param model: the model name of the record that will be visualized
        :param res_id: the id of the record
        :param access_token: token that gives access to the record
            bypassing the rights and rules restriction of the user.
        :param kwargs: Typically, it can receive a partner_id and a hash (sign_token).
            If so, those two parameters are used to authentify the recipient in the chatter, if any.
        :return:
        """
        if issubclass(type(request.env[model]),
                      request.env.registry['portal.mixin']):
            uid = request.session.uid or request.env.ref('base.public_user').id
            record_sudo = request.env[model].sudo().browse(res_id).exists()
            try:
                record_sudo.with_user(uid).check_access_rights('read')
                record_sudo.with_user(uid).check_access_rule('read')
            except AccessError:
                if record_sudo.access_token and access_token and consteq(
                        record_sudo.access_token, access_token):
                    record_action = record_sudo.with_context(
                        force_website=True).get_access_action()
                    if record_action['type'] == 'ir.actions.act_url':
                        pid = kwargs.get('pid')
                        hash = kwargs.get('hash')
                        url = record_action['url']
                        if pid and hash:
                            url = urls.url_parse(url)
                            url_params = url.decode_query()
                            url_params.update([("pid", pid), ("hash", hash)])
                            url = url.replace(
                                query=urls.url_encode(url_params)).to_url()
                        return werkzeug.utils.redirect(url)
        return super(MailController,
                     cls)._redirect_to_record(model,
                                              res_id,
                                              access_token=access_token)
Esempio n. 10
0
 def _contract_check_access(self, contract_id, access_token=None):
     contract = request.env['saas.contract'].browse([contract_id])
     contract_sudo = contract.sudo()
     partner = request.env.user.partner_id
     try:
         if contract.exists():
             if partner.id != contract.partner_id.id:
                 raise AccessError("Not Allowed")
             else:
                 contract.check_access_rights('read')
                 contract.check_access_rule('read')
         else:
             _logger.info("------------------ No Record Found--------")
             raise AccessError("Not allowed")
     except AccessError:
         _logger.info("-------------5-----------")
         if contract.exists():
             if not access_token or not consteq(contract_sudo.access_token,
                                                access_token):
                 raise
         else:
             raise
     return contract_sudo
Esempio n. 11
0
    def binary_content(cls,
                       xmlid=None,
                       model='ir.attachment',
                       id=None,
                       field='datas',
                       unique=False,
                       filename=None,
                       filename_field='datas_fname',
                       download=False,
                       mimetype=None,
                       default_mimetype='application/octet-stream',
                       access_token=None,
                       related_id=None,
                       access_mode=None,
                       env=None):
        """ Get file, attachment or downloadable content

        If the ``xmlid`` and ``id`` parameter is omitted, fetches the default value for the
        binary field (via ``default_get``), otherwise fetches the field for
        that precise record.

        :param str xmlid: xmlid of the record
        :param str model: name of the model to fetch the binary from
        :param int id: id of the record from which to fetch the binary
        :param str field: binary field
        :param bool unique: add a max-age for the cache control
        :param str filename: choose a filename
        :param str filename_field: if not create an filename with model-id-field
        :param bool download: apply headers to download the file
        :param str mimetype: mintype of the field (for headers)
        :param related_id: the id of another record used for custom_check
        :param  access_mode: if truthy, will call custom_check to fetch the object that contains the binary.
        :param str default_mimetype: default mintype if no mintype found
        :param str access_token: optional token for unauthenticated access
                                 only available  for ir.attachment
        :param Environment env: by default use request.env
        :returns: (status, headers, content)
        """
        env = env or request.env
        # get object and content
        obj = None
        if xmlid:
            obj = cls._xmlid_to_obj(env, xmlid)
        elif id and model in env.registry:
            obj = env[model].browse(int(id))
        # obj exists
        if not obj or not obj.exists() or field not in obj:
            return (404, [], None)

        # access token grant access
        if model == 'ir.attachment' and access_token:
            obj = obj.sudo()
            if access_mode:
                if not cls._check_access_mode(env,
                                              id,
                                              access_mode,
                                              model,
                                              access_token=access_token,
                                              related_id=related_id):
                    return (403, [], None)
            elif not consteq(obj.access_token or u'', access_token):
                return (403, [], None)

        # check read access
        try:
            last_update = obj['__last_update']
        except AccessError:
            return (403, [], None)

        status, headers, content = None, [], None

        # attachment by url check
        module_resource_path = None
        if model == 'ir.attachment' and obj.type == 'url' and obj.url:
            url_match = re.match("^/(\w+)/(.+)$", obj.url)
            if url_match:
                module = url_match.group(1)
                module_path = get_module_path(module)
                module_resource_path = get_resource_path(
                    module, url_match.group(2))
                if module_path and module_resource_path:
                    module_path = os.path.join(
                        os.path.normpath(module_path),
                        '')  # join ensures the path ends with '/'
                    module_resource_path = os.path.normpath(
                        module_resource_path)
                    if module_resource_path.startswith(module_path):
                        with open(module_resource_path, 'rb') as f:
                            content = base64.b64encode(f.read())
                        last_update = pycompat.text_type(
                            os.path.getmtime(module_resource_path))

            if not module_resource_path:
                module_resource_path = obj.url

            if not content:
                status = 301
                content = module_resource_path
        else:
            content = obj[field] or ''

        # filename
        if not filename:
            if filename_field in obj:
                filename = obj[filename_field]
            if not filename and module_resource_path:
                filename = os.path.basename(module_resource_path)
            if not filename:
                filename = "%s-%s-%s" % (obj._name, obj.id, field)

        # mimetype
        mimetype = 'mimetype' in obj and obj.mimetype or False
        if not mimetype:
            if filename:
                mimetype = mimetypes.guess_type(filename)[0]
            if not mimetype and getattr(env[model]._fields[field],
                                        'attachment', False):
                # for binary fields, fetch the ir_attachement for mimetype check
                attach_mimetype = env['ir.attachment'].search_read(
                    domain=[('res_model', '=', model), ('res_id', '=', id),
                            ('res_field', '=', field)],
                    fields=['mimetype'],
                    limit=1)
                mimetype = attach_mimetype and attach_mimetype[0]['mimetype']
            if not mimetype:
                mimetype = guess_mimetype(base64.b64decode(content),
                                          default=default_mimetype)

        # extension
        _, existing_extension = os.path.splitext(filename)
        if not existing_extension:
            extension = mimetypes.guess_extension(mimetype)
            if extension:
                filename = "%s%s" % (filename, extension)

        headers += [('Content-Type', mimetype),
                    ('X-Content-Type-Options', 'nosniff')]

        # cache
        etag = bool(request) and request.httprequest.headers.get(
            'If-None-Match')
        retag = '"%s"' % hashlib.md5(
            pycompat.to_text(content).encode('utf-8')).hexdigest()
        status = status or (304 if etag == retag else 200)
        headers.append(('ETag', retag))
        headers.append(
            ('Cache-Control', 'max-age=%s' % (STATIC_CACHE if unique else 0)))

        # content-disposition default name
        if download:
            headers.append(
                ('Content-Disposition', cls.content_disposition(filename)))
        return (status, headers, content)
Esempio n. 12
0
 def _valid_unsubscribe_token(self, mailing_id, res_id, email, token):
     if not (mailing_id and res_id and email and token):
         return False
     mailing = request.env['mail.mass_mailing'].sudo().browse(mailing_id)
     return consteq(mailing._unsubscribe_token(res_id, email), token)
Esempio n. 13
0
def _has_token_access(res_model, res_id, token=''):
    record = request.env[res_model].browse(res_id).sudo()
    token_field = request.env[res_model]._mail_post_token_field
    return (token and record and consteq(record[token_field], token))