Пример #1
0
def test_objstore(bucket):
    keys = []

    message = "ƒƒƒ Hello World ∂∂∂"

    ObjectStore.set_string_object(bucket, "test", message)
    keys.append("test")

    assert (message == ObjectStore.get_string_object(bucket, "test"))

    message = "€€#¢∞ Hello ˚ƒ´πµçµΩ"

    ObjectStore.set_string_object(bucket, "test/something", message)
    keys.append("test/something")

    assert (message == ObjectStore.get_string_object(bucket, "test/something"))

    data = {
        "cat": "mieow",
        "dog": "woof",
        "sounds": [1, 2, 3, 4, 5],
        "flag": True
    }

    ObjectStore.set_object_from_json(bucket, "test/object", data)
    keys.append("test/object")

    assert (data == ObjectStore.get_object_from_json(bucket, "test/object"))

    names = ObjectStore.get_all_object_names(bucket)

    assert (len(names) == len(keys))

    for name in names:
        assert (name in keys)
Пример #2
0
def get_service_user_account_uid(accounting_service_uid):
    """Return the UID of the financial Acquire.Accounting.Account
       that is held on the accounting service with UID
       'accounting_service_uid' for the service user on this
       service. This is the account to which payment for this
       service should be sent
    """
    assert_running_service()

    from Acquire.Service import get_service_account_bucket as \
        _get_service_account_bucket
    from Acquire.ObjectStore import ObjectStore as _ObjectStore

    bucket = _get_service_account_bucket()

    key = "%s/account/%s" % (_service_key, accounting_service_uid)

    try:
        account_uid = _ObjectStore.get_string_object(bucket, key)
    except:
        account_uid = None

    if account_uid is None:
        from Acquire.Service import ServiceAccountError
        raise ServiceAccountError(
            "This service does not have a valid financial account on "
            "the accounting service at '%s'" % accounting_service_uid)

    return account_uid
Пример #3
0
    def contains(self, account, bucket=None):
        """Return whether or not this group contains the passed account

            Args:
                account (:obj:`Account`): Account to check against group
                bucket (dict, default=None): Bucket to load data from
            Returns:
                bool : True if account in group, else False
        """
        self._assert_is_readable()

        from Acquire.Accounting import Account as _Account
        if not isinstance(account, _Account):
            raise TypeError("The passed account must be of type Account")

        if bucket is None:
            from Acquire.Service import get_service_account_bucket \
                as _get_service_account_bucket
            bucket = _get_service_account_bucket()

        # read the UID of the account in this group that matches the
        # passed account's name
        try:
            from Acquire.ObjectStore import ObjectStore as _ObjectStore
            account_uid = _ObjectStore.get_string_object(
                bucket, self._account_key(account.name()))
        except:
            account_uid = None

        return account.uid() == account_uid
Пример #4
0
    def fully_unlock(self):
        """This fully unlocks the mutex, removing all levels
           of recursion

           Returns:
                None
        """
        if self._is_locked == 0:
            return

        from Acquire.ObjectStore import ObjectStore as _ObjectStore
        from Acquire.ObjectStore import get_datetime_now as _get_datetime_now

        try:
            holder = _ObjectStore.get_string_object(self._bucket, self._key)
        except:
            holder = None

        if holder == self._lockstring:
            # we hold the mutex - delete the key
            _ObjectStore.delete_object(self._bucket, self._key)

        self._lockstring = None
        self._is_locked = 0

        if self._end_lease < _get_datetime_now():
            self._end_lease = None
            from Acquire.ObjectStore import MutexTimeoutError
            raise MutexTimeoutError("The lease on this mutex expired before "
                                    "this mutex was unlocked!")
        else:
            self._end_lease = None
Пример #5
0
def create_service_user_account(service, accounting_service_url):
    """Call this function to create the financial service account
       for this service on the accounting service at 'accounting_service_url'

       This does nothing if the account already exists
    """
    assert_running_service()

    accounting_service = service.get_trusted_service(
        service_url=accounting_service_url)
    accounting_service_uid = accounting_service.uid()

    key = "%s/account/%s" % (_service_key, accounting_service_uid)
    bucket = service.bucket()

    from Acquire.ObjectStore import ObjectStore as _ObjectStore

    try:
        account_uid = _ObjectStore.get_string_object(bucket, key)
    except:
        account_uid = None

    if account_uid:
        # we already have an account...
        return

    service_user = service.login_service_user()

    try:
        from Acquire.Client import create_account as _create_account
        from Acquire.Client import deposit as _deposit

        account = _create_account(
            service_user,
            "main",
            "Main account to receive payment for all use on service "
            "%s (%s)" % (service.canonical_url(), service.uid()),
            accounting_service=accounting_service)

        _deposit(user=service_user,
                 value=100.0,
                 account_name="main",
                 accounting_service=accounting_service)

        account_uid = account.uid()

        _ObjectStore.set_string_object(bucket, key, account_uid)
    except Exception as e:
        from Acquire.Service import exception_to_string
        from Acquire.Service import ServiceAccountError
        raise ServiceAccountError(
            "Unable to create a financial account for the service "
            "principal for '%s' on accounting service '%s'\n\nERROR\n%s" %
            (str(service), str(accounting_service), exception_to_string(e)))
