Пример #1
0
 def test_23_verify_empty_hash(self):
     "test verify() allows hash=None"
     handlers = [hash.md5_crypt, hash.des_crypt, hash.bsdi_crypt]
     cc = CryptContext(handlers, policy=None)
     self.assertTrue(not cc.verify("test", None))
     for handler in handlers:
         self.assertTrue(not cc.verify("test", None, scheme=handler.name))
Пример #2
0
def change_password(conn, user_id, new_pass):
    """ Changes the specified user_id password

    :param conn: PostgreSQL connection object
    :param user_id: Users id
    :param new_pass: New password that will be set to the user
    :return: True if the password was changed, None otherwise
    """
    logger.info('Changing password for user_id %s', user_id)
    default_crypt_context = CryptContext(
        ['pbkdf2_sha512', 'md5_crypt'],
        deprecated=['md5_crypt'],
    )
    conn['cursor'].execute(("select password, password_crypt"
                            " from res_users where id = %(id)s"), {'id': user_id})
    res_user = conn['cursor'].fetchone()
    res = None
    if res_user:
        if not res_user[0] and res_user[1]:
            logger.info('Encrypted password')
            crypted_passwd = default_crypt_context.encrypt(new_pass)
            conn['cursor'].execute(("update res_users set password='',"
                                    " password_crypt=%(passwd)s where id = %(id)s"),
                                   {'passwd': crypted_passwd, 'id': user_id})
            res = True
        elif res_user[0] and res_user[1] == '':
            logger.info('Non encrypted password')
            conn['cursor'].execute("update res_users set password=%(passwd)s where id = %(id)s",
                                   {'passwd': new_pass, 'id': user_id})
            res = True
    return res
Пример #3
0
    def validate_passwd(self, senha):
         #Criando um objeto que usará criptografia do método shs256, rounds default de 80000
        cripto = CryptContext(schemes="sha256_crypt")

        #Comparando o valor da string com o valor criptografado
        okornot = cripto.verify(senha, self.senha)
        return okornot
Пример #4
0
Файл: main.py Проект: kdwarn/wyr
def login():
    ''' Let users log in. '''
    if request.method == 'GET':
        return render_template('index.html', next=request.args.get('next'))
    else:
        username = request.form['wyr_username']
        password = request.form['wyr_password']
        remember = request.form.getlist('remember')
        next = request.form['next']

        #first see if username exists
        if User.query.filter_by(username=username).count() == 1:
            #get their encrypted pass and check it
            user = User.query.filter_by(username=username).first()

            myctx = CryptContext(schemes=['pbkdf2_sha256'])
            if myctx.verify(password, user.password) == True:
                if remember:
                    login_user(user, remember=True)
                else:
                    login_user(user)

                flash('Welcome back, {}.'.format(username))

            else:
                flash('Sorry, the password is incorrect.')

        else:
            flash('Username does not exist.')

        if next:
            return redirect('https://www.whatyouveread.com' + next)

        return redirect(url_for('main.index'))
Пример #5
0
class HashHandler:
    default_scheme = 'sha512_crypt'
    valid_schemes = ('sha512_crypt', 'pbkdf2_sha512')

    def __init__(self):
        try:
            from passlib.context import CryptContext
            self.enabled = True
        except ImportError:
            self.enabled = False

        if self.enabled:
            self.context = CryptContext(schemes=self.valid_schemes)

    def encrypt(self, *args, **kwargs):
        return self.context.encrypt(*args, **kwargs)

    def verify(self, password, hash):
        is_valid = False

        if self.enabled:
            is_valid = self.context.verify(password, hash)

        del password
        return is_valid
Пример #6
0
    def register_context():
        bad_config = ""
        constructed_context = None
        try:
            if context:
                if isinstance(context, CryptContext):
                    constructed_context = context
                else:
                    msg = "'context' must be an instance of passlib.CryptContext"
                    bad_config = msg
            elif ini_string:
                constructed_context = CryptContext.from_string(ini_string)
            elif ini_file:
                constructed_context = CryptContext.from_path(ini_file)
            elif context_dict:
                constructed_context = CryptContext(**context_dict)
            else:
                # no required arguments have been passed, error
                bad_config = 'requires a CryptContext or configuration data'
        except IOError:
            bad_config = "unable to open %s" % ini_file
        except ValueError:
            bad_config = "received invalid or incompatible configuration options"
        except KeyError:
            bad_config = "received unknown or forbidden configuration options"
        except TypeError:
            bad_config = "received configuration options of the wrong type"

        if bad_config:
            raise ConfigurationError("set_password_context %s" % bad_config)

        config.registry.password_context = constructed_context
Пример #7
0
Файл: main.py Проект: kdwarn/wyr
def activate():
    ''' Activate user account - finish the sign up process now that the email
    is verified - get user's password, do checks on it, and insert user into database
    '''

    #send user to form to set password if hash is good
    if request.method == 'GET':

        #first, pull user's email and username out of hash
        hash = request.args.get('code')
        serializer = URLSafeTimedSerializer(current_app.config['SECRET_KEY'])
        try:
            decoded = serializer.loads(hash, salt='sign_up', max_age=3600)
        except SignatureExpired:
            flash('Activation period expired. Please sign up again.')
            return redirect(url_for('main.index'))
        except:
            flash("Error activating your account. Please sign up again below.")
            return redirect(url_for('main.index'))

        return render_template('activate.html', username=decoded[0], email=decoded[1])

    # get user's desired password, check, add account
    if request.method == 'POST':

        username = request.form['username']
        email = request.form['email']
        password = request.form['password']
        confirm_password = request.form['confirm_password']

        #checks - password
        if password != confirm_password:
            flash("Your passwords did not match. Please try again.")
            return render_template('activate.html', username=username, email=email)
        if len(password) < 5:
            flash("Your password is too short. Please try again.")
            return render_template('activate.html', username=username, email=email)
        #checks - if user already completed sign up, redirect
        if User.query.filter_by(username=username).count() > 0:
            flash("You've already activated your account.")
            return redirect(url_for('main.index'))

        # use passlib to encrypt password
        myctx = CryptContext(schemes=['pbkdf2_sha256'])
        hashed_password = myctx.encrypt(password)

        # create a salt
        alphabet = string.ascii_letters + string.digits
        salt = ''.join(secrets.choice(alphabet) for i in range(32))

        #add user
        user = User(username, hashed_password, salt, email)
        db.session.add(user)
        db.session.commit()

        login_user(user)

        flash('Thank you. Your account has been activated.')
        return redirect(url_for('main.settings'))
Пример #8
0
Файл: main.py Проект: kdwarn/wyr
def change_password():
    ''' Let users change password '''

    if request.method == 'GET':
        return render_template('change_password.html')
    elif request.method == 'POST':
        if request.form['submit'] == 'Cancel':
            flash('Password change cancelled.')
            return redirect(url_for('main.settings'))

        current_password = request.form['wyr_current_password']
        new_password = request.form['wyr_new_password']
        confirm_password = request.form['wyr_confirm_password']

        #first verify current password
        myctx = CryptContext(schemes=['pbkdf2_sha256'])
        if myctx.verify(current_password, current_user.password) == True:
            #password checks
            if len(new_password) < 5:
                flash('Password is too short. Please try again.')
                return redirect(url_for('main.change_password'))
            elif new_password != confirm_password:
                flash('The confirmation password did not match the new password you entered.')
                return redirect(url_for('main.change_password'))
            else:
                #use passlib to encrypt password
                myctx = CryptContext(schemes=['pbkdf2_sha256'])
                hash = myctx.encrypt(new_password)

                current_user.password = hash
                db.session.commit()

                # send user email to confirm, allow reset of password
                #hash for confirm change
                serializer = URLSafeSerializer(current_app.config['SECRET_KEY'])
                email_hash = serializer.dumps([current_user.email], salt='reset_password')

                to = current_user.email
                subject = 'Password Change'
                text = """The password for your What You've Read account has been
                changed. If this was not you, someone has access to your account. You should
                <a href="http://www.whatyouveread.com/reset_password?code={}">reset your
                password</a> immediately.<br>
                <br>
                -Kris @ What You've Read""".format(email_hash)

                common.send_simple_message(to, subject, text)

                flash('Your password has been updated.')
                return redirect(url_for('main.settings'))
        else:
            flash('Password is incorrect.')
            return redirect(url_for('main.change_password'))
    else:
        return abort(405)
def generate_admin_password_hash(admin_password, salt):
    # Flask password hash generation approach for Cloudify 4.x where x<=2
    pwd_hmac = base64.b64encode(
        # Encodes put in to keep hmac happy with unicode strings
        hmac.new(salt.encode('utf-8'), admin_password.encode('utf-8'),
                 hashlib.sha512).digest()
    ).decode('ascii')

    # This ctx is nothing to do with a cloudify ctx.
    pass_ctx = CryptContext(schemes=['pbkdf2_sha256'])
    return pass_ctx.encrypt(pwd_hmac)
Пример #10
0
    def test_21_identify(self):
        "test identify() border cases"
        handlers = ["md5_crypt", "des_crypt", "bsdi_crypt"]
        cc = CryptContext(handlers, policy=None)

        #check unknown hash
        self.assertEqual(cc.identify('$9$232323123$1287319827'), None)
        self.assertRaises(ValueError, cc.identify, '$9$232323123$1287319827', required=True)

        #make sure "None" is accepted
        self.assertEqual(cc.identify(None), None)
        self.assertRaises(ValueError, cc.identify, None, required=True)
Пример #11
0
def walter_credentials(request, walter, clear_walter_cached_credentials, session):
    password = "******"
    cc = CryptContext(["bcrypt_sha256"])
    credentials = cc.encrypt(password)
    thirty_from_now = datetime.datetime.now() + datetime.timedelta(days=30)
    credential = CredentialModel(user_id=walter.pk_id,
                                 credential=credentials,
                                 expiration_dt=thirty_from_now)
    session = session()
    session.add(credential)
    session.commit()

    return credentials
Пример #12
0
    def _verify_by_hashcode(pincode, hashcode):
        logger.debug('Will test against %s' % hashcode)
        from passlib.context import CryptContext
        myctx = CryptContext(schemes=['sha256_crypt', 'sha512_crypt',
                                      'bcrypt', 'md5_crypt'])

        try:
            if not myctx.verify(pincode, hashcode):
                raise totpcgi.UserPincodeError('Pincode did not match.')

            return True

        except ValueError:
            raise totpcgi.UserPincodeError('Unsupported hashcode format')
Пример #13
0
    def test_25_verify_and_update(self):
        "test verify_and_update()"
        cc = CryptContext(**self.sample_policy_1)

        #create some hashes
        h1 = cc.encrypt("password", scheme="des_crypt")
        h2 = cc.encrypt("password", scheme="sha256_crypt")

        #check bad password, deprecated hash
        ok, new_hash = cc.verify_and_update("wrongpass", h1)
        self.assertFalse(ok)
        self.assertIs(new_hash, None)

        #check bad password, good hash
        ok, new_hash = cc.verify_and_update("wrongpass", h2)
        self.assertFalse(ok)
        self.assertIs(new_hash, None)

        #check right password, deprecated hash
        ok, new_hash = cc.verify_and_update("password", h1)
        self.assertTrue(ok)
        self.assertTrue(cc.identify(new_hash), "sha256_crypt")

        #check right password, good hash
        ok, new_hash = cc.verify_and_update("password", h2)
        self.assertTrue(ok)
        self.assertIs(new_hash, None)
