Created HashPassword bson serializer
This commit is contained in:
parent
88e271f9da
commit
14f5a341a3
|
@ -1,7 +1,7 @@
|
|||
package lila.user
|
||||
|
||||
import com.roundeights.hasher.Implicits._
|
||||
import reactivemongo.bson.Macros
|
||||
import reactivemongo.bson._
|
||||
|
||||
import lila.common.EmailAddress
|
||||
import lila.db.dsl._
|
||||
|
@ -15,17 +15,17 @@ final class Authenticator(
|
|||
) {
|
||||
import Authenticator._
|
||||
|
||||
def passEnc(pass: String): HashedPass = passHasher.hash(pass)
|
||||
def passEnc(pass: String): HashedPassword = passHasher.hash(pass)
|
||||
|
||||
def compare(auth: AuthData, p: String): Boolean = {
|
||||
val newP = auth.salt.fold(p) { s =>
|
||||
val salted = s"$p{$s}" // BC
|
||||
(~auth.sha512).fold(salted.sha512, salted.sha1).hex
|
||||
}
|
||||
auth.bcryptHash match {
|
||||
auth.bpass match {
|
||||
// Deprecated fallback. Log & fail after DB migration.
|
||||
case None => auth.password ?? { p => onShaLogin(); p == newP }
|
||||
case Some(bHash) => passHasher.check(bHash, newP)
|
||||
case Some(bpass) => passHasher.check(bpass, newP)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,15 +80,19 @@ object Authenticator {
|
|||
|
||||
case class AuthData(
|
||||
_id: User.ID,
|
||||
bpass: Option[Array[Byte]] = None,
|
||||
bpass: Option[HashedPassword] = None,
|
||||
password: Option[String] = None,
|
||||
salt: Option[String] = None,
|
||||
sha512: Option[Boolean] = None
|
||||
) {
|
||||
|
||||
def bcryptHash = bpass map HashedPass
|
||||
|
||||
def hashToken: String = bpass.fold(~password) { _.sha512.hex }
|
||||
def hashToken: String = bpass.fold(~password) { _.bytes.sha512.hex }
|
||||
}
|
||||
|
||||
implicit val HashedPasswordBsonHandler = new BSONHandler[BSONBinary, HashedPassword] {
|
||||
def read(b: BSONBinary) = HashedPassword(b.byteArray)
|
||||
def write(hash: HashedPassword) = BSONBinary(hash.bytes, Subtype.GenericBinarySubtype)
|
||||
}
|
||||
|
||||
implicit val AuthDataBSONHandler = Macros.handler[AuthData]
|
||||
}
|
||||
|
|
|
@ -42,8 +42,8 @@ private[user] object Aes {
|
|||
def iv(bytes: Array[Byte]): InitVector = new IvParameterSpec(bytes)
|
||||
}
|
||||
|
||||
case class HashedPass(bytes: Array[Byte]) extends AnyVal {
|
||||
def parse = bytes.size == 39 option bytes.splitAt(16)
|
||||
case class HashedPassword(bytes: Array[Byte]) extends AnyVal {
|
||||
def parse = bytes.length == 39 option bytes.splitAt(16)
|
||||
}
|
||||
|
||||
final class PasswordHasher(
|
||||
|
@ -57,14 +57,14 @@ final class PasswordHasher(
|
|||
private def bHash(salt: Array[Byte], p: String) =
|
||||
hashTimer(BCrypt.hashpwRaw(p.sha512, 'a', logRounds, salt))
|
||||
|
||||
def hash(p: String): HashedPass = {
|
||||
def hash(p: String): HashedPassword = {
|
||||
val salt = new Array[Byte](16)
|
||||
new SecureRandom().nextBytes(salt)
|
||||
|
||||
HashedPass(salt ++ aes.encrypt(Aes.iv(salt), bHash(salt, p)))
|
||||
HashedPassword(salt ++ aes.encrypt(Aes.iv(salt), bHash(salt, p)))
|
||||
}
|
||||
|
||||
def check(bytes: HashedPass, p: String): Boolean = bytes.parse ?? {
|
||||
def check(bytes: HashedPassword, p: String): Boolean = bytes.parse ?? {
|
||||
case (salt, encHash) =>
|
||||
val hash = aes.decrypt(Aes.iv(salt), encHash)
|
||||
BCrypt.bytesEqualSecure(hash, bHash(salt, p))
|
||||
|
|
|
@ -226,7 +226,7 @@ object UserRepo {
|
|||
|
||||
def create(
|
||||
username: String,
|
||||
passwordHash: HashedPass,
|
||||
passwordHash: HashedPassword,
|
||||
email: EmailAddress,
|
||||
blind: Boolean,
|
||||
mobileApiVersion: Option[ApiVersion],
|
||||
|
@ -301,7 +301,7 @@ object UserRepo {
|
|||
}
|
||||
)
|
||||
|
||||
import Authenticator.AuthDataBSONHandler
|
||||
import Authenticator._
|
||||
def getPasswordHash(id: User.ID): Fu[Option[String]] =
|
||||
coll.byId[Authenticator.AuthData](id) map {
|
||||
_.map { _.hashToken }
|
||||
|
@ -400,7 +400,7 @@ object UserRepo {
|
|||
|
||||
private def newUser(
|
||||
username: String,
|
||||
passwordHash: HashedPass,
|
||||
passwordHash: HashedPassword,
|
||||
email: EmailAddress,
|
||||
blind: Boolean,
|
||||
mobileApiVersion: Option[ApiVersion],
|
||||
|
@ -416,7 +416,7 @@ object UserRepo {
|
|||
F.username -> username,
|
||||
F.email -> email,
|
||||
F.mustConfirmEmail -> mustConfirmEmail.option(DateTime.now),
|
||||
F.bpass -> passwordHash.bytes,
|
||||
F.bpass -> passwordHash,
|
||||
F.perfs -> $empty,
|
||||
F.count -> Count.default,
|
||||
F.enabled -> true,
|
||||
|
|
Loading…
Reference in a new issue