Пример #6
0
    def get_service_key(self, service_uid=None, service_url=None):
        """Return the key for the passed service in the object store"""
        if service_uid is not None:
            return self._get_key_for_uid(service_uid)
        else:
            bucket = self.get_bucket()
            key = self._get_key_for_url(service_url)

            try:
                from Acquire.ObjectStore import ObjectStore as _ObjectStore
                service_key = _ObjectStore.get_string_object(bucket=bucket,
                                                             key=key)
            except:
                service_key = None

            return service_key
Пример #7
0
    def contains(self, account, bucket=None):
        """Return whether or not this group contains the passed account"""
        if not isinstance(account, _Account):
            raise TypeError("The passed account must be of type Account")

        if bucket is None:
            bucket = _login_to_service_account()

        # read the UID of the account in this group that matches the
        # passed account's name
        try:
            account_uid = _ObjectStore.get_string_object(
                bucket, self._account_key(account.name()))
        except:
            account_uid = None

        return account.uid() == account_uid
Пример #8
0
    def get_status(uid):
        """Return the status of the LoginSession with specified UID"""
        from Acquire.ObjectStore import ObjectStore as _ObjectStore
        from Acquire.Service import get_service_account_bucket \
            as _get_service_account_bucket

        bucket = _get_service_account_bucket()

        try:
            key = "%s/status/%s" % (_sessions_key, uid)
            status = _ObjectStore.get_string_object(bucket=bucket, key=key)
        except:
            status = None

        if status is None:
            from Acquire.Identity import LoginSessionError
            raise LoginSessionError("Cannot find a session with UID '%s'" %
                                    uid)

        return status
Пример #9
0
    def get_account(self, name, bucket=None):
        """Return the account called 'name' from this group

            Args:
                name (:obj:`str`): Name of account to retrieve
                bucket (:obj:`dict`): Bucket to load data from

            Returns:
                :obj:`Account`: Account object
        """
        self._assert_is_readable()

        if bucket is None:
            from Acquire.Service import get_service_account_bucket \
                as _get_service_account_bucket
            bucket = _get_service_account_bucket()

        try:
            from Acquire.ObjectStore import ObjectStore as _ObjectStore
            account_uid = _ObjectStore.get_string_object(
                bucket, self._account_key(name))
        except:
            account_uid = None

        if account_uid is None:
            # ensure that the user always has a "main" account
            if name == "main":
                return self.create_account("main",
                                           "primary user account",
                                           overdraft_limit=0,
                                           bucket=bucket)

            from Acquire.Accounting import AccountError
            raise AccountError("There is no account called '%s' in the "
                               "group '%s'" % (name, self.group()))

        from Acquire.Accounting import Account as _Account
        return _Account(uid=account_uid, bucket=bucket)
Пример #10
0
    def get_account(self, name, bucket=None):
        """Return the account called 'name' from this group"""
        if bucket is None:
            bucket = _login_to_service_account()

        try:
            account_uid = _ObjectStore.get_string_object(
                bucket, self._account_key(name))
        except:
            account_uid = None

        if account_uid is None:
            # ensure that the user always has a "main" account
            if name == "main":
                return self.create_account("main",
                                           "primary user account",
                                           overdraft_limit=0,
                                           bucket=bucket)

            raise AccountError("There is no account called '%s' in the "
                               "group '%s'" % (name, self.group()))

        return _Account(uid=account_uid, bucket=bucket)
Пример #11
0
def load_service_key_from_objstore(fingerprint):
    """This function will see if we have an old key with the requested
       fingerprint, and if so, we will try to load and return that
       key from the object store
    """
    from Acquire.ObjectStore import ObjectStore as _ObjectStore
    from Acquire.Service import get_service_account_bucket \
        as _get_service_account_bucket
    from Acquire.Crypto import KeyManipulationError

    bucket = _get_service_account_bucket()

    try:
        key = "%s/oldkeys/fingerprints/%s" % (_service_key, fingerprint)
        keyfile = _ObjectStore.get_string_object(bucket, key)
    except:
        keyfile = None

    if keyfile is None:
        raise KeyManipulationError(
            "Cannot find a key or certificate with fingerprint '%s' : %s" %
            (fingerprint, key))

    try:
        keydata = _ObjectStore.get_object_from_json(bucket, keyfile)
    except Exception as e:
        keydata = None
        error = str(e)

    if keydata is None:
        raise KeyManipulationError(
            "Unable to load the key or certificate with fingerprint '%s': %s" %
            (fingerprint, error))

    service = get_this_service(need_private_access=True)
    return service.load_keys(keydata)[fingerprint]
