コード例 #1
0
    def _init_message_id(self, message_mdn):
        if is_none_or_whitespace(message_mdn.mdn_message_id):
            raise ValueError("mdn-message-id is required")
        if is_none_or_whitespace(message_mdn.original_message_id):
            raise ValueError("original-message-id is required")

        self.mdn_message_id = message_mdn.mdn_message_id
        self.original_message_id = message_mdn.original_message_id
コード例 #2
0
    def _init_mic(self, is_mic, message_mdn):
        if not isinstance(is_mic, bool):
            raise ValueError("mdn mic flag invalid")
        if is_mic and (is_none_or_whitespace(message_mdn.mdn_mic_digest) or
                       is_none_or_whitespace(message_mdn.mdn_mic_algorithm)):
            raise ValueError("mdn mic digest or algorithm invalid")

        self.is_mic = is_mic
        self.mic_algorithm = message_mdn.mdn_mic_algorithm
        self.mic_digest = message_mdn.mdn_mic_digest
        self.mic_description = None
コード例 #3
0
    def _build_feedback_content_mime(self):
        mdn_feedback_content = email.Message.Message()
        mdn_feedback_content.set_type('message/disposition-notification')

        mdn_lines = []

        mdn_lines.append('Reporting-UA: {title}/{version}'.format(
            title=ngas2.__title__, version=ngas2.__version__))

        mdn_lines.append('Original-Recipient: rfc822; {local_identity}'.format(
            local_identity=self.context.agreement.local_identity))

        mdn_lines.append('Final-Recipient: rfc822; {local_identity}'.format(
            local_identity=self.context.agreement.local_identity))

        mdn_lines.append('Original-Message-ID: {message_id}'.format(
            message_id=self.original_message_id))

        disposition_description = 'Disposition: {mode}; {type}'.format(
            mode=self.disposition_mode, type=self.disposition_type)

        if not is_none_or_whitespace(self.disposition_modifier_code):
            disposition_description += '/{code}'.format(
                code=self.disposition_modifier_code)
            if not is_none_or_whitespace(self.disposition_modifier_value):
                disposition_description += ': {value}'.format(
                    value=self.disposition_modifier_value)

        self.disposition_description = disposition_description

        mdn_lines.append(self.disposition_description)

        if self.disposition_type == 'processed' and is_none_or_whitespace(
                self.disposition_modifier_code):
            self.validate_status = StatusType.successful
        else:
            self.validate_status = StatusType.failed

        if self.is_mic:
            self.mic_description = 'Received-content-MIC: {digest}, {algorithm}'.format(
                digest=self.mic_digest, algorithm=self.mic_algorithm)

            mdn_lines.append(self.mic_description)

        mdn_feedback_content.set_payload('\r\n'.join(mdn_lines) + '\r\n')
        del mdn_feedback_content['MIME-Version']

        self.context.trace('build mdn processed result information')

        return mdn_feedback_content
コード例 #4
0
    def _verify_signed_message(self, content_type):
        if content_type != 'multipart/signed':
            raise AS2MdnException('content-type invalid when signed required')

        mdn_signature_thumbprint = self.context.agreement.outbound_agreement.message_encryption_certificate.thumbprint
        mdn_signature_cert_path = self.context.agreement.outbound_agreement.message_encryption_certificate.local_file_path
        mdn_signature_cert_ca_path = self.context.agreement.outbound_agreement.message_encryption_certificate.local_ca_file_path
        mdn_signature_cert_verify = self.context.agreement.outbound_agreement.message_encryption_certificate.is_need_verify

        try:
            if is_none_or_whitespace(mdn_signature_cert_ca_path):
                mdn_signature_cert_ca_path = mdn_signature_cert_path

            f_mime_string = SMIMEHelper.format_with_cr_lf(
                SMIMEHelper.mime_to_string(self.mime_message))

            SMIMEHelper.verify_signed_text(f_mime_string,
                                           mdn_signature_cert_path,
                                           mdn_signature_cert_ca_path,
                                           mdn_signature_cert_verify)

            self.context.trace(
                "mdn signature verify finished; thumbprint: {thumbprint}, verify certificate: {verify}",
                thumbprint=mdn_signature_thumbprint,
                verify=mdn_signature_cert_verify)
        except:
            logger.exception(
                "mdn signature verify failed; mdn-message-id: {id}".format(
                    id=self.mdn_message_id))
            raise AS2MdnException(
                "mdn signature verify failed; thumbprint: {thumbprint}, verify certificate: {verify}, due to: {message}",
                thumbprint=mdn_signature_thumbprint,
                verify=mdn_signature_cert_verify,
                message=sys.exc_info()[1])
