Example #1
0
def verify_totp_code(to_verify: str, my_secret: str, length: int, interval: int, hash_type: str) -> bool:
    """
    Verify given time-based one time password using library passlib

    :param to_verify: given time-based one time password 
    :type to_verify: str
    :param my_secret: my local stored secret
    :type my_secret: str
    :param length: time-based one time password length 
        Caution Due to a limitation of the HOTP algorithm, 
        the 10th digit can only take on values 0 .. 2, 
        and thus offers very little extra security.
        Please use maxim lenght of 9 instead 10 
        limitation see here https://passlib.readthedocs.io/en/stable/lib/passlib.totp.html#totptoken        
    :type length: int
    :param interval: totp interval in sec
    :type interval: int
    :param hash_type: hash code type to be used sha1, sha256 or sha512
    :type hash_type: str
    :return: result of totp matches
    :rtype: bool
    """
    logger.debug("verify totp with library passlib")
    totp = TOTP(key=my_secret, digits=length, period=interval, alg=hash_type)
    if totp.generate().token == str(to_verify):
        return True
    else:
        return False
Example #2
0
def generate_totp_code(my_secret: str, length: int, interval: int,
                       hash_type: str) -> bool:
    """
    Generate a new time-based one time password using library passlib

    :param my_secret: my local stored secret
    :type my_secret: str
    :param length: time-based one time password length 
        Caution Due to a limitation of the HOTP algorithm, 
        the 10th digit can only take on values 0 .. 2, 
        and thus offers very little extra security.
        Please use maxim lenght of 9 instead 10 
        limitation see here:
        https://passlib.readthedocs.io/en/stable/lib/passlib.totp.html#totptoken        
    :type length: int
    :param interval: totp interval in sec
    :type interval: int
    :param hash_type: hash code type to be used sha1, sha256 or sha512
    :type hash_type: str
    :return: totp code
    :rtype: int
    """
    logger.debug("generate new totp code with library passlib")
    totp = TOTP(key=my_secret, digits=length, period=interval, alg=hash_type)
    return totp.generate().token
Example #3
0
 def totp_hook(self, secret=None):
     nonlocal totp
     if totp is None:
         totp = TOTP(secret)
     if secret:
         return totp.generate().token
     else:
         # on check, take advantage of window because previous token has been
         # "burned" so we can't generate the same, but tour is so fast
         # we're pretty certainly within the same 30s
         return totp.generate(time.time() + 30).token
Example #4
0
 def new_user(self):
     self.username = input('Enter username: '******'Enter password: '******'Two factor auth (y, n)?: ')
     if totp.lower() == 'y':
         self.totp_enabled = True
         
         totp_factory = TOTP.using(secrets_path='totp_sec', issuer='sec.thelonelylands.com')
         totp = totp_factory.new()
         self.totp = json.dumps(totp.to_json())
         uri = totp.to_uri(label=self.username)
         
         input('Please enlarge your screen and press enter')
         print(pyqrcode.create(uri).terminal(quiet_zone=1))
         print("Alternatively: " + totp.pretty_key())
         
         token = input('Enter token to verify: ')
         verification = self.auth_verify(token)
         if verification:
             print('Successfully created user')
             return self
         else:
             print('User creation failed')
             return 
     else:
         print('Successfully created user')
         return self
Example #5
0
def verifyRegistration(request):
    registrationId = request.args.get('registrationId')
    otp1 = request.args.get('otp1')

    otp2 = request.args.get('otp2')

    TotpFactory = TOTP.using(secrets_path=PATH_TO_SECRET,issuer=ISSUER)

    querystr = AccountAdministratorRegistration \
                .query \
                .filter_by(registrationId=registrationId) \
                .all()

    if querystr:
        registrationObj = querystr[0]
        secretToken = regObj.secretToken

        totp = TotpFactory.from_json(secretToken)
        try:
            if not registrationObj.expired and totp.verify(otp1,secretToken) and totp.verify(otp2,secretToken, window=60) and __verifyQR__():
                registrationObj.expired = True
                registrationObj.expiryDate = datetime.now()
                db.session.add(registrationObj)
                db.session.flush()


            return {"Success": True,"SuccessResponse": [{"message": "Valid Registration"}]}, 200
        except:

            return {"Success": False,"ErrorResponse": [{"code": "12", "message": "Invalid OTP/Registration"}]}, 404
    else:
        return {"Success": False,"ErrorResponse": [{"code": "12", "message": "No Such Id"}]}, 404
