Пример #1
0
    def _sendEmail(self):
        """
        Prepares the e-mail by gathering all relevant information and
        then sends it out.

        :return: A tuple of success and status_message
        :rtype: bool, string
        """
        otp = self._getNextOtp()
        email_address = self._email_address
        if not email_address:
            raise Exception("No e-mail address was defined for this token.")

        owner = get_token_owner(self)
        message = self._getEmailMessage(user=owner)

        if "<otp>" not in message:
            message = message + "<otp>"

        message = message.replace("<otp>", otp)
        message = message.replace("<serial>", self.getSerial())

        subject = self._getEmailSubject(user=owner)
        subject = subject.replace("<otp>", otp)
        subject = subject.replace("<serial>", self.getSerial())

        email_provider = loadProviderFromPolicy(provider_type='email',
                                                user=owner)
        status, status_message = email_provider.submitMessage(email_address,
                                                              subject=subject,
                                                              message=message)
        return status, status_message
Пример #2
0
    def _submit_to_provider(self, otp_value):
        """
        submit the voice message - former method name was checkPin

        :param otp_value: One time password to transfer to voice provider
        :return: Tuple of success and result message
        """

        owner = get_token_owner(self)

        message = get_voice_message(owner, owner.realm)
        language = get_voice_language(owner, owner.realm)

        voice_provider = loadProviderFromPolicy(
                                            provider_type='voice',
                                            realm=owner.realm,
                                            user=owner)

        success, result = voice_provider.submitVoiceMessage(
                                calleeNumber=self.get_mobile_number(owner),
                                messageTemplate=message,
                                otp=otp_value,
                                locale=language)

        return success, result
Пример #3
0
    def _sendEmail(self):
        """
        Prepares the e-mail by gathering all relevant information and
        then sends it out.

        :return: A tuple of success and status_message
        :rtype: bool, string
        """

        otp = self._getNextOtp()
        owner = get_token_owner(self)

        email_address = self._get_email_address(owner)
        if not email_address:
            raise Exception("No e-mail address was defined for this token.")

        message = self._getEmailMessage(user=owner)
        subject = self._getEmailSubject(user=owner)

        replacements = {}
        replacements["otp"] = otp
        replacements["serial"] = self.getSerial()

        # ------------------------------------------------------------------ --

        # add user detail to replacements, so we are aware of surename++

        if owner and owner.login:
            user_detail = owner.getUserInfo()
            if "cryptpass" in user_detail:
                del user_detail["cryptpass"]

            replacements.update(user_detail)

        # ------------------------------------------------------------------ --

        try:

            email_provider = loadProviderFromPolicy(provider_type="email",
                                                    user=owner)

            status, status_message = email_provider.submitMessage(
                email_address,
                subject=subject,
                message=message,
                replacements=replacements,
            )

        except Exception as exx:
            LOG.error("Failed to submit EMail: %r", exx)
            raise

        return status, status_message
Пример #4
0
    def createChallenge(self, transaction_id, options):

        """
        entry hook for the challenge logic. when this function is called
        a challenge with an transaction was created.

        :param transaction_id: A unique transaction id used to identity
            the challenge object

        :param options: additional options as a dictionary

        :raises TokenStateError: If token state is not 'active' or
            'pairing_response_received'

        :returns: A tuple (success, message, data, attributes)
            with success being a boolean indicating if the call
            to this method was successful, message being a string
            that is passed to the user, attributes being additional
            output data (unused in here)
        """

        valid_states = ['pairing_response_received',
                        'active']

        self.ensure_state_is_in(valid_states)

        # ------------------------------------------------------------------- --

        # inside the challenge url we sent a callback url for the client
        # which is defined by an authentication policy

        owner = get_token_owner(self)
        if owner and owner.login and owner.realm:
            realms = [owner.realm]
        else:
            realms = self.getRealms()

        callback_policy_name = 'pushtoken_challenge_callback_url'
        callback_url = get_single_auth_policy(callback_policy_name,
                                              user=owner, realms=realms)

        if not callback_url:
            raise Exception(_('Policy pushtoken_challenge_callback_url must '
                              'have a value'))

        # ------------------------------------------------------------------- --

        # load and configure provider

        # the realm logic was taken from the
        # provider loading in the smstoken class
        # TODO: refactor & centralize logic

        realm = None
        if realms:
            realm = realms[0]

        push_provider = loadProviderFromPolicy(provider_type='push',
                                               realm=realm,
                                               user=owner)

        # ------------------------------------------------------------------- --

        if self.current_state == 'pairing_response_received':

            content_type = CONTENT_TYPE_PAIRING

            message = ''
            challenge_url, sig_base = self.create_challenge_url(transaction_id,
                                                                content_type,
                                                                callback_url)

        else:

            content_type_as_str = options.get('content_type')

            try:

                # pylons silently converts all ints in json
                # to unicode :(

                content_type = int(content_type_as_str)

            except:

                raise ValueError('Unrecognized content type: %s'
                                 % content_type_as_str)

            # --------------------------------------------------------------- --

            if content_type == CONTENT_TYPE_SIGNREQ:

                message = options.get('data')
                challenge_url, sig_base = self.create_challenge_url(
                                                transaction_id, content_type,
                                                callback_url, message=message)

            # --------------------------------------------------------------- --

            elif content_type == CONTENT_TYPE_LOGIN:

                message = options.get('data')
                login, __, host = message.partition('@')

                challenge_url, sig_base = self.create_challenge_url(
                                                transaction_id, content_type,
                                                callback_url, login=login,
                                                host=host)

            else:

                raise ValueError('Unrecognized content type: %s' % content_type)

        # ------------------------------------------------------------------- --

        # send the challenge_url to the push notification proxy

        token_info = self.getTokenInfo()
        gda = token_info['gda']

        log.debug("pushing notification: %r : %r", challenge_url, gda)

        success, response = push_provider.push_notification(challenge_url, gda)

        if not success:
            raise Exception('push mechanism failed. response was %r'
                            % response)

        # ------------------------------------------------------------------- --

        # we save sig_base in the challenge data, because we need it in
        # checkOtp to verify the signature

        b64_sig_base = b64encode(sig_base)
        data = {'sig_base': b64_sig_base}

        if self.current_state == 'pairing_response_received':
            self.change_state('pairing_challenge_sent')

        # ------------------------------------------------------------------- --

        # don't pass the challenge_url as message to the user

        return (True, '', data, {})