コード例 #5
0
    def _decode_disposition(self, mdn):
        disposition_description = mdn.get('Disposition')

        self.disposition_description = disposition_description
        if is_none_or_whitespace(disposition_description):
            raise AS2MdnException('disposition is required')

        disposition = self.disposition_description.split(';')
        if len(disposition) != 2:
            raise AS2MdnException('disposition is invalid')

        self.disposition_mode = disposition[0].strip()

        disposition_result = disposition[1].strip().split('/')
        if len(disposition_result) == 1:
            self.disposition_type = disposition_result[0].strip()
        elif len(disposition_result) == 2:
            self.disposition_type = disposition_result[0].strip()

            disposition_modifier = disposition_result[1].split(':')
            if len(disposition_modifier) == 1:
                self.disposition_modifier_code = disposition_modifier[0].strip(
                )
            elif len(disposition_modifier) == 2:
                self.disposition_modifier_code = disposition_modifier[0].strip(
                )
                self.disposition_modifier_value = disposition_modifier[
                    1].strip()
            else:
                raise AS2MdnException('disposition modifier is invalid')
        else:
            raise AS2MdnException('disposition type or modifier is invalid')
コード例 #6
0
    def _verify_signature(self):
        if not self.is_signed:
            self.context.trace("verify signature ignored")
            return

        content_type = self.mime_message.get_content_type().lower()
        if content_type != 'multipart/signed':
            raise AS2VerifySignatureException('verify signature failed; content-type:{type} invalid',
                                              type=content_type)

        cert_thumbprint = self.context.agreement.inbound_agreement.message_verify_certificate.thumbprint
        cert_local_file_path = self.context.agreement.inbound_agreement.message_verify_certificate.local_file_path
        cert_ca_local_file_path = self.context.agreement.inbound_agreement.message_verify_certificate.local_ca_file_path
        cert_verify = self.context.agreement.inbound_agreement.message_verify_certificate.is_need_verify

        try:
            if is_none_or_whitespace(cert_ca_local_file_path):
                cert_ca_local_file_path = cert_local_file_path

            for part in self.mime_message.get_payload():
                if not isinstance(part, email.message.Message):
                    continue
                part_type = part.get_content_type().lower()
                part_encoding = part.get('Content-Transfer-Encoding', '').lower()
                if 'application/pkcs7-signature' == part_type and 'base64' != part_encoding:
                    del part['Content-Transfer-Encoding']
                    email.encoders.encode_base64(part)
                    self.context.trace('signature content transfer encoding to base64')

            f_mime_string = SMIMEHelper.format_with_cr_lf(
                SMIMEHelper.mime_to_string(self.mime_message))

            SMIMEHelper.verify_signed_text(
                f_mime_string,
                cert_local_file_path,
                cert_ca_local_file_path,
                cert_verify)

            self.mic_algorithm = self.mime_message.get_param('micalg').lower()

            parts = [part for part in self.mime_message.walk() if
                     part.get_content_type() not in ['multipart/signed', 'application/pkcs7-signature']]

            if len(parts) != 1:
                raise AS2VerifySignatureException(
                    "verify signature failed; due to multiple part content in mime message")

            self.mime_message = parts[0]

            self.context.trace("verify signature finished; thumbprint: {thumbprint}, verify certificate: {verify}",
                               thumbprint=cert_thumbprint,
                               verify=cert_verify)
        except:
            logger.exception("verify signature failed; message-id: {id}".format(id=self.message_id))
            raise AS2VerifySignatureException(
                "verify signature failed; thumbprint: {thumbprint}, verify certificate: {verify}, due to: {message}",
                thumbprint=cert_thumbprint,
                verify=cert_verify,
                message=sys.exc_info()[1])
