1
1
import Async
2
+ import Crypto
2
3
3
4
/// A PostgreSQL frontend client.
4
5
public final class PostgreSQLConnection {
@@ -52,7 +53,7 @@ public final class PostgreSQLConnection {
52
53
}
53
54
54
55
/// Authenticates the `PostgreSQLClient` using a username with no password.
55
- public func authenticate( username: String , database: String ? = nil ) -> Future < Void > {
56
+ public func authenticate( username: String , database: String ? = nil , password : String ? = nil ) -> Future < Void > {
56
57
let startup = PostgreSQLStartupMessage . versionThree ( parameters: [
57
58
" user " : username,
58
59
" database " : database ?? username
@@ -70,18 +71,55 @@ public final class PostgreSQLConnection {
70
71
throw PostgreSQLError ( identifier: " authRequest " , reason: " No authorization request / status sent. " )
71
72
}
72
73
74
+ let input : [ PostgreSQLMessage ]
73
75
switch auth {
74
- case . ok: return . done
75
- case . plaintext: throw PostgreSQLError ( identifier: " plaintext " , reason: " Plaintext password not supported. Use MD5. " )
76
+ case . ok:
77
+ guard password == nil else {
78
+ throw PostgreSQLError ( identifier: " trust " , reason: " No password is required " )
79
+ }
80
+ input = [ ]
81
+ case . plaintext:
82
+ guard let password = password else {
83
+ throw PostgreSQLError ( identifier: " password " , reason: " Password is required " )
84
+ }
85
+ let passwordMessage = PostgreSQLPasswordMessage ( password: password)
86
+ input = [ . password( passwordMessage) ]
76
87
case . md5( let salt) :
77
- /// FIXME: hash password
78
- let password = PostgreSQLPasswordMessage ( password: " 123 " )
79
- return self . queueStream. enqueue ( [ . password( password) ] ) { message in
80
- switch message {
81
- case . error( let error) :
82
- throw error
83
- default : return true
84
- }
88
+ guard let password = password else {
89
+ throw PostgreSQLError ( identifier: " password " , reason: " Password is required " )
90
+ }
91
+ guard let passwordData = password. data ( using: . utf8) else {
92
+ throw PostgreSQLError ( identifier: " passwordUTF8 " , reason: " Could not convert password to UTF-8 encoded Data. " )
93
+ }
94
+
95
+ guard let usernameData = username. data ( using: . utf8) else {
96
+ throw PostgreSQLError ( identifier: " usernameUTF8 " , reason: " Could not convert username to UTF-8 encoded Data. " )
97
+ }
98
+
99
+ let hasher = MD5 ( )
100
+ // pwdhash = md5(password + username).hexdigest()
101
+ var passwordUsernameData = passwordData + usernameData
102
+ hasher. update ( sequence: & passwordUsernameData)
103
+ hasher. finalize ( )
104
+ guard let pwdhash = hasher. hash. hexString. data ( using: . utf8) else {
105
+ throw PostgreSQLError ( identifier: " hashUTF8 " , reason: " Could not convert password hash to UTF-8 encoded Data. " )
106
+ }
107
+ hasher. reset ( )
108
+ // hash = ′ md 5′ + md 5(pwdhash + salt ).hexdigest ()
109
+ var saltedData = pwdhash + salt
110
+ hasher. update ( sequence: & saltedData)
111
+ hasher. finalize ( )
112
+ let passwordMessage = PostgreSQLPasswordMessage ( password: " md5 " + hasher. hash. hexString)
113
+ input = [ . password( passwordMessage) ]
114
+ }
115
+
116
+ return self . queueStream. enqueue ( input) { message in
117
+ switch message {
118
+ case . error( let error) : throw error
119
+ case . readyForQuery: return true
120
+ case . authenticationRequest: return false
121
+ case . parameterStatus, . backendKeyData: return false
122
+ default : throw PostgreSQLError ( identifier: " authenticationMessage " , reason: " Unexpected authentication message: \( message) " )
85
123
}
86
124
}
87
125
}
0 commit comments