Пример #5
0
    def sendSMS(self, message=None, transactionid=None):
        '''
        send sms

        :param message: the sms submit message - could contain placeholders
         like <otp> or <serial>
        :type message: string

        :return: submitted message
        :rtype: string

        '''
        log.debug("[sendSMS] begin. process the submitting of "
                                              "the sms message %r" % (message))

        ret = None

        if not message:
            message = "<otp>"

        if not SMSPROVIDER_IMPORTED:
            raise Exception("The SMSProvider could not be imported. Maybe you "
                            "didn't install the package (Debian "
                            "linotp-smsprovider or PyPI SMSProvider)")

        phone = self.getPhone()
        otp = self.getNextOtp()
        serial = self.getSerial()

        if '<otp>' not in message:
            log.error('Message unconfigured: prepending <otp> to message')
            if isinstance(message, basestring):
                message = "<otp> %s" % message
            else:
                message = "<otp> %r" % message

        message = message.replace("<otp>", otp)
        message = message.replace("<serial>", serial)

        if transactionid:
            message = message.replace("<transactionid>", transactionid)

        log.debug("[sendSMS] sending SMS to phone number %s " % phone)

        owner = get_token_owner(self)

        sms_provider = loadProviderFromPolicy(provider_type='sms', user=owner)

        if not sms_provider:
            raise Exception('unable to load provider')

        log.debug("[sendSMS] submitMessage: %r, to phone %r", message, phone)
        ret = sms_provider.submitMessage(phone, message)

        if not ret:
            raise Exception("Failed to submit message")
        log.debug("[sendSMS] message submitted")

        # # after submit set validity time
        self.setValidUntil()

        # return OTP for selftest purposes
        log.debug("[sendSMS] end. sms message submitted: message %r"
                                                                % (message))
        return ret, message
Пример #6
0
    def sendSMS(self, message=None, transactionid=None):
        '''
        send sms

        :param message: the sms submit message - could contain placeholders
         like <otp> or <serial>
        :type message: string

        :return: submitted message
        :rtype: string

        '''
        log.debug("[sendSMS] begin. process the submitting of "
                  "the sms message %r" % (message))

        ret = None

        if not message:
            message = "<otp>"

        if not SMSPROVIDER_IMPORTED:
            raise Exception("The SMSProvider could not be imported. Maybe you "
                            "didn't install the package (Debian "
                            "linotp-smsprovider or PyPI SMSProvider)")

        phone = self.getPhone()
        otp = self.getNextOtp()
        serial = self.getSerial()

        if '<otp>' not in message:
            log.error('Message unconfigured: prepending <otp> to message')
            if isinstance(message, basestring):
                message = "<otp> %s" % message
            else:
                message = "<otp> %r" % message

        message = message.replace("<otp>", otp)
        message = message.replace("<serial>", serial)

        if transactionid:
            message = message.replace("<transactionid>", transactionid)

        log.debug("[sendSMS] sending SMS to phone number %s " % phone)

        realm = None
        realms = self.getRealms()
        if realms:
            realm = realms[0]

        # we require the token owner to get the phone number and the provider
        owner = get_token_owner(self)
        if not owner or not owner.login:
            log.warning("Missing required token owner")

        sms_provider = loadProviderFromPolicy(provider_type='sms',
                                              realm=realm,
                                              user=owner)

        if not sms_provider:
            raise Exception('unable to load provider')

        log.debug("[sendSMS] submitMessage: %r, to phone %r", message, phone)
        ret = sms_provider.submitMessage(phone, message)

        if not ret:
            raise Exception("Failed to submit message")
        log.debug("[sendSMS] message submitted")

        # # after submit set validity time
        self.setValidUntil()

        # return OTP for selftest purposes
        log.debug("[sendSMS] end. sms message submitted: message %r" %
                  (message))
        return ret, message