コード例 #7
0
ファイル: partner.py プロジェクト: mars-aws01/work
    def _get_send_data(self, data_dfs_path):
        data = self.body
        if not is_none_or_whitespace(data_dfs_path):
            data = DFSHelper.download(data_dfs_path)
            if data is None:
                raise Exception("get send data from dfs failed")

        return data
コード例 #8
0
ファイル: partner.py プロジェクト: mars-aws01/work
    def _get_send_request(self):
        headers = {
            k.lower().replace('_', '-'): v
            for k, v in self.headers.items()
        }
        business_id = headers.get(self.ngas2_business_id)
        agreement_id = headers.get(self.ngas2_agreement_id)
        data_dfs_path = headers.get(self.ngas2_data_dfs_path)

        if is_none_or_whitespace(agreement_id):
            raise Exception("as2 agreement id is required")

        if is_none_or_whitespace(data_dfs_path) and is_none_or_whitespace(
                self.body):
            raise Exception("as2 data is required")

        return business_id, agreement_id, data_dfs_path
コード例 #9
0
    def _init_message_id(self):
        id = self.headers.get("message-id")
        if is_none_or_whitespace(id):
            raise AS2Exception('message-id is required')
        if len(id) > 998:
            raise AS2Exception('message-id length is a maximum of 998 characters')

        self.message_id = id.strip()
コード例 #10
0
    def _decode_original_message_id(self, mdn):
        original_message_id = mdn.get('Original-Message-ID')
        if is_none_or_whitespace(original_message_id):
            raise AS2MdnException('original-message-id is required')
        if len(original_message_id) > 998:
            raise AS2MdnException(
                'original-message-id length is a maximum of 998 characters')

        self.original_message_id = original_message_id
コード例 #11
0
    def _init_mdn_message_id(self):
        msg_id = self.headers.get("message-id", str(uuid.uuid4()).upper())
        if is_none_or_whitespace(msg_id):
            raise AS2MdnException('mdn-message-id is required')
        if len(msg_id) > 998:
            raise AS2MdnException(
                'mdn-message-id length is a maximum of 998 characters')

        self.mdn_message_id = msg_id.strip()
コード例 #12
0
ファイル: partner.py プロジェクト: mars-aws01/work
    def _get_primary_agreement(self, local_identity, trading_identity):
        if is_none_or_whitespace(trading_identity):
            raise Exception("as2-from is required")
        if is_none_or_whitespace(local_identity):
            raise Exception("as2-to is required")

        cache = InMemoryCache('PartnerManager', 'Agreement')
        cache_key = local_identity + '_' + trading_identity
        cache_value = cache[cache_key]
        if cache_value is not None:
            return cache_value

        try:
            condition = {
                'LocalIdentity': local_identity,
                'TradingIdentity': trading_identity,
                'IsPrimary': True
            }

            docs = CloudStoreHelper().find_documents(condition, Agreement)

            if docs is None or docs.records is None or len(docs.records) != 1:
                raise Exception("agreement is null or length invalid")

            agreement = docs.records[0]

            if agreement.status != AgreementStatus.active:
                raise Exception("agreement status invalid")
        except:
            raise Exception("get as2 agreement failed; due to {msg}".format(
                msg=sys.exc_info()[1]))

        self._build_outbound_agreement_certificate(
            agreement.outbound_agreement)

        self._build_inbound_agreement_certificate(agreement.inbound_agreement)

        cache[cache_key] = agreement

        return agreement