Пример #12
0
def run(args):
    """This function is called by the user to log in and validate
       that a session is authorised to connect"""

    status = 0
    message = None
    provisioning_uri = None
    assigned_device_uid = None

    short_uid = args["short_uid"]
    username = args["username"]
    password = args["password"]
    otpcode = args["otpcode"]

    try:
        remember_device = args["remember_device"]
    except:
        remember_device = False

    try:
        device_uid = args["device_uid"]
    except:
        device_uid = None

    # create the user account for the user
    user_account = UserAccount(username)

    # log into the central identity account to query
    # the current status of this login session
    bucket = login_to_service_account()

    # locate the session referred to by this uid
    base_key = "requests/%s" % short_uid
    session_keys = ObjectStore.get_all_object_names(bucket, base_key)

    # try all of the sessions to find the one that the user
    # may be referring to...
    login_session_key = None
    request_session_key = None

    for session_key in session_keys:
        request_session_key = "%s/%s" % (base_key, session_key)
        session_user = ObjectStore.get_string_object(
            bucket, request_session_key)

        # did the right user request this session?
        if user_account.name() == session_user:
            if login_session_key:
                # this is an extremely unlikely edge case, whereby
                # two login requests within a 30 minute interval for the
                # same user result in the same short UID. This should be
                # signified as an error and the user asked to create a
                # new request
                raise LoginError(
                    "You have found an extremely rare edge-case "
                    "whereby two different login requests have randomly "
                    "obtained the same short UID. As we can't work out "
                    "which request is valid, the login is denied. Please "
                    "create a new login request, which will then have a "
                    "new login request UID")
            else:
                login_session_key = session_key

    if not login_session_key:
        raise LoginError(
            "There is no active login request with the "
            "short UID '%s' for user '%s'" % (short_uid, username))

    login_session_key = "sessions/%s/%s" % (user_account.sanitised_name(),
                                            login_session_key)

    # fully load the user account from the object store so that we
    # can validate the username and password
    try:
        account_key = "accounts/%s" % user_account.sanitised_name()
        user_account = UserAccount.from_data(
            ObjectStore.get_object_from_json(bucket, account_key))
    except:
        raise LoginError("No account available with username '%s'" %
                         username)

    if (not remember_device) and device_uid:
        # see if this device has been seen before
        device_key = "devices/%s/%s" % (user_account.sanitised_name(),
                                        device_uid)

        try:
            device_secret = ObjectStore.get_string_object(bucket,
                                                          device_key)
        except:
            device_secret = None

        if device_secret is None:
            raise LoginError(
                "The login device is not recognised. Please try to "
                "log in again using your master one-time-password.")
    else:
        device_secret = None

    # now try to log into this account using the supplied
    # password and one-time-code
    try:
        if device_secret:
            user_account.validate_password(password, otpcode,
                                           device_secret=device_secret)
        elif remember_device:
            (device_secret, provisioning_uri) = \
                        user_account.validate_password(
                                    password, otpcode,
                                    remember_device=True)

            device_uid = str(uuid.uuid4())
            device_key = "devices/%s/%s" % (user_account.sanitised_name(),
                                            device_uid)

            assigned_device_uid = device_uid
        else:
            user_account.validate_password(password, otpcode)
    except:
        # don't leak info about why validation failed
        raise LoginError("The password or OTP code is incorrect")

    # the user is valid - load up the actual login session
    login_session = LoginSession.from_data(
                        ObjectStore.get_object_from_json(bucket,
                                                         login_session_key))

    # we must record the session against which this otpcode has
    # been validated. This is to stop us validating an otpcode more than
    # once (e.g. if the password and code have been intercepted).
    # Any sessions validated using the same code should be treated
    # as immediately suspcious
    otproot = "otps/%s" % user_account.sanitised_name()
    sessions = ObjectStore.get_all_strings(bucket, otproot)

    utcnow = datetime.datetime.utcnow()

    for session in sessions:
        otpkey = "%s/%s" % (otproot, session)
        otpstring = ObjectStore.get_string_object(bucket, otpkey)

        (timestamp, code) = otpstring.split("|||")

        # remove all codes that are more than 10 minutes old. The
        # otp codes are only valid for 3 minutes, so no need to record
        # codes that have been used that are older than that...
        timedelta = utcnow - datetime.datetime.fromtimestamp(
                                                    float(timestamp))

        if timedelta.seconds > 600:
            try:
                ObjectStore.delete_object(bucket, otpkey)
            except:
                pass

        elif code == str(otpcode):
            # Low probability there is some recycling,
            # but very suspicious if the code was validated within the last
            # 10 minutes... (as 3 minute timeout of a code)
            suspect_key = "sessions/%s/%s" % (
                user_account.sanitised_name(), session)

            suspect_session = None

            try:
                suspect_session = LoginSession.from_data(
                        ObjectStore.get_object_from_json(bucket,
                                                         suspect_key))
            except:
                pass

            if suspect_session:
                suspect_session.set_suspicious()
                ObjectStore.set_object_from_json(bucket, suspect_key,
                                                 suspect_session.to_data())

            raise LoginError(
                "Cannot authorise the login as the one-time-code "
                "you supplied has already been used within the last 10 "
                "minutes. The chance of this happening is really low, so "
                "we are treating this as a suspicious event. You need to "
                "try another code. Meanwhile, the other login that used "
                "this code has been put into a 'suspicious' state.")

    # record the value and timestamp of when this otpcode was used
    otpkey = "%s/%s" % (otproot, login_session.uuid())
    otpstring = "%s|||%s" % (datetime.datetime.utcnow().timestamp(),
                             otpcode)

    ObjectStore.set_string_object(bucket, otpkey, otpstring)

    login_session.set_approved()

    # write this session back to the object store
    ObjectStore.set_object_from_json(bucket, login_session_key,
                                     login_session.to_data())

    # save the device secret as everything has now worked
    if assigned_device_uid:
        ObjectStore.set_string_object(bucket, device_key,
                                      device_secret)

    # finally, remove this from the list of requested logins
    try:
        ObjectStore.delete_object(bucket, request_session_key)
    except:
        pass

    status = 0
    message = "Success: Status = %s" % login_session.status()

    return_value = create_return_value(status, message)

    if provisioning_uri:
        return_value["provisioning_uri"] = provisioning_uri
        return_value["device_uid"] = assigned_device_uid

    return return_value
