1
- import Crypto
2
1
import NIO
3
- import Logging
4
2
5
3
extension PostgresConnection {
6
4
public func authenticate(
@@ -9,155 +7,17 @@ extension PostgresConnection {
9
7
password: String ? = nil ,
10
8
logger: Logger = . init( label: " codes.vapor.postgres " )
11
9
) -> EventLoopFuture < Void > {
12
- let auth = PostgresAuthenticationRequest (
10
+ let authContext = AuthContext (
13
11
username: username,
14
- database: database,
15
- password: password
16
- )
17
- return self . send ( auth, logger: self . logger)
18
- }
19
- }
20
-
21
- // MARK: Private
12
+ password: password,
13
+ database: database)
14
+ let outgoing = PSQLOutgoingEvent . authenticate ( authContext)
15
+ self . underlying. channel. triggerUserOutboundEvent ( outgoing, promise: nil )
22
16
23
- private final class PostgresAuthenticationRequest : PostgresRequest {
24
- enum State {
25
- case ready
26
- case saslInitialSent( SASLAuthenticationManager < SASLMechanism . SCRAM . SHA256 > )
27
- case saslChallengeResponse( SASLAuthenticationManager < SASLMechanism . SCRAM . SHA256 > )
28
- case saslWaitOkay
29
- case done
30
- }
31
-
32
- let username : String
33
- let database : String ?
34
- let password : String ?
35
- var state : State
36
-
37
- init ( username: String , database: String ? , password: String ? ) {
38
- self . state = . ready
39
- self . username = username
40
- self . database = database
41
- self . password = password
42
- }
43
-
44
- func log( to logger: Logger ) {
45
- logger. debug ( " Logging into Postgres db \( self . database ?? " nil " ) as \( self . username) " )
46
- }
47
-
48
- func respond( to message: PostgresMessage ) throws -> [ PostgresMessage ] ? {
49
- if case . error = message. identifier {
50
- // terminate immediately on error
51
- return nil
52
- }
53
-
54
- switch self . state {
55
- case . ready:
56
- switch message. identifier {
57
- case . authentication:
58
- let auth = try PostgresMessage . Authentication ( message: message)
59
- switch auth {
60
- case . md5( let salt) :
61
- let pwdhash = self . md5 ( ( self . password ?? " " ) + self . username) . hexdigest ( )
62
- let hash = " md5 " + self . md5 ( self . bytes ( pwdhash) + salt) . hexdigest ( )
63
- return try [ PostgresMessage . Password ( string: hash) . message ( ) ]
64
- case . plaintext:
65
- return try [ PostgresMessage . Password ( string: self . password ?? " " ) . message ( ) ]
66
- case . saslMechanisms( let saslMechanisms) :
67
- if saslMechanisms. contains ( " SCRAM-SHA-256 " ) && self . password != nil {
68
- let saslManager = SASLAuthenticationManager ( asClientSpeaking:
69
- SASLMechanism . SCRAM. SHA256 ( username: self . username, password: { self . password! } ) )
70
- var message : PostgresMessage ?
71
-
72
- if ( try saslManager. handle ( message: nil , sender: { bytes in
73
- message = try PostgresMessage . SASLInitialResponse ( mechanism: " SCRAM-SHA-256 " , initialData: bytes) . message ( )
74
- } ) ) {
75
- self . state = . saslWaitOkay
76
- } else {
77
- self . state = . saslInitialSent( saslManager)
78
- }
79
- return [ message ] . compactMap { $0 }
80
- } else {
81
- throw PostgresError . protocol ( " Unable to authenticate with any available SASL mechanism: \( saslMechanisms) " )
82
- }
83
- case . saslContinue, . saslFinal:
84
- throw PostgresError . protocol ( " Unexpected SASL response to start message: \( message) " )
85
- case . ok:
86
- self . state = . done
87
- return [ ]
88
- }
89
- default : throw PostgresError . protocol ( " Unexpected response to start message: \( message) " )
90
- }
91
- case . saslInitialSent( let manager) ,
92
- . saslChallengeResponse( let manager) :
93
- switch message. identifier {
94
- case . authentication:
95
- let auth = try PostgresMessage . Authentication ( message: message)
96
- switch auth {
97
- case . saslContinue( let data) , . saslFinal( let data) :
98
- var message : PostgresMessage ?
99
- if try manager. handle ( message: data, sender: { bytes in
100
- message = try PostgresMessage . SASLResponse ( responseData: bytes) . message ( )
101
- } ) {
102
- self . state = . saslWaitOkay
103
- } else {
104
- self . state = . saslChallengeResponse( manager)
105
- }
106
- return [ message ] . compactMap { $0 }
107
- default : throw PostgresError . protocol ( " Unexpected response during SASL negotiation: \( message) " )
108
- }
109
- default : throw PostgresError . protocol ( " Unexpected response during SASL negotiation: \( message) " )
110
- }
111
- case . saslWaitOkay:
112
- switch message. identifier {
113
- case . authentication:
114
- let auth = try PostgresMessage . Authentication ( message: message)
115
- switch auth {
116
- case . ok:
117
- self . state = . done
118
- return [ ]
119
- default : throw PostgresError . protocol ( " Unexpected response while waiting for post-SASL ok: \( message) " )
120
- }
121
- default : throw PostgresError . protocol ( " Unexpected response while waiting for post-SASL ok: \( message) " )
122
- }
123
- case . done:
124
- switch message. identifier {
125
- case . parameterStatus:
126
- // self.status[status.parameter] = status.value
127
- return [ ]
128
- case . backendKeyData:
129
- // self.processID = data.processID
130
- // self.secretKey = data.secretKey
131
- return [ ]
132
- case . readyForQuery:
133
- return nil
134
- default : throw PostgresError . protocol ( " Unexpected response to password authentication: \( message) " )
135
- }
17
+ return self . underlying. channel. pipeline. handler ( type: PSQLEventsHandler . self) . flatMap { handler in
18
+ handler. authenticateFuture
19
+ } . flatMapErrorThrowing { error in
20
+ throw error. asAppropriatePostgresError
136
21
}
137
-
138
- }
139
-
140
- func start( ) throws -> [ PostgresMessage ] {
141
- return try [
142
- PostgresMessage . Startup. versionThree ( parameters: [
143
- " user " : self . username,
144
- " database " : self . database ?? username
145
- ] ) . message ( )
146
- ]
147
- }
148
-
149
- // MARK: Private
150
-
151
- private func md5( _ string: String ) -> [ UInt8 ] {
152
- return md5 ( self . bytes ( string) )
153
- }
154
-
155
- private func md5( _ message: [ UInt8 ] ) -> [ UInt8 ] {
156
- let digest = Insecure . MD5. hash ( data: message)
157
- return . init( digest)
158
- }
159
-
160
- func bytes( _ string: String ) -> [ UInt8 ] {
161
- return Array ( string. utf8)
162
22
}
163
23
}
0 commit comments