Example #6
0
    def initialize(self):
        self.name = 'user'
        self.ensureIndices(
            ['login', 'email', 'groupInvites.groupId', 'size', 'created'])
        self.prefixSearchFields = ('login', ('firstName', 'i'), ('lastName',
                                                                 'i'))

        self.ensureTextIndex({
            'login': 1,
            'firstName': 1,
            'lastName': 1
        },
                             language='none')

        self.exposeFields(level=AccessType.READ,
                          fields=('_id', 'login', 'public', 'firstName',
                                  'lastName', 'admin', 'created'))
        self.exposeFields(level=AccessType.ADMIN,
                          fields=('size', 'email', 'groups', 'groupInvites',
                                  'status', 'emailVerified'))

        # To ensure compatibility with authenticator apps, other defaults shouldn't be changed
        self._TotpFactory = TOTP.using(
            # An application secret could be set here, if it existed
            wallet=None)

        events.bind('model.user.save.created',
                    CoreEventHandler.USER_SELF_ACCESS, self._grantSelfAccess)
        events.bind('model.user.save.created',
                    CoreEventHandler.USER_DEFAULT_FOLDERS,
                    self._addDefaultFolders)
Example #7
0
def totp_factory(core_settings):
    authc_config = core_settings.AUTHC_CONFIG
    totp_settings = authc_config.get('totp')
    totp_context = totp_settings.get('context')
    totp_secrets = totp_context.get('secrets')

    return TOTP.using(secrets=totp_secrets, issuer="testing")
Example #8
0
def create_totp_factory(env_var=None, file_path=None, authc_settings=None):
    if not authc_settings:
        yosai_settings = LazySettings(env_var=env_var, file_path=file_path)
        authc_settings = AuthenticationSettings(yosai_settings)

    totp_context = authc_settings.totp_context
    return TOTP.using(**totp_context)
Example #9
0
def generateOTP(request):
    adminId = request.args.get('adminId')
    # Generate OTP
    TotpFactory = TOTP.using(secrets_path=PATH_TO_SECRET,issuer=ISSUER)
    totp = TotpFactory.new(digits=8)
    d = datetime.now()


    # Insert OR Update
    querystr = OneTimePassword \
                .query \
                .filter_by(adminId=adminId) \
                .all()
    token_obj = totp.generate()
    if querystr:
        updateqstr = querystr[0]
        updateqstr.adminOTP = totp.to_json()
        updateqstr.adminOTPCreation = d
        updateqstr.adminOTPValidity = token_obj.expire_time


    else:
        # Create New Record
        totpDBObj = OneTimePassword(adminId=adminId, adminOTP = totp.to_json(), adminOTPCreation = d, adminOTPValidity = token_obj.expire_time)
        print(totpDBObj)
        db.session.add(totpDBObj)

    try:
        safeCommit()

        return {"Success": True,"SuccessResponse": [{"otp": token_obj.token}]}, 200
    except:

        return {"Success": False,"ErrorResponse": [{"code": "12", "message": "Could not Generate OTP"}]}, 404
Example #10
0
    def verify_credentials(self, authc_token, authc_info):
        submitted = authc_token.credentials
        stored = self.get_stored_credentials(authc_token, authc_info)
        service = self.cc_token_resolver[authc_token.__class__]

        try:
            if isinstance(authc_token, UsernamePasswordToken):
                result = service.verify(submitted, stored)
                if not result:
                    raise IncorrectCredentialsException
            else:
                totp = TOTP(key=stored)
                totp.verify(submitted)

        except (ValueError, TokenError):
            raise IncorrectCredentialsException
Example #11
0
    def initialize(self):
        self.name = 'user'
        self.ensureIndices(['login', 'email', 'groupInvites.groupId', 'size',
                            'created'])
        self.prefixSearchFields = (
            'login', ('firstName', 'i'), ('lastName', 'i'))

        self.ensureTextIndex({
            'login': 1,
            'firstName': 1,
            'lastName': 1
        }, language='none')

        self.exposeFields(level=AccessType.READ, fields=(
            '_id', 'login', 'public', 'firstName', 'lastName', 'admin',
            'created'))
        self.exposeFields(level=AccessType.ADMIN, fields=(
            'size', 'email', 'groups', 'groupInvites', 'status',
            'emailVerified'))

        # To ensure compatibility with authenticator apps, other defaults shouldn't be changed
        self._TotpFactory = TOTP.using(
            # An application secret could be set here, if it existed
            wallet=None
        )

        events.bind('model.user.save.created',
                    CoreEventHandler.USER_SELF_ACCESS, self._grantSelfAccess)
        events.bind('model.user.save.created',
                    CoreEventHandler.USER_DEFAULT_FOLDERS,
                    self._addDefaultFolders)