Пример #13
0
    def create_account(self,
                       name,
                       description=None,
                       overdraft_limit=None,
                       bucket=None):
        """Create a new account called 'name' in this group. This will
           return the existing account if it already exists
        """
        if name is None:
            raise ValueError("You must pass a name of the new account")

        account_key = self._account_key(name)

        if bucket is None:
            bucket = _login_to_service_account()

        try:
            account_uid = _ObjectStore.get_string_object(bucket, account_key)
        except:
            account_uid = None

        if account_uid is not None:
            # this account already exists - just return it
            account = _Account(uid=account_uid, bucket=bucket)

            if overdraft_limit is not None:
                account.set_overdraft_limit(overdraft_limit, bucket=bucket)

            return account

        # make sure that no-one has created this account before
        m = _Mutex(account_key, timeout=600, lease_time=600, bucket=bucket)

        try:
            account_uid = _ObjectStore.get_string_object(bucket, account_key)
        except:
            account_uid = None

        if account_uid is not None:
            m.unlock()
            # this account already exists - just return it
            account = _Account(uid=account_uid, bucket=bucket)

            if overdraft_limit is not None:
                account.set_overdraft_limit(overdraft_limit, bucket=bucket)

            return account

        # write a temporary UID to the object store so that we
        # can ensure we are the only function to create it
        try:
            _ObjectStore.set_string_object(bucket, account_key,
                                           "under_construction")
        except:
            m.unlock()
            raise

        m.unlock()

        # ok - we are the only function creating this account. Let's try
        # to create it properly
        try:
            account = _Account(name=name,
                               description=description,
                               bucket=bucket)
        except:
            try:
                _ObjectStore.delete_object(bucket, account_key)
            except:
                pass

            raise

        if overdraft_limit is not None:
            account.set_overdraft_limit(overdraft_limit, bucket=bucket)

        _ObjectStore.set_string_object(bucket, account_key, account.uid())

        return account
Пример #14
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
Пример #15
0
def test_objstore(bucket):
    keys = []

    message = "ƒƒƒ Hello World ∂∂∂"

    ObjectStore.set_string_object(bucket, "test", message)
    keys.append("test")

    assert(message == ObjectStore.get_string_object(bucket, "test"))

    message = "€€#¢∞ Hello ˚ƒ´πµçµΩ"

    ObjectStore.set_string_object(bucket, "test/something", message)
    keys.append("test/something")

    assert(message == ObjectStore.get_string_object(bucket, "test/something"))

    data = {"cat": "mieow",
            "dog": "woof",
            "sounds": [1, 2, 3, 4, 5],
            "flag": True}

    ObjectStore.set_object_from_json(bucket, "test/object", data)
    keys.append("test/object")

    assert(data == ObjectStore.get_object_from_json(bucket, "test/object"))

    names = ObjectStore.get_all_object_names(bucket)

    assert(len(names) == len(keys))

    names = ObjectStore.get_all_object_names(bucket, "test")

    assert(len(names) == 3)

    names = ObjectStore.get_all_object_names(bucket, "test/")

    assert(len(names) == 2)

    names = ObjectStore.get_all_object_names(bucket, "test/some")

    assert(len(names) == 1)

    for name in names:
        assert(name in keys)

    new_bucket = ObjectStore.create_bucket(bucket, "new_bucket")

    ObjectStore.set_object_from_json(new_bucket, "test/object2", data)
    assert(data == ObjectStore.get_object_from_json(new_bucket,
                                                    "test/object2"))

    with pytest.raises(ObjectStoreError):
        new_bucket = ObjectStore.create_bucket(bucket, "testing_objstore")

    with pytest.raises(ObjectStoreError):
        new_bucket = ObjectStore.create_bucket(bucket, "new_bucket")

    with pytest.raises(ObjectStoreError):
        new_bucket = ObjectStore.get_bucket(bucket, "get_bucket",
                                            create_if_needed=False)

    new_bucket = ObjectStore.get_bucket(bucket, "get_bucket",
                                        create_if_needed=True)

    test_key = "test_string"
    test_value = "test_string_value"

    ObjectStore.set_string_object(new_bucket, test_key, test_value)

    new_bucket2 = ObjectStore.get_bucket(bucket, "get_bucket",
                                         create_if_needed=False)

    test_value2 = ObjectStore.get_string_object(new_bucket2, test_key)

    assert(test_value == test_value2)