Пример #14
0
    def test_01_replace(self):
        "test replace()"

        cc = CryptContext(["md5_crypt", "bsdi_crypt", "des_crypt"])
        self.assertIs(cc.policy.get_handler(), hash.md5_crypt)

        cc2 = cc.replace()
        self.assertIsNot(cc2, cc)
        self.assertIs(cc2.policy, cc.policy)

        cc3 = cc.replace(default="bsdi_crypt")
        self.assertIsNot(cc3, cc)
        self.assertIsNot(cc3.policy, cc.policy)
        self.assertIs(cc3.policy.get_handler(), hash.bsdi_crypt)
Пример #15
0
def f1():
    if len(sys.argv) != 3:
        print "ERROR args"
        return 0

    myctx = CryptContext(schemes=["pbkdf2_sha256", "pbkdf2_sha512"])

    pw = base64.b64decode(sys.argv[1])
    hash = base64.b64decode(sys.argv[2])

    if not myctx.identify(hash):
        return 2

    return 1 if myctx.verify(pw, hash) else 0
Пример #16
0
    def test_95_context_algs(self):
        """test handling of 'algs' in context object"""
        handler = self.handler
        from passlib.context import CryptContext
        c1 = CryptContext(["scram"], scram__algs="sha1,md5")

        h = c1.hash("dummy")
        self.assertEqual(handler.extract_digest_algs(h), ["md5", "sha-1"])
        self.assertFalse(c1.needs_update(h))

        c2 = c1.copy(scram__algs="sha1")
        self.assertFalse(c2.needs_update(h))

        c2 = c1.copy(scram__algs="sha1,sha256")
        self.assertTrue(c2.needs_update(h))
Пример #17
0
    def __init__(self, **kwargs):
        """
        All keyword arguments are forwarded to the construction of a
        `passlib.context.CryptContext` object.

        The following usage will create a password column that will
        automatically hash new passwords as `pbkdf2_sha512` but still compare
        passwords against pre-existing `md5_crypt` hashes. As passwords are
        compared; the password hash in the database will be updated to
        be `pbkdf2_sha512`. ::

            class Model(Base):
                password = sa.Column(PasswordType(
                    schemes=[
                        'pbkdf2_sha512',
                        'md5_crypt'
                    ],

                    deprecated=['md5_crypt']
                ))

        """

        # Bail if passlib is not found.
        if passlib is None:
            raise ImproperlyConfigured(
                "'passlib' is required to use 'PasswordType'")

        # Construct the passlib crypt context.
        self.context = CryptContext(**kwargs)
Пример #18
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
        )

        self._cryptContext = CryptContext(
            schemes=['bcrypt']
        )

        events.bind('model.user.save.created',
                    CoreEventHandler.USER_SELF_ACCESS, self._grantSelfAccess)
        events.bind('model.user.save.created',
                    CoreEventHandler.USER_DEFAULT_FOLDERS,
                    self._addDefaultFolders)
Пример #19
0
    def __init__(self, key, data=None, digest=None, cryptcontext=None):
        if cryptcontext is None:
            # @todo make the CryptContext schemes not hard-coded
            self.cryptcontext = CryptContext(schemes=['ldap_pbkdf2_sha512'],
                                             default='ldap_pbkdf2_sha512',
                                             all__vary_rounds=0.1)
        else:
            self.cryptcontext = cryptcontext

        if data is not None and digest is not None:
            self.data = data

            # okay, first verify the digest we got to check for a mismatch --
            # we have to use the passlib verify, in case an older scheme was
            # used, instead of just calculating the new digest and comparing
            # strings
            if not self.cryptcontext.verify(data, digest):
                raise SaltboxException("Given data doesn't verify with given digest")

            # if self.digest_str isn't our default hash scheme, recalculate it
            # and save it as default hash scheme
            # @todo this always recalculates; wasteful
            self.digest()

        elif data is not None:
            self.data = data
            self.digest()

        elif digest is not None:
            self.digest_str = digest
Пример #20
0
    def generate_password_hashes(self, password):
        """
        Refer to passlib documentation for adding new hashers:

            https://pythonhosted.org/passlib/lib/passlib.hash.html
        """

        password_schemes = ['pbkdf2_sha256', 'sha512_crypt', 'sha256_crypt', 'des_crypt', 'md5_crypt']
        pwd_context = CryptContext(schemes=password_schemes)

        self.sha512_crypt = pwd_context.hash(password, 'sha512_crypt')
        self.sha256_crypt = pwd_context.hash(password, 'sha256_crypt')
        self.des_crypt = pwd_context.hash(password, 'des_crypt')
        self.md5_crypt = pwd_context.hash(password, 'md5_crypt')

        self.save()
Пример #21
0
def encrypt_pwd(toyz_settings, pwd):
    """
    Use the passlib module to create a hash for the given password.
    
    Parameters
        - toyz_settings ( :py:class:`toyz.utils.core.ToyzSettings` ): Settings for the 
          current application
        - pwd (*string* ): password the user has entered
    
    Returns
        - pwd_hash (*string* ): Password hash to be stored for the given password. If passwords
          are not encrypted, this will just return the ``pwd`` passed to the function.
    """
    from passlib.context import CryptContext
    pwd_context = CryptContext(**toyz_settings.security.pwd_context)
    return pwd_context.encrypt(pwd)
    def test_01_replace(self):
        "test replace()"

        cc = CryptContext(["md5_crypt", "bsdi_crypt", "des_crypt"])
        self.assertIs(cc.policy.get_handler(), hash.md5_crypt)

        cc2 = cc.replace()
        self.assertIsNot(cc2, cc)
        # NOTE: was not able to maintain backward compatibility with this...
        ##self.assertIs(cc2.policy, cc.policy)

        cc3 = cc.replace(default="bsdi_crypt")
        self.assertIsNot(cc3, cc)
        # NOTE: was not able to maintain backward compatibility with this...
        ##self.assertIs(cc3.policy, cc.policy)
        self.assertIs(cc3.policy.get_handler(), hash.bsdi_crypt)
Пример #23
0
    def __init__(self, config):
        """Create a password context for hashing and verification.

        :param config: The `IConfiguration` instance.
        """
        config_string = load_external(config.passwords.configuration)
        self._context = CryptContext.from_string(config_string)
Пример #24
0
    def generate_password_hashes(self, password):
        """
        Generate password hashes with SHA512, PBKDF2/SHA-256 and DES crypt.

        Refer to passlib documentation for adding new hashers:

            https://pythonhosted.org/passlib/lib/passlib.hash.html
        """

        password_schemes = ['pbkdf2_sha256', 'sha512_crypt', 'des_crypt']
        pwd_context = CryptContext(schemes=password_schemes)

        self.pbkdf2_sha256 = pwd_context.encrypt(password, scheme='pbkdf2_sha256')
        self.sha512_crypt = pwd_context.encrypt(password, scheme='sha512_crypt')
        self.des_crypt = pwd_context.encrypt(password, scheme='des_crypt')

        self.save()
Пример #25
0
    def test_22_verify(self):
        "test verify() scheme kwd"
        handlers = ["md5_crypt", "des_crypt", "bsdi_crypt"]
        cc = CryptContext(handlers, policy=None)

        h = hash.md5_crypt.encrypt("test")

        #check base verify
        self.assertTrue(cc.verify("test", h))
        self.assertTrue(not cc.verify("notest", h))

        #check verify using right alg
        self.assertTrue(cc.verify('test', h, scheme='md5_crypt'))
        self.assertTrue(not cc.verify('notest', h, scheme='md5_crypt'))

        #check verify using wrong alg
        self.assertRaises(ValueError, cc.verify, 'test', h, scheme='bsdi_crypt')
    def test_02_no_handlers(self):
        "test no handlers"

        #check constructor...
        cc = CryptContext()
        self.assertRaises(KeyError, cc.identify, 'hash', required=True)
        self.assertRaises(KeyError, cc.encrypt, 'secret')
        self.assertRaises(KeyError, cc.verify, 'secret', 'hash')

        #check updating policy after the fact...
        cc = CryptContext(['md5_crypt'])
        p = CryptPolicy(schemes=[])
        cc.policy = p

        self.assertRaises(KeyError, cc.identify, 'hash', required=True)
        self.assertRaises(KeyError, cc.encrypt, 'secret')
        self.assertRaises(KeyError, cc.verify, 'secret', 'hash')
Пример #27
0
Файл: user.py Проект: ddcy/zoe
class User(BaseState):
    """
    :type name: str
    :type hashed_password: str
    :type role: list
    :type gateway_docker_id: str
    :type gateway_urls: list
    :type network_id: str
    """

    api_in_attrs = ['name', 'role']
    api_out_attrs = ['name', 'role', 'gateway_urls']
    private_attrs = ['hashed_password', 'gateway_docker_id', 'network_id']

    def __init__(self, state):
        super().__init__(state)

        self.name = ''
        self.hashed_password = ''
        self.role = ''
        self.gateway_docker_id = None
        self.gateway_urls = []
        self.network_id = None

        # Links to other objects
        self.applications = []

        self.pwd_context = CryptContext(schemes=["sha512_crypt"], sha512_crypt__default_rounds=get_conf().passlib_rounds)

    def set_password(self, pw):
        self.hashed_password = self.pwd_context.encrypt(pw)

    def verify_password(self, pw):
        return self.pwd_context.verify(pw, self.hashed_password)

    @property
    def owner(self):
        return self

    def can_see_non_owner_objects(self):
        return self.role == 'admin'

    def set_gateway_urls(self, cont_info):
        socks_url = 'socks://' + cont_info['ports']['1080/tcp'][0] + ':' + cont_info['ports']['1080/tcp'][1]
        self.gateway_urls = [socks_url]
    def test_00_constructor(self):
        "test constructor"
        #create crypt context using handlers
        cc = CryptContext([hash.md5_crypt, hash.bsdi_crypt, hash.des_crypt])
        c,b,a = cc.policy.iter_handlers()
        self.assertIs(a, hash.des_crypt)
        self.assertIs(b, hash.bsdi_crypt)
        self.assertIs(c, hash.md5_crypt)

        #create context using names
        cc = CryptContext(["md5_crypt", "bsdi_crypt", "des_crypt"])
        c,b,a = cc.policy.iter_handlers()
        self.assertIs(a, hash.des_crypt)
        self.assertIs(b, hash.bsdi_crypt)
        self.assertIs(c, hash.md5_crypt)

        # policy kwd
        policy = cc.policy
        cc = CryptContext(policy=policy)
        self.assertEqual(cc.to_dict(), policy.to_dict())

        cc = CryptContext(policy=policy, default="bsdi_crypt")
        self.assertNotEqual(cc.to_dict(), policy.to_dict())
        self.assertEqual(cc.to_dict(), dict(schemes=["md5_crypt","bsdi_crypt","des_crypt"],
                                            default="bsdi_crypt"))

        self.assertRaises(TypeError, setattr, cc, 'policy', None)
        self.assertRaises(TypeError, CryptContext, policy='x')
Пример #29
0
    def __init__(self):
        try:
            from passlib.context import CryptContext
            self.enabled = True
        except ImportError:
            self.enabled = False

        if self.enabled:
            self.context = CryptContext(schemes=self.valid_schemes)
Пример #30
0
 def __init__(self, config):
     # Is the context coming from a file system or Python path?
     if config.passwords.path.startswith('python:'):
         resource_path = config.passwords.path[7:]
         package, dot, resource = resource_path.rpartition('.')
         config_string = resource_string(package, resource + '.cfg')
     else:
         with open(config.passwords.path, 'rb') as fp:
             config_string = fp.read()
     self._context = CryptContext.from_string(config_string)