Example #12
0
 def __init__(self, secrets, issuer):
     """ Initialize a totp factory.
     secrets are used to encrypt the per-user totp_secret on disk.
     """
     # This should be a dict with at least one entry
     if not isinstance(secrets, dict) or len(secrets) < 1:
         raise ValueError("secrets needs to be a dict with at least one entry")
     self._totp = TOTP.using(issuer=issuer, secrets=secrets)
Example #13
0
 def gen_totp_qr(self):
     totp_factory = TOTP.using(secrets_path='totp_sec', issuer='sec.thelonelylands.com')
     totp = totp_factory.from_source(json.loads(self.totp))
     uri = totp.to_uri(label=self.username)
     
     input('Please enlarge your screen and press enter.')
     print(pyqrcode.create(uri).terminal(quiet_zone=1))
     print("Alternatively: " + totp.pretty_key())
Example #14
0
def generateQRImage(request):
    registrationId = request.args.get('registrationId')
    TotpFactory = TOTP.using(secrets_path=PATH_TO_SECRET,issuer=ISSUER)
    totp = TotpFactory.new()
    uri = totp.to_uri(label=registrationId)
    qrurl = pyqrcode.create(uri)
    outputBuffer = BytesIO()
    qrurl.png(outputBuffer)
    outputBuffer.seek(0)
    return send_file(outputBuffer, mimetype="image/png")
Example #15
0
def remembered_valid_thedude_totp_token(thedude_totp_key, cache_handler):
    keys = cache_handler.keys('*authentication*')
    for key in keys:
        cache_handler.cache_region.delete(key)

    token = int(TOTP(key=thedude_totp_key, digits=6).generate().token)
    yield TOTPToken(token, remember_me=True)

    keys = cache_handler.keys('*authentication*')
    for key in keys:
        cache_handler.cache_region.delete(key)
Example #16
0
    def auth_verify(self, token):
        try:
            int(token)
        except ValueError:
            return False
        else:
            token = int(token)

        totp_factory = TOTP.using(secrets_path='totp_sec', issuer='sec.thelonelylands.com')
        source = totp_factory.from_source(json.loads(self.totp))
        
        try:
            verify = TOTP.verify(token=token, source=source, last_counter=self.totp_counter, window=10)
        except (passlib_errors.UsedTokenError, 
                passlib_errors.InvalidTokenError, 
                passlib_errors.MalformedTokenError):
            return False
        else: 
            self.totp_counter = verify.counter
            return True
Example #17
0
def _tokenFromTotpUri(totpUri, valid=True):
    # Create an external TOTP instance
    from passlib.totp import TOTP
    totp = TOTP.from_uri(totpUri)

    # Generate a valid token
    otpToken = totp.generate().token

    if not valid:
        # Increment the token by 1 to invalidate it
        otpToken = '%06d' % ((int(otpToken) + 1) % int(1e6))

    return otpToken
Example #18
0
def _tokenFromTotpUri(totpUri, valid=True):
    # Create an external TOTP instance
    from passlib.totp import TOTP
    totp = TOTP.from_uri(totpUri)

    # Generate a valid token
    otpToken = totp.generate().token

    if not valid:
        # Increment the token by 1 to invalidate it
        otpToken = '%06d' % ((int(otpToken) + 1) % int(1e6))

    return otpToken
Example #19
0
def tf_setup(app):
    """ Initialize a totp factory.

    The TWO_FACTOR_SECRET is used to encrypt the per-user totp_secret on disk.
    """
    secrets = config_value("TWO_FACTOR_SECRET", app=app)
    # This should be a dict with at least one entry
    if not isinstance(secrets, dict) or len(secrets) < 1:
        raise ValueError(
            "TWO_FACTOR_SECRET needs to be a dict with at least one"
            "entry")
    return TOTP.using(issuer=config_value("TWO_FACTOR_URI_SERVICE_NAME",
                                          app=app),
                      secrets=secrets)
    def test_user_get_login_no_secret(self):
        User = Pool().get('res.user')
        user = User(name='totp', login='******')
        user.save()

        with self.assertRaises(LoginException) as cm:
            User.get_login('totp', {})
        self.assertEqual(cm.exception.name, 'totp_code')
        self.assertEqual(cm.exception.type, 'char')

        totp_code = TOTP(key=TOTP_SECRET_KEY).generate().token
        self.assertFalse(User.get_login('totp', {
            'totp_code': totp_code,
        }))
