in finagle-postgresql/src/main/scala/com/twitter/finagle/postgresql/machine/HandshakeMachine.scala [66:129]
override def receive(
state: State,
msg: BackendMessage
): StateMachine.TransitionResult[State, Response.ConnectionParameters] = (state, msg) match {
case (Authenticating, AuthenticationMD5Password(salt)) =>
def hex(input: Array[Byte]) = input.map(s => f"$s%02x").mkString
def bytes(str: String) = str.getBytes(StandardCharsets.UTF_8)
def md5(input: Array[Byte]*): String =
hex(
input
.foldLeft(MessageDigest.getInstance("MD5")) { case (d, v) => d.update(v); d }.digest())
credentials.password match {
case None => Complete(ReadyForQuery(NoTx), Some(Throw(PgSqlPasswordRequired)))
case Some(password) =>
// concat('md5', md5(concat(md5(concat(password, username)), random-salt)))
val hashed = md5(
bytes(md5(bytes(password), bytes(credentials.username))),
Buf.ByteArray.Owned.extract(salt)
)
Transition(Authenticating, Send(FrontendMessage.PasswordMessage(s"md5$hashed")))
}
case (Authenticating, AuthenticationCleartextPassword) =>
credentials.password match {
case None => Complete(ReadyForQuery(NoTx), Some(Throw(PgSqlPasswordRequired)))
case Some(password) =>
Transition(Authenticating, Send(FrontendMessage.PasswordMessage(password)))
}
case (
Authenticating,
AuthenticationOk
) => // This can happen at Startup when there's no password
Transition(BackendStarting(Nil, None), NoOp)
case (BackendStarting(params, bkd), p: BackendMessage.ParameterStatus) =>
Transition(BackendStarting(p :: params, bkd), NoOp)
case (BackendStarting(params, _), bkd: BackendMessage.BackendKeyData) =>
Transition(BackendStarting(params, Some(bkd)), NoOp)
case (BackendStarting(params, bkd), ready: BackendMessage.ReadyForQuery) =>
Complete(ready, Some(Return(Response.ConnectionParameters(params, bkd))))
case (state, _: BackendMessage.NoticeResponse) => Transition(state, NoOp) // TODO: don't ignore
case (_, e: BackendMessage.ErrorResponse) =>
// The backend closes the connection, so we use a bogus ReadyForQuery value
Complete(ReadyForQuery(NoTx), Some(Throw(PgSqlServerError(e))))
case (
_,
AuthenticationGSS | AuthenticationKerberosV5 | AuthenticationSCMCredential |
AuthenticationSSPI | AuthenticationSASL(_)
) =>
Complete(
ReadyForQuery(NoTx),
Some(
Throw(PgSqlUnsupportedAuthenticationMechanism(
msg.asInstanceOf[BackendMessage.AuthenticationMessage])))
)
case (state, msg) => throw PgSqlNoSuchTransition("HandshakeMachine", state, msg)
}