Пример #31
0
    @classmethod
    def from_string(cls, hash):
        salt, chk = uh.parse_mc2(hash, u'')
        if chk is None:
            raise ValueError('missing checksum')
        return cls(salt=salt, checksum=chk)

    def to_string(self):
        return to_hash_str(u'%s$%s' % (self.salt, self.checksum or u''))

    def _calc_checksum(self, secret):
        return md5crypt(secret, self.salt.encode('ascii')).decode('utf-8')


_CRYPTO_CTX = CryptContext(['sha512_crypt', CustomMD5Crypt, 'des_crypt', 'ldap_salted_sha1'],
                           deprecated=['cubicwebmd5crypt', 'des_crypt'])

verify_and_update = _CRYPTO_CTX.verify_and_update


def crypt_password(passwd, salt=None):
    """return the encrypted password using the given salt or a generated one
    """
    if salt is None:
        return _CRYPTO_CTX.hash(passwd).encode('ascii')
    # empty hash, accept any password for backwards compat
    if salt == '':
        return salt
    try:
        if _CRYPTO_CTX.verify(passwd, salt):
            return salt
Пример #32
0
from datetime import datetime, timedelta

import jwt
from fastapi import Depends, HTTPException
from jwt import PyJWTError
from passlib.context import CryptContext
from pydantic import EmailStr
from starlette import status

from config import apisecrets, oauth2_scheme
from models import User
from v1.services.user import UserService

user_service = UserService()
PWD_CONTEXT = CryptContext(schemes=["bcrypt"], deprecated="auto")
SECRET_KEY = apisecrets.SECRET_KEY
ACCESS_TOKEN_ALGORITHM = 'HS256'
CREDENTIALS_EXCEPTION = HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,
                                      detail="Invalid credentials.",
                                      headers={"WWW-Authenticate": "Bearer"})


class AuthService:
    def valid_password(self, plain_pass: str, hashed_pass: str) -> bool:
        return PWD_CONTEXT.verify(plain_pass, hashed_pass)

    def authenticate_user(self, email: EmailStr, password: str) -> User:
        user = user_service.get_user_by_email(email)
        if not user:
            return
        if not self.valid_password(password, user.password):
Пример #33
0
 def context(self):
     """
     per-test CryptContext() created from .config.
     """
     return CryptContext._norm_source(self.config)
Пример #34
0
from itertools import chain
from flask import Flask, render_template, flash, redirect, url_for, session, request, logging, json
from flask_mysqldb import MySQL
from flask_mail import Mail, Message
from wtforms import Form, StringField, TextAreaField, PasswordField, validators, SelectField, HiddenField
from wtforms.fields.html5 import DateTimeLocalField
from passlib.context import CryptContext
from functools import wraps
from wtforms import form
from config import config
import os

cryptcontext = CryptContext(schemes=["sha256_crypt", "md5_crypt", "des_crypt"]) # Schemes for encrypting passwords

app = Flask(__name__)

# Config MySQL
app.config['MYSQL_HOST'] = config.MYSQL_HOST
app.config['MYSQL_USER'] = config.MYSQL_USER
app.config['MYSQL_PASSWORD'] = config.MYSQL_PASSWORD
app.config['MYSQL_DB'] = config.MYSQL_DB
app.config['MYSQL_CURSORCLASS'] = config.MYSQL_CURSORCLASS
app.secret_key = os.urandom(12).hex()

# init MySQL
mysql = MySQL(app)

# init Mail
# Ensure 2 Factor Authentication is off and allow less secure app access
mail = Mail(app)
Пример #35
0
HASHING_PREFIX_DJANGO = "pbkdf2_sha256"
# The prefix of the hashed using pbkdf2_sha256 algorithm in Passlib
HASHING_PREFIX_PBKDF2_SHA256 = "$pbkdf2-sha256"

# This will never be a valid encoded hash
UNUSABLE_PASSWORD_PREFIX = '!'
# Number of random chars to add after UNUSABLE_PASSWORD_PREFIX
UNUSABLE_PASSWORD_SUFFIX_LENGTH = 40

HASHING_KEY = "HashingKey"

pwd_context = CryptContext(
    # The list of hashes that we support
    schemes=["pbkdf2_sha256", "des_crypt"],
    # The default hashing mechanism
    default="pbkdf2_sha256",

    # We set the number of rounds that should be used...
    pbkdf2_sha256__default_rounds=8000,
)


def create_unusable_pass():
    return UNUSABLE_PASSWORD_PREFIX + get_random_string(
        UNUSABLE_PASSWORD_SUFFIX_LENGTH)


def is_password_usable(enc_pass):
    if enc_pass is None or enc_pass.startswith(UNUSABLE_PASSWORD_PREFIX):
        return False
Пример #36
0
from passlib.context import CryptContext
from jwt import decode as jwt_decode

from db import Account

password_crypto_context = CryptContext(
    schemes=["pbkdf2_sha256"],
    default="pbkdf2_sha256",
    pbkdf2_sha256__default_rounds=30000,
)


def password_hash(cleartext: str):
    return password_crypto_context.hash(cleartext)


def password_compare(cleartext: str, hashed: str):
    return password_crypto_context.verify(cleartext, hashed)


def read_yggt(access_token_string: str):
    """
    :param access_token_string: 32 or 36 char UUID or JWT
    :return: yggt value
    """
    if len(access_token_string) == 32:
        return access_token_string
    elif len(access_token_string) == 36:
        return access_token_string.replace("-", "")
    else:
        return jwt_decode(jwt=access_token_string, verify=False)["yggt"]
Пример #37
0
# -*- coding: utf-8 -*-
from passlib.context import CryptContext

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")


def verify_password(plain_password, hashed_password):
    """ Is the hash of the two values equal
    :param plain_password: plaintext
    :param hashed_password: hash password
    :return:
    """
    return pwd_context.verify(plain_password, hashed_password)


def gen_password_hash(data):
    """ Generate password hash
    :param data: plaintext data
    :return:
    """
    return pwd_context.hash(data)
Пример #38
0
#!/usr/bin/python3
import argparse
import json
from os import _exit

try:
    from passlib.context import CryptContext
except ImportError:
    print("You need to run")
    print("python -m pip install passlib bcrypt")
    _exit(0)

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

try:
    with open("../web/web_user_db.json", "r") as f:
        users_db = json.loads(f.read())

except FileNotFoundError:
    users_db = {}
    print("web_user_db.json file does not exist, will create.")
    users_db = dict()

except json.decoder.JSONDecodeError:
    print("empty web_user_db.json")
    # Create an empty dictionary to add to
    users_db = dict()

parser = argparse.ArgumentParser(
    description="Create user for web authentication")
