Skip to content

Commit 9dfee7d

Browse files
committed
[_688] implement server_version_without_auth
This is a method of the iRODSSession object that returns the connected iRODS server version without having to authenticate first.
1 parent 5d8a43f commit 9dfee7d

File tree

7 files changed

+102
-29
lines changed

7 files changed

+102
-29
lines changed

irods/connection.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ def __init__(self, pool, account):
8383
self._disconnected = False
8484

8585
scheme = self.account._original_authentication_scheme
86+
if self.pool and not self.pool._need_auth:
87+
return
8688

8789
# These variables are just useful diagnostics. The login_XYZ() methods should fail by
8890
# raising exceptions if they encounter authentication errors.

irods/helpers/__init__.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,23 @@ def xml_mode(s):
3636
yield
3737
finally:
3838
ET(None)
39+
40+
41+
class _unlikely_value:
42+
pass
43+
44+
45+
@contextlib.contextmanager
46+
def temporarily_assign_attribute(
47+
target, attr, value, not_set_indicator=_unlikely_value()
48+
):
49+
save = not_set_indicator
50+
try:
51+
save = getattr(target, attr, not_set_indicator)
52+
setattr(target, attr, value)
53+
yield
54+
finally:
55+
if save != not_set_indicator:
56+
setattr(target, attr, save)
57+
else:
58+
delattr(target, attr)

irods/keywords.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
"""From rodsKeyWdDef.hpp
2-
"""
1+
"""From rodsKeyWdDef.hpp"""
32

43
ALL_KW = "all" # operation done on all replicas #
54
COPIES_KW = "copies" # the number of copies #

irods/pool.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import contextlib
12
import datetime
23
import logging
34
import threading
@@ -57,6 +58,7 @@ def __init__(
5758
or application_name
5859
or DEFAULT_APPLICATION_NAME
5960
)
61+
self._need_auth = True
6062