Пример #16
0
    def register_service(self, service, force_new_uid=False):
        """Register the passed service"""
        from Acquire.Service import Service as _Service
        from Acquire.ObjectStore import ObjectStore as _ObjectStore

        if not isinstance(service, _Service):
            raise TypeError("You can only register Service objects")

        if service.uid() != "STAGE1":
            raise PermissionError("You cannot register a service twice!")

        # first, stop a single domain monopolising resources...
        bucket = self.get_bucket()
        domain = self._get_domain(service.service_url())
        domainroot = self._get_root_key_for_domain(domain=domain)

        try:
            pending_keys = _ObjectStore.get_all_object_names(
                                        bucket=bucket,
                                        prefix="%s/pending/" % domainroot)
            num_pending = len(pending_keys)
        except:
            num_pending = 0

        if num_pending >= 4:
            raise PermissionError(
                "You cannot register a new service as you have reached "
                "the quota (4) for the number of pending services registered "
                "against the domain '%s'. Please get some of these services "
                "so that you can make them active." % domain)

        try:
            active_keys = _ObjectStore.get_all_object_names(
                                        bucket=bucket,
                                        prefix="%s/active/" % domainroot)
            num_active = len(active_keys)
        except:
            num_active = 0

        if num_active + num_pending >= 16:
            raise PermissionError(
                "You cannot register a new service as you have reached "
                "the quota (16) for the number registered against the "
                "domain '%s'" % domain)

        # first, challenge the service to ensure that it exists
        # and our keys are correct
        service = self.challenge_service(service)

        if service.uid() != "STAGE1":
            raise PermissionError("You cannot register a service twice!")

        bucket = self.get_bucket()
        urlkey = self._get_key_for_url(service.canonical_url())

        try:
            uidkey = _ObjectStore.get_string_object(bucket=bucket,
                                                    key=urlkey)
        except:
            uidkey = None

        service_uid = None

        if uidkey is not None:
            # there is already a service registered at this domain. Since
            # we have successfully challenged the service, this must be
            # someone re-bootstrapping a service. It is safe to give them
            # back their UID if requested
            if not force_new_uid:
                service_uid = self._get_uid_from_key(uidkey)

        if service_uid is None:
            # how many services from this domain are still pending?

            service_uid = _generate_service_uid(
                                        bucket=self.get_bucket(),
                                        registry_uid=self.registry_uid())

        # save this service to the object store
        uidkey = self._get_key_for_uid(service_uid)

        _ObjectStore.set_object_from_json(bucket=bucket, key=uidkey,
                                          data=service.to_data())

        _ObjectStore.set_string_object(bucket=bucket, key=urlkey,
                                       string_data=uidkey)

        domainkey = self._get_root_key_for_domain(domain=domain)

        _ObjectStore.set_string_object(
                                bucket=bucket,
                                key="%s/pending/%s" % (domainkey, service_uid),
                                string_data=uidkey)

        return service_uid
Пример #17
0
def run(args):
    """This function will allow anyone to query who matches
       the passed UID or username (map from one to the other)"""

    status = 0
    message = None
    user_uid = None
    username = None
    public_key = None
    public_cert = None
    logout_timestamp = None
    login_status = None

    try:
        user_uid = args["user_uid"]
    except:
        pass

    try:
        username = args["username"]
    except:
        pass

    try:
        session_uid = args["session_uid"]
    except:
        session_uid = None

    bucket = None
    user_account = None

    if user_uid is None and username is None:
        raise WhoisLookupError(
            "You must supply either a username or user_uid to look up...")

    elif user_uid is None:
        # look up the user_uid from the username
        user_account = UserAccount(username)
        bucket = login_to_service_account()
        user_key = "accounts/%s" % user_account.sanitised_name()

        try:
            user_account = UserAccount.from_data(
                ObjectStore.get_object_from_json(bucket, user_key))
        except:
            raise WhoisLookupError("Cannot find an account for name '%s'" %
                                   username)

        user_uid = user_account.uid()

    elif username is None:
        # look up the username from the uuid
        bucket = login_to_service_account()

        uid_key = "whois/%s" % user_uid

        try:
            username = ObjectStore.get_string_object(bucket, uid_key)
        except:
            raise WhoisLookupError("Cannot find an account for user_uid '%s'" %
                                   user_uid)

    else:
        raise WhoisLookupError("You must only supply one of the username "
                               "or user_uid to look up - not both!")

    if session_uid:
        # now look up the public signing key for this session, if it is
        # a valid login session
        if user_account is None:
            user_account = UserAccount(username)

        user_session_key = "sessions/%s/%s" % \
            (user_account.sanitised_name(), session_uid)

        try:
            login_session = LoginSession.from_data(
                ObjectStore.get_object_from_json(bucket, user_session_key))
        except:
            login_session = None

        if login_session is None:
            user_session_key = "expired_sessions/%s/%s" % \
                                    (user_account.sanitised_name(),
                                     session_uid)

            login_session = LoginSession.from_data(
                ObjectStore.get_object_from_json(bucket, user_session_key))

        if login_session is None:
            raise InvalidSessionError("Cannot find the session '%s'" %
                                      session_uid)

        if login_session.is_approved():
            public_key = login_session.public_key()
            public_cert = login_session.public_certificate()

        elif login_session.is_logged_out():
            public_cert = login_session.public_certificate()
            logout_timestamp = login_session.logout_time().timestamp()

        else:
            raise InvalidSessionError("You cannot get the keys for a session "
                                      "for which the user has not logged in!")

        login_status = login_session.status()

    status = 0
    message = "Success"

    return_value = create_return_value(status, message)

    if user_uid:
        return_value["user_uid"] = str(user_uid)

    if username:
        return_value["username"] = str(username)

    if public_key:
        return_value["public_key"] = public_key.to_data()

    if public_cert:
        return_value["public_cert"] = public_cert.to_data()

    if logout_timestamp:
        return_value["logout_timestamp"] = logout_timestamp

    if login_status:
        return_value["login_status"] = str(login_status)

    return return_value