parser.add_argument("--user", help="Specifiy the username", required=True)
Пример #39
0
class Praetorian:
    """
    Comprises the implementation for the flask-praetorian flask extension.
    Provides a tool that allows password authentication and token provision
    for applications and designated endpoints
    """
    def __init__(self, app=None, user_class=None, is_blacklisted=None):
        self.pwd_ctx = None
        self.hash_scheme = None
        self.salt = None

        if app is not None and user_class is not None:
            self.init_app(app, user_class, is_blacklisted)

    def init_app(self, app, user_class, is_blacklisted=None):
        """
        Initializes the Praetorian extension

        :param: app:            The flask app to bind this extension to
        :param: user_class:     The class used to interact with user data
        :param: is_blacklisted: A method that may optionally be used to
                                check the token against a blacklist when
                                access or refresh is requested
                                Should take the jti for the token to check
                                as a single argument. Returns True if
                                the jti is blacklisted, False otherwise.
                                By default, always returns False.
        """
        PraetorianError.require_condition(
            app.config.get('SECRET_KEY') is not None,
            "There must be a SECRET_KEY app config setting set",
        )

        self.hash_autoupdate = app.config.get(
            'PRAETORIAN_HASH_AUTOUPDATE',
            DEFAULT_HASH_AUTOUPDATE,
        )

        self.hash_autotest = app.config.get(
            'PRAETORIAN_HASH_AUTOTEST',
            DEFAULT_HASH_AUTOTEST,
        )

        self.pwd_ctx = CryptContext(
            schemes=app.config.get('PRAETORIAN_HASH_ALLOWED_SCHEMES',
                                   DEFAULT_HASH_ALLOWED_SCHEMES),
            default=app.config.get('PRAETORIAN_HASH_SCHEME',
                                   DEFAULT_HASH_SCHEME),
            deprecated=app.config.get('PRAETORIAN_HASH_DEPRECATED_SCHEMES',
                                      DEFAULT_HASH_DEPRECATED_SCHEMES),
        )

        valid_schemes = self.pwd_ctx.schemes()
        PraetorianError.require_condition(
            self.hash_scheme in valid_schemes or self.hash_scheme is None,
            "If {} is set, it must be one of the following schemes: {}",
            'PRAETORIAN_HASH_SCHEME',
            valid_schemes,
        )

        self.user_class = self._validate_user_class(user_class)
        self.is_blacklisted = is_blacklisted or (lambda t: False)

        self.encode_key = app.config['SECRET_KEY']
        self.allowed_algorithms = app.config.get(
            'JWT_ALLOWED_ALGORITHMS',
            DEFAULT_JWT_ALLOWED_ALGORITHMS,
        )
        self.encode_algorithm = app.config.get(
            'JWT_ALGORITHM',
            DEFAULT_JWT_ALGORITHM,
        )
        self.access_lifespan = app.config.get(
            'JWT_ACCESS_LIFESPAN',
            DEFAULT_JWT_ACCESS_LIFESPAN,
        )
        self.refresh_lifespan = app.config.get(
            'JWT_REFRESH_LIFESPAN',
            DEFAULT_JWT_REFRESH_LIFESPAN,
        )
        self.reset_lifespan = app.config.get(
            'JWT_RESET_LIFESPAN',
            DEFAULT_JWT_RESET_LIFESPAN,
        )
        self.header_name = app.config.get(
            'JWT_HEADER_NAME',
            DEFAULT_JWT_HEADER_NAME,
        )
        self.header_type = app.config.get(
            'JWT_HEADER_TYPE',
            DEFAULT_JWT_HEADER_TYPE,
        )
        self.user_class_validation_method = app.config.get(
            'USER_CLASS_VALIDATION_METHOD',
            DEFAULT_USER_CLASS_VALIDATION_METHOD,
        )

        self.confirmation_template = app.config.get(
            'PRAETORIAN_CONFIRMATION_TEMPLATE',
            DEFAULT_CONFIRMATION_TEMPLATE,
        )
        self.confirmation_uri = app.config.get('PRAETORIAN_CONFIRMATION_URI', )
        self.confirmation_sender = app.config.get(
            'PRAETORIAN_CONFIRMATION_SENDER', )
        self.confirmation_subject = app.config.get(
            'PRAETORIAN_CONFIRMATION_SUBJECT',
            DEFAULT_CONFIRMATION_SUBJECT,
        )

        self.reset_template = app.config.get(
            'PRAETORIAN_RESET_TEMPLATE',
            DEFAULT_RESET_TEMPLATE,
        )
        self.reset_uri = app.config.get('PRAETORIAN_RESET_URI', )
        self.reset_sender = app.config.get('PRAETORIAN_RESET_SENDER', )
        self.reset_subject = app.config.get(
            'PRAETORIAN_RESET_SUBJECT',
            DEFAULT_RESET_SUBJECT,
        )

        if isinstance(self.access_lifespan, dict):
            self.access_lifespan = pendulum.duration(**self.access_lifespan)
        elif isinstance(self.access_lifespan, str):
            self.access_lifespan = duration_from_string(self.access_lifespan)
        ConfigurationError.require_condition(
            isinstance(self.access_lifespan, datetime.timedelta),
            "access lifespan was not configured",
        )

        if isinstance(self.refresh_lifespan, dict):
            self.refresh_lifespan = pendulum.duration(**self.refresh_lifespan)
        if isinstance(self.refresh_lifespan, str):
            self.refresh_lifespan = duration_from_string(self.refresh_lifespan)
        ConfigurationError.require_condition(
            isinstance(self.refresh_lifespan, datetime.timedelta),
            "refresh lifespan was not configured",
        )

        if not app.config.get('DISABLE_PRAETORIAN_ERROR_HANDLER'):
            app.register_error_handler(
                PraetorianError,
                PraetorianError.build_error_handler(),
            )

        self.is_testing = app.config.get('TESTING', False)

        if not hasattr(app, 'extensions'):
            app.extensions = {}
        app.extensions['praetorian'] = self

        return app

    @classmethod
    def _validate_user_class(cls, user_class):
        """
        Validates the supplied user_class to make sure that it has the
        class methods necessary to function correctly.

        Requirements:

        - ``lookup`` method. Accepts a string parameter, returns instance
        - ``identify`` method. Accepts an identity parameter, returns instance
        """
        PraetorianError.require_condition(
            getattr(user_class, 'lookup', None) is not None,
            textwrap.dedent("""
                The user_class must have a lookup class method:
                user_class.lookup(<str>) -> <user instance>
            """),
        )
        PraetorianError.require_condition(
            getattr(user_class, 'identify', None) is not None,
            textwrap.dedent("""
                The user_class must have an identify class method:
                user_class.identify(<identity>) -> <user instance>
            """),
        )
        # TODO: Figure out how to check for an identity property
        return user_class

    def authenticate(self, username, password):
        """
        Verifies that a password matches the stored password for that username.
        If verification passes, the matching user instance is returned
        """
        PraetorianError.require_condition(
            self.user_class is not None,
            "Praetorian must be initialized before this method is available",
        )
        user = self.user_class.lookup(username)
        MissingUserError.require_condition(
            user is not None,
            'Could not find the requested user',
        )
        AuthenticationError.require_condition(
            self._verify_password(password, user.password),
            'The password is incorrect',
        )
        """
        If we are set to PRAETORIAN_HASH_AUTOUPDATE then check our hash
            and if needed, update the user.  The developer is responsible
            for using the returned user object and updating the data
            storage endpoint.

        Else, if we are set to PRAETORIAN_HASH_AUTOTEST then check out hash
            and return exception if our hash is using the wrong scheme,
            but don't modify the user.
        """
        if self.hash_autoupdate:
            self.verify_and_update(user=user, password=password)
        elif self.hash_autotest:
            self.verify_and_update(user=user)

        return user

    def _verify_password(self, raw_password, hashed_password):
        """
        Verifies that a plaintext password matches the hashed version of that
        password using the stored passlib password context
        """
        PraetorianError.require_condition(
            self.pwd_ctx is not None,
            "Praetorian must be initialized before this method is available",
        )
        return self.pwd_ctx.verify(raw_password, hashed_password)

    @deprecated('Use `hash_password` instead.')
    def encrypt_password(self, raw_password):
        """
        *NOTE* This should be deprecated as its an incorrect definition for
            what is actually being done -- we are hashing, not encrypting
        """
        return self.hash_password(raw_password)

    def error_handler(self, error):
        """
        Provides a flask error handler that is used for PraetorianErrors
        (and derived exceptions).
        """
        warnings.warn(
            """
            error_handler is deprecated.
            Use FlaskBuzz.build_error_handler instead
            """,
            warnings.DeprecationWarning,
        )
        return error.jsonify(), error.status_code, error.headers

    def _check_user(self, user):
        """
        Checks to make sure that a user is valid. First, checks that the user
        is not None. If this check fails, a MissingUserError is raised. Next,
        checks if the user has a validation method. If the method does not
        exist, the check passes. If the method exists, it is called. If the
        result of the call is not truthy, an InvalidUserError is raised
        """
        MissingUserError.require_condition(
            user is not None,
            'Could not find the requested user',
        )
        user_validate_method = getattr(user, self.user_class_validation_method,
                                       None)
        if user_validate_method is None:
            return
        InvalidUserError.require_condition(
            user_validate_method(),
            "The user is not valid or has had access revoked",
        )

    def encode_jwt_token(self,
                         user,
                         override_access_lifespan=None,
                         override_refresh_lifespan=None,
                         bypass_user_check=False,
                         is_registration_token=False,
                         is_reset_token=False,
                         **custom_claims):
        """
        Encodes user data into a jwt token that can be used for authorization
        at protected endpoints

        :param: override_access_lifespan:  Override's the instance's access
                                           lifespan to set a custom duration
                                           after which the new token's
                                           accessability will expire. May not
                                           exceed the refresh_lifespan
        :param: override_refresh_lifespan: Override's the instance's refresh
                                           lifespan to set a custom duration
                                           after which the new token's
                                           refreshability will expire.
        :param: bypass_user_check:         Override checking the user for
                                           being real/active.  Used for
                                           registration token generation.
        :param: is_registration_token:     Indicates that the token will be
                                           used only for email-based
                                           registration
        :param: custom_claims:             Additional claims that should
                                           be packed in the payload. Note that
                                           any claims supplied here must be
                                           JSON compatible types
        """
        ClaimCollisionError.require_condition(
            set(custom_claims.keys()).isdisjoint(RESERVED_CLAIMS),
            "The custom claims collide with required claims",
        )
        if not bypass_user_check:
            self._check_user(user)

        moment = pendulum.now('UTC')

        if override_refresh_lifespan is None:
            refresh_lifespan = self.refresh_lifespan
        else:
            refresh_lifespan = override_refresh_lifespan
        refresh_expiration = (moment + refresh_lifespan).int_timestamp

        if override_access_lifespan is None:
            access_lifespan = self.access_lifespan
        else:
            access_lifespan = override_access_lifespan
        access_expiration = min(
            (moment + access_lifespan).int_timestamp,
            refresh_expiration,
        )

        payload_parts = {
            'iat': moment.int_timestamp,
            'exp': access_expiration,
            'jti': str(uuid.uuid4()),
            'id': user.identity,
            'rls': ','.join(user.rolenames),
            REFRESH_EXPIRATION_CLAIM: refresh_expiration,
        }
        if is_registration_token:
            payload_parts[IS_REGISTRATION_TOKEN_CLAIM] = True
        if is_reset_token:
            payload_parts[IS_RESET_TOKEN_CLAIM] = True
        flask.current_app.logger.debug(
            "Attaching custom claims: {}".format(custom_claims), )
        payload_parts.update(custom_claims)
        return jwt.encode(
            payload_parts,
            self.encode_key,
            self.encode_algorithm,
        ).decode('utf-8')

    def encode_eternal_jwt_token(self, user, **custom_claims):
        """
        This utility function encodes a jwt token that never expires

        .. note:: This should be used sparingly since the token could become
                  a security concern if it is ever lost. If you use this
                  method, you should be sure that your application also
                  implements a blacklist so that a given token can be blocked
                  should it be lost or become a security concern
        """
        return self.encode_jwt_token(user,
                                     override_access_lifespan=VITAM_AETERNUM,
                                     override_refresh_lifespan=VITAM_AETERNUM,
                                     **custom_claims)

    def refresh_jwt_token(self, token, override_access_lifespan=None):
        """
        Creates a new token for a user if and only if the old token's access
        permission is expired but its refresh permission is not yet expired.
        The new token's refresh expiration moment is the same as the old
        token's, but the new token's access expiration is refreshed

        :param: token:                     The existing jwt token that needs to
                                           be replaced with a new, refreshed
                                           token
        :param: override_access_lifespan:  Override's the instance's access
                                           lifespan to set a custom duration
                                           after which the new token's
                                           accessability will expire. May not
                                           exceed the refresh lifespan
        """
        moment = pendulum.now('UTC')
        data = self.extract_jwt_token(token, access_type=AccessType.refresh)

        user = self.user_class.identify(data['id'])
        self._check_user(user)

        if override_access_lifespan is None:
            access_lifespan = self.access_lifespan
        else:
            access_lifespan = override_access_lifespan
        refresh_expiration = data[REFRESH_EXPIRATION_CLAIM]
        access_expiration = min(
            (moment + access_lifespan).int_timestamp,
            refresh_expiration,
        )

        custom_claims = {
            k: v
            for (k, v) in data.items() if k not in RESERVED_CLAIMS
        }
        payload_parts = {
            'iat': moment.int_timestamp,
            'exp': access_expiration,
            'jti': data['jti'],
            'id': data['id'],
            'rls': ','.join(user.rolenames),
            REFRESH_EXPIRATION_CLAIM: refresh_expiration,
        }
        payload_parts.update(custom_claims)
        return jwt.encode(
            payload_parts,
            self.encode_key,
            self.encode_algorithm,
        ).decode('utf-8')

    def extract_jwt_token(self, token, access_type=AccessType.access):
        """
        Extracts a data dictionary from a jwt token
        """
        # Note: we disable exp verification because we will do it ourselves
        with InvalidTokenHeader.handle_errors('failed to decode JWT token'):
            data = jwt.decode(
                token,
                self.encode_key,
                algorithms=self.allowed_algorithms,
                options={'verify_exp': False},
            )
        self._validate_jwt_data(data, access_type=access_type)
        return data

    def _validate_jwt_data(self, data, access_type):
        """
        Validates that the data for a jwt token is valid
        """
        MissingClaimError.require_condition(
            'jti' in data,
            'Token is missing jti claim',
        )
        BlacklistedError.require_condition(
            not self.is_blacklisted(data['jti']),
            'Token has a blacklisted jti',
        )
        MissingClaimError.require_condition(
            'id' in data,
            'Token is missing id field',
        )
        MissingClaimError.require_condition(
            'exp' in data,
            'Token is missing exp claim',
        )
        MissingClaimError.require_condition(
            REFRESH_EXPIRATION_CLAIM in data,
            'Token is missing {} claim'.format(REFRESH_EXPIRATION_CLAIM),
        )
        moment = pendulum.now('UTC').int_timestamp
        if access_type == AccessType.access:
            MisusedRegistrationToken.require_condition(
                IS_REGISTRATION_TOKEN_CLAIM not in data,
                "registration token used for access")
            MisusedResetToken.require_condition(
                IS_RESET_TOKEN_CLAIM not in data,
                "password reset token used for access")
            ExpiredAccessError.require_condition(
                moment <= data['exp'],
                'access permission has expired',
            )
        elif access_type == AccessType.refresh:
            MisusedRegistrationToken.require_condition(
                IS_REGISTRATION_TOKEN_CLAIM not in data,
                "registration token used for refresh")
            MisusedResetToken.require_condition(
                IS_RESET_TOKEN_CLAIM not in data,
                "password reset token used for refresh")
            EarlyRefreshError.require_condition(
                moment > data['exp'],
                'access permission for token has not expired. may not refresh',
            )
            ExpiredRefreshError.require_condition(
                moment <= data[REFRESH_EXPIRATION_CLAIM],
                'refresh permission for token has expired',
            )
        elif access_type == AccessType.register:
            ExpiredAccessError.require_condition(
                moment <= data['exp'],
                'register permission has expired',
            )
            InvalidRegistrationToken.require_condition(
                IS_REGISTRATION_TOKEN_CLAIM in data,
                "invalid registration token used for verification")
            MisusedResetToken.require_condition(
                IS_RESET_TOKEN_CLAIM not in data,
                "password reset token used for registration")
        elif access_type == AccessType.reset:
            MisusedRegistrationToken.require_condition(
                IS_REGISTRATION_TOKEN_CLAIM not in data,
                "registration token used for reset")
            ExpiredAccessError.require_condition(
                moment <= data['exp'],
                'reset permission has expired',
            )
            InvalidResetToken.require_condition(
                IS_RESET_TOKEN_CLAIM in data,
                "invalid reset token used for verification")

    def _unpack_header(self, headers):
        """
        Unpacks a jwt token from a request header
        """
        jwt_header = headers.get(self.header_name)
        MissingTokenHeader.require_condition(
            jwt_header is not None,
            "JWT token not found in headers under '{}'",
            self.header_name,
        )

        match = re.match(self.header_type + r'\s*([\w\.-]+)', jwt_header)
        InvalidTokenHeader.require_condition(
            match is not None,
            "JWT header structure is invalid",
        )
        token = match.group(1)
        return token

    def read_token_from_header(self):
        """
        Unpacks a jwt token from the current flask request
        """
        return self._unpack_header(flask.request.headers)

    def pack_header_for_user(self,
                             user,
                             override_access_lifespan=None,
                             override_refresh_lifespan=None,
                             **custom_claims):
        """
        Encodes a jwt token and packages it into a header dict for a given user

        :param: user:                      The user to package the header for
        :param: override_access_lifespan:  Override's the instance's access
                                           lifespan to set a custom duration
                                           after which the new token's
                                           accessability will expire. May not
                                           exceed the refresh_lifespan
        :param: override_refresh_lifespan: Override's the instance's refresh
                                           lifespan to set a custom duration
                                           after which the new token's
                                           refreshability will expire.
        :param: custom_claims:             Additional claims that should
                                           be packed in the payload. Note that
                                           any claims supplied here must be
                                           JSON compatible types
        """
        token = self.encode_jwt_token(
            user,
            override_access_lifespan=override_access_lifespan,
            override_refresh_lifespan=override_refresh_lifespan,
            **custom_claims)
        return {self.header_name: self.header_type + ' ' + token}

    def send_registration_email(self,
                                email,
                                user=None,
                                template=None,
                                confirmation_sender=None,
                                confirmation_uri=None,
                                subject=None,
                                override_access_lifespan=None):
        """
        Sends a registration email to a new user, containing a time expiring
            token usable for validation.  This requires your application
            is initiliazed with a `mail` extension, which supports
            Flask-Mail's `Message()` object and a `send()` method.

        Returns a dict containing the information sent, along with the
            `result` from mail send.
        :param: user:                     The user object to tie claim to
                                          (username, id, email, etc)
        :param: template:                 HTML Template for confirmation email.
                                          If not provided, a stock entry is
                                          used
        :param: confirmation_sender:      The sender that shoudl be attached
                                          to the confirmation email. Overrides
                                          the PRAETORIAN_CONFIRMRATION_SENDER
                                          config setting
        :param: confirmation_uri:         The uri that should be visited to
                                          complete email registration. Should
                                          usually be a uri to a frontend or
                                          external service that calls a
                                          'finalize' method in the api to
                                          complete registration. Will override
                                          the PRAETORIAN_CONFIRMATION_URI
                                          config setting
        :param: subject:                  The registration email subject.
                                          Will override the
                                          PRAETORIAN_CONFIRMATION_SUBJECT
                                          config setting.
        :param: override_access_lifespan: Overrides the JWT_ACCESS_LIFESPAN
                                          to set an access lifespan for the
                                          registration token.
        """
        if subject is None:
            subject = self.confirmation_subject

        if confirmation_uri is None:
            confirmation_uri = self.confirmation_uri

        sender = confirmation_sender or self.confirmation_sender

        flask.current_app.logger.debug(
            "Generating token with lifespan: {}".format(
                override_access_lifespan))
        custom_token = self.encode_jwt_token(
            user,
            override_access_lifespan=override_access_lifespan,
            bypass_user_check=True,
            is_registration_token=True,
        )

        return self.send_token_email(email,
                                     user,
                                     template,
                                     confirmation_sender,
                                     confirmation_uri,
                                     subject,
                                     custom_token=custom_token,
                                     sender=sender)

    def send_reset_email(self,
                         email,
                         template=None,
                         reset_sender=None,
                         reset_uri=None,
                         subject=None,
                         override_access_lifespan=None):
        """
        Sends a password reset email to a user, containing a time expiring
            token usable for validation.  This requires your application
            is initiliazed with a `mail` extension, which supports
            Flask-Mail's `Message()` object and a `send()` method.

        Returns a dict containing the information sent, along with the
            `result` from mail send.
        :param: email:                    The email address to attempt to
                                          send to
        :param: template:                 HTML Template for reset email.
                                          If not provided, a stock entry is
                                          used
        :param: confirmation_sender:      The sender that shoudl be attached
                                          to the reset email. Overrides
                                          the PRAETORIAN_RESET_SENDER
                                          config setting
        :param: confirmation_uri:         The uri that should be visited to
                                          complete password reset. Should
                                          usually be a uri to a frontend or
                                          external service that calls the
                                          'validate_reset_token()' method in
                                          the api to complete reset. Will
                                          override the PRAETORIAN_RESET_URI
                                          config setting
        :param: subject:                  The reset email subject.
                                          Will override the
                                          PRAETORIAN_RESET_SUBJECT
                                          config setting.
        :param: override_access_lifespan: Overrides the JWT_ACCESS_LIFESPAN
                                          to set an access lifespan for the
                                          registration token.
        """
        if subject is None:
            subject = self.reset_subject

        if reset_uri is None:
            reset_uri = self.reset_uri

        sender = reset_sender or self.reset_sender

        user = self.user_class.lookup(email)
        MissingUserError.require_condition(
            user is not None,
            'Could not find the requested user',
        )

        flask.current_app.logger.debug(
            "Generating token with lifespan: {}".format(
                override_access_lifespan))
        custom_token = self.encode_jwt_token(
            user,
            override_access_lifespan=override_access_lifespan,
            bypass_user_check=False,
            is_reset_token=True,
        )

        return self.send_token_email(user.email,
                                     user,
                                     template,
                                     reset_sender,
                                     reset_uri,
                                     subject,
                                     custom_token=custom_token,
                                     sender=sender)

    def send_token_email(self,
                         email,
                         user=None,
                         template=None,
                         action_sender=None,
                         action_uri=None,
                         subject=None,
                         override_access_lifespan=None,
                         custom_token=None,
                         sender='no-reply@praetorian'):
        """
        Sends an email to a user, containing a time expiring
            token usable for several actions.  This requires
            your application is initiliazed with a `mail` extension,
            which supports Flask-Mail's `Message()` object and
            a `send()` method.

        Returns a dict containing the information sent, along with the
            `result` from mail send.
        :param: email:                    The email address to use
                                          (username, id, email, etc)
        :param: user:                     The user object to tie claim to
                                          (username, id, email, etc)
        :param: template:                 HTML Template for confirmation email.
                                          If not provided, a stock entry is
                                          used
        :param: action_sender:            The sender that should be attached
                                          to the confirmation email.
        :param: action_uri:               The uri that should be visited to
                                          complete the token action.
        :param: subject:                  The email subject.
        :param: override_access_lifespan: Overrides the JWT_ACCESS_LIFESPAN
                                          to set an access lifespan for the
                                          registration token.
        """
        notification = {
            'result': None,
            'message': None,
            'user': str(user),
            'email': email,
            'token': custom_token,
            'subject': subject,
            'confirmation_uri': action_uri,  # backwards compatibility
            'action_uri': action_uri,
        }

        PraetorianError.require_condition(
            action_sender,
            "A sender is required to send confirmation email",
        )

        PraetorianError.require_condition(
            custom_token,
            "A custom_token is required to send notification email",
        )

        if template is None:
            with open(self.confirmation_template) as fh:
                template = fh.read()

        with PraetorianError.handle_errors('fail sending email'):
            flask.current_app.logger.debug(
                "NOTIFICATION: {}".format(notification))

            jinja_tmpl = jinja2.Template(template)
            notification['message'] = jinja_tmpl.render(notification).strip()

            msg = Message(html=notification['message'],
                          sender=action_sender,
                          subject=notification['subject'],
                          recipients=[notification['email']])

            flask.current_app.logger.debug("Sending email to {}".format(email))
            notification['result'] = flask.current_app.extensions['mail'].send(
                msg)

        return notification

    def get_user_from_registration_token(self, token):
        """
        Gets a user based on the registration token that is supplied. Verifies
        that the token is a regisration token and that the user can be properly
        retrieved
        """
        data = self.extract_jwt_token(token, access_type=AccessType.register)
        flask.current_app.logger.debug("DATA: {}".format(data))
        user_id = data.get('id')
        PraetorianError.require_condition(
            user_id is not None,
            "Could not fetch an id from the registration token",
        )
        user = self.user_class.identify(user_id)
        PraetorianError.require_condition(
            user is not None,
            "Could not identify the user from the registration token",
        )
        return user

    def validate_reset_token(self, token):
        """
        Validates a password reset request based on the reset token
        that is supplied. Verifies that the token is a reset token
        and that the user can be properly retrieved
        """
        data = self.extract_jwt_token(token, access_type=AccessType.reset)
        user_id = data.get('id')
        PraetorianError.require_condition(
            user_id is not None,
            "Could not fetch an id from the reset token",
        )
        user = self.user_class.identify(user_id)
        PraetorianError.require_condition(
            user is not None,
            "Could not identify the user from the reset token",
        )
        return user

    def hash_password(self, raw_password):
        """
        Hashes a plaintext password using the stored passlib password context
        """
        PraetorianError.require_condition(
            self.pwd_ctx is not None,
            "Praetorian must be initialized before this method is available",
        )
        """
        `scheme` is now set with self.pwd_ctx.update(default=scheme) due
            to the depreciation in upcoming passlib 2.0.
         zillions of warnings suck.
        """
        return self.pwd_ctx.hash(raw_password)

    def verify_and_update(self, user=None, password=None):
        """
        Validate a password hash contained in the user object is
             hashed with the defined hash scheme (PRAETORIAN_HASH_SCHEME).
        If not, raise an Exception of `LegacySchema`, unless the
             `password` arguement is provided, in which case an attempt
             to call `user.save()` will be made, updating the hashed
             password to the currently desired hash scheme
             (PRAETORIAN_HASH_SCHEME).

        :param: user:     The user object to tie claim to
                              (username, id, email, etc). *MUST*
                              include the hashed password field,
                              defined as `user.password`
        :param: password: The user's provide password from login.
                          If present, this is used to validate
                              and then attempt to update with the
                              new PRAETORIAN_HASH_SCHEME scheme.
        """
        if self.pwd_ctx.needs_update(user.password):
            if password:
                (rv, updated) = self.pwd_ctx.verify_and_update(
                    password,
                    user.password,
                )
                AuthenticationError.require_condition(
                    rv,
                    "Could not verify password",
                )
                user.password = updated
            else:
                used_hash = self.pwd_ctx.identify(user.password)
                desired_hash = self.hash_scheme
                raise LegacyScheme(
                    "Hash using non-current scheme '{}'.  Use '{}' instead.".
                    format(used_hash, desired_hash))

        return user
