def test_admin_keyword_for_tickets(self): N_TICKETS = 3 # Create some tickets as alice. with self.login(self.alice) as alice: alice_home_path = self.irods_homedir(alice, path_only=True) ticket_strings = [ Ticket(alice).issue('read', alice_home_path).string for _ in range(N_TICKETS) ] # As rodsadmin, use the ADMIN_KW flag to delete alice's tickets. with helpers.make_session() as ses: alices_tickets = [ t[TicketQuery.Ticket.string] for t in ses.query(TicketQuery.Ticket).filter( TicketQuery.Owner.name == 'alice') ] self.assertEqual(len(alices_tickets), N_TICKETS) for s in alices_tickets: Ticket(ses, s).delete(**{kw.ADMIN_KW: ''}) alices_tickets = [ t[TicketQuery.Ticket.string] for t in ses.query(TicketQuery.Ticket).filter( TicketQuery.Owner.name == 'alice') ] self.assertEqual(len(alices_tickets), 0)
def create_session(ticket: str = None): # if we have a ticket, create an anonymous session and apply it to it if ticket is not None and ticket != '': session = iRODSSession(host='data.cyverse.org', port=1247, user='******', password='', zone='iplant') Ticket(session, ticket).supply() return session # otherwise check for an iRODS environment file (if we don't have one, abort) try: env_file = os.environ.get('IRODS_ENVIRONMENT_FILE') except KeyError: default_env_file = '~/.irods/irods_environment.json' if isfile(default_env_file): env_file = expanduser(default_env_file) else: raise ValueError( f"No iRODS authentication method provided (need ticket or environment file)" ) ssl_context = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH, cafile=None, capath=None, cadata=None) ssl_settings = {'ssl_context': ssl_context} return iRODSSession(irods_env_file=env_file, **ssl_settings)
def test_file_exists(remote_base_path): with TemporaryDirectory() as testdir: file1_name = 'f1.txt' file2_name = 'f2.txt' file1_path = join(testdir, file1_name) remote_path = join(remote_base_path, str(uuid.uuid4())) try: # prep collection create_collection(remote_path, token) # create files with open(file1_path, "w") as file1: file1.write('Hello, 1!') # upload files upload_file(file1_path, remote_path, token) # create iRODS session session = iRODSSession(host='data.cyverse.org', port=1247, user='******', password='', zone='iplant') ticket = TerrainTicket.get([join(remote_path, file1_name)], uses=2) Ticket(session, ticket).supply() # test remote files exist assert irods_store.file_exists(join(remote_path, file1_name), session) assert not irods_store.file_exists(join(remote_path, file2_name), session) finally: delete_collection(remote_path, token)
def _ticket_write_helper(self, obj_type): with iRODSSession(**self.rodsuser_params) as user_sess: Ticket(user_sess, self.tickets[obj_type]['write']).supply() data = user_sess.data_objects.get(self.data_path) with data.open('w') as obj: obj.write(self.MODIFIED_DATA) self.assertEqual(data.open('r').read(), self.MODIFIED_DATA)
def setUp(self): """Create objects for test""" self.sess = helpers.make_session() user = self.sess.users.get(self.sess.username) if user.type != 'rodsadmin': self.skipTest('''Test runnable only by rodsadmin.''') admin = self.sess delete_my_tickets(admin) # Create test collection self.coll_path = '/{}/home/{}/ticket_test_dir'.format( admin.zone, admin.username) self.coll = helpers.make_collection(admin, self.coll_path) # Create anonymous test user self.user = admin.users.create('anonymous', 'rodsuser') self.rodsuser_params = { 'host': admin.host, 'port': admin.port, 'user': '******', 'password': '', 'zone': admin.zone } # make new data object in the test collection with some initialized content self.INITIALIZED_DATA = b'1' * 16 self.data_path = '{self.coll_path}/ticketed_data'.format(**locals()) helpers.make_object(admin, self.data_path, content=self.INITIALIZED_DATA) self.MODIFIED_DATA = b'2' * 16 # make new tickets for the various combinations self.tickets = {'coll': {}, 'data': {}} for obj_type in ('coll', 'data'): for access in ('read', 'write'): ticket = Ticket(admin) self.tickets[obj_type][access] = ticket.string ticket.issue(access, getattr(self, obj_type + '_path'))
def create_irods_session(url): """Create an iRODS session.""" if "ssl" in url.scheme: ssl_settings = { "ssl_context": ssl.create_default_context( purpose=ssl.Purpose.CLIENT_AUTH, cafile=None, capath=None, cadata=None ) } else: ssl_settings = {} if "pam" in url.scheme: irods_authentication_scheme = "pam" else: irods_authentication_scheme = "native" logger.info("Creating iRODS session") session = iRODSSession( host=url.hostname, port=(url.port or 1247), user=url.username, password=url.password or "", zone=(url.path or "/").split("/")[1], irods_authentication_scheme=irods_authentication_scheme, irods_client_server_negotiation=settings.IRODS_CLIENT_SERVER_NEGOTIATION, irods_client_server_policy=settings.IRODS_CLIENT_SERVER_POLICY, irods_ssl_verify_server=settings.IRODS_SSL_VERIFY_SERVER, irods_encryption_algorithm=settings.IRODS_ENCRYPTION_ALGORITHM, irods_encryption_key_size=settings.IRODS_ENCRYPTION_KEY_SIZE, irods_encryption_num_hash_rounds=settings.IRODS_ENCRYPTION_NUM_HASH_ROUNDS, irods_encryption_salt_size=settings.IRODS_ENCRYPTION_SALT_SIZE, **ssl_settings ) query = parse_qs(url.query) if "ticket" in query: logger.info("Setting ticket for session") ticket = Ticket(session, query["ticket"][0]) ticket.supply() return session
def _ticket_read_helper(self, obj_type, download=False): with iRODSSession(**self.rodsuser_params) as user_sess: temp_file = [] if download: temp_file += [tempfile.mktemp()] try: Ticket(user_sess, self.tickets[obj_type]['read']).supply() data = user_sess.data_objects.get(self.data_path, *temp_file) self.assertEqual(data.open('r').read(), self.INITIALIZED_DATA) if temp_file: with open(temp_file[0], 'rb') as local_file: self.assertEqual(local_file.read(), self.INITIALIZED_DATA) finally: if temp_file and os.path.exists(temp_file[0]): os.unlink(temp_file[0])
def test_upload_directory(remote_base_path): with TemporaryDirectory() as testdir: file1_name = 'f1.txt' file2_name = 'f2.txt' file1_path = join(testdir, file1_name) file2_path = join(testdir, file2_name) coll_name = str(uuid.uuid4()) remote_path = join(remote_base_path, coll_name) try: # prep collection create_collection(remote_path, token) # create files with open(file1_path, "w") as file1, open(file2_path, "w") as file2: file1.write('Hello, 1!') file2.write('Hello, 2!') # create iRODS session and get ticket with iRODSSession(host='data.cyverse.org', port=1247, user='******', password='', zone='iplant') as session: ticket = TerrainTicket.get([ join(remote_path, file1_name), join(remote_path, file2_name) ], 'write', False) Ticket(session, ticket).supply() # upload files irods_store.push_dir(testdir, remote_path, session=session, include_patterns=['.txt']) # check uploads coll = session.query(Collection).one() collection = iRODSCollection(session.collections, coll) file_names = [o.name for o in collection.data_objects] print(file_names) assert file1_name in file_names assert file2_name in file_names finally: delete_collection(remote_path, token)
def test_download_directory(remote_base_path): with TemporaryDirectory() as testdir: file1_name = 'f1.txt' file2_name = 'f2.txt' file1_path = join(testdir, file1_name) file2_path = join(testdir, file2_name) remote_path = join(remote_base_path, str(uuid.uuid4())) try: # prep collection create_collection(remote_path, token) # create files with open(file1_path, "w") as file1, open(file2_path, "w") as file2: file1.write('Hello, 1!') file2.write('Hello, 2!') # upload files upload_file(file1_path, remote_path, token) upload_file(file2_path, remote_path, token) # remove local files os.remove(file1_path) os.remove(file2_path) # create iRODS session and get ticket with iRODSSession(host='data.cyverse.org', port=1247, user='******', password='', zone='iplant') as session: ticket = TerrainTicket.get([remote_path], 'write', False) Ticket(session, ticket).supply() # download files irods_store.pull_dir(remote_path, testdir, session=session, patterns=['.txt']) # check downloads assert isfile(file1_path) assert isfile(file2_path) finally: delete_collection(remote_path, token)
def test_directory_exists(remote_base_path): remote_path = join(remote_base_path, str(uuid.uuid4())) with TemporaryDirectory() as testdir: try: # prep collection create_collection(remote_path, token) # create iRODS session session = iRODSSession(host='data.cyverse.org', port=1247, user='******', password='', zone='iplant') ticket = TerrainTicket.get([remote_path]) Ticket(session, ticket).supply() # test remote directories exist assert irods_store.dir_exists(remote_path, session) assert not irods_store.dir_exists( join(remote_base_path, "not_a_collection"), session) finally: delete_collection(remote_path, token)
def test_download_file(remote_base_path): with TemporaryDirectory() as testdir: file_name = 'f1.txt' file_path = join(testdir, file_name) remote_path = join(remote_base_path, str(uuid.uuid4())) local_path = join(testdir, f"d{file_name}") try: # prep collection create_collection(remote_path, token) # create files with open(file_path, "w") as file: file.write('Hello, 1!') # upload files upload_file(file_path, remote_path, token) # create iRODS session and get ticket with iRODSSession(host='data.cyverse.org', port=1247, user='******', password='', zone='iplant') as session: ticket = TerrainTicket.get([remote_path], 'write', False) Ticket(session, ticket).supply() # download file irods_store.pull_file(join(remote_path, file_name), local_path, session=session) # check download assert isfile(local_path) finally: delete_collection(remote_path, token)
def test_object_read_and_write_tickets(self): if self.alice is None or self.bob is None: self.skipTest( "A rodsuser (alice and/or bob) could not be created.") t = None data_objs = [] tmpfiles = [] try: # Create ticket for read access to alice's home collection. alice = self.login(self.alice) home = self.irods_homedir(alice) # Create 'R' and 'W' in alice's home collection. data_objs = [ helpers.make_object(alice, home.path + "/" + name, content='abcxyz') for name in ('R', 'W') ] tickets = { 'R': Ticket(alice).issue('read', home.path + "/R").string, 'W': Ticket(alice).issue('write', home.path + "/W").string } # Test only write ticket allows upload. with self.login(self.bob) as bob: rw_names = {} for name in ('R', 'W'): Ticket(bob, tickets[name]).supply() with tempfile.NamedTemporaryFile(delete=False) as tmpf: tmpfiles += [tmpf] rw_names[name] = tmpf.name tmpf.write(b'hello') if name == 'W': bob.data_objects.put(tmpf.name, home.path + "/" + name) else: try: bob.data_objects.put(tmpf.name, home.path + "/" + name) except ex.CAT_NO_ACCESS_PERMISSION: pass else: raise AssertionError( "A read ticket allowed a data object write operation to happen without error." ) # Test upload was successful, by getting and confirming contents. with self.login( self.bob ) as bob: # This check must be in a new session or we get CollectionDoesNotExist. - Possibly a new issue [ ] for name in ('R', 'W'): Ticket(bob, tickets[name]).supply() bob.data_objects.get(home.path + "/" + name, rw_names[name], **{kw.FORCE_FLAG_KW: ''}) with open(rw_names[name], 'r') as tmpread: self.assertEqual(tmpread.read(), 'abcxyz' if name == 'R' else 'hello') finally: if t: t.delete() for d in data_objs: d.unlink(force=True) for file_ in tmpfiles: os.unlink(file_.name) alice.cleanup()
def delete_my_tickets(session): my_userid = session.users.get(session.username).id my_tickets = session.query( TicketQuery.Ticket).filter(TicketQuery.Ticket.user_id == my_userid) for res in my_tickets: Ticket(session, result=res).delete()
def test_coll_read_ticket_between_rodsusers(self): t = None data_objs = [] tmpfiles = [] try: # Create ticket for read access to alice's home collection. alice = self.login(self.alice) tc = Ticket(alice) home = self.irods_homedir(alice) tc.issue('read', home.path) # Create 'x' and 'y' in alice's home collection data_objs = [ helpers.make_object(alice, home.path + "/" + name, content='abcxyz') for name in ('x', 'y') ] with self.login(self.bob) as bob: ts = Ticket(bob, tc.string) ts.supply() # Check collection access ticket allows bob to list both subobjects self.assertEqual(len(self.list_objects(bob)), 2) # and that we can get (and read) them properly. for name in ('x', 'y'): with tempfile.NamedTemporaryFile(delete=False) as tmpf: tmpfiles += [tmpf] bob.data_objects.get(home.path + "/" + name, tmpf.name, **{kw.FORCE_FLAG_KW: ''}) with open(tmpf.name, 'r') as tmpread: self.assertEqual(tmpread.read(), 'abcxyz') td = Ticket(alice) td.issue('read', home.path + "/x") with self.login(self.bob) as bob: ts = Ticket(bob, td.string) ts.supply() # Check data access ticket allows bob to list only one data object self.assertEqual(len(self.list_objects(bob)), 1) # ... and fetch that object (verifying content) with tempfile.NamedTemporaryFile(delete=False) as tmpf: tmpfiles += [tmpf] bob.data_objects.get(home.path + "/x", tmpf.name, **{kw.FORCE_FLAG_KW: ''}) with open(tmpf.name, 'r') as tmpread: self.assertEqual(tmpread.read(), 'abcxyz') # ... but not fetch the other data object owned by alice. with self.assertRaises(ex.DataObjectDoesNotExist): bob.data_objects.get(home.path + "/y") finally: if t: t.delete() for d in data_objs: d.unlink(force=True) for file_ in tmpfiles: os.unlink(file_.name) alice.cleanup()
def ticket(self, path): ticket = Ticket(self.prc) # print("TEST", self.prc, path) ticket.issue('read', path) return ticket
def ticket_supply(self, code): # use ticket for access ticket = Ticket(self.prc, code) ticket.supply()
def test_ticket_expiry(self): with helpers.make_session() as ses: t1 = t2 = dobj = None try: gm_now = time.gmtime() gm_later = time.gmtime(calendar.timegm(gm_now) + 10) home = self.irods_homedir(ses) dobj = helpers.make_object(ses, home.path + '/dummy', content='abcxyz') later_ts = gmtime_to_timestamp(gm_later) later_epoch = calendar.timegm(gm_later) t1 = Ticket(ses) t2 = Ticket(ses) tickets = [ _.issue('read', dobj.path).string for _ in ( t1, t2, ) ] t1.modify( 'expire', later_ts ) # - Specify expiry with the human readable timestamp. t2.modify('expire', later_epoch ) # - Specify expiry formatted as epoch seconds. # Check normal access succeeds prior to expiration for ticket_string in tickets: with self.login(self.alice) as alice: Ticket(alice, ticket_string).supply() alice.data_objects.get(dobj.path) # Check that both time formats have effected the same expiry time (The catalog returns epoch secs.) timestamps = [] for ticket_string in tickets: t = ses.query(TicketQuery.Ticket).filter( TicketQuery.Ticket.string == ticket_string).one() timestamps.append(t[TicketQuery.Ticket.expiry_ts]) self.assertEqual(len(timestamps), 2) self.assertEqual(timestamps[0], timestamps[1]) # Wait for tickets to expire. epoch = int(time.time()) while epoch <= later_epoch: time.sleep(later_epoch - epoch + 1) epoch = int(time.time()) Expected_Exception = ex.CAT_TICKET_EXPIRED if ses.server_version >= (4,2,9) \ else ex.SYS_FILE_DESC_OUT_OF_RANGE # Check tickets no longer allow access. for ticket_string in tickets: with self.login( self.alice) as alice, tempfile.NamedTemporaryFile( ) as f: Ticket(alice, ticket_string).supply() with self.assertRaises(Expected_Exception): alice.data_objects.get(dobj.path, f.name, **{kw.FORCE_FLAG_KW: ''}) finally: if t1: t1.delete() if t2: t2.delete() if dobj: dobj.unlink(force=True)