@@ -45,6 +45,12 @@ type (
4545 // SSLProtocolVersion is a ssl_min_protocol_version or
4646 // ssl_max_protocol_version setting.
4747 SSLProtocolVersion string
48+
49+ // RequireAuth is a require_auth setting.
50+ RequireAuth string
51+
52+ // RequireAuths is a require_auth setting.
53+ RequireAuths []RequireAuth
4854)
4955
5056// Values for [SSLMode] that pq supports.
@@ -179,6 +185,41 @@ func (s SSLProtocolVersion) tlsconf() uint16 {
179185 }
180186}
181187
188+ // Values for [RequireAuth] that pq supports.
189+ const (
190+ RequireAuthNone = RequireAuth ("none" )
191+ RequireAuthPassword = RequireAuth ("password" )
192+ RequireAuthMD5 = RequireAuth ("md5" )
193+ RequireAuthGSS = RequireAuth ("gss" )
194+ RequireAuthScramSHA256 = RequireAuth ("scram-sha-256" )
195+ RequireAuthAny = RequireAuth ("!none" )
196+ RequireAuthNotPassword = RequireAuth ("!password" )
197+ RequireAuthNotMD5 = RequireAuth ("!md5" )
198+ RequireAuthNotGSS = RequireAuth ("!gss" )
199+ RequireAuthNotScramSHA256 = RequireAuth ("!scram-sha-256" )
200+
201+ // Not (yet) supported by pq
202+ // RequireAuthSSPI = "sspi"
203+ // RequireAuthOAuth = "oauth"
204+ // RequireAuthNotSSPI = "!sspi"
205+ // RequireAuthNotOAuth = "!oauth"
206+ )
207+
208+ var requireAuths = []RequireAuth {RequireAuthNone , RequireAuthPassword , RequireAuthMD5 ,
209+ RequireAuthGSS , RequireAuthScramSHA256 , RequireAuthAny , RequireAuthNotPassword ,
210+ RequireAuthNotMD5 , RequireAuthNotGSS , RequireAuthNotScramSHA256 }
211+
212+ func (r RequireAuths ) String () string {
213+ var b strings.Builder
214+ for i , rr := range r {
215+ if i > 0 {
216+ b .WriteString ("," )
217+ }
218+ b .WriteString (string (rr ))
219+ }
220+ return b .String ()
221+ }
222+
182223// Connector represents a fixed configuration for the pq driver with a given
183224// dsn. Connector satisfies the [database/sql/driver.Connector] interface and
184225// can be used to create any number of DB Conn's via [sql.OpenDB].
@@ -341,14 +382,14 @@ type Config struct {
341382 //
342383 // The default is determined by [tls.Config.MinVersion], which is TLSv1.2 at
343384 // the time of writing.
344- SSLMinProtocolVersion SSLProtocolVersion `postgres:"ssl_min_protocol_version" env:"SSLPGMINPROTOCOLVERSION "`
385+ SSLMinProtocolVersion SSLProtocolVersion `postgres:"ssl_min_protocol_version" env:"PGSSLMINPROTOCOLVERSION "`
345386
346387 // Maximum SSL/TLS protocol version to allow for the connection. If not set,
347388 // this parameter is ignored and the connection will use the maximum bound
348389 // defined by the backend, if set. Setting the maximum protocol version is
349390 // mainly useful for testing or if some component has issues working with a
350391 // newer protocol.
351- SSLMaxProtocolVersion SSLProtocolVersion `postgres:"ssl_max_protocol_version" env:"SSLPGMAXPROTOCOLVERSION "`
392+ SSLMaxProtocolVersion SSLProtocolVersion `postgres:"ssl_max_protocol_version" env:"PGSSLMAXPROTOCOLVERSION "`
352393
353394 // Interpert sslcert and sslkey as PEM encoded data, rather than a path to a
354395 // PEM file. This is a pq extension, not supported in libpq.
@@ -431,6 +472,25 @@ type Config struct {
431472 // Path to connection service file. Defaults to ~/.pg_service.conf.
432473 ServiceFile string `postgres:"-" env:"PGSERVICEFILE"`
433474
475+ // Require an authentication method from the server and refuse to connect if
476+ // the server does not use the requested method.
477+ //
478+ // This accepts a comma-separated list.
479+ //
480+ // Methods may be negated with a ! prefix, in which case the server must
481+ // *not* attempt the listed method, and the server is free not to
482+ // authenticate the client at all. Negated and non-negated forms may not be
483+ // combined in the same setting with a comma-separated list.
484+ //
485+ // As a special case the "none" method requires the server not to use an
486+ // authentication challenge. This does not prohibit client certificate
487+ // authentication via TLS or GSS authentication via its encrypted transport.
488+ // This can be negated to require some form of authentication.
489+ //
490+ // By default any authentication method is accepted and the server is free
491+ // to skip authentication altogether.
492+ RequireAuth RequireAuths `postgres:"require_auth" env:"PGREQUIREAUTH"`
493+
434494 // Runtime parameters: any unrecognized parameter in the DSN will be added
435495 // to this and sent to PostgreSQL during startup.
436496 Runtime map [string ]string `postgres:"-" env:"-"`
@@ -517,7 +577,8 @@ func NewConfig(dsn string) (Config, error) {
517577// Clone returns a copy of the [Config].
518578func (cfg Config ) Clone () Config {
519579 c := cfg
520- c .Runtime , c .Multi , c .set = maps .Clone (cfg .Runtime ), slices .Clone (cfg .Multi ), slices .Clone (cfg .set )
580+ c .Runtime , c .Multi , c .RequireAuth , c .set = maps .Clone (cfg .Runtime ), slices .Clone (cfg .Multi ),
581+ slices .Clone (cfg .RequireAuth ), slices .Clone (cfg .set )
521582 return c
522583}
523584
@@ -672,8 +733,8 @@ func (cfg *Config) fromEnv(env []string) error {
672733 switch k {
673734 case "PGREQUIRESSL" , "PGSSLCOMPRESSION" , // Deprecated.
674735 "PGREALM" , "PGGSSENCMODE" , "PGGSSDELEGATION" , "PGGSSLIB" , // krb stuff
675- "PGREQUIREAUTH " , "PGCHANNELBINDING " ,
676- "PGSSLCERTMODE" , "PGSSLCRL" , "PGSSLCRLDIR" , " PGREQUIREPEER" :
736+ "PGCHANNELBINDING " , "PGSSLCRL" , "PGSSLCRLDIR " ,
737+ "PGSSLCERTMODE" , "PGREQUIREPEER" :
677738 return fmt .Errorf ("pq: environment variable $%s is not supported" , k )
678739 case "PGKRBSRVNAME" :
679740 if newGss == nil {
@@ -833,8 +894,9 @@ func (cfg *Config) setFromTag(o map[string]string, tag string, service bool) err
833894 loadbalancehosts = (tag == "postgres" && k == "load_balance_hosts" ) || (tag == "env" && k == "PGLOADBALANCEHOSTS" )
834895 minprotocolversion = (tag == "postgres" && k == "min_protocol_version" ) || (tag == "env" && k == "PGMINPROTOCOLVERSION" )
835896 maxprotocolversion = (tag == "postgres" && k == "max_protocol_version" ) || (tag == "env" && k == "PGMAXPROTOCOLVERSION" )
836- sslminprotocolversion = (tag == "postgres" && k == "ssl_min_protocol_version" ) || (tag == "env" && k == "SSLPGMINPROTOCOLVERSION" )
837- sslmaxprotocolversion = (tag == "postgres" && k == "ssl_max_protocol_version" ) || (tag == "env" && k == "SSLPGMAXPROTOCOLVERSION" )
897+ sslminprotocolversion = (tag == "postgres" && k == "ssl_min_protocol_version" ) || (tag == "env" && k == "PGSSLMINPROTOCOLVERSION" )
898+ sslmaxprotocolversion = (tag == "postgres" && k == "ssl_max_protocol_version" ) || (tag == "env" && k == "PGSSLMAXPROTOCOLVERSION" )
899+ requireauth = (tag == "postgres" && k == "require_auth" ) || (tag == "env" && k == "PGREQUIREAUTH" )
838900 )
839901 if k == "" || k == "-" {
840902 continue
@@ -908,6 +970,31 @@ func (cfg *Config) setFromTag(o map[string]string, tag string, service bool) err
908970 cfg .multiHost = append (cfg .multiHost , vv [1 :]... )
909971 }
910972 rv .SetString (v )
973+ case reflect .Slice :
974+ if requireauth {
975+ if v == "" {
976+ rv .Set (reflect .ValueOf ((RequireAuths )(nil )))
977+ continue
978+ }
979+ var (
980+ vv = strings .Split (v , "," )
981+ s = make (RequireAuths , len (vv ))
982+ neg = len (vv ) > 0 && strings .HasPrefix (vv [0 ], "!" )
983+ )
984+ for i := range vv {
985+ if ! slices .Contains (requireAuths , RequireAuth (vv [i ])) {
986+ return fmt .Errorf (f + `%q is not supported; supported values are %s` , k , vv [i ], pqutil .Join (requireAuths ))
987+ }
988+ if neg && ! strings .HasPrefix (vv [i ], "!" ) {
989+ return fmt .Errorf (f + `require_auth method %q cannot be mixed with negative methods` , k , vv [i ])
990+ }
991+ if ! neg && strings .HasPrefix (vv [i ], "!" ) {
992+ return fmt .Errorf (f + `negative require_auth method %q cannot be mixed with non-negative methods` , k , vv [i ])
993+ }
994+ s [i ] = RequireAuth (vv [i ])
995+ }
996+ rv .Set (reflect .ValueOf (s ))
997+ }
911998 case reflect .Int64 :
912999 n , err := strconv .ParseInt (v , 10 , 64 )
9131000 if err != nil {
0 commit comments