def untrust_service(service): """Stop trusting the passed service. This will remove the service as being trusted. You must pass in a valid admin_user authorisation for this service """ from Acquire.Service import is_running_service as _is_running_service if _is_running_service(): from Acquire.Service import get_service_account_bucket as \ _get_service_account_bucket from Acquire.ObjectStore import url_to_encoded as \ _url_to_encoded bucket = _get_service_account_bucket() urlkey = "_trusted/url/%s" % _url_to_encoded(service.canonical_url()) uidkey = "_trusted/uid/%s" % service.uid() # delete the trusted service by both canonical_url and uid try: _ObjectStore.delete_object(bucket, uidkey) except: pass try: _ObjectStore.delete_object(bucket, urlkey) except: pass from Acquire.Service import clear_services_cache \ as _clear_services_cache _clear_services_cache() else: from Acquire.Client import Wallet as _Wallet wallet = _Wallet() wallet.remove_service(service)
def trust_service(service): """Trust the passed service. This will record this service as trusted, e.g. saving the keys and certificates for this service and allowing it to be used for the specified type. """ from Acquire.Service import is_running_service as _is_running_service if _is_running_service(): from Acquire.Service import get_service_account_bucket as \ _get_service_account_bucket from Acquire.ObjectStore import url_to_encoded as \ _url_to_encoded bucket = _get_service_account_bucket() urlkey = "_trusted/url/%s" % _url_to_encoded(service.canonical_url()) uidkey = "_trusted/uid/%s" % service.uid() service_data = service.to_data() # store the trusted service by both canonical_url and uid from Acquire.ObjectStore import ObjectStore as _ObjectStore _ObjectStore.set_object_from_json(bucket, uidkey, service_data) _ObjectStore.set_string_object(bucket, urlkey, uidkey) from Acquire.Service import clear_services_cache \ as _clear_services_cache _clear_services_cache() else: from Acquire.Client import Wallet as _Wallet wallet = _Wallet() wallet.add_service(service)
def __init__(self): """Construct a wallet to hold all user credentials""" from Acquire.Service import is_running_service \ as _is_running_service if _is_running_service(): from Acquire.Service import get_this_service \ as _get_this_service service = _get_this_service(need_private_access=False) raise PermissionError( "You cannot open a Wallet on a running Service (%s)" % service) self._wallet_key = None self._service_info = {} import os as _os wallet_dir = _get_wallet_dir() if not _os.path.exists(wallet_dir): _os.makedirs(wallet_dir, mode=0o700, exist_ok=False) elif not _os.path.isdir(wallet_dir): raise TypeError("The wallet directory must be a directory!") self._wallet_dir = wallet_dir
def _get_identity_service(identity_url=None): """Function to return the identity service for the system""" if identity_url is None: identity_url = _get_identity_url() from Acquire.Service import is_running_service as _is_running_service if _is_running_service(): from Acquire.Service import get_trusted_service \ as _get_trusted_service return _get_trusted_service(service_url=identity_url, service_type='identity') from Acquire.Client import LoginError try: from Acquire.Client import Wallet as _Wallet wallet = _Wallet() service = wallet.get_service(service_url=identity_url, service_type="identity") except Exception as e: from Acquire.Service import exception_to_string raise LoginError("Have not received the identity service info from " "the identity service at '%s'\n\nCAUSE: %s" % (identity_url, exception_to_string(e))) if not service.can_identify_users(): raise LoginError( "You can only use a valid identity service to log in! " "The service at '%s' is a '%s'" % (identity_url, service.service_type())) return service
def register(par, url_checksum, details_function, cleanup_function=None): """Register the passed PAR, passing in the checksum of the PAR's secret URL (so we can verify the close), and optionally supplying a cleanup_function that is called when the PAR is closed. The passed 'details_function' should be used to extract the object-store driver-specific details from the PAR and convert them into a dictionary. The signature should be; driver_details = details_function(par) """ from Acquire.Service import is_running_service as _is_running_service if not _is_running_service(): return from Acquire.Service import get_service_account_bucket \ as _get_service_account_bucket from Acquire.ObjectStore import OSPar as _OSPar from Acquire.ObjectStore import ObjectStore as _ObjectStore from Acquire.ObjectStore import Function as _Function from Acquire.ObjectStore import datetime_to_string \ as _datetime_to_string if par is None: return if not isinstance(par, _OSPar): raise TypeError("You can only register pars of type PAR") if par.is_null(): return data = {} data["par"] = par.to_data() if details_function is None: data["driver_details"] = par._driver_details else: data["driver_details"] = details_function(par) data["url_checksum"] = url_checksum if cleanup_function is not None: if not isinstance(cleanup_function, _Function): cleanup_function = _Function(cleanup_function) data["cleanup_function"] = cleanup_function.to_data() expire_string = _datetime_to_string(par.expires_when()) key = "%s/uid/%s/%s" % (_registry_key, par.uid(), expire_string) bucket = _get_service_account_bucket() _ObjectStore.set_object_from_json(bucket, key, data) key = "%s/expire/%s/%s" % (_registry_key, expire_string, par.uid()) _ObjectStore.set_object_from_json(bucket, key, par.uid())
def close(par): """Close the passed PAR. This will remove the registration for the PAR and will also call the associated cleanup_function (if any) """ from Acquire.Service import is_running_service as _is_running_service if not _is_running_service(): return from Acquire.Service import get_service_account_bucket \ as _get_service_account_bucket from Acquire.ObjectStore import OSPar as _OSPar from Acquire.ObjectStore import ObjectStore as _ObjectStore from Acquire.ObjectStore import datetime_to_string \ as _datetime_to_string from Acquire.ObjectStore import Function as _Function if par is None: return if not isinstance(par, _OSPar): raise TypeError("You can only close OSPar objects!") if par.is_null(): return expire_string = _datetime_to_string(par.expires_when()) bucket = _get_service_account_bucket() key = "%s/expire/%s/%s" % (_registry_key, expire_string, par.uid()) try: _ObjectStore.delete_object(bucket=bucket, key=key) except: pass key = "%s/uid/%s/%s" % (_registry_key, par.uid(), expire_string) try: data = _ObjectStore.take_object_from_json(bucket=bucket, key=key) except: data = None if data is None: # this PAR has already been closed return if "cleanup_function" in data: cleanup_function = _Function.from_data(data["cleanup_function"]) cleanup_function(par=par)
def get_trusted_services(): """Return a dictionary of all trusted services indexed by their type """ from Acquire.Service import is_running_service as _is_running_service if _is_running_service(): from Acquire.Service import get_this_service as _get_this_service from Acquire.Service import Service as _Service from Acquire.Service import get_service_account_bucket as \ _get_service_account_bucket from Acquire.ObjectStore import ObjectStore as _ObjectStore from Acquire.ObjectStore import url_to_encoded as \ _url_to_encoded # we already trust ourselves service = _get_this_service() trusted_services = {} trusted_services[service.service_type()] = [service] bucket = _get_service_account_bucket() uidkey = "_trusted/uid/" datas = _ObjectStore.get_all_objects(bucket, uidkey) for data in datas: remote_service = _Service.from_data(data) if remote_service.should_refresh_keys(): # need to update the keys in our copy of the service remote_service.refresh_keys() key = "%s/%s" % (uidkey, remote_service.uid()) _ObjectStore.set_object_from_json(bucket, key, remote_service.to_data()) if remote_service.service_type() in datas: datas[remote_service.service_type()].append(remote_service) else: datas[remote_service.service_type()] = [remote_service] return datas else: # this is running on the client from Acquire.Client import Wallet as _Wallet wallet = _Wallet() return wallet.get_services()
def get_trusted_service(service_url=None, service_uid=None, service_type=None, autofetch=True): """Return the trusted service info for the service with specified service_url or service_uid""" if service_url is not None: from Acquire.Service import Service as _Service service_url = _Service.get_canonical_url(service_url, service_type=service_type) from Acquire.Service import is_running_service as _is_running_service if _is_running_service(): from Acquire.Service import get_this_service as _get_this_service from Acquire.Service import Service as _Service from Acquire.Service import get_service_account_bucket as \ _get_service_account_bucket from Acquire.ObjectStore import ObjectStore as _ObjectStore from Acquire.ObjectStore import url_to_encoded as \ _url_to_encoded service = _get_this_service() if service_url is not None and service.canonical_url() == service_url: # we trust ourselves :-) return service if service_uid is not None and service.uid() == service_uid: # we trust ourselves :-) return service bucket = _get_service_account_bucket() uidkey = None data = None if service_uid is not None: uidkey = "_trusted/uid/%s" % service_uid try: data = _ObjectStore.get_object_from_json(bucket, uidkey) except: pass elif service_url is not None: urlkey = "_trusted/url/%s" % _url_to_encoded(service_url) try: uidkey = _ObjectStore.get_string_object(bucket, urlkey) if uidkey is not None: data = _ObjectStore.get_object_from_json(bucket, uidkey) except: pass if data is not None: remote_service = _Service.from_data(data) if remote_service.should_refresh_keys(): # need to update the keys in our copy of the service remote_service.refresh_keys() if uidkey is not None: _ObjectStore.set_object_from_json(bucket, uidkey, remote_service.to_data()) return remote_service if not autofetch: from Acquire.Service import ServiceAccountError if service_uid is not None: raise ServiceAccountError( "We do not trust the service with UID '%s'" % service_uid) else: raise ServiceAccountError( "We do not trust the service at URL '%s'" % service_url) # we can try to fetch this data - we will ask our own # registry from Acquire.Registry import get_trusted_registry_service \ as _get_trusted_registry_service registry = _get_trusted_registry_service(service_uid=service.uid()) service = registry.get_service(service_uid=service_uid, service_url=service_url) from Acquire.Service import trust_service as _trust_service _trust_service(service) return service else: # this is running on the client from Acquire.Client import Wallet as _Wallet wallet = _Wallet() service = wallet.get_service(service_uid=service_uid, service_url=service_url, service_type=service_type, autofetch=autofetch) return service
def _is_running_service(): """Return whether or not this is the Cluster on the compute service (otherwise it must be on the client) """ from Acquire.Service import is_running_service as _is_running_service return _is_running_service()
def call_function(service_url, function=None, args=None, args_key=None, response_key=None, public_cert=None): """Call the remote function called 'function' at 'service_url' passing in named function arguments in 'kwargs'. If 'args_key' is supplied, then encrypt the arguments using 'args'. If 'response_key' is supplied, then tell the remote server to encrypt the response using the public version of 'response_key', so that we can decrypt it in the response. If 'public_cert' is supplied then we will ask the service to sign their response using their service signing certificate, and we will validate the signature using 'public_cert' """ if args is None: args = {} from Acquire.Service import is_running_service as _is_running_service from Acquire.ObjectStore import get_datetime_now_to_string \ as _get_datetime_now_to_string service = None if _is_running_service(): from Acquire.Service import get_this_service as _get_this_service try: service = _get_this_service(need_private_access=False) except: pass if service is not None: if service.canonical_url() == service_url: result = service._call_local_function(function=function, args=args) return unpack_return_value(return_value=result) response_key = _get_key(response_key) if response_key: args_json = pack_arguments(function=function, args=args, key=args_key, response_key=response_key.public_key(), public_cert=public_cert) else: args_json = pack_arguments(function=function, args=args, key=args_key) response = None try: from Acquire.Stubs import requests as _requests response = _requests.post(service_url, data=args_json, timeout=60.0) except Exception as e: from Acquire.Service import RemoteFunctionCallError raise RemoteFunctionCallError( "Cannot call remote function '%s' at '%s' because of a possible " "network issue: requests exeption = '%s'" % (function, service_url, str(e))) args = None args_json = None args_key = None if response.status_code != 200: from Acquire.Service import RemoteFunctionCallError raise RemoteFunctionCallError( "Cannot call remote function '%s' as '%s'. Invalid error code " "%d returned. Message:\n%s" % (function, service_url, response.status_code, str( response.content))) if response.encoding == "utf-8" or response.encoding is None: result = response.content.decode("utf-8") else: from Acquire.Service import RemoteFunctionCallError raise RemoteFunctionCallError( "Cannot call remote function '%s' as '%s'. Invalid data encoding " "%s returned. Message:\n%s" % (function, service_url, response.encoding, str(response.content))) return unpack_return_value(return_value=result, key=response_key, public_cert=public_cert, function=function, service=service_url)
def get(par_uid, details_function, url_checksum=None): """Return the PAR that matches the passed PAR_UID. If 'url_checksum' is supplied then this verifies that the checksum of the secret URL is correct. This returns the PAR with a completed 'driver_details'. The 'driver_details' is created from the dictionary of data saved with the PAR. The signature should be; driver_details = details_function(data) """ if par_uid is None or len(par_uid) == 0: return from Acquire.Service import is_running_service as _is_running_service if not _is_running_service(): return from Acquire.Service import get_service_account_bucket \ as _get_service_account_bucket from Acquire.ObjectStore import OSPar as _OSPar from Acquire.ObjectStore import ObjectStore as _ObjectStore from Acquire.ObjectStore import string_to_datetime \ as _string_to_datetime key = "%s/uid/%s" % (_registry_key, par_uid) bucket = _get_service_account_bucket() objs = _ObjectStore.get_all_objects_from_json(bucket=bucket, prefix=key) data = None for obj in objs.values(): if url_checksum is not None: if url_checksum == obj["url_checksum"]: data = obj break else: data = obj break if data is None: from Acquire.ObjectStore import ObjectStoreError raise ObjectStoreError( "There is matching PAR available to close...") par = _OSPar.from_data(data["par"]) if "driver_details" in data: if details_function is not None: driver_details = details_function(data["driver_details"]) par._driver_details = driver_details else: par._driver_details = driver_details return par