Пример #18
0
    def create_account(self,
                       name,
                       description=None,
                       overdraft_limit=None,
                       bucket=None,
                       authorisation=None):
        """Create a new account called 'name' in this group. This will
           return the existing account if it already exists

           Args:
                name (str): Name of account to create
                description (default=None): Description of account
                overdraft_limit (int, default=None): Limit of overdraft
                bucket (dict, default=None): Bucket to load data from

            Returns:
                Account: New Account object
        """
        if name is None:
            raise ValueError("You must pass a name of the new account")

        from Acquire.Identity import Authorisation as _Authorisation

        if authorisation is not None:
            if not isinstance(authorisation, _Authorisation):
                raise TypeError("The authorisation must be type Authorisation")

            if self._user_guid is not None:
                if self._user_guid != authorisation.user_guid():
                    raise PermissionError(
                        "The same user who opened this accounts group (%s) "
                        "must create accounts in this group (%s)" %
                        (self._user_guid, authorisation.user_guid()))

            authorisation.verify("create_account %s" % name)

        self._assert_is_writeable()

        account_key = self._account_key(name)

        if bucket is None:
            from Acquire.Service import get_service_account_bucket \
                as _get_service_account_bucket
            bucket = _get_service_account_bucket()

        from Acquire.ObjectStore import ObjectStore as _ObjectStore
        from Acquire.Accounting import Account as _Account

        try:
            account_uid = _ObjectStore.get_string_object(bucket, account_key)
        except:
            account_uid = None

        if account_uid is not None:
            # this account already exists - just return it
            account = _Account(uid=account_uid, bucket=bucket)

            if account.group_name() != self.name():
                account.set_group(self)

            if overdraft_limit is not None:
                account.set_overdraft_limit(overdraft_limit, bucket=bucket)

            return account

        # make sure that no-one has created this account before
        from Acquire.ObjectStore import Mutex as _Mutex
        m = _Mutex(account_key, timeout=600, lease_time=600, bucket=bucket)

        try:
            account_uid = _ObjectStore.get_string_object(bucket, account_key)
        except:
            account_uid = None

        if account_uid is not None:
            m.unlock()
            # this account already exists - just return it
            account = _Account(uid=account_uid, bucket=bucket)

            if account.group_name() != self.name():
                account.set_group(self)

            if overdraft_limit is not None:
                account.set_overdraft_limit(overdraft_limit, bucket=bucket)

            return account

        # write a temporary UID to the object store so that we
        # can ensure we are the only function to create it
        try:
            _ObjectStore.set_string_object(bucket, account_key,
                                           "under_construction")
        except:
            m.unlock()
            raise

        m.unlock()

        # ok - we are the only function creating this account. Let's try
        # to create it properly. The account is created with the same
        # ACLRules of the group.
        try:
            from Acquire.Identity import ACLRules as _ACLRules
            account = _Account(name=name,
                               description=description,
                               group_name=self.name(),
                               aclrules=_ACLRules.inherit(),
                               bucket=bucket)
        except:
            try:
                _ObjectStore.delete_object(bucket, account_key)
            except:
                pass

            raise

        if overdraft_limit is not None:
            account.set_overdraft_limit(overdraft_limit, bucket=bucket)

        _ObjectStore.set_string_object(bucket, account_key, account.uid())

        return account
Пример #19
0
    def assert_once(self, stale_time=7200, scope=None, permissions=None):
        """Assert that this is in the one and only time that this
           service has seen this authorisation. This records the
           UID of the authorisation to the object store and then
           verifies that the signature of the UID is correct.

           There is a small race condition if the service asserts
           the authorisation at the exact same time, but this is
           a highly unlikely occurance. The aim is to prevent
           replay attacks.
        """
        if self.is_null():
            raise PermissionError("Cannot assert_once a null Authorisation")

        if self.is_stale(stale_time):
            if now < self._auth_datetime:
                raise PermissionError(
                    "Cannot assert_once an Authorisation signed "
                    "in the future - please check your clock")
            else:
                raise PermissionError(
                    "Cannot assert_once a stale Authorisation")

        from Acquire.ObjectStore import ObjectStore as _ObjectStore
        from Acquire.Service import get_service_account_bucket \
            as _get_service_account_bucket
        from Acquire.ObjectStore import get_datetime_now_to_string \
            as _get_datetime_now_to_string

        bucket = _get_service_account_bucket()
        authkey = "auth_once/%s" % self._uid
        now = _get_datetime_now_to_string()

        try:
            data = _ObjectStore.get_string_object(bucket=bucket, key=authkey)
        except:
            data = None

        if data is not None:
            raise PermissionError(
                "Cannot auth_once the authorisation as it has been used "
                "before on this service!")

        # This is the first time this authorisation has been seen.
        # Record this to the object store to prevent anyone else
        # from using this authorisation on this service. There is a
        # small race condition here, but this would be extremely
        # challenging to exploit, and mitigating it would be a
        # significant performance problem. Ideally the object store
        # would have a "test_and_set" to enable us to set only if
        # the previous value is None
        _ObjectStore.set_string_object(bucket=bucket,
                                       key=authkey,
                                       string_data=now)

        # Now validate that the signature of the UID is correct
        public_cert = self._get_user_public_cert(scope=scope,
                                                 permissions=permissions)

        if public_cert is None:
            raise PermissionError(
                "There is no public certificate for this user in "
                "scope '%s' with permissions '%s'" % (scope, permissions))

        try:
            public_cert.verify(self._siguid, self._uid)
        except Exception as e:
            raise PermissionError(
                "Cannot auth_once the authorisation as the signature "
                "is invalid! % s" % str(e))