コード例 #13
0
    def _decode_received_content_mic(self, mdn):
        received_content_mic_description = mdn.get('Received-Content-MIC')

        self.mic_description = received_content_mic_description
        if is_none_or_whitespace(received_content_mic_description):
            return

        mic = self.mic_description.split(',')
        if len(mic) != 2:
            raise AS2MdnException('received-content-mic is invalid')

        self.mic_digest = mic[0].strip()
        self.mic_algorithm = mic[1].strip()
コード例 #14
0
    def _fetch_content(self):
        charset = self.mime_message.get_charset()
        content_type = self.mime_message.get_content_type()
        content = self.mime_message.get_payload(decode=True)

        self.content = content
        self.content_length = len(content)
        self.content_hash = hashlib.md5(content).hexdigest()

        if not is_none_or_whitespace(content_type):
            self.content_type = content_type.lower()
        self.content_charset = None if charset is None else str(charset)

        self.context.trace("fetch content finished")
コード例 #15
0
ファイル: send.py プロジェクト: mars-aws01/work
    def __init__(self, message_id, data_feed_name, data_feed_content, context):
        if not isinstance(context, AS2Context):
            raise ValueError('context invalid')

        if is_none_or_whitespace(message_id):
            raise ValueError('message id is null or empty')

        if is_none_or_whitespace(data_feed_name):
            self.data_feed_name = ngas2.__title__
        else:
            self.data_feed_name = data_feed_name

        self.data_feed_content = data_feed_content
        self.context = context
        self.message_id = message_id
        self.headers = None
        self.body = None
        self.mime_message = None
        self.is_mic = False
        self.mic_content = None
        self.mic_digest = None
        self.mic_algorithm = None
        self.mic_description = None
コード例 #16
0
    def _build_confirmation_mime(self):
        confirmation_text = self.context.agreement.inbound_agreement.mdn_confirmation_text
        if is_none_or_whitespace(confirmation_text):
            confirmation_text = "received by {title}/{version}".format(
                title=ngas2.__title__, version=ngas2.__version__)

        mdn_confirmation_text = email.Message.Message()
        mdn_confirmation_text.set_payload(
            "{confirmation_text}".format(confirmation_text=confirmation_text))
        mdn_confirmation_text.set_type('text/plain')
        del mdn_confirmation_text['MIME-Version']

        self.context.trace('build mdn confirmation information')

        return mdn_confirmation_text
コード例 #17
0
ファイル: partner.py プロジェクト: mars-aws01/work
    def receive_async_mdn(self, agreement_id=None):
        if is_none_or_whitespace(agreement_id):
            headers = {
                k.lower().replace('_', '-'): v
                for k, v in self.headers.items()
            }
            trading_identity = decode_as2_identity(headers.get('as2-from', ''))
            local_identity = decode_as2_identity(headers.get('as2-to', ''))
            agreement = self._get_primary_agreement(local_identity,
                                                    trading_identity)
        else:
            agreement = self._get_agreement(agreement_id)

        context = AS2Context(agreement, None)

        self._receive_async_mdn(self.headers, self.body, context)
コード例 #18
0
    def _init_disposition(self, message_mdn):
        if message_mdn.mdn_disposition_mode not in [
                'automatic-action/MDN-sent-automatically',
                'manual-action/MDN-sent-manually'
        ]:
            raise ValueError("mdn disposition mode invalid")
        if message_mdn.mdn_disposition_type not in ['processed', 'failed']:
            raise ValueError("mdn disposition type invalid")
        if not is_none_or_whitespace(
                message_mdn.mdn_disposition_modifier_code) \
                and message_mdn.mdn_disposition_modifier_code not in ['error', 'warning', 'failure']:
            raise ValueError("mdn disposition modifier code invalid")

        self.disposition_mode = message_mdn.mdn_disposition_mode
        self.disposition_type = message_mdn.mdn_disposition_type
        self.disposition_modifier_code = message_mdn.mdn_disposition_modifier_code
        self.disposition_modifier_value = message_mdn.mdn_disposition_modifier_value
        self.disposition_description = None