Example #21
0
def invalid_thedude_totp_token(cache_handler, yosai, monkeypatch):
    keys = cache_handler.keys('*authentication*')
    for key in keys:
        cache_handler.cache_region.delete(key)

    token = int(TOTP(key='AYAGB3C5RPYX5375L5VY2ULKZXMXWLZF', digits=6).generate().token)
    yield TOTPToken(totp_token=token)

    keys = cache_handler.keys('*authentication*')
    for key in keys:
        cache_handler.cache_region.delete(key)

    with Yosai.context(yosai):
        new_subject = Yosai.get_current_subject()
        da = new_subject.security_manager.authenticator
        monkeypatch.setattr(da.authc_settings, 'account_lock_threshold', 3)
        da.init_locking()
        da.locking_realm.unlock_account('thedude')
Example #22
0
    def initialize(self):
        self.name = 'protoUser'
        self.ensureIndices(['email', 'groupInvites.groupId'])
        self.prefixSearchFields = ('email')
        self.ensureTextIndex({
            'email': 1,
        }, language='none')
        self.exposeFields(level=AccessType.READ,
                          fields=('_id', 'public', 'email', 'created'))
        self.exposeFields(level=AccessType.ADMIN,
                          fields=('groupInvites', 'status'))

        # To ensure compatibility with authenticator apps, other defaults shouldn't be changed
        self._TotpFactory = TOTP.using(
            # An application secret could be set here, if it existed
            wallet=None)

        self._cryptContext = CryptContext(schemes=['bcrypt'])
Example #23
0
    def test_totp_check(self):
        pool = Pool()
        TOTPLogin = pool.get('res.user.login.totp')
        User = pool.get('res.user')
        user = User(name='totp', login='******', totp_secret=TOTP_SECRET_KEY)
        user.save()

        time = current_timestamp()
        totp_code = TOTP(key=TOTP_SECRET_KEY).generate(time=time).token
        totp_login, = TOTPLogin.create([{'user_id': user.id}])

        self.assertFalse(totp_login.check('0', _time=time))
        self.assertFalse(totp_login.check('invalid_code', _time=time))
        self.assertFalse(totp_login.check('000000', _time=time))

        self.assertTrue(totp_login.check(totp_code, _time=time))

        # Second check raises warning about replay attack
        with self.assertRaises(LoginException) as cm:
            totp_login.check(totp_code, _time=time)
        self.assertEqual(cm.exception.name, 'totp_code')
        self.assertRegex(cm.exception.message, r'(?i)warning.*already.*used')
Example #24
0
def handleRegistration(request):
    email = request.args.get('email')
    otp = request.args.get('otp')
    if __getAdminIdByEmail__(email):
        adminId = __getAdminIdByEmail__(email)
    else:
        return {"Success": False,"ErrorResponse": [{"code": "12", "message": "Invalid Email"}]}, 404
    TotpFactory = TOTP.using(secrets_path=PATH_TO_SECRET,issuer=ISSUER)

    querystr = OneTimePassword \
                .query \
                .filter_by(adminId=adminId) \
                .all()

    if querystr:
        currentAdminOTPjson = querystr[0]
        adminOTPjson = currentAdminOTPjson.adminOTP
        totp = TotpFactory.from_json(adminOTPjson)

        try:
            totp.verify(otp,adminOTPjson)

        except:

            return {"Success": False,"ErrorResponse": [{"code": "12", "message": "Invalid OTP"}]}, 404

        currentAdminOTPjson.adminOTPValidity = 0
        totp = TotpFactory.new(digits=8)
        registrationObj = AccountAdministratorRegistration(adminId=adminId,secretToken=totp.to_json())
        db.session.add(registrationObj)
        db.session.flush()

        return {"Success": True,"SuccessResponse": [{"registrationId": registrationObj.registrationId}]}, 200

    else:
        return {"Success": False,"ErrorResponse": [{"code": "12", "message": "No Id Found for the email provided"}]}, 404
Example #25
0
##

