def checkUserLogin(username, pwrd, cursor, cnx): pwd_context = CryptContext( schemes=["pbkdf2_sha256"], default="pbkdf2_sha256", pbkdf2_sha256__default_rounds= 30000 #num of times it will hash before writing ) query = ( "SELECT username, password FROM providerTable WHERE username = %s") cursor.execute(query, (username, )) for un, pw in cursor: #this loop will not be entered if it does not exist in the providerDB if (True == (pwd_context.verify(pwrd, pw))): return True else: #because there can only be 1 match, this query can only be run 1 time max return False query = ("SELECT username, password FROM patientTable WHERE username = %s") cursor.execute(query, (username, )) for un, pw in cursor: #this loop will not be entered if it does not exist in the patientDB if (True == (pwd_context.verify(pwrd, pw))): return True else: return False return False
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))
def test_20_basic(self): "test basic encrypt/identify/verify functionality" handlers = [hash.md5_crypt, hash.des_crypt, hash.bsdi_crypt] cc = CryptContext(handlers, policy=None) #run through handlers for crypt in handlers: h = cc.encrypt("test", scheme=crypt.name) self.assertEqual(cc.identify(h), crypt.name) self.assertEqual(cc.identify(h, resolve=True), crypt) self.assertTrue(cc.verify('test', h)) self.assertTrue(not cc.verify('notest', h)) #test default h = cc.encrypt("test") self.assertEqual(cc.identify(h), "md5_crypt") #test genhash h = cc.genhash('secret', cc.genconfig()) self.assertEqual(cc.identify(h), 'md5_crypt') h = cc.genhash('secret', cc.genconfig(), scheme='md5_crypt') self.assertEqual(cc.identify(h), 'md5_crypt') self.assertRaises(ValueError, cc.genhash, 'secret', cc.genconfig(), scheme="des_crypt")
def verify(username, password): crypter2 = CryptContext(schemes=['sha256_crypt']) if not (username and password): return False if crypter2.verify(password, USER_DATA[list( USER_DATA.keys())[0]]) and crypter2.verify( username, list(USER_DATA.keys())[0]): return User(id=123)
def test_bcrypt_password_no_bcrypt(self, monkeypatch): """Deal with a missing bcrypt backend.""" def mock_verify(pwd, crypted_pwd): raise MissingBackendError( "bcrypt: no backends available -- recommend you install one " "(e.g., 'pip install bcrypt')") context = CryptContext(schemes=["bcrypt"]) monkeypatch.setattr(context, "verify", mock_verify) with pytest.raises(MissingBackendError) as excinfo: context.verify("foo", "bar") assert "bcrypt: no backends available" in str(excinfo.value)
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
def POST(self): from passlib.context import CryptContext password_context = CryptContext(schemes=["pbkdf2_sha512"], deprecated="auto") if session.login == 1: return render.index() i = web.input() username = i.user ident = db.select('gallery.users', where='username=$username', vars=locals())[0] try: if password_context.verify(i.passwd, ident['password']): session.login = 1 session.userid = ident['id'] session.admin = ident['admin'] return web.seeother('/') else: session.login = 0 session.admin = 0 session.userid = 0 return render.login() #return render.login_error() except: session.login=0 session.admin=0 session.userid=0 return render.login()
class Authentication: def __init__(self, *, secret_key, encryption_algorithm="HS256"): self.crypt_context = CryptContext(schemes=["bcrypt"], deprecated="auto") self.secret_key = secret_key self.encryption_algorithm = encryption_algorithm def verify_password(self, plain_password, hashed_password): return self.crypt_context.verify(plain_password, hashed_password) def hash_password(self, plain_password): return self.crypt_context.hash(plain_password) def generate_access_token(self, token_data: dict, expires_delta: Optional[timedelta] = None): token_data = token_data.copy() if expires_delta: expires = datetime.utcnow() + expires_delta else: expires = datetime.utcnow() + timedelta(minutes=15) token_data.update({"exp": expires}) encoded_jwt = jwt.encode(token_data, self.secret_key, algorithm=self.encryption_algorithm) return {"access_token": encoded_jwt, "expires": expires} def decode_token(self, token): return jwt.decode(token, self.secret_key, algorithms=[self.encryption_algorithm])
class LioPassword: def __init__(self): self.pwd_context = CryptContext( schemes=["sha256_crypt", "md5_crypt", "des_crypt"]) # Hash password def match_password(self, password: str, password_in_db: str): """ Hàm dùng cho việc so sánh xác thực mật khẩu :param password: mật khẩu dùng để so sánh :param password_in_db: mật khẩu được lấy từ Database :return: True hoặc False """ return self.pwd_context.verify(password, password_in_db) def hash_password(self, password: str): """ Hàm dùng cho việc mã hoá mật khẩu :return: chuỗi sau khi được mã hoá """ return self.pwd_context.hash(password)
def change_password(connection: Dict, data: Dict) -> None: cxt = CryptContext(schemes=['pbkdf2_sha512']) pw_hash = cxt.hash(data['password']) with psycopg2.connect(**connection) as conn: with conn.cursor() as cursor: update_query = ('UPDATE res_users ' 'SET password = %(password)s ' 'WHERE login = %(login)s') cursor.execute(update_query, { 'password': pw_hash, 'login': data['login'], }) with conn.cursor() as cursor: query = ('SELECT password FROM res_users ' 'WHERE login = %(login)s') cursor.execute(query, { 'login': data['login'], }) result = cursor.fetchone() if not result: raise Exception('No results found!') check_password = result[0] if cxt.verify(data['password'], check_password): print('Success!') else: raise Exception('Hash stored in database did not match!')
class User(BaseModel): ROLES = [('ADMIN', 'admin'), ('USER', 'user')] username = peewee.CharField(max_length=255, help_text='user login name') secret = peewee.CharField(max_length=77, help_text='user login password') role = peewee.CharField(max_length=5, choices=ROLES, help_text='user login type') locked = peewee.BooleanField(default=True, help_text='user is locked') def __init__(self, *args, **kwargs): self._secret_schemes = ["sha256_crypt", "des_crypt"] self._secret_ctx = CryptContext(schemes=self._secret_schemes) super(User, self).__init__(*args, **kwargs) def __str__(self): return self.username def to_dict(self): data = super(User, self).to_dict() data.pop('secret') data.setdefault('user_id', self.id) return data @property def password(self): return self.secret @password.setter def password(self, password): self.secret = self._secret_ctx.hash(password) def verify(self, password): return self._secret_ctx.verify(password, self.secret)
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'))
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
class WeatherDataUsers: def __init__(self): self.crypt_context = CryptContext(schemes=['bcrypt'], deprecated='auto') self.users = [WeatherDataUser(**user) for user in _weather_data_users] def get_all(self) -> Generator[WeatherDataUser, None, None]: for user in self.users: yield user def get(self, username: str) -> WeatherDataUser: for user in self.users: if username == user.username: return user.copy(deep=True) def verify_password(self, user: WeatherDataUser, password: str) -> bool: # This is not ideal however there is a pretty big hit hashing the # user password (~300 ms/user). For the server that's not a big deal # however for other use cases (like in the db package) it make tool # startup slow. Since the user implementation is all fake and only # called by login, hash the plain text password here instead of in # init or implementing a lock scheme. The plain text password could # simply be checked here however I wanted to hang onto to how the # CryptContext can be used. return self.crypt_context.verify( password, self.crypt_context.hash(user.hashed_password))
class PasswordUtils(object): def __init__(self, config): self.salt = config.get('SECURITY_PASSWORD_SALT', None) self.pw_hash = config.get('SECURITY_PASSWORD_HASH', None) if self.salt is None: raise RuntimeError( "The configuration value 'SECURITY_PASSWORD_SALT' must be set") if self.pw_hash is None: raise RuntimeError( "The configuration value 'SECURITY_PASSWORD_HASH' must be set") self.pwd_context = CryptContext(schemes=[self.pw_hash]) def get_hmac(self, password): h = hmac.new(self.encode_string(self.salt), self.encode_string(password), hashlib.sha512) return base64.b64encode(h.digest()) def encrypt_password(self, password): signed = self.get_hmac(password).decode('ascii') return self.pwd_context.encrypt(signed) def verify_password(self, password, password_hash): password = self.get_hmac(password) return self.pwd_context.verify(password, password_hash) def encode_string(self, string): if isinstance(string, unicode): string = string.encode('utf-8') return string
def checkPass(formpass, datapass): from passlib.context import CryptContext pwd_context = CryptContext( schemes=["pbkdf2_sha256", "md5_crypt", "des_crypt"], default="pbkdf2_sha256", pbkdf2_sha256__default_rounds=30000) match = pwd_context.verify(formpass, datapass) return match
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')
class Hash(): def __init__(self): self.pwd_cxt = CryptContext(schemes=["bcrypt"], deprecated="auto") def bcrypt(self, password: str) -> str: return self.pwd_cxt.hash(password) def verify(self, plain_password: str, hash_password: str) -> bool: return self.pwd_cxt.verify(plain_password, hash_password)
def testpass(password, dictionary): context = CryptContext( schemes=['des_crypt', 'md5_crypt', 'sha256_crypt', 'sha512_crypt']) for word in dictionary: try: if context.verify(word, password): return word except ValueError, e: pass
class HashingHelper: def __init__(self): self._crypt_context = CryptContext(schemes=["bcrypt"], deprecated="auto") def hash_password(self, password: str): return self._crypt_context.hash(password) def verify_password(self, plain_password: str, hashed_password: str): return self._crypt_context.verify(plain_password, hashed_password)
def verify_password_with_context(plain_password: str, hashed_password: str, context: CryptContext) -> bool: """ This will verify a password by using a given algorithm :param plain_password: str -> The plain text password :param hashed_password: str -> The hashed password :param context: CryptoContext -> The context to use :return: bool -> Tells if plain matches the hashed password """ return context.verify(plain_password, hashed_password)
def userAuth(email, password): """Validate credentials""" pwd_context = CryptContext(schemes=['bcrypt']) user = User.query.filter({'email': email}).first() if pwd_context.verify(password, user.password): return True return False
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)
class PasswordHasher: def __init__(self): self.password_hash = None self.myctx = CryptContext(schemes=["sha256_crypt"]) def hash_password(self, password): self.password_hash = self.myctx.hash(password) return self.password_hash def verify_password(self, password, user): return self.myctx.verify(password, user.password)
def check_pswd(red_pwd, inp_pwd): """ check passwords :param red_pwd: :param inp_pwd: @return boolean: """ pwd_context = CryptContext(schemes=["pbkdf2_sha256"], default="pbkdf2_sha256", pbkdf2_sha256__default_rounds=3000) return pwd_context.verify(inp_pwd, red_pwd)
class PasswordHasher: __slots__ = ["context"] def __init__(self, settings): self.context = CryptContext(**settings.deep_get("passwords", default={})) def check(self, password_hash, password): return self.context.verify(password, password_hash) def hash(self, password): return self.context.encrypt(password)
async def authenticate_user( session: Session, username: str, password: str, context: CryptContext = pwd_context) -> Union[User, None]: user = await get_user(session, username) if not user: return None if not context.verify(password, user.password): return None return user
class PasswordContext: def __init__(self): self.context = CryptContext( schemes=['bcrypt'], deprecated='auto', ) def hash(self, password): return self.context.hash(password) def verify(self, password, password_hash): return self.context.verify(password, password_hash)
class PasswordManager(object): """Hash and verify user passwords using passlib """ def __init__(self, app): """ Create a passlib CryptContext. """ self.app = app self.auth = app.auth # Create a passlib CryptContext self.password_crypt_context = CryptContext( schemes=self.auth.AUTH_PASSLIB_CRYPTCONTEXT_SCHEMES, **self.auth.AUTH_PASSLIB_CRYPTCONTEXT_KEYWORDS) def hash_password(self, password): """ Hash plaintext ``password`` using the ``password_hash`` specified in the constructor. Args: password(str): Plaintext password that the user types in. Returns: hashed password. Example: ``user.password = hash_password('mypassword')`` """ # Use passlib's CryptContext to hash a password if self.auth.AUTH_ENABLE_PASSWORD_HASH: return self.password_crypt_context.encrypt(password) else: return password def verify_password(self, password, password_hash): """ Verify plaintext ``password`` against ``hashed password``. Args: password(str): Plaintext password that the user types in. password_hash(str): Password hash generated by a previous call to ``hash_password()``. Returns: | True when ``password`` matches ``password_hash``. | False otherwise. Example: :: if verify_password('mypassword', user.password): login_user(user) """ # Use passlib's CryptContext to verify a password if self.auth.AUTH_ENABLE_PASSWORD_HASH: return self.password_crypt_context.verify(password, password_hash) else: return password == password_hash def set_password(self, password, user): user.password = self.hash_password(password)
def check_pwd(toyz_settings, user_id, pwd): """ Check to see if a users password matches the one stored in the database. Parameters - toyz_settings ( :py:class:`toyz.utils.core.ToyzSettings` ): Settings for the current application - user_id (*string* ): Id of the user logging in - pwd: (*string* ): password the user has entered Returns - valid_login (*bool* ): True if the user name and password match """ from passlib.context import CryptContext pwd_context = CryptContext(**toyz_settings.security.pwd_context) users = db_utils.get_all_ids(toyz_settings.db, user_type='user_id') if user_id not in users: # Dummy check to prevent a timing attack to guess user names pwd_context.verify('foo', 'bar') return False user_hash = db_utils.get_param(toyz_settings.db, 'pwd', user_id=user_id) return pwd_context.verify(pwd, user_hash)
class PasswordHashProvider: def __init__(self): self.pwd_context = CryptContext( schemes=["pbkdf2_sha256"], default="pbkdf2_sha256", pbkdf2_sha256__default_rounds=30000 ) def encrypt_password(self, password): return self.pwd_context.encrypt(password) def check_encrypted_password(self, password, hashed): return self.pwd_context.verify(password, hashed)
class Crypto: def __init__(self): self.password_context = CryptContext( schemes=["pbkdf2_sha256"], default="pbkdf2_sha256", pbkdf2_sha256__default_rounds=30000 ) def cypher(self, password): return self.password_context.encrypt(password) def verify_encrypted_password(self, password, hashed): return self.password_context.verify(password, hashed)
class HashContext(object): """ | Class to generate/verify hash securitied string. | Hashed string can used to store context or any unencrypt context. """ def __init__(self, app) -> None: # Use the applications's SECRET_KEY as default. secret_key = app.config.get('SECRET_KEY', None) secret_key = app.config.get('IDENTITY_HASH_SALT', secret_key) if not secret_key: raise SystemError( 'Config setting SECRET_KEY or IDENTITY_HASH_SALT is missing.') schemes = app.config.get('IDENTITY_HASH_SCHEMES', ['bcrypt']) schemes_keywords = app.config.get('IDENTITY_HASH_OPTIONS', {}) # Create a passlib CryptContext self.crypt_context = CryptContext(schemes, **schemes_keywords) def hash_context(self, context: str) -> str: """ Hash plaintext ``context`` using the ``IDENTITY_HASH_SCHEMES`` specified in the app config. :param context: Plaintext string that the user types in. :return: hashed context. Example: ``user.context = hash_context('mycontext')`` """ # Use passlib's CryptContext to hash a context context_hash = self.crypt_context.encrypt(context) return context_hash def verify_context(self, context: str, context_hash: str) -> bool: """ Verify plaintext ``context`` against ``hashed context``. :param context: Plaintext context that the user types in. :param context_hash: context hash generated by a previous call to ``hash_context()``. :return: | True when ``context`` matches ``context_hash``. | False otherwise. Example: :: if verify_context('mycontext', user.context): login_user(user) """ # Use passlib's CryptContext to verify a context return self.crypt_context.verify(context, context_hash)
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')
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
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 delete_account(): ''' delete account, after password validation ''' if request.method == 'GET': return render_template('delete_account.html') elif request.method == 'POST': confirm = request.form['delete_account'] if confirm == 'Yes': current_password = request.form['wyr_current_password'] #verify current password myctx = CryptContext(schemes=['pbkdf2_sha256']) if myctx.verify(current_password, current_user.password) == True: User.query.filter_by(id=current_user.id).delete() db.session.commit() flash('Account deleted. Sorry to see you go!') return redirect(url_for('main.index')) else: flash('Password incorrect.') return redirect(url_for('main.settings')) else: flash('Account deletion cancelled.') return redirect(url_for('main.settings')) else: return redirect(url_for('main.index'))
def check_chave(self, chave): #Criando um objeto que usará criptografia do método sha256, rounds default de 80000 cripto = CryptContext(schemes="sha256_crypt") #Comparando o valor da string(uuid) com o valor criptografado(chave) okornot = cripto.verify(self.uuid, chave) return okornot
'pyramid_tm', 'SQLAlchemy', 'transaction', 'zope.sqlalchemy', 'waitress', 'wtforms', 'passlib', 'markdown', 'pygments', ] #Added from class notes from passlib.context import CryptContext password_context = CryptContext(schemes=['pbkdf2_sha512']) hashed = password_context.encrypt('password') if password_context.verify('password', hashed): print ("It matched") setup(name='learning_journal', version='0.0', description='learning_journal', long_description=README + '\n\n' + CHANGES, classifiers=[ "Programming Language :: Python", "Framework :: Pyramid", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", ], author='', author_email='', url='',
class HashEntity(object): """ Represents a primary key (owner? owner+key? @todo ????) and an associated digest (aka hash, which is a reserved word in Python). Also stores data for convenience, though it's not ever saved. @todo needs work on what inconsistent (is_verified = False) states it can and can't exist in, etc. """ key = None data = None digest_str = None cryptcontext = None 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 def __str__(self): my_data = self.data or '' my_digest = self.digest_str or '' return "HashEntity: data length %d, digest '%s'" % (len(my_data), my_digest) def digest(self, data=None): """ Calculates the digest of self.data and sets self.digest_str. @param data If set, replaces self.data before calculating digest. @return None """ self.data = data or self.data if self.data is None: raise SaltboxException('Cannot calculate digest with no data.') self.digest_str = self._calculate_digest() def verify(self, data=None): """ Calculates the digest of some data and compares to self.digest_str. If the data argument is None, self.data will be verified against self.digest_str. @param data If set, replaces self.data before verifying digest. @return boolean True if the digest of self.data matches self.digest_str, else False. """ check_data = data or self.data if check_data is None: raise SaltboxException('Cannot verify digest with no data.') if self.digest_str is None: raise SaltboxException('Cannot verify an empty digest.') return self._verify_digest(check_data) @property def is_verified(self): return self.verify() def clear(self): """ Clears all data variables, but not the cryptcontext. """ self.key = None self.data = None self.digest_str = None def save(self): assert self.is_verified, "Cannot save HashEntity which doesn't verify." raise NotImplementedError # @todo def _calculate_digest(self, data=None): data = data or self.data return self.cryptcontext.encrypt(data) def _verify_digest(self, data=None): data = data or self.data return self.cryptcontext.verify(data, self.digest_str)
class User(AccessControlledModel): """ This model represents the users of the system. """ 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) def validate(self, doc): """ Validate the user every time it is stored in the database. """ doc['login'] = doc.get('login', '').lower().strip() doc['email'] = doc.get('email', '').lower().strip() doc['firstName'] = doc.get('firstName', '').strip() doc['lastName'] = doc.get('lastName', '').strip() doc['status'] = doc.get('status', 'enabled') if 'salt' not in doc: # Internal error, this should not happen raise Exception('Tried to save user document with no salt.') if not doc['firstName']: raise ValidationException('First name must not be empty.', 'firstName') if not doc['lastName']: raise ValidationException('Last name must not be empty.', 'lastName') if doc['status'] not in ('pending', 'enabled', 'disabled'): raise ValidationException( 'Status must be pending, enabled, or disabled.', 'status') if 'hashAlg' in doc: # This is a legacy field; hash algorithms are now inline with the password hash del doc['hashAlg'] self._validateLogin(doc['login']) if not mail_utils.validateEmailAddress(doc['email']): raise ValidationException('Invalid email address.', 'email') # Ensure unique logins q = {'login': doc['login']} if '_id' in doc: q['_id'] = {'$ne': doc['_id']} existing = self.findOne(q) if existing is not None: raise ValidationException('That login is already registered.', 'login') # Ensure unique emails q = {'email': doc['email']} if '_id' in doc: q['_id'] = {'$ne': doc['_id']} existing = self.findOne(q) if existing is not None: raise ValidationException('That email is already registered.', 'email') # If this is the first user being created, make it an admin existing = self.findOne({}) if existing is None: doc['admin'] = True # Ensure settings don't stop this user from logging in doc['emailVerified'] = True doc['status'] = 'enabled' return doc def _validateLogin(self, login): if '@' in login: # Hard-code this constraint so we can always easily distinguish # an email address from a login raise ValidationException('Login may not contain "@".', 'login') if not re.match(r'^[a-z][\da-z\-\.]{3,}$', login): raise ValidationException( 'Login must be at least 4 characters, start with a letter, and may only contain ' 'letters, numbers, dashes, and dots.', 'login') def filter(self, doc, user, additionalKeys=None): filteredDoc = super(User, self).filter(doc, user, additionalKeys) level = self.getAccessLevel(doc, user) if level >= AccessType.ADMIN: filteredDoc['otp'] = doc.get('otp', {}).get('enabled', False) return filteredDoc def authenticate(self, login, password, otpToken=None): """ Validate a user login via username and password. If authentication fails, a ``AccessException`` is raised. :param login: The user's login or email. :type login: str :param password: The user's password. :type password: str :param otpToken: A one-time password for the user. If "True", then the one-time password (if required) is assumed to be concatenated to the password. :type otpToken: str or bool or None :returns: The corresponding user if the login was successful. :rtype: dict """ event = events.trigger('model.user.authenticate', { 'login': login, 'password': password }) if event.defaultPrevented and len(event.responses): return event.responses[-1] login = login.lower().strip() loginField = 'email' if '@' in login else 'login' user = self.findOne({loginField: login}) if user is None: raise AccessException('Login failed.') # Handle users with no password if not self.hasPassword(user): e = events.trigger('no_password_login_attempt', { 'user': user, 'password': password }) if len(e.responses): return e.responses[-1] raise ValidationException( 'This user does not have a password. You must log in with an ' 'external service, or reset your password.') # Handle OTP token concatenation if otpToken is True and self.hasOtpEnabled(user): # Assume the last (typically 6) characters are the OTP, so split at that point otpTokenLength = self._TotpFactory.digits otpToken = password[-otpTokenLength:] password = password[:-otpTokenLength] # Verify password if not self._cryptContext.verify(password, user['salt']): raise AccessException('Login failed.') # Verify OTP if self.hasOtpEnabled(user): if otpToken is None: raise AccessException( 'User authentication must include a one-time password ' '(typically in the "Girder-OTP" header).') self.verifyOtp(user, otpToken) elif isinstance(otpToken, six.string_types): raise AccessException('The user has not enabled one-time passwords.') # This has the same behavior as User.canLogin, but returns more # detailed error messages if user.get('status', 'enabled') == 'disabled': raise AccessException('Account is disabled.', extra='disabled') if self.emailVerificationRequired(user): raise AccessException( 'Email verification required.', extra='emailVerification') if self.adminApprovalRequired(user): raise AccessException('Account approval required.', extra='accountApproval') return user def remove(self, user, progress=None, **kwargs): """ Delete a user, and all references to it in the database. :param user: The user document to delete. :type user: dict :param progress: A progress context to record progress on. :type progress: girder.utility.progress.ProgressContext or None. """ from .folder import Folder from .group import Group from .token import Token # Delete all authentication tokens owned by this user Token().removeWithQuery({'userId': user['_id']}) # Delete all pending group invites for this user Group().update( {'requests': user['_id']}, {'$pull': {'requests': user['_id']}} ) # Delete all of the folders under this user folderModel = Folder() folders = folderModel.find({ 'parentId': user['_id'], 'parentCollection': 'user' }) for folder in folders: folderModel.remove(folder, progress=progress, **kwargs) # Finally, delete the user document itself AccessControlledModel.remove(self, user) if progress: progress.update(increment=1, message='Deleted user ' + user['login']) def getAdmins(self): """ Helper to return a cursor of all site-admin users. The number of site admins is assumed to be small enough that we will not need to page the results for now. """ return self.find({'admin': True}) def search(self, text=None, user=None, limit=0, offset=0, sort=None): """ List all users. Since users are access-controlled, this will filter them by access policy. :param text: Pass this to perform a full-text search for users. :param user: The user running the query. Only returns users that this user can see. :param limit: Result limit. :param offset: Result offset. :param sort: The sort structure to pass to pymongo. :returns: Iterable of users. """ # Perform the find; we'll do access-based filtering of the result set # afterward. if text is not None: cursor = self.textSearch(text, sort=sort) else: cursor = self.find({}, sort=sort) return self.filterResultsByPermission( cursor=cursor, user=user, level=AccessType.READ, limit=limit, offset=offset) def hasPassword(self, user): """ Returns whether or not the given user has a password stored in the database. If not, it is expected that the user will be authenticated by an external service. :param user: The user to test. :type user: dict :returns: bool """ return user['salt'] is not None def setPassword(self, user, password, save=True): """ Change a user's password. :param user: The user whose password to change. :param password: The new password. If set to None, no password will be stored for this user. This should be done in cases where an external system is responsible for authenticating the user. """ if password is None: user['salt'] = None else: cur_config = config.getConfig() # Normally this would go in validate() but password is a special case. if not re.match(cur_config['users']['password_regex'], password): raise ValidationException(cur_config['users']['password_description'], 'password') user['salt'] = self._cryptContext.hash(password) if save: self.save(user) def initializeOtp(self, user): """ Initialize the use of one-time passwords with this user. This does not save the modified user model. :param user: The user to modify. :return: The new OTP keys, each in KeyUriFormat. :rtype: dict """ totp = self._TotpFactory.new() user['otp'] = { 'enabled': False, 'totp': totp.to_dict() } # Use the brand name as the OTP issuer if it's non-default (since that's prettier and more # meaningful for users), but fallback to the site hostname if the brand name isn't set # (to disambiguate otherwise identical "Girder" issuers) # Prevent circular import from girder.api.rest import getUrlParts brandName = Setting().get(SettingKey.BRAND_NAME) defaultBrandName = Setting().getDefault(SettingKey.BRAND_NAME) # OTP URIs ( https://github.com/google/google-authenticator/wiki/Key-Uri-Format ) do not # allow colons, so use only the hostname component serverHostname = getUrlParts().netloc.partition(':')[0] # Normally, the issuer would be set when "self._TotpFactory" is instantiated, but that # happens during model initialization, when there's no current request, so the server # hostname is not known then otpIssuer = brandName if brandName != defaultBrandName else serverHostname return { 'totpUri': totp.to_uri(label=user['login'], issuer=otpIssuer) } def hasOtpEnabled(self, user): return 'otp' in user and user['otp']['enabled'] def verifyOtp(self, user, otpToken): lastCounterKey = 'girder.models.user.%s.otp.totp.counter' % user['_id'] # The last successfully-authenticated key (which is blacklisted from reuse) lastCounter = rateLimitBuffer.get(lastCounterKey) or None try: totpMatch = self._TotpFactory.verify( otpToken, user['otp']['totp'], last_counter=lastCounter) except TokenError as e: raise AccessException('One-time password validation failed: %s' % e) # The totpMatch.cache_seconds tells us prospectively how long the counter needs to be cached # for, but dogpile.cache expiration times work retrospectively (on "get"), so there's no # point to using it (over-caching just wastes cache resources, but does not impact # "totp.verify" security) rateLimitBuffer.set(lastCounterKey, totpMatch.counter) def createUser(self, login, password, firstName, lastName, email, admin=False, public=True): """ Create a new user with the given information. The user will be created with the default "Public" and "Private" folders. :param admin: Whether user is global administrator. :type admin: bool :param public: Whether user is publicly visible. :type public: bool :returns: The user document that was created. """ from .setting import Setting requireApproval = Setting().get(SettingKey.REGISTRATION_POLICY) == 'approve' if admin: requireApproval = False user = { 'login': login, 'email': email, 'firstName': firstName, 'lastName': lastName, 'created': datetime.datetime.utcnow(), 'emailVerified': False, 'status': 'pending' if requireApproval else 'enabled', 'admin': admin, 'size': 0, 'groups': [], 'groupInvites': [] } self.setPassword(user, password, save=False) self.setPublic(user, public, save=False) user = self.save(user) verifyEmail = Setting().get(SettingKey.EMAIL_VERIFICATION) != 'disabled' if verifyEmail: self._sendVerificationEmail(user) if requireApproval: self._sendApprovalEmail(user) return user def canLogin(self, user): """ Returns True if the user is allowed to login, e.g. email verification is not needed and admin approval is not needed. """ if user.get('status', 'enabled') == 'disabled': return False if self.emailVerificationRequired(user): return False if self.adminApprovalRequired(user): return False return True def emailVerificationRequired(self, user): """ Returns True if email verification is required and this user has not yet verified their email address. """ from .setting import Setting return (not user['emailVerified']) and \ Setting().get(SettingKey.EMAIL_VERIFICATION) == 'required' def adminApprovalRequired(self, user): """ Returns True if the registration policy requires admin approval and this user is pending approval. """ from .setting import Setting return user.get('status', 'enabled') == 'pending' and \ Setting().get(SettingKey.REGISTRATION_POLICY) == 'approve' def _sendApprovalEmail(self, user): url = '%s#user/%s' % ( mail_utils.getEmailUrlPrefix(), str(user['_id'])) text = mail_utils.renderTemplate('accountApproval.mako', { 'user': user, 'url': url }) mail_utils.sendEmail( toAdmins=True, subject='Girder: Account pending approval', text=text) def _sendApprovedEmail(self, user): text = mail_utils.renderTemplate('accountApproved.mako', { 'user': user, 'url': mail_utils.getEmailUrlPrefix() }) mail_utils.sendEmail( to=user.get('email'), subject='Girder: Account approved', text=text) def _sendVerificationEmail(self, user): from .token import Token token = Token().createToken( user, days=1, scope=TokenScope.EMAIL_VERIFICATION) url = '%s#useraccount/%s/verification/%s' % ( mail_utils.getEmailUrlPrefix(), str(user['_id']), str(token['_id'])) text = mail_utils.renderTemplate('emailVerification.mako', { 'url': url }) mail_utils.sendEmail( to=user.get('email'), subject='Girder: Email verification', text=text) def _grantSelfAccess(self, event): """ This callback grants a user admin access to itself. This generally should not be called or overridden directly, but it may be unregistered from the `model.user.save.created` event. """ user = event.info self.setUserAccess(user, user, level=AccessType.ADMIN, save=True) def _addDefaultFolders(self, event): """ This callback creates "Public" and "Private" folders on a user, after it is first created. This generally should not be called or overridden directly, but it may be unregistered from the `model.user.save.created` event. """ from .folder import Folder from .setting import Setting if Setting().get(SettingKey.USER_DEFAULT_FOLDERS) == 'public_private': user = event.info publicFolder = Folder().createFolder( user, 'Public', parentType='user', public=True, creator=user) privateFolder = Folder().createFolder( user, 'Private', parentType='user', public=False, creator=user) # Give the user admin access to their own folders Folder().setUserAccess(publicFolder, user, AccessType.ADMIN, save=True) Folder().setUserAccess(privateFolder, user, AccessType.ADMIN, save=True) def fileList(self, doc, user=None, path='', includeMetadata=False, subpath=True, data=True): """ This function generates a list of 2-tuples whose first element is the relative path to the file from the user's folders root and whose second element depends on the value of the `data` flag. If `data=True`, the second element will be a generator that will generate the bytes of the file data as stored in the assetstore. If `data=False`, the second element is the file document itself. :param doc: the user to list. :param user: a user used to validate data that is returned. :param path: a path prefix to add to the results. :param includeMetadata: if True and there is any metadata, include a result which is the JSON string of the metadata. This is given a name of metadata[-(number).json that is distinct from any file within the item. :param subpath: if True, add the user's name to the path. :param data: If True return raw content of each file as stored in the assetstore, otherwise return file document. :type data: bool """ from .folder import Folder if subpath: path = os.path.join(path, doc['login']) folderModel = Folder() for folder in folderModel.childFolders(parentType='user', parent=doc, user=user): for (filepath, file) in folderModel.fileList( folder, user, path, includeMetadata, subpath=True, data=data): yield (filepath, file) def subtreeCount(self, doc, includeItems=True, user=None, level=None): """ Return the size of the user's folders. The user is counted as well. :param doc: The user. :param includeItems: Whether to include items in the subtree count, or just folders. :type includeItems: bool :param user: If filtering by permission, the user to filter against. :param level: If filtering by permission, the required permission level. :type level: AccessLevel """ from .folder import Folder count = 1 folderModel = Folder() folders = folderModel.findWithPermissions({ 'parentId': doc['_id'], 'parentCollection': 'user' }, fields='access', user=user, level=level) count += sum(folderModel.subtreeCount( folder, includeItems=includeItems, user=user, level=level) for folder in folders) return count def countFolders(self, user, filterUser=None, level=None): """ Returns the number of top level folders under this user. Access checking is optional; to circumvent access checks, pass ``level=None``. :param user: The user whose top level folders to count. :type collection: dict :param filterUser: If performing access checks, the user to check against. :type filterUser: dict or None :param level: The required access level, or None to return the raw top-level folder count. """ from .folder import Folder fields = () if level is None else ('access', 'public') folderModel = Folder() folders = folderModel.findWithPermissions({ 'parentId': user['_id'], 'parentCollection': 'user' }, fields=fields, user=filterUser, level=level) return folders.count() def updateSize(self, doc): """ Recursively recomputes the size of this user and its underlying folders and fixes the sizes as needed. :param doc: The user. :type doc: dict """ from .folder import Folder size = 0 fixes = 0 folderModel = Folder() folders = folderModel.find({ 'parentId': doc['_id'], 'parentCollection': 'user' }) for folder in folders: # fix folder size if needed _, f = folderModel.updateSize(folder) fixes += f # get total recursive folder size folder = folderModel.load(folder['_id'], force=True) size += folderModel.getSizeRecursive(folder) # fix value if incorrect if size != doc.get('size'): self.update({'_id': doc['_id']}, update={'$set': {'size': size}}) fixes += 1 return size, fixes
def change_email(): ''' Change user email ''' # change email or display form to enter new email or send confirmation if request.method == 'GET': # if this is coming from link sent to current email address, send another # to new email address if request.args.get('code'): hash = request.args.get('code') serializer = URLSafeTimedSerializer(current_app.config['SECRET_KEY']) try: decoded = serializer.loads(hash, salt='change_email', max_age=3600) except: flash("""Error confirming your credentials. Please try again later or contact us if this problem continues to exist.""") return redirect(url_for('main.settings')) #if for some reason some other logged in user clicks the link if decoded[0] != current_user.username: flash("Username does not match. Email not changed.") redirect(url_for('main.index')) serializer = URLSafeTimedSerializer(current_app.config['SECRET_KEY']) email_hash = serializer.dumps([current_user.username, decoded[1]], salt='change_email') to = decoded[1] subject = 'Email address change' text = """What You've Read has received a request to change your email address to this one. If this was you, please follow <a href="http://www.whatyouveread.com/change_email?confirm={}"> this link</a> to confirm.""".format(email_hash) common.send_simple_message(to, subject, text) flash("""Please check your email at your new email address and follow the link provided to confirm it.""") return redirect(url_for('main.settings')) #if this is coming from the link sent to confirm the change, change it if request.args.get('confirm'): hash = request.args.get('confirm') serializer = URLSafeTimedSerializer(current_app.config['SECRET_KEY']) try: decoded = serializer.loads(hash, salt='change_email', max_age=3600) except: flash("""Error confirming your credentials. Please try again later or contact us if this problem continues to exist.""") return redirect(url_for('main.settings')) #if for some reason some other logged in user clicks the link if decoded[0] != current_user.username: flash("Username does not match. Email not changed.") redirect(url_for('main.index')) current_user.email = decoded[1] db.session.commit() flash('Your email has been changed.') return redirect(url_for('main.settings')) #else, display the original form to request the email change return render_template('change_email.html') # send email to current email address to confirm the change elif request.method == 'POST': if request.form['submit'] == "Cancel": flash('Email change cancelled.') return redirect(url_for('main.settings')) new_email = request.form['new_email'] password = request.form['password'] #minimum check that it's an email: if '@' not in new_email: flash('That didn\'t look like an email address. Please try again.') return redirect(url_for('main.change_email')) #check if email already in use in another account if User.query.filter_by(email=new_email).count() > 0: flash('Sorry, that email address is already in use.') return redirect(url_for('main.change_email')) # verify password myctx = CryptContext(schemes=['pbkdf2_sha256']) if myctx.verify(password, current_user.password) == True: #hash for confirm change serializer = URLSafeTimedSerializer(current_app.config['SECRET_KEY']) email_hash = serializer.dumps([current_user.username, new_email], salt='change_email') # hash for resetting password if user didn't initiate this (change salt # and use regular serializer, not timed one) serializer2 = URLSafeSerializer(current_app.config['SECRET_KEY']) email_hash2 = serializer2.dumps([current_user.email], salt='reset_password') to = current_user.email subject = 'Email address change' text = """What You've Read has received a request to change your email address to {}. If this was you, please follow <a href="http://www.whatyouveread.com/change_email?code={}"> this link</a> to confirm. <br><br> 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.""".format(new_email, email_hash, email_hash2) common.send_simple_message(to, subject, text) flash("""Please check your email at your current email address and follow the link provided.""") return redirect(url_for('main.settings')) else: flash('Password is incorrect.') return redirect(url_for('main.change_email')) else: return abort(405)
# This script is just to save me having to go out and find a hash generator every time I want to test something. # It takes in a string from the command line and outputs a hash for each algorithm in the python hashlib. import hashlib from passlib.context import CryptContext schemes = ["sha1_crypt", "sha256_crypt", "sha512_crypt", "md5_crypt", "des_crypt", 'ldap_salted_sha1', 'ldap_salted_md5', 'ldap_sha1', 'ldap_md5', 'ldap_plaintext', "mysql323"] myctx = CryptContext(schemes) key = raw_input("enter key: ") print "hashlib hashes:\n" for algorithm in hashlib.algorithms: print "The %s hash representation of %s is: " % (algorithm, key) print hashlib.new(algorithm, key).hexdigest() print "\npasslib hashes:\n" for algorithm in schemes: print "The %s hash representation of %s is: " % (algorithm, key) print myctx.encrypt(key, algorithm) hash1 = myctx.encrypt(key, 'ldap_md5') if myctx.verify(key, hash1): print "true" else: print "false"