def delete_global_account_limit(account, rse_expression, issuer, vo='def', session=None): """ Delete a global account limit.. :param account: The account name. :param rse_expression: The rse expression. :param issuer: The issuer account_core. :param vo: The VO to act on. :param session: The database session in use. :returns: True if successful; False otherwise. """ kwargs = {'account': account, 'rse_expression': rse_expression} if not rucio.api.permission.has_permission(issuer=issuer, vo=vo, action='delete_global_account_limit', kwargs=kwargs, session=session): raise rucio.common.exception.AccessDenied('Account %s can not delete global account limits.' % (issuer)) account = InternalAccount(account, vo=vo) if not account_exists(account=account, session=session): raise rucio.common.exception.AccountNotFound('Account %s does not exist' % (account)) return account_limit_core.delete_global_account_limit(account=account, rse_expression=rse_expression, session=session)
def update_replication_rule(rule_id, options, issuer): """ Update lock state of a replication rule. :param rule_id: The rule_id to lock. :param options: Options dictionary. :param issuer: The issuing account of this operation :raises: RuleNotFound if no Rule can be found. """ kwargs = {'rule_id': rule_id, 'options': options} if 'approve' in options: if not has_permission(issuer=issuer, action='approve_rule', kwargs=kwargs): raise AccessDenied('Account %s can not approve/deny this replication rule.' % (issuer)) issuer = InternalAccount(issuer) if options['approve']: rule.approve_rule(rule_id=rule_id, approver=issuer) else: rule.deny_rule(rule_id=rule_id, approver=issuer, reason=options.get('comment', None)) else: if not has_permission(issuer=issuer, action='update_rule', kwargs=kwargs): raise AccessDenied('Account %s can not update this replication rule.' % (issuer)) rule.update_rule(rule_id=rule_id, options=options)
def update_account(account, key, value, issuer='root', vo='def', session=None): """ Update a property of an account_core. :param account: Name of the account_core. :param key: Account property like status. :param value: Property value. :param issuer: The issuer account :param vo: The VO to act on. :param session: The database session in use. """ validate_schema(name='account', obj=account, vo=vo) kwargs = {} if not rucio.api.permission.has_permission(issuer=issuer, vo=vo, action='update_account', kwargs=kwargs, session=session): raise rucio.common.exception.AccessDenied( 'Account %s can not change %s of the account' % (issuer, key)) account = InternalAccount(account, vo=vo) return account_core.update_account(account, key, value, session=session)
def get_auth_token_gss(account, gsscred, appid, ip=None): """ Authenticate a Rucio account temporarily via a GSS token. The tokens lifetime is 1 hour. :param account: Account identifier as a string. :param gsscred: GSS principal@REALM as a string. :param appid: The application identifier as a string. :param ip: IP address of the client as a string. :returns: Authentication token as a variable-length string. """ kwargs = {'account': account, 'gsscred': gsscred} if not permission.has_permission( issuer=account, action='get_auth_token_gss', kwargs=kwargs): raise exception.AccessDenied( 'User with identity %s can not log to account %s' % (gsscred, account)) account = InternalAccount(account) return authentication.get_auth_token_gss(account, gsscred, appid, ip)
def delete_local_account_limit(account, rse, issuer, vo='def'): """ Delete an account limit.. :param account: The account name. :param rse: The rse name. :param issuer: The issuer account_core. :param vo: The VO to act on. :returns: True if successful; False otherwise. """ rse_id = get_rse_id(rse=rse, vo=vo) kwargs = {'account': account, 'rse': rse, 'rse_id': rse_id} if not rucio.api.permission.has_permission(issuer=issuer, vo=vo, action='delete_local_account_limit', kwargs=kwargs): raise rucio.common.exception.AccessDenied('Account %s can not delete account limits.' % (issuer)) account = InternalAccount(account, vo=vo) if not account_exists(account=account): raise rucio.common.exception.AccountNotFound('Account %s does not exist' % (account)) return account_limit_core.delete_local_account_limit(account=account, rse_id=rse_id)
def set_local_account_limit(account, rse, bytes_, issuer, vo='def', session=None): """ Set an account limit.. :param account: The account name. :param rse: The rse name. :param bytes_: The limit in bytes. :param issuer: The issuer account_core. :param vo: The VO to act on. :param session: The database session in use. """ rse_id = get_rse_id(rse=rse, vo=vo, session=session) kwargs = {'account': account, 'rse': rse, 'rse_id': rse_id, 'bytes': bytes_} if not rucio.api.permission.has_permission(issuer=issuer, vo=vo, action='set_local_account_limit', kwargs=kwargs, session=session): raise rucio.common.exception.AccessDenied('Account %s can not set account limits.' % (issuer)) account = InternalAccount(account, vo=vo) if not account_exists(account=account, session=session): raise rucio.common.exception.AccountNotFound('Account %s does not exist' % (account)) account_limit_core.set_local_account_limit(account=account, rse_id=rse_id, bytes_=bytes_, session=session)
def get_auth_token_saml(account, saml_nameid, appid, ip=None): """ Authenticate a Rucio account temporarily via SSO. The token lifetime is 1 hour. :param account: Account identifier as a string. :param saml_nameid: NameId returned in SAML response as a string. :param appid: The application identifier as a string. :param ip: IP address of the client as a string. :returns: Authentication token as a variable-length string. """ kwargs = {'account': account, 'saml_nameid': saml_nameid} if not permission.has_permission( issuer=account, action='get_auth_token_saml', kwargs=kwargs): raise exception.AccessDenied( 'User with identity %s can not log to account %s' % (saml_nameid, account)) account = InternalAccount(account) return authentication.get_auth_token_saml(account, saml_nameid, appid, ip)
def declare_bad_file_replicas(pfns, reason, issuer): """ Declare a list of bad replicas. :param pfns: The list of PFNs. :param reason: The reason of the loss. :param issuer: The issuer account. """ kwargs = {} if not permission.has_permission(issuer=issuer, action='declare_bad_file_replicas', kwargs=kwargs): raise exception.AccessDenied('Account %s can not declare bad replicas' % (issuer)) issuer = InternalAccount(issuer) replicas = replica.declare_bad_file_replicas(pfns=pfns, reason=reason, issuer=issuer, status=BadFilesStatus.BAD) for k in list(replicas): try: rse = get_rse_name(rse_id=k) replicas[rse] = replicas.pop(k) except exception.RSENotFound: pass return replicas
def setUpClass(cls): if config_get_bool('common', 'multi_vo', raise_exception=False, default=False): cls.vo = { 'vo': config_get('client', 'vo', raise_exception=False, default='tst') } else: cls.vo = {} cls.account = InternalAccount('jdoe', **cls.vo) cls.rse_1_name = 'MOCK4' cls.rse_2_name = 'MOCK5' cls.mock1_id = get_rse_id(cls.rse_1_name, **cls.vo) cls.mock2_id = get_rse_id(cls.rse_2_name, **cls.vo) cls.db_session = session.get_session() cls.rse_1 = {'id': cls.mock1_id, 'staging_area': False} cls.rse_2 = {'id': cls.mock2_id, 'staging_area': False}
def remove_dids_from_followed(dids, account, issuer, session=None, vo='def'): """ Bulk mark datasets as not followed :param dids: A list of dids. :param account: The account owner. :param session: The database session in use. """ kwargs = {'dids': dids, 'issuer': issuer} if not rucio.api.permission.has_permission( issuer=issuer, vo=vo, action='remove_dids_from_followed', kwargs=kwargs, session=session): raise rucio.common.exception.AccessDenied( 'Account %s can not bulk remove data identifiers from followed table' % (issuer)) account = InternalAccount(account, vo=vo) return did.remove_dids_from_followed(dids=dids, account=account, session=session)
def test_get_auth_token_ssh_fail(self): """AUTHENTICATION (CORE): SSH RSA public key exchange (wrong signature).""" root = InternalAccount('root', **self.vo) try: add_account_identity(PUBLIC_KEY, IdentityType.SSH, root, email='*****@*****.**') except Duplicate: pass # might already exist, can skip signature = ssh_sign(PRIVATE_KEY, 'sign_something_else') result = get_auth_token_ssh(account='root', signature=signature, appid='test', ip='127.0.0.1', **self.vo) assert_is_none(result) del_account_identity(PUBLIC_KEY, IdentityType.SSH, root)
def get_global_account_usage(account, rse_expression, issuer, vo='def', session=None): """ Get the account usage and connect it with (if available) the account limits of the account. :param account: The account to read. :param rse_expression: The rse expression to read (If none, get all). :param issuer: The issuer account. :param vo: The VO to act on. :param session: The database session in use. :returns: List of dicts {'rse_id', 'rse', 'bytes', 'files', 'bytes_limit', 'bytes_remaining'} """ kwargs = {'account': account, 'rse_expression': rse_expression} if not rucio.api.permission.has_permission(issuer=issuer, vo=vo, action='get_global_account_usage', kwargs=kwargs, session=session): raise rucio.common.exception.AccessDenied('Account %s can not list global account usage.' % (issuer)) account = InternalAccount(account, vo=vo) if not account_exists(account=account, session=session): raise rucio.common.exception.AccountNotFound('Account %s does not exist' % (account)) return [api_update_return_dict(d, session=session) for d in account_limit_core.get_global_account_usage(account=account, rse_expression=rse_expression, session=session)]
def convert_to_svo(old_vo, delete_vos=False, commit_changes=False, skip_history=False, echo=True): """ Converts a multi-VO database to a single-VO one by renaming the given VO and (optionally) deleting entries for other VOs and the super_root. Intended to be run on a copy of the original database that contains several VOs. :param old_vo: The 3 character string for the old VO. :param delete_vos: If True then all entries associated with a VO other than `old_vo` will be deleted. :param commit_changes: If True then changes are made against the database directly and the old super_root account will be (soft) deleted. If False, then nothing is commited and the commands needed are dumped to be run later. :param skip_history: If True then tables without FKC containing historical data will not be converted to save time. """ if not config_get_bool('common', 'multi_vo', False, False): print( 'Multi-VO mode is not enabled in the config file, aborting conversion.' ) return rename_vo(old_vo, 'def', commit_changes=commit_changes, skip_history=skip_history) s = session.get_session() if delete_vos: success_all = True for vo in list_vos(session=s): if vo['vo'] != 'def': success = remove_vo(vo['vo'], commit_changes=commit_changes, skip_history=skip_history) success_all = success_all and success if commit_changes and success_all: del_account(InternalAccount('super_root', vo='def'), session=s) s.close()
def get_auth_token_saml(account, saml_nameid, appid, ip=None, vo='def', session=None): """ Authenticate a Rucio account temporarily via SSO. The token lifetime is 1 hour. :param account: Account identifier as a string. :param saml_nameid: NameId returned in SAML response as a string. :param appid: The application identifier as a string. :param ip: IP address of the client as a string. :param session: The database session in use. :returns: A dict with token and expires_at entries. """ kwargs = {'account': account, 'saml_nameid': saml_nameid} if not permission.has_permission(issuer=account, vo=vo, action='get_auth_token_saml', kwargs=kwargs, session=session): raise exception.AccessDenied( 'User with identity %s can not log to account %s' % (saml_nameid, account)) account = InternalAccount(account, vo=vo) return authentication.get_auth_token_saml(account, saml_nameid, appid, ip, session=session)
def update_subscription(name, account, metadata=None, issuer=None): """ Updates a subscription :param name: Name of the subscription :type: String :param account: Account identifier :type account: String :param metadata: Dictionary of metadata to update. Supported keys : filter, replication_rules, comments, lifetime, retroactive, dry_run, priority, last_processed :type metadata: Dict :raises: SubscriptionNotFound if subscription is not found """ if not has_permission(issuer=issuer, action='update_subscription', kwargs={'account': account}): raise AccessDenied('Account %s can not update subscription' % (issuer)) try: if not isinstance(metadata, dict): raise TypeError('metadata should be a dict') if 'filter' in metadata and metadata['filter']: if not isinstance(metadata['filter'], dict): raise TypeError('filter should be a dict') validate_schema(name='subscription_filter', obj=metadata['filter']) if 'replication_rules' in metadata and metadata['replication_rules']: if not isinstance(metadata['replication_rules'], list): raise TypeError('replication_rules should be a list') else: for rule in metadata['replication_rules']: validate_schema(name='activity', obj=rule.get('activity', 'default')) except ValueError as error: raise TypeError(error) account = InternalAccount(account) return subscription.update_subscription(name=name, account=account, metadata=metadata)
def get_auth_oidc(account, vo='def', **kwargs): """ Assembles the authorization request of the Rucio Client tailored to the Rucio user & Identity Provider. Saves authentication session parameters in the oauth_requests DB table (for later use-cases). This information is saved for the token lifetime of a token to allow token exchange and refresh. Returns authorization URL as a string or a redirection url to be used in user's browser for authentication. :param account: Rucio Account identifier as a string. :param vo: The VO to act on. :param auth_scope: space separated list of scope names. Scope parameter defines which user's info the user allows to provide to the Rucio Client. :param audience: audience for which tokens are requested ('rucio' is the default) :param auto: If True, the function will return authorization URL to the Rucio Client which will log-in with user's IdP credentials automatically. Also it will instruct the IdP to return an AuthZ code to another Rucio REST endpoint /oidc_token. If False, the function will return a URL to be used by the user in the browser in order to authenticate via IdP (which will then return with AuthZ code to /oidc_code REST endpoint). :param polling: If True, '_polling' string will be appended to the access_msg in the DB oauth_requests table to inform the authorization stage that the Rucio Client is polling the server for a token (and no fetchcode needs to be returned at the end). :param refresh_lifetime: specifies how long the OAuth daemon should be refreshing this token. Default is 96 hours. :param ip: IP address of the client as a string. :param session: The database session in use. :returns: User & Rucio OIDC Client specific Authorization or Redirection URL as a string OR a redirection url to be used in user's browser for authentication. """ # no permission layer for the moment ! account = InternalAccount(account, vo=vo) return oidc.get_auth_oidc(account, **kwargs)
def get_auth_token_ssh(account, signature, appid, ip=None, vo='def', session=None): """ Authenticate a Rucio account temporarily via SSH key exchange. The token lifetime is 1 hour. :param account: Account identifier as a string. :param signature: Response to challenge token signed with SSH private key as a base64 encoded string. :param appid: The application identifier as a string. :param ip: IP address of the client as a string. :param vo: The VO to act on. :param session: The database session in use. :returns: A dict with token and expires_at entries. """ kwargs = {'account': account, 'signature': signature} if not permission.has_permission(issuer=account, vo=vo, action='get_auth_token_ssh', kwargs=kwargs, session=session): raise exception.AccessDenied( 'User with provided signature can not log to account %s' % account) account = InternalAccount(account, vo=vo) return authentication.get_auth_token_ssh(account, signature, appid, ip, session=session)
def test_ssh_fail(vo, rest_client): """AUTHENTICATION (REST): SSH RSA public key exchange (wrong credentials).""" root = InternalAccount('root', vo=vo) try: add_account_identity(PUBLIC_KEY, IdentityType.SSH, root, email='*****@*****.**') except Duplicate: pass # might already exist, can skip signature = ssh_sign(PRIVATE_KEY, 'sign_something_else') headers_dict = { 'X-Rucio-Account': 'root', 'X-Rucio-SSH-Signature': signature } response = rest_client.get('/auth/ssh', headers=headers(hdrdict(headers_dict), vohdr(vo))) assert response.status_code == 401 del_account_identity(PUBLIC_KEY, IdentityType.SSH, root)
def get_ssh_challenge_token(account, appid, ip=None, vo='def'): """ Get a challenge token for subsequent SSH public key authentication. The challenge token lifetime is 5 seconds. :param account: Account identifier as a string. :param appid: The application identifier as a string. :param ip: IP address of the client as a string. :param vo: The VO to act on. :returns: A models.Token object as saved to the database. """ kwargs = {'account': account} if not permission.has_permission( issuer=account, vo=vo, action='get_ssh_challenge_token', kwargs=kwargs): raise exception.AccessDenied( 'User can not get challenge token for account %s' % account) account = InternalAccount(account, vo=vo) return authentication.get_ssh_challenge_token(account, appid, ip)
def create_did_sample(input_scope, input_name, output_scope, output_name, issuer, nbfiles, vo='def'): """ Create a sample from an input collection. :param input_scope: The scope of the input DID. :param input_name: The name of the input DID. :param output_scope: The scope of the output dataset. :param output_name: The name of the output dataset. :param account: The account. :param nbfiles: The number of files to register in the output dataset. :param issuer: The issuer account. :param vo: The VO to act on. """ kwargs = {'issuer': issuer, 'scope': output_scope} if not rucio.api.permission.has_permission( issuer=issuer, vo=vo, action='create_did_sample', kwargs=kwargs): raise rucio.common.exception.AccessDenied( 'Account %s can not bulk add data identifier' % (issuer)) input_scope = InternalScope(input_scope, vo=vo) output_scope = InternalScope(output_scope, vo=vo) issuer = InternalAccount(issuer, vo=vo) return did.create_did_sample(input_scope=input_scope, input_name=input_name, output_scope=output_scope, output_name=output_name, account=issuer, nbfiles=nbfiles)
def get_subscription_by_id(subscription_id, vo='def'): """ Get a specific subscription by id. :param subscription_id: The subscription_id to select. :param vo: The VO of the user issuing command. :raises: SubscriptionNotFound if no Subscription can be found. """ sub = subscription.get_subscription_by_id(subscription_id) if sub['account'].vo != vo: raise AccessDenied('Unable to get subscription') sub['account'] = sub['account'].external if 'filter' in sub: fil = loads(sub['filter']) if 'account' in fil: fil['account'] = [InternalAccount(acc, fromExternal=False).external for acc in fil['account']] if 'scope' in fil: fil['scope'] = [InternalScope(sco, fromExternal=False).external for sco in fil['scope']] sub['filter'] = dumps(fil) return sub
def test_remove_did_from_followed(self): """ DATA IDENTIFIERS (CORE): Mark a did as not followed """ tmp_scope = InternalScope('mock') dsn = 'dsn_%s' % generate_uuid() root = InternalAccount('root') add_did(scope=tmp_scope, name=dsn, type=DIDType.DATASET, account=root) add_did_to_followed(scope=tmp_scope, name=dsn, account=root) users = get_users_following_did(scope=tmp_scope, name=dsn) rows = 0 for user in users: rows += 1 assert_equal(rows, 1) remove_did_from_followed(scope=tmp_scope, name=dsn, account=root) users = get_users_following_did(scope=tmp_scope, name=dsn) rows = 0 for user in users: rows += 1 assert_equal(rows, 0)
def add_account(account, type, email, issuer, vo='def'): """ Creates an account with the provided account name, contact information, etc. :param account: The account name. :param type: The account type :param email: The Email address associated with the account. :param issuer: The issuer account_core. :param vo: The VO to act on. """ validate_schema(name='account', obj=account, vo=vo) kwargs = {'account': account, 'type': type} if not rucio.api.permission.has_permission( issuer=issuer, vo=vo, action='add_account', kwargs=kwargs): raise rucio.common.exception.AccessDenied( 'Account %s can not add account' % (issuer)) account = InternalAccount(account, vo=vo) account_core.add_account(account, AccountType[type.upper()], email)
def test_access_rule_vo(self): """ MULTI VO (CORE): Test accessing rules from a different VO """ scope = InternalScope('mock', **self.vo) dataset = 'dataset_' + str(generate_uuid()) account = InternalAccount('root', **self.vo) rse_str = ''.join(choice(ascii_uppercase) for x in range(10)) rse_name = 'MOCK_%s' % rse_str rse_id = add_rse(rse_name, 'root', **self.vo) add_replica(rse_id=rse_id, scope=scope, name=dataset, bytes=10, account=account) rule_id = add_rule(dids=[{'scope': scope, 'name': dataset}], account=account, copies=1, rse_expression='MOCK', grouping='NONE', weight='fakeweight', lifetime=None, locked=False, subscription_id=None)[0] with assert_raises(AccessDenied): delete_replication_rule(rule_id=rule_id, purge_replicas=False, issuer='root', **self.new_vo) # check locks are not accessible from other VO locks = list(get_replica_locks_for_rule_id(rule_id, **self.vo)) assert_equal(len(locks), 1) locks = list(get_replica_locks_for_rule_id(rule_id, **self.new_vo)) assert_equal(len(locks), 0) delete_replication_rule(rule_id=rule_id, purge_replicas=False, issuer='root', **self.vo) rule_dict = get_replication_rule(rule_id=rule_id, issuer='root', **self.vo) assert_is_not_none(rule_dict['expires_at'])
def test_ssh_success(self): """AUTHENTICATION (REST): SSH RSA public key exchange (correct credentials).""" root = InternalAccount('root', **self.vo) try: add_account_identity(PUBLIC_KEY, IdentityType.SSH, root, email='*****@*****.**') except Duplicate: pass # might already exist, can skip options = [] headers = {'X-Rucio-Account': 'root'} headers.update(self.vo_header) result = TestApp(APP.wsgifunc(*options)).get('/ssh_challenge_token', headers=headers, expect_errors=True) assert result.status == 200 assert 'challenge-' in result.header('X-Rucio-SSH-Challenge-Token') signature = ssh_sign(PRIVATE_KEY, result.header('X-Rucio-SSH-Challenge-Token')) headers = { 'X-Rucio-Account': 'root', 'X-Rucio-SSH-Signature': signature } headers.update(self.vo_header) result = TestApp(APP.wsgifunc(*options)).get('/ssh', headers=headers, expect_errors=True) assert result.status == 200 assert len(result.header('X-Rucio-Auth-Token')) > 32 del_account_identity(PUBLIC_KEY, IdentityType.SSH, root)
def recover_vo_root_identity(root_vo, identity_key, id_type, email, issuer, default=False, password=None, vo='def'): """ Adds a membership association between identity and the root account for given VO. :param root_vo: The VO whose root needs recovery :param identity_key: The identity key name. For example x509 DN, or a username. :param id_type: The type of the authentication (x509, gss, userpass, ssh, saml). :param email: The Email address associated with the identity. :param issuer: The issuer account. :param default: If True, the account should be used by default with the provided identity. :param password: Password if id_type is userpass. :param vo: the VO to act on. """ kwargs = {} root_vo = vo_core.map_vo(root_vo) if not has_permission( issuer=issuer, vo=vo, action='recover_vo_root_identity', kwargs=kwargs): raise exception.AccessDenied( 'Account %s can not recover root identity' % (issuer)) account = InternalAccount('root', vo=root_vo) return identity.add_account_identity(identity=identity_key, type_=IdentityType[id_type.upper()], default=default, email=email, account=account, password=password)
def setUp(self): self.rse = 'MOCK4' self.file_sizes = 2 self.upload_client = UploadClient() self.account_client = AccountClient() self.session = get_session() if config_get_bool('common', 'multi_vo', raise_exception=False, default=False): self.vo = { 'vo': config_get('client', 'vo', raise_exception=False, default='tst') } else: self.vo = {} self.account = InternalAccount('root', **self.vo) self.scope = InternalScope('mock', **self.vo) self.rse_id = get_rse_id(self.rse, session=self.session, **self.vo)
def test_delete_identity_of_account(vo, rest_client): """ ACCOUNT (REST): send a DELETE to remove an identity of an account.""" account = account_name_generator() identity = uuid() password = '******' add_account(account, 'USER', '*****@*****.**', 'root', vo=vo) add_identity(identity, IdentityType.USERPASS, '*****@*****.**', password) add_account_identity(identity, IdentityType.USERPASS, InternalAccount(account, vo=vo), '*****@*****.**') auth_response = rest_client.get('/auth/userpass', headers=headers(loginhdr(account, identity, password), vohdr(vo))) assert auth_response.status_code == 200 assert 'X-Rucio-Auth-Token' in auth_response.headers token = str(auth_response.headers.get('X-Rucio-Auth-Token')) assert len(token) != 0 # normal deletion data = {'authtype': 'USERPASS', 'identity': identity} response = rest_client.delete('/accounts/' + account + '/identities', headers=headers(auth(token)), json=data) assert response.status_code == 200 # unauthorized deletion other_account = account_name_generator() data = {'authtype': 'USERPASS', 'identity': identity} response = rest_client.delete('/accounts/' + other_account + '/identities', headers=headers(auth(token)), json=data) assert response.status_code == 401
def list_subscriptions(name=None, account=None, state=None, vo='def'): """ Returns a dictionary with the subscription information : Examples: ``{'status': 'INACTIVE/ACTIVE/BROKEN', 'last_modified_date': ...}`` :param name: Name of the subscription :type: String :param account: Account identifier :type account: String :param state: Filter for subscription state :type state: String :param vo: The VO to act on. :type vo: String :returns: Dictionary containing subscription parameter :rtype: Dict :raises: exception.NotFound if subscription is not found """ if account is not None: account = InternalAccount(account, vo=vo) subs = subscription.list_subscriptions(name, account, state) for sub in subs: sub['account'] = sub['account'].external yield sub
def add_temporary_dids(dids, issuer, vo='def', session=None): """ Bulk add temporary data identifiers. :param dids: A list of dids. :param issuer: The issuer account. :param vo: The VO to act on. :param session: The database session in use. """ for did in dids: if 'rse' in did and 'rse_id' not in did: rse_id = None if did['rse'] is not None: rse_id = get_rse_id(rse=did['rse'], vo=vo, session=session) did['rse_id'] = rse_id if 'scope' in did: did['scope'] = InternalScope(did['scope'], vo=vo) if 'parent_scope' in did: did['parent_scope'] = InternalScope(did['parent_scope'], vo=vo) issuer = InternalAccount(issuer, vo=vo) return temporary_did.add_temporary_dids(dids=dids, account=issuer, session=session)