-
Notifications
You must be signed in to change notification settings - Fork 32
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
Session ID and constructor #400
Conversation
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.
I think omero-user-token
and joinSession
is a red herring. I am able to reproduce the initial issue when calling omero.gateway.BlitzGateway.getSession
using the following minimal example
import omero
from omero.gateway import BlitzGateway
client=omero.client(server)
client.createSession(user, password)
with BlitzGateway(client_obj=client) as conn:
conn.getUser()
conn.getSession()
Minimally, the proposed change allows the session UUID to be retrieved dynamical and conn.getSession()
works as expected in the scenario above.
This raises a few higher level API questions about using client objects directly which I could not answer by looking at the implementation or the documentation:
-
are there other APIs depending on the internal
self._sessionUuid
value and if should this internal field be updated as well ingetSession()
? -
what is the expectation when passing a client object to the constructor of the BlitzGateway? Is the contract that the client should have an initialised session? Or is it possible to pass an empty client and use another API to create a session?
-
related to the previous point, I was surprised to see that
import omero from omero.gateway import BlitzGateway client=omero.client(server) client.createSession(user, password) with BlitzGateway(client_obj=client) as conn: conn.connect()
returns
False
although this has some relationship to the fact that_sessionUuid
is set toNone
. Is this a valid workflow we should consider or is it expected that the user should never callconnect()
if a client object is passed?
I was thinking about similar issues last night and I agree that it requires some investigations |
From the
This indicates that a connection is established so the client should have an established connection but nothing is set. |
I'd also been looking into this and came to a similar conclusion. Fetching the UUID in the If my understanding is correct this would then allow users to join or rejoin the session by calling |
Digging a bit more into the problem
returns I use the following code snippet to validate
This will return If |
Setting the
error
No idea about this ldap error since I am not using a LDAP user |
This works fine so something in the gateway is happening |
I found the problem:
|
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.
This might have created some possible regressions one of the gateway integration tests - see https://merge-ci.openmicroscopy.org/jenkins/job/OMERO-test-integration/374/testReport/OmeroPy.test.integration.gatewaytest.test_chmod/TestDefaultSetup/testAuthorCanEdit/
Also it would be useful to update the PR title and description to match the latest changes and possibly list the different connection/reconnection scenarios that should be tested
The regression is possibly due to the fact that the session is not discarded if it is coming from the client. |
I have updated the description with various scenarii I have tried |
src/omero/gateway/__init__.py
Outdated
@@ -1525,14 +1525,24 @@ def __init__(self, username=None, passwd=None, client_obj=None, group=None, | |||
self.ice_config = [os.path.abspath(str(x)) for x in [_f for _f in self.ice_config if _f]] | |||
|
|||
self.host = host | |||
if self.c is not None: | |||
self.host = self.c.getProperty("omero.host") |
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.
I could imagine an scenario where a user tries to connect to a server with the wrong client object, which would then cause some ambiguity over exactly which server Blitz is connected to.
Could I suggest that we check if self.host is already set to something different here, and if there's a mismatch throw an error?
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.
We could add a check but i think people either specify the "client" or the host/user/password
Checking the host does not stop the connection to a wrong server. Using the "connect" specifying the sessionID could lead to that situation cf. one of the examples in the description and there is "false" value return
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.
i think the gateway is too "flexible" leading to some very strange situations.
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.
Should we also set the identity if a client with connection is specified?
the same will apply i.e. checking the value from client and via constructor.
Client with session specified, specify user name and password different from the client ones. Identity in gateway does not correspond to the identity in client
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.
I agree that people should either specify client or host, but in the event that they don't I think it's safer to avoid silently overriding the user-supplied host
parameter. Some of the autocompletion tools people now use do have a habit of trying to fill every argument. The user really should be reading the docs, but at the same time I don't think Gateway's Python docs are as helpful as they need to be.
You're probably right that Gateway's excessive flexibility here may be causing more problems than it solves. An alternative might be to validate the connection in the client object and outright reject calls with the other parameters specified - you either init with a connected client or supply credentials, no "mix and match".
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.
There are other problems especially with the connect
method since new clients get created (even if one was specified in the constructor).
So probably need to describe what we expect from each method moving forward
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.
The more I look into it the more I notice points that require clarification.
The clone
method for example will not work when a client with session is specified.
To make it works one should do
BlitzGateway(client_obj=client)
conn.setIdentity(user, password)
We also lose info like useragent
and userid
in that case
There is a test failing at https://merge-ci.openmicroscopy.org/jenkins/job/OMERO-test-integration/lastCompletedBuild/testReport/OmeroPy.test.integration.gatewaytest.test_chmod/TestDefaultSetup/testAuthorCanEdit/ |
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.
Few comments after a review triggered by the failure in https://merge-ci.openmicroscopy.org/jenkins/job/OMERO-test-integration/17/testReport/OmeroPy.test.integration.gatewaytest.test_chmod/TestDefaultSetup/testAuthorCanEdit/
src/omero/gateway/__init__.py
Outdated
self.secure = secure | ||
if self.c is not None: | ||
self.secure = self.c.isSecure() |
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.
Not overly important, but should there be a mismatch test here?
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.
I had a similar question while performing code review but this was marked as resolved. Was this addressed somewhere?
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.
tested added there https://github.com/ome/openmicroscopy/pull/6381/files
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.
My interpretation of @joshmoore's question was: what should happen if a gateway object is constructed with an initialized client object and secure={True,False}
and the encryption state of the client as returned by self.c.isSecure()
does not match the secure
keyword?
@DavidStirling I have added integration tests for the proposed changes (all green) |
LGTM, thanks |
@@ -2029,7 +2049,7 @@ def isSecure(self): | |||
Returns 'True' if the underlying omero.clients.BaseClient is connected | |||
using SSL | |||
""" | |||
return hasattr(self.c, 'isSecure') and self.c.isSecure() or False |
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.
Isn't that modifying the contract of the API before a connection happens e.g. in a scenario like the following
>>> conn=BlitzGateway(username="test",passwd="test",host="localhost",secure=False)
>>> conn.isSecure()
True
>>> conn.secure
False
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.
This raises another question in that case
When a client is not specified, a secure
client is created regardless of the secure
parameter specified. But when a client is specified, it can be secured or unsecured.
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.
The initial connection will (and should) always use an encrypted client. But
omero-py/src/omero/gateway/__init__.py
Line 2074 in 4dc40d6
self.setSecure(self.secure) |
_createSession
might create an unencrypted client depending on the state of secure
:
>>> conn=BlitzGateway(username=user,passwd=password,host="localhost")
>>> conn.connect()
True
>>> conn.isSecure()
False
>>> conn.close()
>>> conn=BlitzGateway(username=user,passwd=password,host="localhost",secure=True)
>>> conn.connect()
True
>>> conn.isSecure()
True
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.
i get that
but as I said the client specified could be insecured and in that case even if secure=True
is passed
conn.isSecure()
will be false
.
Do we want in that case to enforce a "secure" client specified during init
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.
So the scenario we are discussing is
conn=BlitzGateway(client_obj=insecure_client, secure=True)
conn=BlitzGateway(client_obj=secure_client, secure=False)
Interestingly, part of the changes proposed here are to throw an exception on the following use cases
conn=BlitzGateway(client_obj=client, host=different_host)
conn=BlitzGateway(client_obj=client, port=different_port)
My feeling would be to say secure
should behave in the same way i.e. if the constructor receives conflicting information from its inputs, an exception should be sent back to the caller. Unlike host
and port
the default of secure
is False
. A possible (untested) solution would be to change the default to None
to cater for this scenario (and internally default to False
if no client object is passed).
src/omero/gateway/__init__.py
Outdated
self.secure = secure | ||
if self.c is not None: | ||
self.secure = self.c.isSecure() |
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.
I had a similar question while performing code review but this was marked as resolved. Was this addressed somewhere?
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.
The last changes were tested with the following test script
from omero.gateway import BlitzGateway
import omero
print("Connect with user/password, no secure argument")
with BlitzGateway(username=user, passwd=password, host=host) as conn:
print("Connected: " + str(conn.connect()))
print(f"conn.c.isSecure: {conn.c.isSecure()} "
f"conn.isSecure: {conn.isSecure()} "
f"conn.secure: {conn.secure}")
print("Creating encrypted client")
conn.setSecure(True)
print(f"conn.c.isSecure: {conn.c.isSecure()} "
f"conn.isSecure: {conn.isSecure()} "
f"conn.secure: {conn.secure}")
print("\n")
print("Connect with user/password and secure=False")
with BlitzGateway(username=user, passwd=password, host=host, secure=False) as conn:
print("Connected: " + str(conn.connect()))
print(f"conn.c.isSecure: {conn.c.isSecure()} "
f"conn.isSecure: {conn.isSecure()} "
f"conn.secure: {conn.secure}")
print("Creating encrypted client")
conn.setSecure(True)
print(f"conn.c.isSecure: {conn.c.isSecure()} "
f"conn.isSecure: {conn.isSecure()} "
f"conn.secure: {conn.secure}")
print("\n")
print("Connect with user/password and secure=True")
with BlitzGateway(username=user, passwd=password, host=host, secure=True) as conn:
print("Connected: " + str(conn.connect()))
print(f"conn.c.isSecure: {conn.c.isSecure()} "
f"conn.isSecure: {conn.isSecure()} "
f"conn.secure: {conn.secure}")
print("Creating unencrypted client")
conn.setSecure(False)
print(f"conn.c.isSecure: {conn.c.isSecure()} "
f"conn.isSecure: {conn.isSecure()} "
f"conn.secure: {conn.secure}")
c = omero.client(host)
c.createSession(user, password)
print("\n")
print("Connect with encrypted client object")
with BlitzGateway(client_obj=c) as conn:
print(f"conn.c.isSecure: {conn.c.isSecure()} "
f"conn.isSecure: {conn.isSecure()} "
f"conn.secure: {conn.secure}")
print("Creating unencrypted client")
conn.setSecure(False)
print(f"conn.c.isSecure: {conn.c.isSecure()} "
f"conn.isSecure: {conn.isSecure()} "
f"conn.secure: {conn.secure}")
c.closeSession()
c = omero.client(host)
c.createSession(user, password)
c2 = c.createClient(secure=False)
c.__del__()
print("\n")
print("Connect with unencrypted client object")
with BlitzGateway(client_obj=c2) as conn:
print(f"conn.c.isSecure: {conn.c.isSecure()} "
f"conn.isSecure: {conn.isSecure()} "
f"conn.secure: {conn.secure}")
print("Creating encrypted client")
conn.setSecure(True)
print(f"conn.c.isSecure: {conn.c.isSecure()} "
f"conn.isSecure: {conn.isSecure()} "
f"conn.secure: {conn.secure}")
c2.closeSession()
print("\n")
print("Connect with encrypted client object and secure=False")
c = omero.client(host)
c.createSession(user, password)
try:
conn = BlitzGateway(client_obj=c, secure=False)
except Exception as e:
print(str(e))
c.closeSession()
c = omero.client(host)
c.createSession(user, password)
c2 = c.createClient(secure=False)
c.__del__()
print("Connect with unencrypted client object and secure=True")
try:
conn = BlitzGateway(client_obj=c2, secure=True)
except Exception as e:
print(str(e))
c2.closeSession()
with the following output
Connect with user/password, no secure argument
Connected: True
conn.c.isSecure: False conn.isSecure: False conn.secure: False
Creating encrypted client
conn.c.isSecure: True conn.isSecure: True conn.secure: True
Connect with user/password and secure=False
Connected: True
conn.c.isSecure: False conn.isSecure: False conn.secure: False
Creating encrypted client
conn.c.isSecure: True conn.isSecure: True conn.secure: True
Connect with user/password and secure=True
Connected: True
conn.c.isSecure: True conn.isSecure: True conn.secure: True
Creating unencrypted client
conn.c.isSecure: False conn.isSecure: False conn.secure: False
Connect with encrypted client object
conn.c.isSecure: True conn.isSecure: True conn.secure: True
Creating unencrypted client
conn.c.isSecure: False conn.isSecure: False conn.secure: False
Connect with unencrypted client object
conn.c.isSecure: False conn.isSecure: False conn.secure: False
Creating encrypted client
conn.c.isSecure: True conn.isSecure: True conn.secure: True
Connect with encrypted client object and secure=False
Secure flag False and True do not match
Connect with unencrypted client object and secure=True
Secure flag True and False do not match
The state of the secure
keyword is now consistent across all usages. Happy fo this to get merged assuming the integration tests pass
Integration tests are green but I could add a few more to match your detailed review |
Supporting tests are now all green. Proposing to merge and release as 5.19.2 (alongside a few approved PRs) |
No objection from my side. Do you have a shortlist of all PRs proposed for inclusion? |
Would be great to get all those PRs in 👍 |
Looks good to me. I am also in favor of including #405 as it reduces the possibility of calls which can cause memory issues server-side. As discussed on the PR, the public API is not modified so I think it could be amenable for either a patch or minor release increment. |
Specifying a
client
as a parameter leads to strange behaviour when attempting to access session orconnect
Looking at the constructor of the BlitzGateway, the
connected
flag is set even if a client without session is specified.This leads to error like
See TheJacksonLaboratory/ezomero#100 for background
This PR:
host
andport
from the client if the client is passed a parameterconnect
is invoked. In that case a new client is created and the Gateway joins the sessionTo test:
Check that the sessionID is set.
Check that the session is not closed
Check you can connect after setting the identity
Note that none of the code snippets work if the host is not retrieved from the specified client. In order to work the
host
parameter needs to be set