コード例 #19
0
ファイル: partner.py プロジェクト: mars-aws01/work
    def _build_received_as2_context(self, id, message):
        if is_none_or_whitespace(id):
            headers = {
                k.lower().replace('_', '-'): v
                for k, v in self.headers.items()
            }
            trading_identity = decode_as2_identity(headers.get('as2-from', ''))
            local_identity = decode_as2_identity(headers.get('as2-to', ''))
            agreement = self._get_primary_agreement(local_identity,
                                                    trading_identity)
        else:
            agreement = self._get_agreement(id)

        message.local_identity = agreement.local_identity
        message.trading_identity = agreement.trading_identity
        message.agreement_id = agreement.id

        return AS2Context(agreement, message)
コード例 #20
0
ファイル: partner.py プロジェクト: mars-aws01/work
    def _build_certificate(self, cert_id):
        if cert_id is None:
            return None

        try:
            certificate = CloudStoreHelper().get_document(cert_id, Certificate)

            if certificate is None:
                raise Exception("as2 certificate is null")

            if certificate.type == CertificateType.public:
                certificate.local_file_path = os.path.join(
                    self.conf['app_root_dir'], "cert", "public",
                    certificate.thumbprint + ".pem")
            else:
                certificate.local_file_path = os.path.join(
                    self.conf['app_root_dir'], "cert", "private",
                    certificate.thumbprint + ".pem")

            if not is_none_or_whitespace(certificate.pass_phrase):
                certificate.pass_phrase = SMIMEHelper.aes_decrypt(
                    certificate.pass_phrase, self.conf['app_secure_key'])

            if os.path.exists(certificate.local_file_path):
                return certificate

            local_path = os.path.dirname(certificate.local_file_path)

            if not os.path.exists(local_path):
                os.makedirs(local_path)

            if not DFSHelper.download2file(certificate.dfs_file_path,
                                           certificate.local_file_path):
                raise Exception("download certificate file from dfs failed")

            return certificate
        except:
            raise Exception(
                "get as2 certificate failed; certificate-id: {id}, due to {msg}"
                .format(id=cert_id, msg=sys.exc_info()[1]))
コード例 #21
0
ファイル: send.py プロジェクト: mars-aws01/work
    def _request_mdn(self):
        if not self.is_request_mdn:
            self.context.trace("request mdn ignored")
            return

        disposition_notification_to = self.context.agreement.outbound_agreement.disposition_notification_to
        is_mdn_signed = self.context.agreement.outbound_agreement.is_mdn_signed
        mic_alg_desc = ''
        mdn_mode = self.context.agreement.outbound_agreement.mdn_mode
        async_mdn_url = self.context.agreement.outbound_agreement.async_mdn_url

        self.headers['Disposition-Notification-To'] = disposition_notification_to

        if mdn_mode == MdnMode.async:
            self.headers['Receipt-Delivery-Option'] = async_mdn_url

        if is_mdn_signed:
            signed_receipt_protocol = 'signed-receipt-protocol=required,pkcs7-signature'
            signed_receipt_mic_alg_prefix = 'signed-receipt-micalg='
            mdn_signature_algorithm = self.context.agreement.outbound_agreement.mdn_signature_algorithm

            if is_none_or_whitespace(mdn_signature_algorithm):
                mic_alg_desc = 'optional,{micalg}'.format(
                    micalg=','.join(SignatureAlgorithm.all))
            else:
                mic_alg_desc = 'required,{micalg}'.format(
                    micalg=mdn_signature_algorithm)

            signed_receipt_mic_alg = signed_receipt_mic_alg_prefix + mic_alg_desc

            self.headers['Disposition-Notification-Options'] = signed_receipt_protocol + '; ' + signed_receipt_mic_alg

        self.context.trace("request mdn finished; mode: {mode}, signed: {signed}, algorithm: {algorithm}",
                           mode=mdn_mode,
                           signed=is_mdn_signed,
                           algorithm=mic_alg_desc)
