1- import Crypto
21import NIO
3- import Logging
42
53extension PostgresConnection {
64 public func authenticate(
@@ -9,155 +7,17 @@ extension PostgresConnection {
97 password: String ? = nil ,
108 logger: Logger = . init( label: " codes.vapor.postgres " )
119 ) -> EventLoopFuture < Void > {
12- let auth = PostgresAuthenticationRequest (
10+ let authContext = AuthContext (
1311 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 )
2216
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
13621 }
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)
16222 }
16323}
0 commit comments