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 get_service(self, service_uid=None, service_url=None): """Load and return the service with specified url or uid from the registry. This will consult with other registry services to find the matching service """ from Acquire.ObjectStore import ObjectStore as _ObjectStore from Acquire.Service import Service as _Service from Acquire.ObjectStore import string_to_encoded \ as _string_to_encoded from Acquire.Service import get_this_service as _get_this_service this_service = _get_this_service(need_private_access=False) if service_url is not None: from Acquire.Service import Service as _Service service_url = _Service.get_canonical_url(service_url) if this_service.uid() == service_uid: return this_service elif this_service.canonical_url() == service_url: return this_service bucket = self.get_bucket() service_key = self.get_service_key(service_uid=service_uid, service_url=service_url) service = None if service_key is not None: try: data = _ObjectStore.get_object_from_json(bucket=bucket, key=service_key) service = _Service.from_data(data) except: pass if service is not None: must_write = False if service.uid() == "STAGE1": # we need to directly ask the service for its info service = self.challenge_service(service) if service.uid() == "STAGE1": from Acquire.Service import MissingServiceError raise MissingServiceError( "Service %s|%s not available as it is still under " "construction!" % (service_uid, service)) # we can now move this service from pending to active uidkey = self._get_key_for_uid(service.uid()) domain = self._get_domain(service.service_url()) domainroot = self._get_root_key_for_domain(domain=domain) pending_key = "%s/pending/%s" % (domainroot, service.uid()) active_key = "%s/active/%s" % (domainroot, service.uid()) try: _ObjectStore.delete_object(bucket=bucket, key=pending_key) except: pass try: _ObjectStore.set_string_object(bucket=bucket, key=active_key, string_data=uidkey) except: pass must_write = True elif service.should_refresh_keys(): service.refresh_keys() must_write = True if must_write: data = service.to_data() _ObjectStore.set_object_from_json(bucket=bucket, key=service_key, data=data) return service # we only get here if we can't find the service on this registry. # In the future, we will use the initial part of the UID of # the service to ask its registering registry for its data. # For now, we just raise an error from Acquire.Service import MissingServiceError raise MissingServiceError( "No service available: service_url=%s service_uid=%s" % (service_url, service_uid))
def register_service(service, registry_uid): """Call this function to register the passed new service with the specified registry_uid. This function will complete registration and construction of the service """ from Acquire.Service import Service as _Service if not isinstance(service, _Service): raise TypeError("You can only register Service objects") if not service.uid().startswith("STAGE1"): raise PermissionError( "You can only register services that are at STAGE1 of " "construction") if service.service_type() == "registry": from Acquire.Registry import get_registry_details \ as _get_registry_details details = _get_registry_details(registry_uid=registry_uid) from Acquire.Service import Service as _Service canonical_url = _Service.get_canonical_url(details["canonical_url"]) # make sure that everything matches what was specified if canonical_url != service.canonical_url(): raise PermissionError( "Cannot change the canonical URL. I expect %s, but " "you are trying to set to %s" % (service.canonical_url(), details["canonical_url"])) from Acquire.Registry import update_registry_keys_and_certs \ as _update_registry_keys_and_certs _update_registry_keys_and_certs( registry_uid=registry_uid, public_key=service.public_key(), public_certificate=service.public_certificate()) service.create_stage2(service_uid=registry_uid, response=service._uid) return service # get the trusted registry from Acquire.Registry import get_trusted_registry_service \ as _get_trusted_registry_service registry_service = _get_trusted_registry_service(service_uid=registry_uid) if not registry_service.is_registry_service(): raise PermissionError( "You can only register new services on an existing and valid " "registry service. Not %s" % registry_service) from Acquire.ObjectStore import bytes_to_string as _bytes_to_string pubkey = registry_service.public_key() challenge = pubkey.encrypt(service.uid()) args = { "service": service.to_data(), "challenge": _bytes_to_string(challenge), "fingerprint": pubkey.fingerprint() } result = registry_service.call_function(function="register_service", args=args) service_uid = result["service_uid"] response = result["response"] service.create_stage2(service_uid=service_uid, response=response) return service
def setup_this_service(service_type, canonical_url, registry_uid, username, password): """Call this function to setup a new service that will serve at 'canonical_url', will be of the specified service_type. This will be registered at the registry at UID registry_uid (1) Delete the object store value "_service" if you want to reset the actual Service. This will assign a new UID for the service which would reset the certificates and keys. This new service will need to be re-introduced to other services that need to trust it """ assert_running_service() from Acquire.Service import get_service_account_bucket as \ _get_service_account_bucket from Acquire.ObjectStore import Mutex as _Mutex from Acquire.ObjectStore import ObjectStore as _ObjectStore from Acquire.Service import Service as _Service bucket = _get_service_account_bucket() # ensure that this is the only time the service is set up mutex = _Mutex(key=_service_key, bucket=bucket, lease_time=120) try: service_info = _ObjectStore.get_object_from_json(bucket, _service_key) except: service_info = None service = None service_password = _get_service_password() user_uid = None otp = None if service_info: try: service = _Service.from_data(service_info, service_password) except Exception as e: from Acquire.Service import ServiceAccountError raise ServiceAccountError( "Something went wrong reading the Service data. You should " "either debug the error or delete the data at key '%s' " "to allow the service to be reset and constructed again. " "The error was %s" % (_service_key, str(e))) if service.uid().startswith("STAGE1"): from Acquire.Service import ServiceAccountError raise ServiceAccountError( "The service is currently under construction. Please " "try again later...") if service is None: # we need to create the new service if (service_type is None) or (canonical_url is None): from Acquire.Service import ServiceAccountError raise ServiceAccountError( "You need to supply both the service_type and canonical_url " "in order to initialise a new Service") # we need to build the service account - first stage 1 service = _Service.create(service_url=canonical_url, service_type=service_type) # write the stage1 service data, encrypted using the service password. # This will be needed to answer the challenge from the registry service_data = service.to_data(service_password) _ObjectStore.set_object_from_json(bucket, _service_key, service_data) # now we can register the service with a registry - this # will return the stage2-constructed service from Acquire.Registry import register_service as _register_service service = _register_service(service=service, registry_uid=registry_uid) canonical_url = _Service.get_canonical_url(canonical_url) if service.service_type() != service_type or \ service.canonical_url() != canonical_url: from Acquire.Service import ServiceAccountError raise ServiceAccountError( "The existing service has a different type or URL to that " "requested at setup. The request type and URL are %s and %s, " "while the actual service type and URL are %s and %s." % (service_type, canonical_url, service.service_type(), service.canonical_url())) # we can add the first admin user service_uid = service.uid() skelkey = service.skeleton_key().public_key() # now register the new admin user account - remembering to # encode the password from Acquire.Client import Credentials as _Credentials password = _Credentials.encode_password(password=password, identity_uid=service_uid) from Acquire.Identity import UserAccount as _UserAccount (user_uid, otp) = _UserAccount.create(username=username, password=password, _service_uid=service_uid, _service_public_key=skelkey) add_admin_user(service, user_uid) # write the service data, encrypted using the service password service_data = service.to_data(service_password) # reload the data to check it is ok, and also to set the right class service = _Service.from_data(service_data, service_password) # now it is ok, save this data to the object store _ObjectStore.set_object_from_json(bucket, _service_key, service_data) mutex.unlock() from Acquire.Service import clear_service_cache as _clear_service_cache _clear_service_cache() return (service, user_uid, otp)