Пример #40
0
import traceback
from fastapi.security import OAuth2PasswordBearer
from passlib.context import CryptContext
from functools import wraps
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from fastapi_mako import FastAPIMako
import databases
import aiohttp
from config import DB_URL, CLIENT_ID, CLIENT_SECRET, REDIRECT_URI

oauth2_scheme = OAuth2PasswordBearer(tokenUrl='/auth')
pwd_context = CryptContext(schemes=['bcrypt'], deprecated='auto')

mako = FastAPIMako()


db_engine = create_engine(
    DB_URL
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=db_engine)
Base = declarative_base()

# AioDataBase = databases.Database(DB_URL.replace('+pymysql', ''))

class AioDataBase():
    async def __aenter__(self):
        db = databases.Database(DB_URL.replace('+pymysql', ''))
        await db.connect()
        self.db = db
Пример #41
0
from fastapi import Body, APIRouter
from fastapi.encoders import jsonable_encoder
from fastapi.security import HTTPBasicCredentials
from passlib.context import CryptContext

from database.database import admin_collection
from auth.jwt_handler import signJWT
from database.database import add_admin
from models.admin import AdminModel

router = APIRouter()

hash_helper = CryptContext(schemes=["bcrypt"])


@router.post("/login")
async def admin_login(admin_credentials: HTTPBasicCredentials = Body(...)):
    # NEW CODE
    admin_user = await admin_collection.find_one(
        {"email": admin_credentials.username}, {"_id": 0})
    if (admin_user):
        password = hash_helper.verify(admin_credentials.password,
                                      admin_user["password"])
        if (password):
            return signJWT(admin_credentials.username)

        return "Incorrect email or password"

    return "Incorrect email or password"