Пример #7
0
    def createChallenge(self, transaction_id, options):

        """
        entry hook for the challenge logic. when this function is called
        a challenge with an transaction was created.

        :param transaction_id: A unique transaction id used to identity
            the challenge object

        :param options: additional options as a dictionary

        :raises TokenStateError: If token state is not 'active' or
            'pairing_response_received'

        :returns: A tuple (success, message, data, attributes)
            with success being a boolean indicating if the call
            to this method was successful, message being a string
            that is passed to the user, attributes being additional
            output data (unused in here)
        """
        _ = context['translate']

        valid_states = ['active',
                        'pairing_response_received',

                        # we support re activation
                        # as long as the token is not active
                        'pairing_challenge_sent',
                        ]

        self.ensure_state_is_in(valid_states)

        # ------------------------------------------------------------------- --

        # inside the challenge url we sent a callback url for the client
        # which is defined by an authentication policy

        owner = get_token_owner(self)
        if owner and owner.login and owner.realm:
            realms = [owner.realm]
        else:
            realms = self.getRealms()

        callback_policy_name = 'pushtoken_challenge_callback_url'
        callback_url = get_single_auth_policy(callback_policy_name,
                                              user=owner, realms=realms)

        if not callback_url:
            raise Exception(_('Policy pushtoken_challenge_callback_url must '
                              'have a value'))

        # ------------------------------------------------------------------- --

        # load and configure provider

        # the realm logic was taken from the
        # provider loading in the smstoken class
        # TODO: refactor & centralize logic

        realm = None
        if realms:
            realm = realms[0]

        push_provider = loadProviderFromPolicy(provider_type='push',
                                               realm=realm,
                                               user=owner)

        # ------------------------------------------------------------------- --

        if self.current_state in ['pairing_response_received',
                                  'pairing_challenge_sent']:

            content_type = CONTENT_TYPE_PAIRING

            message = ''
            challenge_url, sig_base = self.create_challenge_url(transaction_id,
                                                                content_type,
                                                                callback_url)

        elif self.current_state in ['active']:

            content_type_as_str = options.get(
                'content_type', CONTENT_TYPE_SIGNREQ)

            try:

                # pylons silently converts all ints in json
                # to unicode :(

                content_type = int(content_type_as_str)

            except:

                raise ValueError('Unrecognized content type: %s'
                                 % content_type_as_str)

            # --------------------------------------------------------------- --

            if content_type == CONTENT_TYPE_SIGNREQ:

                message = options.get('data')
                challenge_url, sig_base = self.create_challenge_url(
                                                transaction_id, content_type,
                                                callback_url, message=message)

            # --------------------------------------------------------------- --

            elif content_type == CONTENT_TYPE_LOGIN:

                message = options.get('data')
                login, __, host = message.partition('@')

                challenge_url, sig_base = self.create_challenge_url(
                                                transaction_id, content_type,
                                                callback_url, login=login,
                                                host=host)

            else:

                raise ValueError('Unrecognized content type: %s' % content_type)

        # ------------------------------------------------------------------- --

        # send the challenge_url to the push notification proxy

        token_info = self.getTokenInfo()
        gda = token_info['gda']

        log.debug("pushing notification: %r : %r", challenge_url, gda)

        success, response = push_provider.push_notification(
                                                challenge_url,
                                                gda,
                                                transaction_id)

        if not success:
            raise Exception('push mechanism failed. response was %r'
                            % response)

        # ------------------------------------------------------------------- --

        # we save sig_base in the challenge data, because we need it in
        # checkOtp to verify the signature

        b64_sig_base = b64encode(sig_base)
        data = {'sig_base': b64_sig_base}

        if self.current_state == 'pairing_response_received':
            self.change_state('pairing_challenge_sent')

        # ------------------------------------------------------------------- --

        # don't pass the challenge_url as message to the user

        return (True, '', data, {})