def upgrade(): pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") session.add(User(username='******', hashed_password=pwd_context.hash('user'))) session.add( User(username='******', hashed_password=pwd_context.hash('admin'))) session.commit()
def create_user(username, password1): """Hashes the password and inserts a new user into database""" # define hashing parameters hasher = CryptContext(schemes=["sha256_crypt"]) # hash the user password hash1 = hasher.hash(password1) # check if the username is not already taken rows = db.execute("SELECT * FROM users WHERE username = :username", username=username) if len(rows) == 1: return apology("username already exists") else: # insert a new user to the database and redirect to the index page with the current balance db.execute( "INSERT INTO users (username,hash) VALUES(:username, :hash)", username=username, hash=hash1) rows = db.execute("SELECT * FROM users WHERE username = :username", username=request.form.get("username")) session["user_id"] = rows[0]["id"] balance = show_balance() return render_template( "index.html", cash=balance["cash"], portfoliorows=balance["portfoliorows"], total=balance["total"], title= '<div class="alert alert-success" role="alert"> A new user was created successfuly! </div>' )
def create_user(request): ''' Create user in the database Password is encrypted with SHA256 ''' if ('username' in request and 'password' in request and 'email' in request) and \ (type(request['username'] == str) and type(request['password'] == str) and type(request['email'] == str)): duplicate = db.session.query(User).filter( User.email == request['email']).first() if duplicate is None: myctx = CryptContext(schemes=["sha256_crypt"]) new_user = User( username=request['username'], email=request['email'], password=myctx.hash(request['password']) ) db.session.add(new_user) db.session.commit() return True else: print('register failed') return False else: return False
def hashPassword(password): pwd_context = CryptContext( schemes=["pbkdf2_sha256"], default="pbkdf2_sha256", pbkdf2_sha256__default_rounds=30000 ) return pwd_context.hash(password)
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 UserService(DBModelService): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") def get_user(self, id_: Optional[int] = None, username: Optional[str] = None) -> Union[User, None]: if id_ is not None: user = self._db.query(User).get(id_) user.owned_recipes_count = fast_count( self._db.query(Recipe).filter(Recipe.author_id == user.id)) return user elif username is not None: return self._db.query(User).filter( User.username == username).first() else: return None def create_user(self, username: str, password: str): instance = User(username=username) instance.is_active = True instance.hpass = self.pwd_context.hash(password) self._db.add(instance) self._db.commit() self._db.refresh(instance) return instance
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 buildconfig(request): if request.method == "POST": form = RequestedConfigForm(request.POST) if form.is_valid(): template = loader.get_template('esxicfg/success.html') allchar = string.ascii_letters + string.punctuation + string.digits password = "".join(choice(allchar) for x in range(randint(8, 12))) myctx = CryptContext(schemes=["md5_crypt"]) password_hash = myctx.hash(password) newNode = Node(password_hash=password_hash, network_autoconfig=request.POST['NetworkMode'], network_vlan=request.POST['NetworkVLAN'], network_manualip=request.POST['NetworkIP'], network_manualnm=request.POST['NetworkNM'], network_manualgw=request.POST['NetworkGW'], ssh_config=request.POST['SSHmode']) newNode.save() context = { 'SITE_FQDN': settings.SITE_FQDN, 'password': password, 'node_id': newNode.id } return HttpResponse(template.render(context, request)) else: template = loader.get_template('esxicfg/mainpage.html') context = {error_message: 'Invalid options, please try again.'} return HttpResponse(template.render(content, request)) else: template = loader.get_template('esxicfg/mainpage.html') context = {error_message: 'Form input are required.'} return HttpResponse(template.render(content, request))
def changepass(currentpass, newpass1): """Hashes the password and inserts into database""" # check if the current password is valid id = session["user_id"] passwordrow = db.execute("SELECT hash FROM users WHERE id = :id", id=id) if len(passwordrow) != 1 or not pwd_context.verify(currentpass, passwordrow[0]["hash"]): return apology("invalid password") # hash the new password hasher = CryptContext(schemes=["sha256_crypt"]) hash1 = hasher.hash(newpass1) # update the pasword db.execute("UPDATE users SET hash = :hash WHERE id = :id", hash=hash1, id=id) # refresh the balance and redirect to index balance = show_balance() return render_template( "index.html", cash=balance["cash"], portfoliorows=balance["portfoliorows"], total=balance["total"], title= '<div class="alert alert-success" role="alert"> Password was changed! </div>' )
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()
def createAdminUser(username, pw, fn, ln, cursor, cnx): if (mySQL_userDB.isUsernameTaken(username, cursor, cnx)): return "taken error" policy = PasswordPolicy.from_names( length=8, # min length: 8 uppercase=1, # need min. 2 uppercase letters numbers=1 # need min. 2 digits ) isEnough = policy.test(pw) if len(isEnough) != 0: return "weak password" pwd_context = CryptContext(schemes=["pbkdf2_sha256"], default="pbkdf2_sha256", pbkdf2_sha256__default_rounds=30000) formatInsert = ("INSERT INTO adminTable " "(username, password,fname," "lname,creationDate) " "VALUES (%s, %s,%s, %s,%s)" ) #NOTE: use %s even with numbers #their care team is not assigned at creation, so N/A insertContent = (username, pwd_context.hash(pw), fn, ln, str(date.today().strftime("%B %d, %Y"))) cursor.execute(formatInsert, insertContent) cnx.commit() return "success"
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))
def encryptPassword(password): """ This function encrypts a password and returns a hash. """ context = CryptContext(schemes=[encryption_algorithm]) # replaced 'encrypt' (deprecated as of 1.7) with 'hash' return context.hash(password)
def CreateNewUser(username, password, country, email, gender, bday, race): myctx = CryptContext(schemes=["sha256_crypt", "md5_crypt", "des_crypt"]) ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" salt = [] [salt.append(random.choice(ALPHABET)) for x in range(16)] salt = "".join(salt) hash1 = myctx.hash(password + salt) username = username.lower() HashedPwd = hash1 PwdSalt = salt cursor.execute( """ INSERT INTO [dbo].[Users] ([Username] ,[HashedPwd] ,[PwdSalt] ,[Country] ,[Email] ,[Gender] ,[BirthYear] ,[Race] ,[TimeStamp]) VALUES (?, ?, ?, ?, ?, ?, ?, ?, (getdate())) """, [username, HashedPwd, PwdSalt, country, email, gender, bday, race]) cursor.commit()
def set_password(user_id, password): context = CryptContext(schemes=["bcrypt"]) m = models.User u = m.query.filter(m.id == user_id).first() u.password = context.hash(password) models.db.session.commit() return True
def change(): user_id = session["user_id"] if request.method == "POST": # ensure username was submitted # ensure password was submitted if not request.form.get("password"): return apology("must provide password") elif not request.form.get("password_1"): return apology("must provide password(again)") if request.form.get("password") != request.form.get("password_1"): return apology("password(again) must equit password") myctx = CryptContext(["sha256_crypt"]) passwd = myctx.hash(request.form.get("password")) loginTime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") rows = db.execute( "update users set passwd = :passwd,loginTime = :loginTime where userId = :user_id", passwd=passwd, loginTime=loginTime, user_id=user_id) if rows != 1: return apology("change passwd is failed!") return redirect(url_for("mine"))
def reset_password(db: Session, data: UserResponseResetPassword): pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") _user = db.query(User).filter_by(document = data.document).first() _used_token = db.query(UserResetPassword).filter_by(user_id= _user.id).first() _used_token.used_token = True _user.password = pwd_context.hash(data.password) db.commit() return "Senha alterada"
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.hash(password, 'pbkdf2_sha256') self.sha512_crypt = pwd_context.hash(password, 'sha512_crypt') self.des_crypt = pwd_context.hash(password, 'des_crypt') self.save()
def get_password_hash_with_context(password, context: CryptContext) -> str: """ Hash a password by using a given algorithm :param password: str -> The plain text password :param context: CryptContext -> the context to use to generate hash of the password :return: str -> The hashed password """ return context.hash(password)
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 encryptPass(password): from passlib.context import CryptContext global pwd_context pwd_context = CryptContext( schemes=["pbkdf2_sha256", "md5_crypt", "des_crypt"], default="pbkdf2_sha256", pbkdf2_sha256__default_rounds=30000) enpass = pwd_context.hash(password) return enpass
def createuser(self, username, password): from passlib.context import CryptContext password_context = CryptContext(schemes=["pbkdf2_sha512"], deprecated="auto") cryptedpassword = password_context.hash(password) db.insert('gallery.users', admin=False, password=cryptedpassword, username=username) createduser = db.select('gallery.users', where="username=${un}", vars={'un':username}) db.insert('gallery.userflags', userid=createduser[0]['id'], flagtype="newuser")
def register(): # forget any user_id session.clear() # if user reached route via POST (as by submitting a form via POST) if request.method == "POST": # query database for username userdata = db.execute( "SELECT * FROM userdata WHERE username = :username", username=request.form.get("name")) # ensure username exists and password is correct if len(userdata) == 1: flash("Username already exists") return render_template("register.html") # encrypt password myctx = CryptContext(schemes=["sha256_crypt"], sha256_crypt__default_rounds=80000) hash = myctx.hash(request.form.get("password")) # insert user/password into userdata userdata = db.execute( "INSERT INTO userdata (username, hash) VALUES (:username, :hash)", username=request.form.get("name"), hash=hash) # query database for username userdata = db.execute( "SELECT * FROM userdata WHERE username = :username", username=request.form.get("name")) # remember wich user has logged in session["userid"] = userdata[0]["id"] # create portfolio db.execute( "CREATE TABLE if not exists portfolio ('id' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 'userid' INTEGER, 'tried' INTEGER, 'saved' INTEGER, FOREIGN KEY(userid) REFERENCES userdata(id))" ) # update portfolio db.execute( "INSERT INTO portfolio (userid, tried, saved) VALUES(:userid, 0, 0)", userid=session["userid"]) # create cookbook db.execute( "CREATE TABLE if not exists cookbook ('id' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 'userid' INTEGER, 'recipeid' INTEGER, 'recipe' TEXT, 'link' TEXT, 'tried' BOOLEAN, 'image' BLOB, FOREIGN KEY(userid) REFERENCES userdata(id), FOREIGN KEY(recipeid) REFERENCES recipe(id))" ) return redirect(url_for("homepage")) else: return render_template("register.html")
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 register(): """Register user.""" # forget any user_id session.clear() # if user reached route via POST (as by submitting a form via POST) if request.method == "POST": # ensure username was submitted if not request.form.get("username"): return apology("must provide username") elif "\'" in request.form.get("username") or ";" in request.form.get( "username"): return apology("username can't contain ' or ;") # ensure password was submitted elif not request.form.get("password"): return apology("must provide password") elif not request.form.get("password_1"): return apology("must provide password(again)") if not request.form.get("nickname"): nickname = request.form.get("username") elif "\'" in request.form.get("nickname") or ";" in request.form.get( "nickname"): return apology("nickname can't contain ' or ;") else: nickname = request.form.get("nickname") if request.form.get("password") != request.form.get("password_1"): return apology("password(again) must equit password") myctx = CryptContext(["sha256_crypt"]) passwd = myctx.hash(request.form.get("password")) createTime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") # query database for username userId = db.execute( "INSERT INTO users (\"userName\",\"passwd\",\"nickName\", \"createTime\", \"loginTime\") VALUES (:userName, :passwd, :nickName, :createTime, :loginTime)", userName=request.form.get("username"), passwd=passwd, nickName=nickname, createTime=createTime, loginTime=createTime) # ensure username exists and password is correct if userId == 0: return apology("invalid username and/or password") # remember which user has logged in session["user_id"] = userId session["user_name"] = request.form.get("username") # redirect user to home page return redirect(url_for("mine")) # else if user reached route via GET (as by clicking a link or via redirect) else: return render_template("register.html")
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)
async def change_password( oid: int, change: ChangePassword, current_user: models.User = Depends(user_with_users_access), session: Session = Depends(db_session)): """Change password.""" user_db = __get_user(session, oid) pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") user_db.hashed_password = pwd_context.hash(change.password) session.commit() return {"status": "ok"}
def _get_weak_hash(self, plaintext): """Create a weaker CryptContext and hash plaintext. (Weaker as in weaker than default context) """ weak_context = CryptContext( schemes=["pbkdf2_sha256"], pbkdf2_sha256__default_rounds=5, ) return weak_context.hash(plaintext)
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)
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))
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
session = Session() session.add_all(users + roles + domains + actions + resources + credential_types) users = dict((user.first_name+'_'+user.last_name, user) for user in session.query(User).all()) domains = dict((domain.name, domain) for domain in session.query(Domain).all()) actions = dict((action.name, action) for action in session.query(Action).all()) resources = dict((resource.name, resource) for resource in session.query(Resource).all()) roles = dict((role.title, role) for role in session.query(Role).all()) cred_types = dict((ct.title, ct) for ct in session.query(CredentialType).all()) thirty_from_now = datetime.datetime.now() + datetime.timedelta(days=30) print('thirty from now is: ', thirty_from_now) cc = CryptContext(schemes=['bcrypt_sha256']) password = cc.hash('letsgobowling') totp_key = 'DP3RDO3FAAFUAFXQELW6OTB2IGM3SS6G' thedude = users['Jeffrey_Lebowski'] passwords = [Credential(user_id=user.pk_id, credential=password, credential_type_id=cred_types['password'].pk_id, expiration_dt=thirty_from_now) for user in users.values()] thedude_totp_key = [Credential(user_id=thedude.pk_id, credential=totp_key, credential_type_id=cred_types['totp_key'].pk_id, expiration_dt=thirty_from_now)] session.add_all(passwords + thedude_totp_key)
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)
if __name__ == '__main__': args, config = init_cmdline (build_parser ()) dba = db_tools.PostgreSQLEngine (**config) db.Base3.metadata.drop_all (dba.engine) db.Base3.metadata.create_all (dba.engine) pwd_context = CryptContext (schemes = [ config['USER_PASSWORD_HASH'] ]) with dba.engine.begin () as src: execute (src, "INSERT INTO role (id, name, description) VALUES (1, 'admin', 'Administrator')", {}) execute (src, "INSERT INTO role (id, name, description) VALUES (2, 'editor', 'Editor')", {}) if args.email: params = { "username" : args.username, "email" : args.email, "password" : pwd_context.hash (args.password) if args.password else '', "active" : True, "confirmed_at" : datetime.datetime.now () } execute (src, "INSERT INTO \"user\" (id, username, email, password, active, confirmed_at) " + "VALUES (1, :username, :email, :password, :active, :confirmed_at)", params) execute (src, "INSERT INTO roles_users (id, user_id, role_id) VALUES (1, 1, 1)", {}) execute (src, "INSERT INTO roles_users (id, user_id, role_id) VALUES (2, 1, 2)", {})
class DatabaseUserService: def __init__(self, session, ratelimiters=None): 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, ) @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 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): # 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.") 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): 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 return True # 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, email, is_active=False, is_staff=False, is_superuser=False): user = User(username=username, name=name, password=self.hasher.hash(password), is_active=is_active, is_staff=is_staff, is_superuser=is_superuser) self.db.add(user) email_object = Email(email=email, user=user, primary=True, verified=False) self.db.add(email_object) # flush the db now so user.id is available self.db.flush() return user def update_user(self, user_id, **changes): user = self.get_user(user_id) for attr, value in changes.items(): setattr(user, attr, value) return user def verify_email(self, user_id, email_address): user = self.get_user(user_id) for email in user.emails: if email.email == email_address: email.verified = True