6163
if connection_refresh_time > 0:
6264
self.refresh_connection = True
@@ -65,6 +67,16 @@ def __init__(
6567
self.refresh_connection = False
6668
self.connection_refresh_time = None
6769

70+
@contextlib.contextmanager
71+
def no_auto_authenticate(self):
72+
import irods.helpers
73+
74+
with irods.helpers.temporarily_assign_attribute(self, "_need_auth", False):
75+
yield self
76+
77+
def set_session_ref(self, session):
78+
self.session_ref = weakref.ref(session) if session is not None else lambda: None
79+
6880
@property
6981
def _conn(self):
7082
return getattr(self._thread_local, "_conn", None)

irods/session.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import ast
22
import atexit
3+
import contextlib
34
import copy
45
import errno
56
import json
@@ -355,10 +356,40 @@ def port(self):
355356

356357
@property
357358
def server_version(self):
359+
return self._server_version()
360+
361+
def server_version_without_auth(self):
362+
"""Returns the same version tuple as iRODSSession's server_version property, but
363+
does not require successful authentication.
364+
"""
365+
from irods.connection import Connection
366+
367+
with self.clone().pool.no_auto_authenticate() as pool:
368+
return Connection(pool, pool.account).server_version
369+
370+
GET_SERVER_VERSION_WITHOUT_AUTH = staticmethod(
371+
lambda s: s.server_version_without_auth()
372+
)
373+
374+
def _server_version(self, version_func=None):
375+
"""The server version can be retrieved by the usage:
376+
377+
session._server_version()
378+
379+
with conditional substitution by another version by use of the environment variable:
380+
381+
PYTHON_IRODSCLIENT_REPORTED_SERVER_VERSION.
382+
383+
Also: if iRODSServer.GET_SERVER_VERSION_WITHOUT_AUTH is passed in version_func, the true server
384+
version can be accessed without first going through authentication.
385+
Example:
386+
ses = irods.helpers.make_session()
387+
vsn = ses._server_version( ses.GET_SERVER_VERSION_WITHOUT_AUTH )
388+
"""
358389
reported_vsn = os.environ.get("PYTHON_IRODSCLIENT_REPORTED_SERVER_VERSION", "")
359390
if reported_vsn:
360391
return tuple(ast.literal_eval(reported_vsn))
361-
return self.__server_version()
392+
return self.__server_version() if version_func is None else version_func(self)
362393

363394
def __server_version(self):
364395
try:

irods/test/connection_test.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
#! /usr/bin/env python
22

3+
import numbers
34
import os
45
import sys
56
import tempfile
67
import unittest
7-
from irods.exception import NetworkException
88
from irods import MAXIMUM_CONNECTION_TIMEOUT
9+
from irods.exception import NetworkException, CAT_INVALID_AUTHENTICATION
10+
import irods.session
911
import irods.test.helpers as helpers
10-
from irods.test.helpers import (
11-
server_side_sleep,
12-
temporarily_assign_attribute as temp_setter,
13-
)
12+
from irods.test.helpers import server_side_sleep
13+
from irods.helpers import temporarily_assign_attribute as temp_setter
1414

1515

1616
class TestConnections(unittest.TestCase):
@@ -34,6 +34,29 @@ def test_connection_destructor(self):
3434
self.assertTrue(conn._disconnected)
3535
conn.release(destroy=True)
3636

37+
def test_server_version_without_authentication__issue_688(self):
38+
sess = self.sess
39+
40+
# Make a session object that cannot authenticate.
41+
non_authenticating_session = irods.session.iRODSSession(
42+
host=sess.host,
43+
port=sess.port,
44+
user=sess.username,
45+
zone=sess.zone,
46+
# No password.
47+
)
48+
49+
# Test server_version_without_auth method returns a value.
50+
version_tup = non_authenticating_session.server_version_without_auth()
51+
52+
# Test returned value is non-empty "version" tuple, i.e. holds only integer values.
53+
self.assertGreater(len(version_tup), 0)
54+
self.assertFalse(any(not isinstance(_, numbers.Integral) for _ in version_tup))
55+
56+
# Test that the older server_version property fails for the unauthenticated session object.
57+
with self.assertRaises(CAT_INVALID_AUTHENTICATION):
58+
_ = non_authenticating_session.server_version
59+
3760
def test_failed_connection(self):
3861
# Make sure no connections are cached in self.sess.pool.idle to be grabbed by get_connection().
3962
# (Necessary after #418 fix; make_session() can probe server_version, which then leaves an idle conn.)

irods/test/helpers.py

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@ def recast(k):
172172
return (config, auth)
173173

174174

175+
def get_server_version_for_test(session, curtail_length):
176+
return session._server_version(session.GET_SERVER_VERSION_WITHOUT_AUTH)[
177+
:curtail_length
178+
]
179+
180+
175181
# Create a connection for test, based on ~/.irods environment by default.
176182

177183

@@ -193,7 +199,7 @@ def make_session(test_server_version=True, **kwargs):
193199
env_file = env_filename_from_keyword_args(kwargs)
194200
session = iRODSSession(irods_env_file=env_file, **kwargs)
195201
if test_server_version:
196-
connected_version = session.server_version[:3]
202+
connected_version = get_server_version_for_test(session, curtail_length=3)
197203
advertised_version = IRODS_VERSION[:3]
198204
if connected_version > advertised_version:
199205
msg = (
@@ -430,26 +436,6 @@ def enableLogging(logger, handlerType, args, level_=logging.INFO):
430436
logger.removeHandler(h)
431437

432438

433-
class _unlikely_value:
434-
pass
435-
436-
437-
@contextlib.contextmanager
438-
def temporarily_assign_attribute(
439-
target, attr, value, not_set_indicator=_unlikely_value()
440-
):
441-
save = not_set_indicator
442-
try:
443-
save = getattr(target, attr, not_set_indicator)
444-
setattr(target, attr, value)
445-
yield
446-
finally:
447-
if save != not_set_indicator:
448-
setattr(target, attr, save)
449-
else:
450-
delattr(target, attr)
451-
452-
453439
# Implement a server-side wait that ensures no TCP communication from server end for a given interval.
454440
# Useful to test the effect of socket inactivity on a client. See python-irodsclient issue #569
455441
def server_side_sleep(session, seconds):

0 commit comments

Comments
 (0)