Пример #20
0
    def lock(self, timeout=None, lease_time=None):
        """Lock the mutex, blocking until the mutex is held, or until
           'timeout' seconds have passed. If we time out, then an exception is
           raised. The lock is held for a maximum of 'lease_time' seconds.

           Args:
                timeout (int): Number of seconds to block
                lease_time (int): Number of seconds to hold the lock
           Returns:
                None
        """
        # if the user does not provide a timeout, then we will set a timeout
        # to 10 seconds
        if timeout is None:
            timeout = 10.0
        else:
            timeout = float(timeout)

        # if the user does not provide a lease_time, then we will set a
        # default of only 10 seconds
        if lease_time is None:
            lease_time = 10.0
        else:
            lease_time = float(lease_time)

        from Acquire.ObjectStore import get_datetime_now as _get_datetime_now
        from Acquire.ObjectStore import datetime_to_string \
            as _datetime_to_string
        from Acquire.ObjectStore import string_to_datetime \
            as _string_to_datetime
        from Acquire.ObjectStore import ObjectStore as _ObjectStore

        if self.is_locked():
            # renew the lease - if there is less than a second remaining
            # on the lease then unlock and then lock again from scratch
            now = _get_datetime_now()

            if (now > self._end_lease) or (now - self._end_lease).seconds < 1:
                self.fully_unlock()
                self.lock(timeout, lease_time)
            else:
                self._end_lease = now + _datetime.timedelta(seconds=lease_time)

                self._lockstring = "%s{}%s" % (
                    self._secret, _datetime_to_string(self._end_lease))

                _ObjectStore.set_string_object(self._bucket, self._key,
                                               self._lockstring)

                self._is_locked += 1

            return

        now = _get_datetime_now()
        endtime = now + _datetime.timedelta(seconds=timeout)

        # This is the first time we are trying to get a lock
        while now < endtime:
            # does anyone else hold the lock?
            try:
                holder = _ObjectStore.get_string_object(
                    self._bucket, self._key)
            except:
                holder = None

            is_held = True

            if holder is None:
                is_held = False
            else:
                end_lease = _string_to_datetime(holder.split("{}")[-1])
                if now > end_lease:
                    # the lease from the other holder has expired :-)
                    is_held = False

            if not is_held:
                # no-one holds this mutex - try to hold it now
                self._end_lease = now + _datetime.timedelta(seconds=lease_time)

                self._lockstring = "%s{}%s" % (
                    self._secret, _datetime_to_string(self._end_lease))

                _ObjectStore.set_string_object(self._bucket, self._key,
                                               self._lockstring)

                holder = _ObjectStore.get_string_object(
                    self._bucket, self._key)
            else:
                self._lockstring = None

            if holder == self._lockstring:
                # it looks like we are the holder - read and write again
                # just to make sure
                holder = _ObjectStore.get_string_object(
                    self._bucket, self._key)

                if holder == self._lockstring:
                    # write again just to make sure
                    _ObjectStore.set_string_object(self._bucket, self._key,
                                                   self._lockstring)

                    holder = _ObjectStore.get_string_object(
                        self._bucket, self._key)

            if holder == self._lockstring:
                # we have read and written our secret to the object store
                # three times. While a race condition is still possible,
                # I'd hope it is now highly unlikely - we now hold the mutex
                self._is_locked = 1
                return

            # only try the lock 4 times a second
            _time.sleep(0.25)

            now = _get_datetime_now()

        from Acquire.ObjectStore import MutexTimeoutError
        raise MutexTimeoutError("Cannot acquire a mutex lock on the "
                                "key '%s'" % self._key)
Пример #21
0
    def get_drive(self, name, aclrules=None, autocreate=True):
        """Return the DriveMeta for the Drive that the user has
           called 'name'. If 'autocreate' is True then this
           drive is automatically created if it does not exist. Note
           that the '/' in the name will be interpreted as drive
           separators.
        """
        if self.is_null():
            raise PermissionError(
                "You cannot get a DriveInfo from a null UserDrives")

        # break the name into directory parts
        from Acquire.ObjectStore import string_to_filepath_parts \
            as _string_to_filepath_parts

        parts = _string_to_filepath_parts(name)

        # first get the root drive...
        root_name = parts[0]

        from Acquire.Service import get_service_account_bucket \
            as _get_service_account_bucket
        from Acquire.ObjectStore import ObjectStore as _ObjectStore

        from Acquire.ObjectStore import string_to_encoded as _string_to_encoded

        encoded_name = _string_to_encoded(root_name)
        drive_name = root_name

        bucket = _get_service_account_bucket()

        drive_key = "%s/%s/%s" % (_drives_root, self._user_guid, encoded_name)

        try:
            drive_uid = _ObjectStore.get_string_object(bucket, drive_key)
        except:
            drive_uid = None

        if drive_uid is not None:
            from Acquire.Storage import DriveInfo as _DriveInfo
            drive = _DriveInfo(drive_uid=drive_uid,
                               is_authorised=self._is_authorised,
                               identifiers=self._identifiers)
        else:
            drive = None

        if drive is None:
            if self._is_authorised and autocreate:
                # create a new UID for the drive and write this to the
                # object store
                from Acquire.ObjectStore import create_uid as _create_uid

                drive_uid = _create_uid()

                drive_uid = _ObjectStore.set_ins_string_object(
                    bucket, drive_key, drive_uid)

                from Acquire.Storage import DriveInfo as _DriveInfo
                drive = _DriveInfo(drive_uid=drive_uid,
                                   identifiers=self._identifiers,
                                   is_authorised=self._is_authorised,
                                   aclrules=aclrules,
                                   autocreate=True)

        if drive is None:
            from Acquire.Storage import MissingDriveError
            raise MissingDriveError("There is no Drive called '%s' available" %
                                    name)

        container = []

        # now we have the drive, get the sub-drive in this drive...
        if len(parts) > 1:
            for subdrive in parts[1:]:
                container.append(drive.uid())
                drive_name = subdrive
                drive = self._get_subdrive(drive_uid=drive.uid(),
                                           name=drive_name,
                                           autocreate=autocreate)

                if drive is None:
                    from Acquire.Storage import MissingDriveError
                    raise MissingDriveError(
                        "There is no Drive called '%s' available" % name)

        from Acquire.Storage import DriveMeta as _DriveMeta

        drivemeta = _DriveMeta(name=drive_name,
                               uid=drive.uid(),
                               container=container,
                               aclrules=drive.aclrules())

        drivemeta.resolve_acl(identifiers=self._identifiers)

        return drivemeta
