New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add plugin manager to allow dynamically supplying passwords #2369
Conversation
…ties Changes internal constructors for PgConnection and related classes to only accept the connection properties object and remove the user and password arguments. Any locations that required those fields can retrieve them from the properties map.
|
||
AuthenticationPlugin authPlugin; | ||
try { | ||
authPlugin = (AuthenticationPlugin) ObjectFactory.instantiate(authPluginClassName, info, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to cache the instance ? Is it possible that different connections would use different authentication plugins ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No it should not be cached as connections should be isolated from each other. If a user needs to maintain state across multiple runtime instantiations then it would need to be done in the user's implementation through either static fields or some kind of proxying.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about that a bit more, perhaps we need a new opaque "arg" property that users can use to customize their implementation. The plugin constructor takes the connection properties but there's no specific property that user's could use for their own purposes. Something like authPluginArg
(e.g. the plugin equivalent of socketFactoryArg
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fairly good chance that the user will only use one auth plugin. I wonder what the cost of instantiating a class really is (in other words is it worth worrying about)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I doubt it's an issue in practice and connections tend to be long lived anyway. The time spent opening the underlying socket and TLS handshake is going to be significantly more than any object creation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough.
I wonder why this is failing on windows ? |
Looks like two separate issues failing the AppVeyor build but neither of them is actually Windows related. The v9.6 environment is failing because the testing isn't correctly being skipped on older platforms. The v10 environment is random failures from I'm going to push a fix for the test skipping. |
…t runtime Adds authenticationPluginClassName connection property that allows end users to specify a class that will provide the connection passwords at runtime. This allows configuring a connection with a password that must be generated on the fly or periodically changes. Custom implementations have access to the full connection properties and the type of authentication that is being requested by the server, e.g. MD5 or SCRAM.
e8ec934
to
b95ef95
Compare
@sehrope I don't see any reason not to merge this. |
Sounds good. I'll merge this in a bit. |
@Nullable | ||
String getPassword(AuthenticationRequestType type) throws PSQLException; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Security best practices suggest that passwords should be used as char[]
so they can be nullified after use: https://www.baeldung.com/java-storing-passwords
@@ -142,6 +142,7 @@ In addition to the standard connection parameters the driver supports a number o | |||
| adaptiveFetchMaximum | Integer | -1 | Specifies maximum number of rows, which can be calculated by adaptiveFetch. Number of rows used by adaptiveFetch cannot go above this value. Any negative number set as adaptiveFetchMaximum is used by adaptiveFetch as infinity number of rows. | |||
| localSocketAddress | String | null | Hostname or IP address given to explicitly configure the interface that the driver will bind the client side of the TCP/IP connection to when connecting. | |||
| quoteReturningIdentifiers | Boolean | true | By default we double quote returning identifiers. Some ORM's already quote them. Switch allows them to turn this off | |||
| authenticationPluginClassName | String | null | Fully qualified class name of the class implementing the AuthenticationPlugin interface. If this is null, the password value in the connection properties will be used. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Frankly speaking, I believe the naming is misleading. What the thing does it supplies passwords, so it is not clear why do we call it authenticationPlugin rather than passwordCallback or something like that.
There's a similar interface in Java: javax.security.auth.callback.PasswordCallback
@sehrope I think these suggestions make sense. Thoughts ? |
@RunWith(Suite.class) | ||
@SuiteClasses({ | ||
AuthenticationPluginTest.class, | ||
}) | ||
public class PluginTestSuite { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need suites? I think we no longer use them.
I considered having the interface be char[] when I put this together but stuck with String as the rest of the driver deals with String passwords directly (e.g. receiving them via java.util.Properties, passing them as a String into the SASL functions). It'd have been a much larger change to cascade it through all the downstream call sites. I'm not against it, just concerned that the overall changes required to meaningfully support that are larger and more likely to break something.
In that case we should get rid of all of the suites. It was added to be consistent with the existing code.
Well to be fair, I did specifically request feedback on the public interface naming... I picked "Auth..." over simply "Password..." because it's meant to handle the authentication stage of the protocol. In most cases that involves a password but it might be extended to handle a non-password step in the future. The interface also includes the additional parameter for the authentication type (e.g. plain, MD5, or SASL) to allow the client further customize it's handling (e.g. reject sending things over in plaintext). I'm fine with changing the name to something more appropriate but it's more than just a password callback. |
I think we should fix that as much as we can. I don't know what to do with
Could you please clarify the use-case? |
The original user issue that lead to this and #2321 (replaced by this PR) is this: #2102 It's for connecting to systems like RDS that allow for passwords that are really short lived digital signatures. They can't be static values as they expire in a matter of minutes so a connection pool that would be periodically creating new connections would need to generate them on the fly. Rather than supporting N different mechanisms directly, the idea is to have the user supply a class that provides the password on the fly at connect time. That way the digital signature or whatever else is involved to generate the password can happen at the last possible moment. |
This is a revised version of #2321. I'll close that one out in favor of this.
The first two commits do some refactoring to consolidate where username and passwords are pulled from the properties map and removing those args from the various internal creation methods. This simplifies the changeset for actual feature which adds a plugin hook for dynamically providing the password. The plugin callback also provides the authentication type so the caller has a bit more information to make a decision (e.g. the caller can ensure they are using SCRAM and not sending the password over plaintext).
Because of the message flow of the driver, if the server is configured with trust authentication then no callback will be invoked.
@davecramer While reviewing, in particular take a peek at the public interfaces to make sure it makes sense as we'll be stuck with them.
All Submissions:
New Feature Submissions:
./gradlew autostyleCheck checkstyleAll
pass ?