77from time import strftime
88from time import strptime
99
10+ from pyunicore .client import Resource
1011from pyunicore .client import Transport
1112from pyunicore .credentials import Credential
1213
@@ -17,8 +18,6 @@ class UFTP:
1718 Uses ftplib to open the session and interact with the UFTPD server
1819 """
1920
20- uftp_session_tag = "___UFTP___MULTI___FILE___SESSION___MODE___"
21-
2221 def __init__ (self ):
2322 self .ftp = None
2423 self .uid = os .getuid ()
@@ -48,7 +47,7 @@ def authenticate(self, security, base_url, base_dir="", preferences=None, persis
4847 if preferences is not None :
4948 transport .preferences = preferences
5049 req = {
51- "serverPath" : base_dir + self . uftp_session_tag ,
50+ "serverPath" : base_dir ,
5251 }
5352 if persistent :
5453 req ["persistent" ] = "true"
@@ -152,3 +151,65 @@ def get_write_socket(self, path, offset):
152151 def get_read_socket (self , path , offset ):
153152 path = self .normalize (path )
154153 return self .ftp .transfercmd ("RETR %s" % path , rest = offset )
154+
155+
156+ class AuthServer :
157+ """
158+ Helper to interact with an UFTP Authserver.
159+ """
160+
161+ def __init__ (self , security : Credential | Transport , url : str ):
162+ if isinstance (security , Credential ):
163+ self .transport = Transport (security )
164+ elif isinstance (security , Transport ):
165+ self .transport = security ._clone ()
166+ else :
167+ raise TypeError ("Need Credential or Transport object" )
168+ self .auth_url = url
169+ parts = url .split ("/rest/auth/" )
170+ if len (parts ) == 1 :
171+ raise ValueError (f"Not an AuthServer URL: { url } " )
172+ self .base_url = parts [0 ] + "/rest/auth"
173+ self .server_name = parts [1 ].split (":" )[0 ]
174+ self .info = self .transport .get (url = self .base_url )
175+
176+ def authenticate (self , base_dir = "" , persistent = True ):
177+ """authenticate to the auth server and return a tuple (host, port, one-time-password)"""
178+ if base_dir != "" and not base_dir .endswith ("/" ):
179+ base_dir += "/"
180+ req = {"serverPath" : base_dir }
181+ if persistent :
182+ req ["persistent" ] = "true"
183+ auth_url = self .base_url + "/" + self .server_name
184+ params = self .transport .post (url = auth_url , json = req ).json ()
185+ return params ["serverHost" ], params ["serverPort" ], params ["secret" ]
186+
187+ def is_sharing_supported (self ) -> bool :
188+ try :
189+ return self .info [self .server_name ]["dataSharing" ]["enabled" ]
190+ except KeyError :
191+ return False
192+
193+ def get_sharing_endpoint (self ) -> Resource :
194+ resource_url = self .info [self .server_name ]["dataSharing" ]["href" ]
195+ return Resource (security = self .transport , resource_url = resource_url )
196+
197+ def issue_auth_token (self , lifetime = - 1 , renewable = False , limited = False ) -> str :
198+ """
199+ Issue an authentication token (JWT) from this Auth server
200+ Args:
201+ lifetime: lifetime in seconds. If <=0, the server default will be used
202+ limited: if True, the token will only be useable on this server
203+ renewable: if True, the token can be used to get a new token
204+ """
205+ url = self .base_url + "/token"
206+ params = {}
207+ if lifetime > 0 :
208+ params ["lifetime" ] = lifetime
209+ if renewable :
210+ params ["renewable" ] = "true"
211+ if limited :
212+ params ["limited" ] = "true"
213+ return self .transport .get (
214+ url = url , headers = {"Accept" : "text/plain" }, to_json = False , params = params
215+ ).text
0 commit comments