Example #1
0
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
Example #2
0
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}
Example #4
0
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
Example #5
0
 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)
Example #6
0
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
Example #7
0
    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
Example #8
0
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
Example #9
0
    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
Example #10
0
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()
Example #11
0
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)
Example #12
0
    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
Example #13
0
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()})
Example #14
0
    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"])
Example #15
0
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
Example #16
0
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
Example #17
0
    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))
Example #18
0
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