import pymongo, hashlib, uuid, hashlib, pyotp, base64,qrcode, pyqrcode
from pymongo import MongoClient
from bottle import post, get, route, run, template, request
from passlib.totp import TOTP, generate_secret
from passlib.exc import TokenError, MalformedTokenError

mongoclient = MongoClient()
db = mongoclient.giw
collection = db['usuarios']

pimienta = "c6y]s4*u#L3r?tZ{3LYM95'vLq%DfmrF{'gjv[vs:B%!_FP3L)r$-r^;~swKcUabrcapata89"

secret = generate_secret()
TotpFactory = TOTP.using(secrets={"1":secret})

totpCounter = 0

##############
# APARTADO 1 #
##############

# MECANISMO DE PROTECCIÓN DE CONTRASEÑAS:
#
# Almacenar las contraseñas en la BBDD de Mongo sin cifrado expone a los usuarios.
# Para almacenar la contraseña de manera segura en la BBDD guardo ésta de la siguiente manera:
# El hash de la contraseña (con SHA512) + sal (generación de cadena aleatoria) + pimienta (cadena de texto estática)
# Además, quiero evitar Brute Force, y para ello uso un algoritmo de ralentizado (PBKDF2)
# La función de hashlib se encarga de añadir la sal
#.
Example #26
0
 def credentials(self, credentials):
     self._credentials = TOTP.normalize_token(credentials)