Пример #42
0
class DatabaseUserService:
    def __init__(self, session, *, ratelimiters=None, metrics):
        if ratelimiters is None:
            ratelimiters = {}
        ratelimiters = collections.defaultdict(DummyRateLimiter, ratelimiters)

        self.db = session
        self.ratelimiters = ratelimiters
        self.hasher = CryptContext(
            schemes=[
                "argon2",
                "bcrypt_sha256",
                "bcrypt",
                "django_bcrypt",
                "unix_disabled",
            ],
            deprecated=["auto"],
            truncate_error=True,
            # Argon 2 Configuration
            argon2__memory_cost=1024,
            argon2__parallelism=6,
            argon2__time_cost=6,
        )
        self._metrics = metrics

    @functools.lru_cache()
    def get_user(self, userid):
        # TODO: We probably don't actually want to just return the database
        #       object here.
        # TODO: We need some sort of Anonymous User.
        return self.db.query(User).get(userid)

    @functools.lru_cache()
    def get_user_by_username(self, username):
        user_id = self.find_userid(username)
        return None if user_id is None else self.get_user(user_id)

    @functools.lru_cache()
    def get_user_by_email(self, email):
        user_id = self.find_userid_by_email(email)
        return None if user_id is None else self.get_user(user_id)

    @functools.lru_cache()
    def find_userid(self, username):
        try:
            user = self.db.query(
                User.id).filter(User.username == username).one()
        except NoResultFound:
            return

        return user.id

    @functools.lru_cache()
    def find_userid_by_email(self, email):
        try:
            # flake8: noqa
            user_id = (self.db.query(
                Email.user_id).filter(Email.email == email).one())[0]
        except NoResultFound:
            return

        return user_id

    def check_password(self, userid, password, *, tags=None):
        tags = tags if tags is not None else []

        self._metrics.increment("warehouse.authentication.start", tags=tags)

        # The very first thing we want to do is check to see if we've hit our
        # global rate limit or not, assuming that we've been configured with a
        # global rate limiter anyways.
        if not self.ratelimiters["global"].test():
            logger.warning("Global failed login threshold reached.")
            self._metrics.increment(
                "warehouse.authentication.ratelimited",
                tags=tags + ["ratelimiter:global"],
            )
            raise TooManyFailedLogins(
                resets_in=self.ratelimiters["global"].resets_in())

        user = self.get_user(userid)
        if user is not None:
            # Now, check to make sure that we haven't hitten a rate limit on a
            # per user basis.
            if not self.ratelimiters["user"].test(user.id):
                self._metrics.increment(
                    "warehouse.authentication.ratelimited",
                    tags=tags + ["ratelimiter:user"],
                )
                raise TooManyFailedLogins(
                    resets_in=self.ratelimiters["user"].resets_in(user.id))

            # Actually check our hash, optionally getting a new hash for it if
            # we should upgrade our saved hashed.
            ok, new_hash = self.hasher.verify_and_update(
                password, user.password)

            # First, check to see if the password that we were given was OK.
            if ok:
                # Then, if the password was OK check to see if we've been given
                # a new password hash from the hasher, if so we'll want to save
                # that hash.
                if new_hash:
                    user.password = new_hash

                self._metrics.increment("warehouse.authentication.ok",
                                        tags=tags)

                return True
            else:
                self._metrics.increment(
                    "warehouse.authentication.failure",
                    tags=tags + ["failure_reason:password"],
                )
        else:
            self._metrics.increment("warehouse.authentication.failure",
                                    tags=tags + ["failure_reason:user"])

        # If we've gotten here, then we'll want to record a failed login in our
        # rate limiting before returning False to indicate a failed password
        # verification.
        if user is not None:
            self.ratelimiters["user"].hit(user.id)
        self.ratelimiters["global"].hit()

        return False

    def create_user(self,
                    username,
                    name,
                    password,
                    is_active=False,
                    is_superuser=False):

        user = User(
            username=username,
            name=name,
            password=self.hasher.hash(password),
            is_active=is_active,
            is_superuser=is_superuser,
        )
        self.db.add(user)
        self.db.flush()  # flush the db now so user.id is available

        return user

    def add_email(self, user_id, email_address, primary=None, verified=False):
        user = self.get_user(user_id)

        # If primary is None, then we're going to auto detect whether this should be the
        # primary address or not. The basic rule is that if the user doesn't already
        # have a primary address, then the address we're adding now is going to be
        # set to their primary.
        if primary is None:
            primary = True if user.primary_email is None else False

        email = Email(email=email_address,
                      user=user,
                      primary=primary,
                      verified=verified)
        self.db.add(email)
        self.db.flush()  # flush the db now so email.id is available

        return email

    def update_user(self, user_id, **changes):
        user = self.get_user(user_id)
        for attr, value in changes.items():
            if attr == PASSWORD_FIELD:
                value = self.hasher.hash(value)
            setattr(user, attr, value)

        # If we've given the user a new password, then we also want to unset the
        # reason for disable... because a new password means no more disabled
        # user.
        if PASSWORD_FIELD in changes:
            user.disabled_for = None

        return user

    def disable_password(self, user_id, reason=None):
        user = self.get_user(user_id)
        user.password = self.hasher.disable()
        user.disabled_for = reason

    def is_disabled(self, user_id):
        user = self.get_user(user_id)

        # User is not disabled.
        if self.hasher.is_enabled(user.password):
            return (False, None)
        # User is disabled.
        else:
            return (True, user.disabled_for)
Пример #43
0
                          BadSignature, SignatureExpired)
import os
# Password/hash management
from passlib.context import CryptContext
# SQLAlchemy extension to map classes to database tables
from sqlalchemy.ext.declarative import declarative_base
# SQLAlchemy database table column type and related column data types
from sqlalchemy import (Column, Enum, ForeignKey, Integer, String, Text)
# For foreign key relationships and mapper
from sqlalchemy.orm import relationship
# SQLAlchemy module to connect to underlying database
from sqlalchemy import create_engine

# Globals
# Hashing
PasswordContext = CryptContext(schemes=['bcrypt'], deprecated='auto')
# Random Secret Key
# Note - drawback of this approach is everything based on it becomes invalid upon
# system restart
# Choose 32 as using SHA256 so want 256 bits of entropy (32 * 8)
SecretKey = os.urandom(32)
# SQLAlchemy setup - this is the base class that our model classes will be
# derived from
Base = declarative_base()


# To do - Ideally create another class to represent providers (Google, Facebook,
# Microsoft, ...); a user can then optionally be linked to one or more providers
class User(Base):
    # DB table name
    __tablename__ = 'user'
Пример #44
0
import datetime
import hashlib
import hmac
import uuid

from passlib.context import CryptContext

import jwt

from .dict_helpers import inverted

PASSLIB_CONTEXT = CryptContext(
    schemes=["pbkdf2_sha256"],
    default="pbkdf2_sha256",
    all__vary_rounds=0.1,

    # Lower number of rounds because we still want tests to finish in seconds: :)
    pbkdf2_sha256__default_rounds=1000,
)


class JWTError(RuntimeError):
    pass