Пример #22
0
def test_par(bucket):
    privkey = get_private_key()
    pubkey = privkey.public_key()

    # first try to create a PAR for the whole bucket
    par = ObjectStore.create_par(bucket, readable=False, writeable=True,
                                 duration=100, encrypt_key=pubkey)

    # should not take 10 seconds to create and return the PAR...
    assert(par.seconds_remaining(buffer=0) > 90)
    assert(par.seconds_remaining(buffer=0) < 101)

    # trying to create a par for a non-existant object should fail
    key = "something"
    value = "∆ƒ^ø  ®∆ ®∆ #®∆… ®#€   €"

    with pytest.raises(PARError):
        par = ObjectStore.create_par(bucket, key=key, encrypt_key=pubkey)

    ObjectStore.set_string_object(bucket, key, value)

    par = ObjectStore.create_par(bucket, key=key, duration=60,
                                 encrypt_key=pubkey)

    assert(par.seconds_remaining(buffer=0) > 55)
    assert(par.seconds_remaining(buffer=0) < 61)

    assert(not par.is_writeable())

    assert(par.key() == key)

    val = par.read(privkey).get_string_object()

    assert(val == value)

    value = "∆˚¬#  #ª ƒ∆ ¬¬¬˚¬∂ß ˚¬ ¬¬¬ßßß"

    with pytest.raises(PARPermissionsError):
        par.write(privkey).set_string_object(value)

    # close the PAR and then assert a closed PAR is null
    par.close()
    assert(par.is_null())

    par = ObjectStore.create_par(bucket, key=key, readable=True,
                                 writeable=True, encrypt_key=pubkey)

    data = par.to_data()
    par2 = OSPar.from_data(data)

    value = "something " + str(uuid.uuid4())

    par2.write(privkey).set_string_object(value)

    val = par.read(privkey).get_string_object()

    assert(val == value)

    par = ObjectStore.create_par(bucket, encrypt_key=pubkey, key=key,
                                 writeable=True, duration=60)

    par.write(privkey).set_string_object(value)

    assert(par.read(privkey).get_string_object() == value)

    assert(ObjectStore.get_string_object(bucket, key) == value)

    par = ObjectStore.create_par(bucket, readable=False,
                                 writeable=True, duration=120,
                                 encrypt_key=pubkey)

    assert(not par.is_readable())
    assert(par.is_writeable())
    assert(par.is_bucket())

    d = "testing"

    keyvals = {"one": "^¬#∆˚¬€", "two": "∆¡πª¨ƒ∆",
               "three": "€√≠ç~ç~€", "four": "hello world!",
               "subdir/five": "#º©√∆˚∆˚¬€ €˚∆ƒ¬"}

    for (key, value) in keyvals.items():
        par.write(privkey).set_string_object("%s/%s" % (d, key), value)

    for key in keyvals.keys():
        par = ObjectStore.create_par(bucket, key="%s/%s" % (d, key),
                                     duration=60, encrypt_key=pubkey)
        value = par.read(privkey).get_string_object()

        assert(keyvals[key] == value)
Пример #23
0
    def _get_subdrive(self, drive_uid, name, autocreate=True):
        """Return the DriveInfo for the Drive that the user has
           called 'name' in the drive with UID 'drive_uid'. If
           'autocreate' is True then this drive is automatically
           created if it does not exist.
        """
        if self.is_null():
            raise PermissionError(
                "You cannot get a DriveInfo from a null UserDrives")

        from Acquire.ObjectStore import string_to_filepath_parts \
            as _string_to_filepath_parts

        parts = _string_to_filepath_parts(name)

        if len(parts) != 1:
            raise ValueError("The passed drive name '%s' is not valid!" % name)

        from Acquire.Service import get_service_account_bucket \
            as _get_service_account_bucket
        from Acquire.ObjectStore import ObjectStore as _ObjectStore

        from Acquire.ObjectStore import string_to_encoded as _string_to_encoded

        encoded_name = _string_to_encoded(name)

        bucket = _get_service_account_bucket()

        drive_key = "%s/%s/%s/%s" % (_subdrives_root, self._user_guid,
                                     drive_uid, encoded_name)

        try:
            drive_uid = _ObjectStore.get_string_object(bucket, drive_key)
        except:
            drive_uid = None

        if drive_uid is not None:
            from Acquire.Storage import DriveInfo as _DriveInfo
            drive = _DriveInfo(drive_uid=drive_uid,
                               identifiers=self._identifiers,
                               is_authorised=self._is_authorised)
        else:
            drive = None

        if drive is None:
            if self._is_authorised and autocreate:
                # create a new UID for the drive and write this to the
                # object store
                from Acquire.ObjectStore import create_uuid as _create_uuid

                drive_uid = _create_uuid()

                drive_uid = _ObjectStore.set_ins_string_object(
                    bucket, drive_key, drive_uid)

                from Acquire.Storage import DriveInfo as _DriveInfo
                drive = _DriveInfo(drive_uid=drive_uid,
                                   identifiers=self._identifiers,
                                   is_authorised=self._is_authorised,
                                   autocreate=True)

        return drive