Example #27
0
def test_two_factor_flag(app, client):
    # trying to verify code without going through two-factor
    # first login function
    wrong_code = b"000000"
    response = client.post(
        "/tf-validate", data=dict(code=wrong_code), follow_redirects=True
    )

    message = b"You currently do not have permissions to access this page"
    assert message in response.data

    # Test login using invalid email
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", data=data, follow_redirects=True)
    assert b"Specified user does not exist" in response.data
    response = client.post(
        "/login",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )
    assert b"Specified user does not exist" in response.data

    # Test login using valid email and invalid password
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", data=data, follow_redirects=True)
    assert b"Invalid password" in response.data
    response = client.post(
        "/login",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )
    assert b"Invalid password" in response.data

    # Test two-factor authentication first login
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", data=data, follow_redirects=True)
    message = b"Two-factor authentication adds an extra layer of security"
    assert message in response.data
    response = client.post(
        "/tf-setup", data=dict(setup="not_a_method"), follow_redirects=True
    )
    assert b"Marked method is not valid" in response.data
    session = get_session(response)
    assert session["tf_state"] == "setup_from_login"

    # try non-existing setup on setup page (using json)
    data = dict(setup="not_a_method")
    response = client.post(
        "/tf-setup",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )
    assert response.status_code == 400
    assert (
        response.json["response"]["errors"]["setup"][0] == "Marked method is not valid"
    )

    data = dict(setup="email")
    response = client.post(
        "/tf-setup",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )

    # Test for sms in process of valid login
    sms_sender = SmsSenderFactory.createSender("test")
    data = dict(email="*****@*****.**", password="******")
    response = client.post(
        "/login",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )
    assert b'"code": 200' in response.data
    assert sms_sender.get_count() == 1
    session = get_session(response)
    assert session["tf_state"] == "ready"

    code = sms_sender.messages[0].split()[-1]
    # submit bad token to two_factor_token_validation
    response = client.post("/tf-validate", data=dict(code=wrong_code))
    assert b"Invalid Token" in response.data

    # sumbit right token and show appropriate response
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"Your token has been confirmed" in response.data

    # Upon completion, session cookie shouldnt have any two factor stuff in it.
    assert not tf_in_session(get_session(response))

    # Test change two_factor view to from sms to mail
    with app.mail.record_messages() as outbox:
        setup_data = dict(setup="email")
        response = client.post("/tf-setup", data=setup_data, follow_redirects=True)
        msg = b"To complete logging in, please enter the code sent to your mail"
        assert msg in response.data

        # Fetch token validate form
        response = client.get("/tf-validate")
        assert response.status_code == 200
        assert b'name="code"' in response.data

    code = outbox[0].body.split()[-1]
    # sumbit right token and show appropriate response
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"You successfully changed your two-factor method" in response.data

    # Test change two_factor password confirmation view to authenticator
    # Setup authenticator
    setup_data = dict(setup="authenticator")
    response = client.post("/tf-setup", data=setup_data, follow_redirects=True)
    assert b"Open an authenticator app on your device" in response.data
    # verify png QRcode is present
    assert b"data:image/svg+xml;base64," in response.data

    # parse out key
    rd = response.data.decode("utf-8")
    matcher = re.match(r".*((?:\S{4}-){7}\S{4}).*", rd, re.DOTALL)
    totp_secret = matcher.group(1)

    # Generate token from passed totp_secret and confirm setup
    totp = TOTP(totp_secret)
    code = totp.generate().token
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"You successfully changed your two-factor method" in response.data

    logout(client)

    # Test login with remember_token
    assert "remember_token" not in [c.name for c in client.cookie_jar]
    data = dict(email="*****@*****.**", password="******", remember=True)
    response = client.post(
        "/login",
        json=data,
        headers={"Content-Type": "application/json"},
        follow_redirects=True,
    )

    # Generate token from passed totp_secret
    code = totp.generate().token
    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"Your token has been confirmed" in response.data

    # Verify that the remember token is properly set
    found = False
    for cookie in client.cookie_jar:
        if cookie.name == "remember_token":
            found = True
            assert cookie.path == "/"
    assert found

    response = logout(client)
    # Verify that logout clears session info
    assert not tf_in_session(get_session(response))

    # Test two-factor authentication first login
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", data=data, follow_redirects=True)
    message = b"Two-factor authentication adds an extra layer of security"
    assert message in response.data

    # check availability of qrcode when this option is not picked
    assert b"data:image/png;base64," not in response.data

    # check availability of qrcode page when this option is picked
    setup_data = dict(setup="authenticator")
    response = client.post("/tf-setup", data=setup_data, follow_redirects=True)
    assert b"Open an authenticator app on your device" in response.data
    assert b"data:image/svg+xml;base64," in response.data

    # check appearence of setup page when sms picked and phone number entered
    sms_sender = SmsSenderFactory.createSender("test")
    data = dict(setup="sms", phone="+442083661177")
    response = client.post("/tf-setup", data=data, follow_redirects=True)
    assert b"To Which Phone Number Should We Send Code To" in response.data
    assert sms_sender.get_count() == 1
    code = sms_sender.messages[0].split()[-1]

    response = client.post("/tf-validate", data=dict(code=code), follow_redirects=True)
    assert b"Your token has been confirmed" in response.data
    assert not tf_in_session(get_session(response))

    logout(client)

    # check when two_factor_rescue function should not appear
    rescue_data_json = dict(help_setup="lost_device")
    response = client.post(
        "/tf-rescue",
        json=rescue_data_json,
        headers={"Content-Type": "application/json"},
    )
    assert b'"code": 400' in response.data

    # check when two_factor_rescue function should appear
    data = dict(email="*****@*****.**", password="******")
    response = client.post("/login", data=data, follow_redirects=True)
    assert b"Please enter your authentication code" in response.data
    rescue_data = dict(help_setup="lost_device")
    response = client.post("/tf-rescue", data=rescue_data, follow_redirects=True)
    message = b"The code for authentication was sent to your email address"
    assert message in response.data
    rescue_data = dict(help_setup="no_mail_access")
    response = client.post("/tf-rescue", data=rescue_data, follow_redirects=True)
    message = b"A mail was sent to us in order to reset your application account"
    assert message in response.data
Example #28
0
from trytond.i18n import gettext
from trytond.model import ModelSQL, ModelView, fields
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval, PYSONEncoder
from trytond.transaction import Transaction

from .exception import (
    TOTPAccessCodeReuseError, TOTPInvalidSecretError, TOTPKeyTooShortError,
    TOTPLoginException)

_totp_issuer = config.get(
    'authentication_totp', 'issuer', default='{company} Tryton')
_totp_key_length = config.get(
    'authentication_totp', 'key_length', default=160)

_TOTPFactory = TOTP.using(secrets_path=config.get(
    'authentication_totp', 'application_secrets_file', default=None))


class User(metaclass=PoolMeta):
    __name__ = 'res.user'

    totp_key = fields.Char("TOTP Key")
    totp_secret = fields.Function(fields.Char(
            "TOTP Secret",
            help="Secret key used for time-based one-time password (TOTP) "
            "user authentication."),
        'get_totp_secret', setter='set_totp_secret')
    totp_qrcode = fields.Function(fields.Binary(
            "TOTP QR Code",
            states={
                'invisible': ~Eval('totp_secret', '') | (not QRCode),