def _decode_token(token, secret_key):
    """
    Returns the payload decoded from the given token using the given secret key.
    """
    try:
        payload = jwt.decode(
Пример #45
0
class Client:
    """ Authentication Client """
    def __init__(self):
        self.db = DBHandler(db='yubiauth')
        self.pwd_context = CryptContext(**settings['CRYPT_CONTEXT'])
        if settings['USE_NATIVE_YKVAL']:
            # Native verify
            from .ykval import Validator
            self.ykval_client = Validator()
        else:
            # Using yubico_client to verify against remote server
            from yubico_client import Yubico
            self.ykval_client = Yubico(settings['YKVAL_CLIENT_ID'],
                                       settings['YKVAL_CLIENT_SECRET'],
                                       api_urls=settings['YKVAL_SERVERS'])

    def _get_user_info(self, username):
        """
        Get user from DB

        Args:
            username

        Returns:
            dictionary of user data

        Raises:
            AuthFail if user does not exist
        """
        user = self.db.get_user(username)
        if not user:
            raise YKAuthError('UNKNOWN_USER')
        logger.debug('[%s] Found user: %s', username, user)
        return user

    def _check_token(self, user, token_id):
        """
        Check Token association with user

        Args:
            user: User data dict as recieved from _get_user_info()
            token_id: Token prefix (aka. publicname)

        Returns:
            None

        Raises:
            AuthFail if token is not associated with the user
            AithFail if token is disabled
        """
        token = self.db.get_token(user['users_id'], token_id)
        if not token:
            logger.error('[%s] Token %s is not associated with user',
                         user['users_name'], token_id)
            raise YKAuthError('INVALID_TOKEN')
        logger.debug('[%s] Found token: %s', user['users_name'], token)
        if not token.get('yubikeys_enabled'):
            logger.error('[%s] Token %s is disabled for %s',
                         user['users_name'], token_id, user['users_name'])
            raise YKAuthError('DISABLED_TOKEN')

    def _validate_password(self, user, password):
        """
        Validate password against the hash in SQL
        """
        valid, new_hash = self.pwd_context.verify_and_update(
            str(password), user['users_auth'])
        if not valid:
            logger.error('[%(users_name)s] Invalid password', user)
            raise YKAuthError('BAD_PASSWORD')
        if new_hash:
            # TODO: update user's hash with new_hash
            logger.warning('[%(users_name)s] User password hash needs update',
                           user)
        return True

    def authenticate(self, username, password, otp):
        """
        Yubistack user authentication

        Args:
            username: Username of the user
            password: Password/PIN of the user
            otp: Yubikey one time password

        Returns:
            dict of authentication data

        Authentication process:
            1. Check if token is enabled
            2. Check if token is associated with the user & enabled
            3. Validate users password
            4. Validate OTP (YKVal)
        """
        token_id = otp[:-TOKEN_LEN]
        # STEP 1: Check if token is enabled
        user = self._get_user_info(username)
        # STEP 2: Check if token is associated with the user & enabled
        self._check_token(user, token_id)
        # STEP 3: Validate users password
        self._validate_password(user, password)
        # STEP 4: Validate OTP
        self.ykval_client.verify(otp)
        return True
Пример #46
0
from anom import Model, props
from markdown import markdown
from passlib.context import CryptContext
from slugify import slugify


# [START password-property]
ctx = CryptContext(schemes=["sha256_crypt"])


class Password(props.String):
    def validate(self, value):
        return ctx.hash(super().validate(value))
# [END password-property]


# [START user-model]
class User(Model, poly=True):
    username = props.String(indexed=True)
    password = Password()
    created_at = props.DateTime(indexed=True, auto_now_add=True)
    updated_at = props.DateTime(indexed=True, auto_now=True)

    @classmethod
    def login(cls, username, password):
        user = User.query().where(User.username == username).get()
        if user is None:
            return None

        if not ctx.verify(password, user.password):
            return None
Пример #47
0
 def init_app(self, app):
     """Lazy initializer which takes an `app` and sets up the internal context."""
     self.pwd_context = CryptContext(
         schemes=app.config['PASSLIB_SCHEMES'], )
Пример #48
0
"""Extensions registry

All extensions here are used as singletons and
initialized in application factory
"""
from flask_sqlalchemy import SQLAlchemy
from passlib.context import CryptContext
from flask_jwt_extended import JWTManager
from flask_marshmallow import Marshmallow
from flask_migrate import Migrate

from neuroapi.commons.apispec import APISpecExt

db = SQLAlchemy()
jwt = JWTManager()
ma = Marshmallow()
migrate = Migrate()
apispec = APISpecExt()
pwd_context = CryptContext(schemes=['pbkdf2_sha256'], deprecated='auto')
Пример #49
0
import logging

from passlib.context import CryptContext

import openerp
from openerp.osv import fields, osv

_logger = logging.getLogger(__name__)

default_crypt_context = CryptContext(
    # kdf which can be verified by the context. The default encryption kdf is
    # the first of the list
    ['pbkdf2_sha512', 'md5_crypt'],
    # deprecated algorithms are still verified as usual, but ``needs_update``
    # will indicate that the stored hash should be replaced by a more recent
    # algorithm. Passlib 1.6 supports an `auto` value which deprecates any
    # algorithm but the default, but Debian only provides 1.5 so...
    deprecated=['md5_crypt'],
)


class res_users(osv.osv):
    _inherit = "res.users"

    def init(self, cr):
        _logger.info(
            "Hashing passwords, may be slow for databases with many users...")
        cr.execute("SELECT id, password FROM res_users"
                   " WHERE password IS NOT NULL"
                   "   AND password != ''")
        for uid, pwd in cr.fetchall():
Пример #50
0
    def __init__(self):
        """ Init Config instance """
        self.cache = CacheClass()

        if self.config_check_enabled:
            self._config_check()

        # define directories
        data_dir = os.path.normpath(self.data_dir)
        self.data_dir = data_dir

        # Try to decode certain names which allow unicode
        self._decode()

        # After that, pre-compile some regexes
        self.cache.item_dict_regex = re.compile(self.item_dict_regex,
                                                re.UNICODE)
        self.cache.item_group_regex = re.compile(self.item_group_regex,
                                                 re.UNICODE)

        # the ..._regexact versions only match if nothing is left (exact match)
        self.cache.item_dict_regexact = re.compile(
            '^{0}$'.format(self.item_dict_regex), re.UNICODE)
        self.cache.item_group_regexact = re.compile(
            '^{0}$'.format(self.item_group_regex), re.UNICODE)

        # compiled functions ACL
        self.cache.acl_functions = AccessControlList(
            [self.acl_functions], valid=self.acl_rights_functions)

        plugins._loadPluginModule(self)

        if self.user_defaults[TIMEZONE] is None:
            self.user_defaults[TIMEZONE] = self.timezone_default
        if self.user_defaults[THEME_NAME] is None:
            self.user_defaults[THEME_NAME] = self.theme_default
        # Note: do not assign user_defaults['locale'] = locale_default
        # to give browser language detection a chance.
        try:
            self.language_default = parse_locale(self.locale_default)[0]
        except ValueError:
            raise error.ConfigurationError(
                "Invalid locale_default value (give something like 'en_US').")

        # post process
        self.auth_can_logout = []
        self.auth_login_inputs = []
        found_names = []
        for auth in self.auth:
            if not auth.name:
                raise error.ConfigurationError(
                    "Auth methods must have a name.")
            if auth.name in found_names:
                raise error.ConfigurationError(
                    "Auth method names must be unique.")
            found_names.append(auth.name)
            if auth.logout_possible and auth.name:
                self.auth_can_logout.append(auth.name)
            for input in auth.login_inputs:
                if input not in self.auth_login_inputs:
                    self.auth_login_inputs.append(input)
        self.auth_have_login = len(self.auth_login_inputs) > 0
        self.auth_methods = found_names

        # internal dict for plugin 'modules' lists
        self._site_plugin_lists = {}

        # check if mail is possible and set flag:
        self.mail_enabled = (self.mail_smarthost is not None or
                             self.mail_sendmail is not None) and self.mail_from
        self.mail_enabled = self.mail_enabled and True or False

        if self.namespace_mapping is None:
            raise error.ConfigurationError(
                "No storage configuration specified! You need to define a namespace_mapping."
            )

        if self.backend_mapping is None:
            raise error.ConfigurationError(
                "No storage configuration specified! You need to define a backend_mapping."
            )

        if self.acl_mapping is None:
            raise error.ConfigurationError(
                "No acl configuration specified! You need to define a acl_mapping."
            )

        if self.secrets is None:  # admin did not setup a real secret
            raise error.ConfigurationError(
                "No secret configured! You need to set secrets = 'somelongsecretstring' in your wiki config."
            )

        if self.interwikiname is None:  # admin did not setup a real interwikiname
            raise error.ConfigurationError(
                "No interwikiname configured! "
                "You need to set interwikiname = u'YourUniqueStableInterwikiName' in your wiki config."
            )

        secret_key_names = [
            'security/ticket',
        ]

        secret_min_length = 10
        if isinstance(self.secrets, str):
            if len(self.secrets) < secret_min_length:
                raise error.ConfigurationError(
                    "The secrets = '...' wiki config setting is a way too short string "
                    "(minimum length is {0} chars)!".format(secret_min_length))
            # for lazy people: set all required secrets to same value
            secrets = {}
            for key in secret_key_names:
                secrets[key] = self.secrets
            self.secrets = secrets

        # we check if we have all secrets we need and that they have minimum length
        for secret_key_name in secret_key_names:
            try:
                secret = self.secrets[secret_key_name]
                if len(secret) < secret_min_length:
                    raise ValueError
            except (KeyError, ValueError):
                raise error.ConfigurationError(
                    "You must set a (at least {0} chars long) secret string for secrets['{1}']!"
                    .format(secret_min_length, secret_key_name))

        from passlib.context import CryptContext
        try:
            self.cache.pwd_context = CryptContext(**self.passlib_crypt_context)
        except ValueError as err:
            raise error.ConfigurationError(
                "passlib_crypt_context configuration is invalid [{0}].".format(
                    err))
Пример #51
0
""" Various utils """
import uuid
from flask import request, abort, jsonify
from passlib.context import CryptContext
import logging

PASSWORD_CONTEXT = CryptContext(schemes=["pbkdf2_sha256"],
                                default="pbkdf2_sha256",
                                pbkdf2_sha256__default_rounds=300)


class RequestHandler(object):
    def __init__(self):
        self.logger = logging.getLogger(type(self).__name__)
        self.set_context(self.get_request_attr("context"))

    def set_context(self, context):
        self.context = context

    def encrypt_password(self, password):
        return PASSWORD_CONTEXT.encrypt(password)

    def check_encrypted_password(self, password, hashed):
        return PASSWORD_CONTEXT.verify(password, hashed)

    def ensure_request_attr_container(self):
        if (not hasattr(request, "flaskdms")):
            setattr(request, "flaskdms", {})

    def set_request_attr(self, key, value):
        self.ensure_request_attr_container()
Пример #52
0
from passlib.context import CryptContext
from models.jwtUser import JWTUser
from datetime import datetime, timedelta
from utils.const import JWT_EXPIRATION_TIME_MINUTES, JWT_ALGORITHM, JWT_SECRET_KEY
import jwt
from fastapi import Depends
from fastapi.security import OAuth2PasswordBearer
import time
import service.JWTUserService as JWTUserService

passwordContext = CryptContext(schemes=["bcrypt"])
oauthSchema = OAuth2PasswordBearer(tokenUrl="/token")


def getHashedPassword(password):
    return passwordContext.hash(password)


def verifyPassword(password, hashPassword):
    try:
        return passwordContext.verify(password, hashPassword)
    except Exception as e:
        return False


# Authenticate username and password to give JWT token
async def authenticateUser(user: JWTUser):
    validUser = await JWTUserService.getUserByEmail(user)

    if validUser != {} and verifyPassword(user.password,
                                          validUser["password"]):
Пример #53
0
def get_oauth_password():
    pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
    return api_auth.get_password_hash(pwd_context, DOCS_PASSWORD)
Пример #54
0
try:
    import configparser as ConfigParser
except ImportError:
    import ConfigParser

import errno
import logging
import optparse
import os
import sys
import odoo
from .. import release, conf, loglevels
from . import appdirs

from passlib.context import CryptContext
crypt_context = CryptContext(schemes=['pbkdf2_sha512', 'plaintext'],
                             deprecated=['plaintext'])

class MyOption (optparse.Option, object):
    """ optparse Option with two additional attributes.

    The list of command line options (getopt.Option) is used to create the
    list of the configuration file options. When reading the file, and then
    reading the command line arguments, we don't want optparse.parse results
    to override the configuration file values. But if we provide default
    values to optparse, optparse will return them and we can't know if they
    were really provided by the user or not. A solution is to not use
    optparse's default attribute, but use a custom one (that will be copied
    to create the default values of the configuration file).

    """
    def __init__(self, *opts, **attrs):
Пример #55
0
def hash_password(password):
    crypt_context = CryptContext(schemes=['bcrypt_sha256'])
    return crypt_context.hash(password)
Пример #56
0
DEFAULT_ENTROPY = 32

# We use a passlib CryptContext to define acceptable hashing algorithms for
# passwords. This allows us to easily
#
# - migrate to new hashing algorithms
# - update the number of rounds used when hashing passwords
#
# simply by using the verify_and_update method of the CryptContext object. See
# the passlib documentation on hash migration for more details:
#
#   https://pythonhosted.org/passlib/lib/passlib.context-tutorial.html#context-migration-example
#
password_context = CryptContext(schemes=["bcrypt"],
                                bcrypt__ident="2b",
                                bcrypt__min_rounds=12)


def derive_key(key_material, salt, info):
    """
    Derive a fixed-size (64-byte) key for use in cryptographic operations.

    The key is derived using HKDF with the SHA-512 hash function. See
    https://tools.ietf.org/html/rfc5869.

    :type key_material: str or bytes
    :type salt: bytes
    :type info: bytes
    """
    if not isinstance(key_material, bytes):
Пример #57
0
def verify_password(password, pwd_hash):
    crypt_context = CryptContext(schemes=['bcrypt_sha256'])
    return crypt_context.verify(password, pwd_hash)
Пример #58
0
class User(DatastoreModel):
    """Neptune users."""

    # tl;dr: values forced to lower case before storage here AND in
    # uniqueness_key()!
    #
    # Emails are stored in two places and must match across them to make
    # sure we don't get duplicate users: in User.email and the key name of
    # the corresponding Unique entity (see uniqueness_key()). And because
    # email addresses are effectively case insensitive while our databases
    # are case sensistive, force them all to lower case before storage.
    # Because people _do_ vary their capitalization between sessions. See
    # #387.
    email = ndb.StringProperty(required=True,
                               validator=lambda prop, value: value.lower())
    name = ndb.StringProperty()
    role = ndb.StringProperty()
    phone_number = ndb.StringProperty()
    hashed_password = ndb.StringProperty()
    google_id = ndb.StringProperty()
    # user type can be: super_admin, program_admin, user, public
    user_type = ndb.StringProperty(default='user')
    # notification option has two possible keys:
    # {
    #   "email": ("yes"|"no"),
    #   "sms":  ("yes"|"no")
    # }
    notification_option_json = ndb.TextProperty(default=r'{}')
    owned_organizations = ndb.StringProperty(repeated=True)
    assc_organizations = ndb.StringProperty(repeated=True)
    owned_programs = ndb.StringProperty(repeated=True)
    owned_projects = ndb.StringProperty(repeated=True)
    assc_projects = ndb.StringProperty(repeated=True)
    owned_data_tables = ndb.StringProperty(repeated=True)
    owned_data_requests = ndb.StringProperty(repeated=True)
    last_login = ndb.DateTimeProperty()

    # App Engine can only run pure-python external libraries, and so we can't get
    # a native (C-based) implementation of bcrypt. Pure python implementations are
    # so slow that [the feasible number of rounds is insecure][1]. This uses the
    # [algorithm recommended by passlib][2].
    # [1]: http://stackoverflow.com/questions/7027196/how-can-i-use-bcrypt-scrypt-on-appengine-for-python
    # [2]: https://pythonhosted.org/passlib/new_app_quickstart.html#sha512-crypt
    password_hashing_context = CryptContext(
        schemes=['sha512_crypt', 'sha256_crypt'],
        default='sha512_crypt',
        all__vary_rounds=0.1,
        # Can change hashing rounds here. 656,000 is the default.
        # sha512_crypt__default_rounds=656000,
        # sha256_crypt__default_rounds=656000,
    )

    json_props = ['notification_option_json']

    @property
    def super_admin(self):
        return self.user_type == 'super_admin'

    @property
    def non_admin(self):
        # Matches either value while we transition. See #985.
        return self.user_type in ('org_admin', 'user')

    @property
    def notification_option(self):
        return (json.loads(self.notification_option_json)
                if self.notification_option_json else None)

    @notification_option.setter
    def notification_option(self, obj):
        self.notification_option_json = json.dumps(obj)
        return obj

    @classmethod
    def create(klass, **kwargs):
        # Create Unique entity based on email, allowing strongly consistent
        # prevention of duplicates.
        is_unique_email = Unique.create(User.uniqueness_key(kwargs['email']))
        if not is_unique_email:
            raise DuplicateUser(
                "There is already a user with email {}.".format(
                    kwargs['email']))

        return super(klass, klass).create(**kwargs)

    @classmethod
    def create_public(klass):
        return super(klass, klass).create(
            id='public',
            name='public',
            email='public',
            user_type='public',
        )

    @classmethod
    def uniqueness_key(klass, email):
        # See #387.
        return u'User.email:{}'.format(email.lower())

    @classmethod
    def email_exists(klass, email):
        """Test if this email has been registered, idempotent."""
        return Unique.get_by_id(User.uniqueness_key(email)) is not None

    @classmethod
    def get_by_auth(klass, auth_type, auth_id):
        # All stored emails are in lower case. If we hope to find them, need
        # to lower case the search param. See #387.
        if auth_type == 'email':
            auth_id = auth_id.lower()

        matches = User.get(order='created', **{auth_type: auth_id})

        if len(matches) == 0:
            return None
        elif len(matches) == 1:
            return matches[0]
        elif len(matches) > 1:
            logging.error(
                u"More than one user matches auth info: {}, {}.".format(
                    auth_type, auth_id))

            # We'll let the function pass on and take the first of multiple
            # duplicate users, which will be the earliest-created one.
            return matches[0]

    @classmethod
    def property_types(klass):
        """Overrides DatastoreModel. Prevents hashed_password from being set."""
        props = super(klass, klass).property_types()
        props.pop('hashed_password', None)
        return props

    @classmethod
    def example_params(klass):
        name = ''.join(random.choice(string.ascii_uppercase) for c in range(3))
        return {
            'name': name,
            'email': name + '@example.com',
            'phone_number': '+1 (555) 555-5555',
            'hashed_password': '******',
            'user_type':
            random.choice(['user', 'program_admin', 'super_admin']),
        }

    @classmethod
    def hash_password(klass, password):
        if re.match(config.password_pattern, password) is None:
            raise BadPassword(u'Bad password: {}'.format(password))
        return klass.password_hashing_context.encrypt(password)

    @classmethod
    def verify_password(klass, password, hashed_password):
        return (klass.password_hashing_context.verify(
            password, hashed_password) if hashed_password else False)

    def __nonzero__(self):
        return False if getattr(self, 'user_type', None) == 'public' else True

    def before_put(self, *args, **kwargs):
        if self.user_type == 'public':
            raise Exception("Public user cannot be saved.")

    def to_client_dict(self, **kwargs):
        """Overrides DatastoreModel, modifies behavior of hashed_password.

        Change hashed_password to a boolean so client can detect if a user
        hasn't set their password yet. Also prevent hash from be unsafely
        exposed.
        """
        output = super(User, self).to_client_dict()
        output['hashed_password'] = bool(self.hashed_password)
        return output

    def create_reset_link(self, domain, token, continue_url='', case=''):
        """Create the kind of jwt-based set password link used by Triton.

        Args:
            domain: str, beginning with protocol, designed this way to make it
                easier to switch btwn localhost on http and deployed on https.
            continue_url: str, page should support redirecting user to this url
                after successful submission
            case: str, either 'reset' or 'invitation', aids the UI in
                displaying helpful text based on why the user has arrived.
        """
        return util.set_query_parameters(
            '{}/set_password/{}'.format(domain, token),
            continue_url=continue_url,
            case=case,
        )
Пример #59
0
def crypt_context():
    return CryptContext(schemes=['sha256_crypt'])
Пример #60
0
    def init_app(self, app, user_class, is_blacklisted=None):
        """
        Initializes the Praetorian extension

        :param: app:            The flask app to bind this extension to
        :param: user_class:     The class used to interact with user data
        :param: is_blacklisted: A method that may optionally be used to
                                check the token against a blacklist when
                                access or refresh is requested
                                Should take the jti for the token to check
                                as a single argument. Returns True if
                                the jti is blacklisted, False otherwise.
                                By default, always returns False.
        """
        PraetorianError.require_condition(
            app.config.get('SECRET_KEY') is not None,
            "There must be a SECRET_KEY app config setting set",
        )

        self.hash_autoupdate = app.config.get(
            'PRAETORIAN_HASH_AUTOUPDATE',
            DEFAULT_HASH_AUTOUPDATE,
        )

        self.hash_autotest = app.config.get(
            'PRAETORIAN_HASH_AUTOTEST',
            DEFAULT_HASH_AUTOTEST,
        )

        self.pwd_ctx = CryptContext(
            schemes=app.config.get('PRAETORIAN_HASH_ALLOWED_SCHEMES',
                                   DEFAULT_HASH_ALLOWED_SCHEMES),
            default=app.config.get('PRAETORIAN_HASH_SCHEME',
                                   DEFAULT_HASH_SCHEME),
            deprecated=app.config.get('PRAETORIAN_HASH_DEPRECATED_SCHEMES',
                                      DEFAULT_HASH_DEPRECATED_SCHEMES),
        )

        valid_schemes = self.pwd_ctx.schemes()
        PraetorianError.require_condition(
            self.hash_scheme in valid_schemes or self.hash_scheme is None,
            "If {} is set, it must be one of the following schemes: {}",
            'PRAETORIAN_HASH_SCHEME',
            valid_schemes,
        )

        self.user_class = self._validate_user_class(user_class)
        self.is_blacklisted = is_blacklisted or (lambda t: False)

        self.encode_key = app.config['SECRET_KEY']
        self.allowed_algorithms = app.config.get(
            'JWT_ALLOWED_ALGORITHMS',
            DEFAULT_JWT_ALLOWED_ALGORITHMS,
        )
        self.encode_algorithm = app.config.get(
            'JWT_ALGORITHM',
            DEFAULT_JWT_ALGORITHM,
        )
        self.access_lifespan = app.config.get(
            'JWT_ACCESS_LIFESPAN',
            DEFAULT_JWT_ACCESS_LIFESPAN,
        )
        self.refresh_lifespan = app.config.get(
            'JWT_REFRESH_LIFESPAN',
            DEFAULT_JWT_REFRESH_LIFESPAN,
        )
        self.reset_lifespan = app.config.get(
            'JWT_RESET_LIFESPAN',
            DEFAULT_JWT_RESET_LIFESPAN,
        )
        self.header_name = app.config.get(
            'JWT_HEADER_NAME',
            DEFAULT_JWT_HEADER_NAME,
        )
        self.header_type = app.config.get(
            'JWT_HEADER_TYPE',
            DEFAULT_JWT_HEADER_TYPE,
        )
        self.user_class_validation_method = app.config.get(
            'USER_CLASS_VALIDATION_METHOD',
            DEFAULT_USER_CLASS_VALIDATION_METHOD,
        )

        self.confirmation_template = app.config.get(
            'PRAETORIAN_CONFIRMATION_TEMPLATE',
            DEFAULT_CONFIRMATION_TEMPLATE,
        )
        self.confirmation_uri = app.config.get('PRAETORIAN_CONFIRMATION_URI', )
        self.confirmation_sender = app.config.get(
            'PRAETORIAN_CONFIRMATION_SENDER', )
        self.confirmation_subject = app.config.get(
            'PRAETORIAN_CONFIRMATION_SUBJECT',
            DEFAULT_CONFIRMATION_SUBJECT,
        )

        self.reset_template = app.config.get(
            'PRAETORIAN_RESET_TEMPLATE',
            DEFAULT_RESET_TEMPLATE,
        )
        self.reset_uri = app.config.get('PRAETORIAN_RESET_URI', )
        self.reset_sender = app.config.get('PRAETORIAN_RESET_SENDER', )
        self.reset_subject = app.config.get(
            'PRAETORIAN_RESET_SUBJECT',
            DEFAULT_RESET_SUBJECT,
        )

        if isinstance(self.access_lifespan, dict):
            self.access_lifespan = pendulum.duration(**self.access_lifespan)
        elif isinstance(self.access_lifespan, str):
            self.access_lifespan = duration_from_string(self.access_lifespan)
        ConfigurationError.require_condition(
            isinstance(self.access_lifespan, datetime.timedelta),
            "access lifespan was not configured",
        )

        if isinstance(self.refresh_lifespan, dict):
            self.refresh_lifespan = pendulum.duration(**self.refresh_lifespan)
        if isinstance(self.refresh_lifespan, str):
            self.refresh_lifespan = duration_from_string(self.refresh_lifespan)
        ConfigurationError.require_condition(
            isinstance(self.refresh_lifespan, datetime.timedelta),
            "refresh lifespan was not configured",
        )

        if not app.config.get('DISABLE_PRAETORIAN_ERROR_HANDLER'):
            app.register_error_handler(
                PraetorianError,
                PraetorianError.build_error_handler(),
            )

        self.is_testing = app.config.get('TESTING', False)

        if not hasattr(app, 'extensions'):
            app.extensions = {}
        app.extensions['praetorian'] = self

        return app