コード例 #22
0
    def _init_to_identity(self):
        identity = self.headers.get("as2-to")
        if is_none_or_whitespace(identity):
            raise AS2Exception('as2-to is required')

        self.to_identity = decode_as2_identity(identity.strip())
コード例 #23
0
ファイル: partner.py プロジェクト: mars-aws01/work
    def _send_data(self, data, context):
        try:
            sender = SendEncoder(context.message.message_id,
                                 context.message.business_id, data, context)

            sent_headers, sent_body = sender.encode()

            target_url = context.agreement.outbound_agreement.target_url
            ssl_version = context.agreement.outbound_agreement.ssl_version

            auth = PartnerManager._build_auth(context.agreement)
            proxies = PartnerManager._build_proxy(context.agreement)

            customer_http_headers = context.agreement.outbound_agreement.customer_http_headers
            if customer_http_headers is not None:
                extend_headers = {
                    item.name: item.value
                    for item in customer_http_headers if item.name is None
                }
                sent_headers.update(extend_headers)

            context.message.sent_headers = [
                NameValuePair(name=k, value=v)
                for k, v in sent_headers.items()
            ]

            if context.agreement.profiler_enabled:
                status, url = self._upload_data_to_dfs(
                    "{key}.out".format(key=context.message.key), sent_body)
                context.message.sent_data_dfs_path = url

            context.message.message_is_mic = sender.is_mic
            context.message.message_mic_digest = sender.mic_digest
            context.message.message_mic_algorithm = sender.mic_algorithm

            resp = HttpHelper.post(url=target_url,
                                   data=sent_body,
                                   headers=sent_headers,
                                   proxies=proxies,
                                   auth=auth,
                                   verify=False)

            status_code, received_headers, received_body = resp.status_code, dict(
                resp.headers), resp.content

            context.message.received_status_code = status_code
            context.message.received_headers = [
                NameValuePair(name=k, value=v)
                for k, v in received_headers.items()
            ]

            if context.agreement.profiler_enabled and not is_none_or_whitespace(
                    received_body):
                status, url = self._upload_data_to_dfs(
                    "{key}.in".format(key=context.message.key), received_body)
                context.message.received_data_dfs_path = url

            if status_code != requests.codes.ok:
                resp.raise_for_status()

            context.message.message_status = StatusType.successful

            return received_headers, received_body
        except AS2Exception:
            raise
        except:
            logger.exception(
                "send data failed; business-id: {business_id}, message-id: {message_id}"
                .format(business_id=context.message.business_id,
                        message_id=context.message.message_id))
            raise Exception("send data failed; due to: {message}".format(
                message=str(sys.exc_info()[1])))
コード例 #24
0
 def _set_validate_status(self):
     if self.disposition_type == 'processed' and is_none_or_whitespace(
             self.disposition_modifier_code):
         self.validate_status = StatusType.successful
     else:
         self.validate_status = StatusType.failed
コード例 #25
0
    def _init_mdn_message_content_type(self):
        content_type = self.headers.get("content-type")
        if is_none_or_whitespace(content_type):
            raise AS2MdnException('mdn-content-type is required')

        self.mdn_message_content_type = content_type.strip()
コード例 #26
0
    def _init_content_type(self):
        content_type = self.headers.get("content-type")
        if is_none_or_whitespace(content_type):
            raise AS2Exception('content-type is required')

        self.content_type = content_type.lower().strip()