def get_this_service(need_private_access=False): """Return the service info object for this service. If private access is needed then this will decrypt and access the private keys and signing certificates, which is slow if you just need the public certificates. """ assert_running_service() from Acquire.Service import MissingServiceAccountError try: service_info = _get_this_service_data() except MissingServiceAccountError: raise except Exception as e: raise MissingServiceAccountError( "Unable to read the service info from the object store! : %s" % str(e)) service_password = None if need_private_access: service_password = _get_service_password() try: from Acquire.Service import Service as _Service if service_password: service = _Service.from_data(service_info, service_password) else: service = _Service.from_data(service_info) except Exception as e: raise MissingServiceAccountError( "Unable to create the ServiceAccount object: %s %s" % (e.__class__, str(e))) if service.should_refresh_keys(): if service_password is None: service_password = _get_service_password() service_info = _refresh_this_service_keys_and_certs( service_info, service_password) if need_private_access: return _Service.from_data(service_info, service_password) else: return _Service.from_data(service_info) else: return service
def _refresh_this_service_keys_and_certs(service_info, service_password): from Acquire.Service import Service as _Service service = _Service.from_data(service_info, service_password) if service._uid == "STAGE1": return service_info if not service.should_refresh_keys(): return service_info oldkeys = service.dump_keys(include_old_keys=False) # now write the old keys to storage from Acquire.ObjectStore import ObjectStore as _ObjectStore from Acquire.ObjectStore import Mutex as _Mutex from Acquire.Service import get_service_account_bucket as \ _get_service_account_bucket bucket = _get_service_account_bucket() key = "%s/oldkeys/%s" % (_service_key, oldkeys["datetime"]) _ObjectStore.set_object_from_json(bucket, key, oldkeys) # now write the pointers from fingerprint to file... for fingerprint in oldkeys.keys(): if fingerprint not in ["datetime", "encrypted_passphrase"]: _ObjectStore.set_string_object( bucket, "%s/oldkeys/fingerprints/%s" % (_service_key, fingerprint), key) # generate new keys last_update = service.last_key_update() service.refresh_keys() # now lock the object store so that we are the only function # that can write the new keys to global state m = _Mutex(key=service.uid(), bucket=bucket) service_data = _ObjectStore.get_object_from_json(bucket, _service_key) service_info = _Service.from_data(service_data) if service_info.last_key_update() == last_update: # no-one else has beaten us - write the updated keys to global state _ObjectStore.set_object_from_json(bucket, _service_key, service.to_data(service_password)) m.unlock() return service_data
def run(args): """Call this function to register a new service with this registry""" service = Service.from_data(args["service"]) challenge = string_to_bytes(args["challenge"]) fingerprint = args["fingerprint"] try: force_new_uid = args["force_new_uid"] except: force_new_uid = False if force_new_uid: force_new_uid = True # respond to the challenge from the service to be registered this_service = get_this_service(need_private_access=True) key = this_service.get_key(fingerprint) response = key.decrypt(challenge) # ok - we can respond to its challenge, so now challenge # it, and if it passes, register the service registry = Registry() service_uid = registry.register_service(service=service, force_new_uid=force_new_uid) return {"service_uid": service_uid, "response": response}
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() privkey = _PrivateKey() response = _call_function(identity_url, response_key=privkey) try: service = _Service.from_data(response["service_info"]) except: raise LoginError("Have not received the identity service info from " "the identity service at '%s' - got '%s'" % (identity_url, response)) if not service.is_identity_service(): 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())) if identity_url != service.service_url(): service.update_service_url(identity_url) return service
def from_data(data, password=None): """Deserialise this object from the passed data. This will only deserialise the private key and private certificate if the password is supplied """ from Acquire.Service import Service as _Service return _Service.from_data(data, password)
def _get_access_service(access_url=None): """Function to return the access service for the system""" if access_url is None: access_url = _get_access_url() privkey = _PrivateKey() response = _call_function(access_url, response_key=privkey) try: service = _Service.from_data(response["service_info"]) except: raise LoginError("Have not received the access service info from " "the access service at '%s' - got '%s'" % (access_url, response)) if not service.is_access_service(): raise LoginError( "You can only use a valid access service to access resources! " "The service at '%s' is a '%s'" % (access_url, service.service_type())) if service.service_url() != access_url: service.update_service_url(access_url) return service
def _get_service(self, service_url): """Return the service data for the passed service""" try: return self._service_info[service_url] except: pass # can we read this from a file? service_file = "%s/certs_%s" % ( ServiceWallet._wallet_dir(), _base64.b64encode(service_url.encode("utf-8")).decode("utf-8")) try: with open(service_file, "rb") as FILE: service_info = _Service.from_data( _unpack_arguments(FILE.read())) self._service_info[service_url] = service_info return service_info except: pass try: key = _PrivateKey() response = _call_function(service_url, response_key=key) service = _Service.from_data(response["service_info"]) except Exception as e: service = None if str(e).find( "You haven't yet created the service account for " "this service. Please create an account first") != -1: return None raise LoginError("Error connecting to the service %s: Error = %s" % (service, str(e))) if service is None: raise LoginError("Error connecting to the service %s. " "Has it been setup?" % service_url) self._service_info[service_url] = service # save this for future reference with open(service_file, "wb") as FILE: FILE.write(_pack_arguments(service.to_data())) return service
def run(args): """This function is used to request access to a bucket for data in the object store. The user can request read-only or read-write access. Access is granted based on a permission list """ status = 0 message = None access_token = None user_uuid = args["user_uuid"] identity_service_url = args["identity_service"] # log into the central access account bucket = login_to_service_account() # is the identity service supplied by the user one that we trust? identity_service = Service.from_data( ObjectStore.get_object_from_json(bucket, "services/%s" % identity_service_url)) if not identity_service: raise RequestBucketError( "You cannot request a bucket because " "this access service does not know or trust your supplied " "identity service (%s)" % identity_service_url) if not identity_service.is_identity_service(): raise RequestBucketError( "You cannot request a bucket because " "the passed service (%s) is not an identity service. It is " "a %s" % (identity_service_url, identity_service.service_type())) # Since we trust this identity service, we can ask it to give us the # public certificate and signing certificate for this user. key = PrivateKey() response = call_function(identity_service_url, "get_user_keys", args_key=identity_service.public_key(), response_key=key, user_uuid=user_uuid) status = 0 message = "Success: Status = %s" % str(response) return_value = create_return_value(status, message) if access_token: return_value["access_token"] = access_token return return_value
def _get_service_info(self, identity_service): """Return the service info for the passed identity service""" try: return self._service_info[identity_service] except: pass # can we read this from a file? service_file = "%s/service_%s" % (Wallet._wallet_dir( ), _base64.b64encode(identity_service.encode("utf-8")).decode("utf-8")) try: with open(service_file, "rb") as FILE: service_info = _Service.from_data( _unpack_arguments(FILE.read())) self._service_info[identity_service] = service_info return service_info except: pass try: key = _PrivateKey() response = _call_function(identity_service, response_key=key) service = _Service.from_data(response["service_info"]) except Exception as e: raise LoginError( "Error connecting to the login service %s: Error = %s" % (identity_service, str(e))) if not service.is_identity_service(): raise LoginError("You cannot log into something that is not " "a valid identity service!") self._service_info[identity_service] = service # save this for future reference with open(service_file, "wb") as FILE: FILE.write(_pack_arguments(service.to_data())) return service
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 refresh_service_keys_and_certs(service, force_refresh=False): """This function will check if any key rotation is needed, and if so, it will automatically refresh the keys and certificates. The old keys and certificates will be stored in a database of old keys and certificates """ assert_running_service() if service._uid == "STAGE1": return service if (not force_refresh) and (not service.should_refresh_keys()): return service # ensure that the current keys are saved to the object store save_service_keys_to_objstore() # generate new keys last_update = service.last_key_update() service.refresh_keys() # now lock the object store so that we are the only function # that can write the new keys to global state from Acquire.Service import get_service_account_bucket as \ _get_service_account_bucket from Acquire.Service import Service as _Service from Acquire.ObjectStore import Mutex as _Mutex from Acquire.ObjectStore import ObjectStore as _ObjectStore bucket = _get_service_account_bucket() m = _Mutex(key=service.uid(), bucket=bucket) service_data = _ObjectStore.get_object_from_json(bucket, _service_key) service_info = _Service.from_data(service_data) if service_info.last_key_update() == last_update: # no-one else has beaten us - write the updated keys to global state _ObjectStore.set_object_from_json( bucket, _service_key, service.to_data(_get_service_password())) m.unlock() # clear the cache as we will need to load a new object clear_serviceinfo_cache() return get_this_service(need_private_access=True)
def get_service(self, service_url=None, service_uid=None): """Ask the registry to return the service with specified service_url or service_uid """ if service_url is None and service_uid is None: raise PermissionError( "You must specify one of service_url or service_uid") args = {"service_uid": service_uid, "service_url": service_url} response = self.call_function(function="get_service", args=args) from Acquire.Service import Service as _Service service = _Service.from_data(response["service_info"]) return service
def test_service(service_url, aaai_services): # get the public service from the default API frontend privkey = get_private_key("testing") response = call_function(service_url, response_key=privkey) service = Service.from_data(response["service_info"]) # also read the service from the object store directly push_testing_objstore(aaai_services["_services"][service_url]) push_is_running_service() private_service = get_this_service(need_private_access=True) pop_is_running_service() pop_testing_objstore() # create some test data that contain unicode characters for # testing encryption, signing and both encryption and signing data = {"hello": "'å∫ç∂ƒ©˙˚'", "key": privkey.public_key().to_data()} encrypted = service.encrypt_data(data) decrypted = private_service.decrypt_data(encrypted) assert(data == decrypted) signed = private_service.sign_data(data) verified = service.verify_data(signed) assert(data == verified) enc_sign = service.encrypt_data(private_service.sign_data(data)) dec_ver = service.verify_data(private_service.decrypt_data(enc_sign)) assert(data == dec_ver) service.call_function("admin/test") admin_user = aaai_services[service_url]["user"] auth = Authorisation(user=admin_user, resource="dump_keys %s" % service.uid()) service.call_function( function="dump_keys", args={"authorisation": auth.to_data()})
def challenge_service(self, service): """Send a challenge to the passed service, returning the actual service returned. This will only pass if our copy of the service matches us with the copy returned from the actual service. This verifies that there is a real service sitting at that URL, and that we have the right keys and certs """ from Acquire.Crypto import PrivateKey as _PrivateKey from Acquire.ObjectStore import bytes_to_string as _bytes_to_string from Acquire.Service import Service as _Service challenge = _PrivateKey.random_passphrase() pubkey = service.public_key() encrypted_challenge = pubkey.encrypt(challenge) args = {"challenge": _bytes_to_string(encrypted_challenge), "fingerprint": pubkey.fingerprint()} if service.uid().startswith("STAGE"): # we cannot call using the service directly, as it is # not yet fully constructed from Acquire.Service import get_this_service as _get_this_service from Acquire.Service import call_function as _call_function this_service = _get_this_service(need_private_access=True) result = _call_function(service_url=service.service_url(), function=None, args=args, args_key=service.public_key(), response_key=this_service.private_key(), public_cert=service.public_certificate()) else: result = service.call_function(function=None, args=args) if result["response"] != challenge: raise PermissionError( "Failure of the service %s to correctly respond " "to the challenge!" % service) return _Service.from_data(result["service_info"])
def aaai_services(tmpdir_factory): """This function creates mocked versions of all of the main services of the system, returning the json describing each service as a dictionary (which is passed to the test functions as the fixture) """ from Acquire.Identity import Authorisation from Acquire.Crypto import PrivateKey, OTP from Acquire.Service import call_function, Service _services = {} _services["registry"] = tmpdir_factory.mktemp("registry") _services["identity"] = tmpdir_factory.mktemp("identity") _services["accounting"] = tmpdir_factory.mktemp("accounting") _services["access"] = tmpdir_factory.mktemp("access") _services["storage"] = tmpdir_factory.mktemp("storage") _services["userdata"] = tmpdir_factory.mktemp("userdata") _services["compute"] = tmpdir_factory.mktemp("compute") _services["hugs"] = tmpdir_factory.mktemp("hugs") wallet_dir = tmpdir_factory.mktemp("wallet") wallet_password = PrivateKey.random_passphrase() _set_services(_services, wallet_dir, wallet_password) password = PrivateKey.random_passphrase() args = {"password": password} responses = {} os.environ["SERVICE_PASSWORD"] = "******" os.environ["STORAGE_COMPARTMENT"] = str(_services["userdata"]) args["canonical_url"] = "registry" args["service_type"] = "registry" args["registry_uid"] = "Z9-Z9" # UID of testing registry response = call_function("registry", function="admin/setup", args=args) registry_service = Service.from_data(response["service"]) registry_otp = OTP(OTP.extract_secret(response["provisioning_uri"])) registry_user = _login_admin("registry", "admin", password, registry_otp) responses["registry"] = { "service": registry_service, "user": registry_user, "response": response, } assert registry_service.registry_uid() == registry_service.uid() service_uids = [registry_service.uid()] args["canonical_url"] = "identity" args["service_type"] = "identity" response = call_function("identity", function="admin/setup", args=args) identity_service = Service.from_data(response["service"]) identity_otp = OTP(OTP.extract_secret(response["provisioning_uri"])) identity_user = _login_admin("identity", "admin", password, identity_otp) responses["identity"] = { "service": identity_service, "user": identity_user, "response": response, } assert identity_service.registry_uid() == registry_service.uid() assert identity_service.uid() not in service_uids service_uids.append(identity_service.uid()) args["canonical_url"] = "accounting" args["service_type"] = "accounting" response = call_function("accounting", function="admin/setup", args=args) accounting_service = Service.from_data(response["service"]) accounting_otp = OTP(OTP.extract_secret(response["provisioning_uri"])) accounting_user = _login_admin("accounting", "admin", password, accounting_otp) responses["accounting"] = { "service": accounting_service, "user": accounting_user, "response": response, } assert accounting_service.registry_uid() == registry_service.uid() assert accounting_service.uid() not in service_uids service_uids.append(accounting_service.uid()) args["canonical_url"] = "access" args["service_type"] = "access" response = call_function("access", function="admin/setup", args=args) responses["access"] = response access_service = Service.from_data(response["service"]) access_otp = OTP(OTP.extract_secret(response["provisioning_uri"])) access_user = _login_admin("access", "admin", password, access_otp) responses["access"] = { "service": access_service, "user": access_user, "response": response, } assert access_service.registry_uid() == registry_service.uid() assert access_service.uid() not in service_uids service_uids.append(access_service.uid()) args["canonical_url"] = "compute" args["service_type"] = "compute" response = call_function("compute", function="admin/setup", args=args) responses["compute"] = response compute_service = Service.from_data(response["service"]) compute_otp = OTP(OTP.extract_secret(response["provisioning_uri"])) compute_user = _login_admin("compute", "admin", password, compute_otp) responses["compute"] = { "service": compute_service, "user": compute_user, "response": response, } assert compute_service.registry_uid() == registry_service.uid() assert compute_service.uid() not in service_uids service_uids.append(compute_service.uid()) args["canonical_url"] = "storage" args["service_type"] = "storage" response = call_function("storage", function="admin/setup", args=args) storage_service = Service.from_data(response["service"]) storage_otp = OTP(OTP.extract_secret(response["provisioning_uri"])) storage_user = _login_admin("storage", "admin", password, storage_otp) responses["storage"] = { "service": storage_service, "user": storage_user, "response": response, } assert storage_service.registry_uid() == registry_service.uid() assert storage_service.uid() not in service_uids service_uids.append(storage_service.uid()) args["canonical_url"] = "hugs" args["service_type"] = "hugs" response = call_function("hugs", function="admin/setup", args=args) responses["hugs"] = response hugs_service = Service.from_data(response["service"]) hugs_otp = OTP(OTP.extract_secret(response["provisioning_uri"])) hugs_user = _login_admin("hugs", "admin", password, hugs_otp) responses["hugs"] = { "service": hugs_service, "user": hugs_user, "response": response, } resource = "trust_accounting_service %s" % accounting_service.uid() args = { "service_url": accounting_service.canonical_url(), "authorisation": Authorisation(user=access_user, resource=resource).to_data(), } access_service.call_function(function="admin/trust_accounting_service", args=args) responses["_services"] = _services return responses
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 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)
def get_trusted_registry_service(registry_uid=None, service_uid=None, service_url=None): """Return the trusted service info for the registry with specified registry_uid, or get any trusted registry service using either 'service_uid' or 'service_url' as a starting hint to locate a suitable registry """ if service_uid is not None: # for the moment, just try to get one registry. Eventually we should # try to get several in case this one is down registry_uid = get_primary_registry_uid(service_uid) return get_trusted_registry_service(registry_uid=registry_uid) if service_url is not None: if service_url.find(".") != -1: # try the main acquire registry first return get_trusted_registry_service(registry_uid="a0-a0") else: # this must be testing... return get_trusted_registry_service(registry_uid="Z9-Z9") if registry_uid is None: raise PermissionError( "You must specify one of registry_uid, service_uid " "or service_url") from Acquire.Service import get_trusted_service as _get_trusted_service try: registry_service = _get_trusted_service(service_uid=registry_uid, autofetch=False) except: registry_service = None if registry_service is not None: if not registry_service.is_registry_service(): from Acquire.Service import ServiceError raise ServiceError("The requested service (%s) for %s is NOT a " "registry service!" % (registry_service, registry_uid)) if registry_service.uid() != registry_uid: from Acquire.Service import ServiceError raise ServiceError( "Disagreement of UID (%s) is NOT the right registry service!" % registry_service) # everything is ok - we have seen this registry before return registry_service # boostrapping from Acquire.Registry import get_registry_details \ as _get_registry_details details = _get_registry_details(registry_uid) from Acquire.Service import call_function as _call_function from Acquire.Service import Service as _Service from Acquire.Crypto import get_private_key as _get_private_key from Acquire.Crypto import PrivateKey as _PrivateKey from Acquire.Crypto import PublicKey as _PublicKey from Acquire.ObjectStore import bytes_to_string as _bytes_to_string from Acquire.ObjectStore import string_to_bytes as _string_to_bytes privkey = _get_private_key(key="registry") pubkey = _PublicKey.from_data(details["public_key"]) pubcert = _PublicKey.from_data(details["public_certificate"]) # ask the registry to return to us their latest details - use # a challenge-response to make sure that the response is # properly returned challenge = _PrivateKey.random_passphrase() encrypted_challenge = _bytes_to_string(pubkey.encrypt(challenge)) args = { "challenge": encrypted_challenge, "fingerprint": pubkey.fingerprint() } result = _call_function(service_url=details["canonical_url"], function=None, args=args, args_key=pubkey, response_key=privkey, public_cert=pubcert) if result["response"] != challenge: from Acquire.Service import ServiceError raise ServiceError( "The requested service (%s) failed to respond to the challenge!" % registry_service) registry_service = _Service.from_data(result["service_info"]) if not registry_service.is_registry_service(): from Acquire.Service import ServiceError raise ServiceError( "The requested service (%s) is NOT a registry service!" % registry_service) if registry_service.uid() != details["uid"]: from Acquire.Service import ServiceError raise ServiceError( "Disagreement of UID (%s) is NOT the right registry service!" % registry_service) # ok - we've got the registry - add this to the set of # trusted services so that we don't need to bootstrap from # the registry details again from Acquire.Service import trust_service as _trust_service _trust_service(registry_service) return registry_service