go-goracle / goracle

Go database/sql driver for connecting to Oracle Database, using the ODPI-C library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Expose odpi dpiConnCreateParams.newPassword

kurt-google opened this issue · comments

Is your feature request related to a problem? Please describe.
odpi appears to expose a method for setting passwords when you connect to an expired account, and would be helpful so expired accounts can be reset via goracle.

Describe the solution you'd like
Exposing it via a URI parameter similar to the rest of the dpiConnCreateParams seems fine and discover-able. It wouldnt be usable via ezconnect or tns style connect strings, but thats fine for me.

Not even that, but just exposing the connection parameter https://oracle.github.io/odpi/doc/structs/dpiConnCreateParams.html#c.dpiConnCreateParams.newPassword

When this value is set and you open a connection via dpiConn_create it will automatically handle calling the dpiConn_changePassword function for you (for standalone connections).

return dpiOci__passwordChange(conn, userName, userNameLength, password,

Reviewing dpiConn_changePassword looks like it checks that the connection is connected so it wont work for resetting an expired account's password.

Calling dpiConn_changePassword directly will require an existing connection. Setting the parameter dpiConnCreateParams.newPassword when establishing the connection will permit the password to be changed as the connection takes place. Authentication takes place with the original password and once the connection is established, the new password is active. I hope that's clear?

Ok, so what about a "newPassword" connection param, which induces a standalone connection (no pooling), fills dpiConnCreateParams.newPassword?

Shall we make it a Once, or can we leave it to the user?
I mean, do we have to erase that newPassword after a successful connection, or can we leave it to the user, and only document that a connection with a newPassword can be used only once?

Or make this thing a goracle-specific function, and keep it out of regular database/sql connection strings?

My preference is just exposing it as a parameter and inducing standalone when set.

I think it makes sense to leave it to the user to drop the parameter once a password is reset. I expect to use it such that a connection is attempted, if the failure is a due to expired passwords, ill reconnect with the parameter. For my use case pooling isnt very important, but if users required pools they should probably drop the standalone and retry normally.

This retry (restablish a pooled connection after reset if user didnt specify standalone) could be put into the driver i suppose and it might be convenient. But i think any solution should make it clear to the user what their final established connection type is (standalone or pooled).

@kurt-google please take a look on 34542e2 !

Sorry for the slow turn around. It doesnt appear to be entering the ODPI change password path. Logs from a run with goracle.Log set and DPI_LOG_LEVEL=4 with a target like "oracle://testexpired:simplepass@127.0.0.1:1521/MYDB?standaloneConnection=1&newPassword=simplepass1" with the connection triggered with a db.Query("select 1 from dual")

Is there an issue with my parameters that you can see?

ODPI [08519] 2019-11-18 14:15:04.547: ODPI-C 3.2.2
ODPI [08519] 2019-11-18 14:15:04.547: debugging messages initialized at level 4
ODPI [08519] 2019-11-18 14:15:04.731: fn start dpiContext_create
ODPI [08519] 2019-11-18 14:15:04.735: fn end dpiContext_create -> 0
ODPI [08519] 2019-11-18 14:15:04.735: fn start dpiContext_getClientVersion(0x563127e377b0)
ODPI [08519] 2019-11-18 14:15:04.735: fn end dpiContext_getClientVersion(0x563127e377b0) -> 0
ODPI [08519] 2019-11-18 14:15:04.735: fn start dpiContext_initConnCreateParams(0x563127e377b0)
ODPI [08519] 2019-11-18 14:15:04.735: fn end dpiContext_initConnCreateParams(0x563127e377b0) -> 0
ODPI [08519] 2019-11-18 14:15:04.735: fn start dpiContext_initCommonCreateParams(0x563127e377b0)
ODPI [08519] 2019-11-18 14:15:04.735: fn end dpiContext_initCommonCreateParams(0x563127e377b0) -> 0
C=dpiConn_create params="oracle://testexpired:SECRET-ETEbf4LVRig=@127.0.0.1:1521/MYDB?connectionClass=GORACLE&poolIncrement=1&poolMaxSessions=1000&poolMinSessions=1&sysdba=0&sysoper=0&sysasm=0&standaloneConnection=1&enableEvents=0&heterogeneousPool=0&prelim=0&poolWaitTimeout=30s&poolSessionMaxLifetime=1h0m0s&poolSessionTimeout=5m0s&timezone=&newPassword=SECRET-omCjsK29dSk=" common="unsupported value type" conn="unsupported value type"
ODPI [08519] 2019-11-18 14:15:04.735: fn start dpiConn_create(0x563127e377b0)
ODPI [08519] 2019-11-18 14:15:05.098: fn end dpiConn_create(0x563127e377b0) -> -1
pools="unsupported value type" conn="oracle://testexpired:SECRET-ETEbf4LVRig=@127.0.0.1:1521/MYDB?connectionClass=GORACLE&poolIncrement=1&poolMaxSessions=1000&poolMinSessions=1&sysdba=0&sysoper=0&sysasm=0&standaloneConnection=1&enableEvents=0&heterogeneousPool=0&prelim=0&poolWaitTimeout=30s&poolSessionMaxLifetime=1h0m0s&poolSessionTimeout=5m0s&timezone=&newPassword=SECRET-omCjsK29dSk="
Username="testexpired" sid="127.0.0.1:1521/MYDB" params={authMode:0 connectionClass:0x563127e377e0 connectionClassLength:7 purity:0 newPassword:0x563127e378d0 newPasswordLength:15 appContext:<nil> numAppContext:0 externalAuth:0 externalHandle:<nil> pool:<nil> tag:<nil> tagLength:0 matchAnyTag:0 outTag:<nil> outTagLength:0 outTagFound:0 shardingKeyColumns:<nil> numShardingKeyColumns:0 superShardingKeyColumns:<nil> numSuperShardingKeyColumns:0 outNewSession:0}: ORA-28001: the password has expired

And a sanity check with setting the expired password with sqlplus works.

standaloneConnection=1
newPassword:0x563127e378d0 newPasswordLength:15

seems to show that the new password is set and provided to ODPI.

Try with DPI_DEBUG_LEVEL=38
and maybe sprinkeling odpi/src/dpiConn.c with printfs (around line 441).

I may help more, if you help me how to setup such an expired user, reliably and repeatably.

@tgulacsi, you can run this command:

alter user myuser password expire;

That will cause the password to be expired and the next time you log in you will get the error: ORA-28001: the password has expired -- unless you specify the "newPassword" in the connection creation parameters.

Thanks @anthony-tuininga !

@kurt-google please see TestNewPassword at the end of z_test.go at 76585c7 !
It works as intended (you have to connect with StandaloneConnection=true, NewPassword=filled, and later with empty NewPassword).

I ran the test from z_test.go and it passed. However it appears to never make a connection to the database when inspected with DPI_DEBUG_LEVEL. Running it and attempting to connect after the test completes the password is still expired.

I've applied 0085087 and retried with pings between the two connections but this also fails with ORA-28001 on the first ping and ORA-01017 on the 2nd where it tries the new password.

--- edit ---
Had the ORA errors swapped when I posted.

This appears similar to the issue we had with prelim connections, if the connection class is set then standalone connections wont be made in the odpi layer. Adding P.StandaloneConnection check to https://github.com/go-goracle/goracle/blob/master/drv.go#L420 got it to call dpiConn__createStandalone however from there my test segfaulted on dpiOci__serverAttach so it may not be the entire solution or my environment might be misconfigured.

--- edit ---
The relevant odpi code at https://github.com/go-goracle/goracle/blob/master/odpi/src/dpiConn.c#L367

The segfault was indeed a misconfiguration of my environment. After fixing my environment and making the modification on drv.go:420 passwords are reset when a connection is opened so my test case:

db := sql.Open(..."?standaloneConnection=1&newPassword=newpass")
db.Query("select 1 from dual")

Works when the user has an expired password (and also when the password is unexpired).

LGTM on 1733a02

Thanks!