Example #1
0
    def __init__(self,
                 username=None,
                 public_key=None,
                 public_cert=None,
                 ipaddr=None,
                 hostname=None,
                 login_message=None,
                 scope=None,
                 permissions=None):
        """Start a new login session for the user with specified
           username, passing in the additional data needed to
           request a login
        """
        if public_key is not None:
            from Acquire.Crypto import PublicKey as _PublicKey
            if not isinstance(public_key, _PublicKey):
                raise TypeError("The public key must be of type PublicKey")

            if not isinstance(public_cert, _PublicKey):
                raise TypeError("The public certificate must be of "
                                "type PublicKey")

            if username is None or len(username) == 0:
                raise PermissionError("You must supply a valid username!")

            self._username = username
            self._pubkey = public_key
            self._pubcert = public_cert

            from Acquire.ObjectStore import get_datetime_now \
                as _get_datetime_now
            from Acquire.ObjectStore import create_uuid as _create_uuid

            self._uid = _create_uuid()
            self._request_datetime = _get_datetime_now()
            self._status = None

            self._ipaddr = ipaddr
            self._hostname = hostname
            self._login_message = login_message
            self._scope = scope
            self._permissions = permissions

            # make sure this session is saved to the object store
            self._set_status("pending")
        else:
            self._uid = None
Example #2
0
    def create(username,
               password,
               _service_uid=None,
               _service_public_key=None):
        """Create a new account with username 'username', which will
           be secured using the passed password.

           Note that this will create an account with a specified
           user UID, meaning that different users can have the same
           username. We identify the right user via the combination
           of username, password and OTP code.

           Normally the UID of the service, and the skeleton key
           used to encrypt the backup password are obtained
           directly from the service. However, when initialising
           a new service we must pass these directly. In those
           cases, pass the object using _service_uid and
           _service_public_key

           This returns a tuple of the user_uid and OTP for the
           newly-created account
        """
        from Acquire.ObjectStore import create_uuid as _create_uuid
        from Acquire.Crypto import PrivateKey as _PrivateKey
        from Acquire.Crypto import PublicKey as _PublicKey
        from Acquire.ObjectStore import ObjectStore as _ObjectStore
        from Acquire.Service import get_service_account_bucket \
            as _get_service_account_bucket
        from Acquire.ObjectStore import bytes_to_string as _bytes_to_string
        from Acquire.Identity import UserCredentials as _UserCredentials
        from Acquire.ObjectStore import get_datetime_now_to_string \
            as _get_datetime_now_to_string

        if _service_public_key is None:
            from Acquire.Service import get_this_service as _get_this_service
            service_pubkey = _get_this_service().public_skeleton_key()
            assert (service_pubkey is not None)
        else:
            service_pubkey = _service_public_key

        if not isinstance(service_pubkey, _PublicKey):
            raise TypeError("The service public key must be type PublicKey")

        if _service_uid is None:
            from Acquire.Service import get_this_service \
                as _get_this_service
            service_uid = _get_this_service(need_private_access=False).uid()
        else:
            service_uid = _service_uid

        # create a UID for this new user
        user_uid = _create_uuid()

        # now create the primary password for this user and use
        # this to encrypt the special keys for this user
        privkey = _PrivateKey(name="user_secret_key %s %s" %
                              (username, user_uid))
        primary_password = _PrivateKey.random_passphrase()

        bucket = _get_service_account_bucket()

        # now create the credentials used to validate a login
        otp = _UserCredentials.create(user_uid=user_uid,
                                      password=password,
                                      primary_password=primary_password)

        # create the user account
        user = UserAccount(username=username,
                           user_uid=user_uid,
                           private_key=privkey,
                           status="active")

        # now save a lookup from the username to this user_uid
        # (many users can have the same username). Use this lookup
        # to hold a recovery password for this account
        recovery_password = _bytes_to_string(
            service_pubkey.encrypt(primary_password))

        key = "%s/names/%s/%s" % (_user_root, user.encoded_name(), user_uid)
        _ObjectStore.set_string_object(bucket=bucket,
                                       key=key,
                                       string_data=recovery_password)

        # now save a lookup from the hashed username+password
        # to the user_uid, so that we can
        # quickly find matching user_uids (expect few people will have
        # exactly the same username and password). This will
        # save the exact time this username-password combination
        # was set
        encoded_password = _UserCredentials.hash(username=username,
                                                 password=password,
                                                 service_uid=service_uid)

        key = "%s/passwords/%s/%s" % (_user_root, encoded_password, user_uid)
        _ObjectStore.set_string_object(
            bucket=bucket, key=key, string_data=_get_datetime_now_to_string())

        # finally(!) save the account itself to the object store
        key = "%s/uids/%s" % (_user_root, user_uid)
        data = user.to_data(passphrase=primary_password)
        _ObjectStore.set_object_from_json(bucket=bucket, key=key, data=data)

        # return the OTP and user_uid
        return (user_uid, otp)
Example #3
0
    def write(account=None, resource=None,
              recipient_url=None, max_spend=None,
              expiry_date=None):
        """Create and return a cheque that can be used at any point
           in the future to authorise a transaction. If 'recipient_url'
           is supplied, then only the service with the matching
           URL can 'cash' the cheque (it will need to sign the cheque
           before sending it to the accounting service). If 'max_spend'
           is specified, then the cheque is only valid up to that
           maximum spend. Otherwise, it is valid up to the maximum
           daily spend limit (or other limits) of the account. If
           'expiry_date' is supplied then this cheque is valid only
           before the supplied datetime. If 'resource' is
           supplied then this cheque is only valid to pay for the
           specified resource (this should be a string that everyone
           agrees represents the resource in question). Note that
           this cheque is for a future transaction. We do not check
           to see if there are sufficient funds now, and this does
           not affect the account. If there are insufficient funds
           when the cheque is cashed (or it breaks spending limits)
           then the cheque will bounce.

           Args:
                account (Account, default=None): Account to use to write
                cheque
                resource (str, default=None): Define the resource to pay for
                recipient_url (str, default=None): URL of service to use
                max_spend (Decimal, default=None): Limit of cheque
                expiry_date (datetime, default=None): Cheque's expiry date
        """

        from Acquire.Client import Account as _Account

        if not isinstance(account, _Account):
            raise TypeError("You must pass a valid Acquire.Client.Account "
                            "object to write a cheque...")

        if max_spend is not None:
            from Acquire.ObjectStore import decimal_to_string \
                as _decimal_to_string
            max_spend = _decimal_to_string(max_spend)

        if expiry_date is not None:
            from Acquire.ObjectStore import datetime_to_string \
                as _datetime_to_string
            expiry_date = _datetime_to_string(expiry_date)

        if recipient_url is not None:
            recipient_url = str(recipient_url)

        from Acquire.ObjectStore import create_uuid as _create_uuid
        from Acquire.Identity import Authorisation as _Authorisation

        info = _json.dumps({"recipient_url": recipient_url,
                            "max_spend": max_spend,
                            "expiry_date": expiry_date,
                            "uid": _create_uuid(),
                            "resource": str(resource),
                            "account_uid": account.uid()})

        auth = _Authorisation(user=account.user(), resource=info)

        data = {"info": info, "authorisation": auth.to_data()}

        cheque = Cheque()

        cheque._cheque = account.accounting_service().encrypt_data(data)
        cheque._accounting_service_url = \
            account.accounting_service().canonical_url()

        return cheque
Example #4
0
    def __init__(self,
                 resource=None,
                 user=None,
                 testing_key=None,
                 testing_user_guid=None):
        """Create an authorisation for the passed resource
           that is authorised by the passed user (who must be authenticated)

           If testing_key is passed, then this authorisation is being
           tested as part of the unit tests
        """

        if resource is not None:
            resource = str(resource)

        self._signature = None
        self._last_validated_datetime = None
        self._scope = None
        self._permissions = None
        self._pubcert = None

        if resource is not None:
            if user is None and testing_key is None:
                raise ValueError(
                    "You must pass in an authenticated user who will "
                    "provide authorisation for resource '%s'" % resource)

        from Acquire.ObjectStore import get_datetime_now \
            as _get_datetime_now
        from Acquire.ObjectStore import create_uuid as _create_uuid

        if user is not None:
            from Acquire.Client import User as _User

            if not isinstance(user, _User):
                raise TypeError("The passed user must be of type User")

            elif not user.is_logged_in():
                raise PermissionError(
                    "The passed user '%s' must be authenticated to enable "
                    "you to generate an authorisation for the account")

            self._user_uid = user.uid()
            self._session_uid = user.session_uid()
            self._identity_url = user.identity_service().canonical_url()
            self._identity_uid = user.identity_service_uid()
            self._auth_datetime = _get_datetime_now()
            self._uid = _create_uuid(short_uid=True,
                                     include_date=self._auth_datetime)
            self._siguid = user.signing_key().sign(self._uid)

            message = self._get_message(resource)
            self._signature = user.signing_key().sign(message)

            self._last_validated_datetime = _get_datetime_now()
            self._last_verified_resource = resource
            self._last_verified_key = None

            if user.guid() != self.user_guid():
                # interesting future case when we allow individual users
                # to be identified by multiple identity services...
                raise PermissionError(
                    "We do not yet support a single user being identified "
                    "by multiple identity services: %s versus %s" %
                    (user.guid(), self.user_guid()))

        elif testing_key is not None:
            self._user_uid = "some user uid"
            self._session_uid = "some session uid"
            self._identity_url = "some identity_url"
            self._identity_uid = "some identity uid"
            self._auth_datetime = _get_datetime_now()
            self._uid = _create_uuid(short_uid=True,
                                     include_date=self._auth_datetime)
            self._is_testing = True
            self._testing_key = testing_key

            if testing_user_guid is not None:
                parts = testing_user_guid.split("@")
                self._user_uid = parts[0]
                self._identity_uid = parts[1]

            message = self._get_message(resource)
            self._signature = testing_key.sign(message)
            self._siguid = testing_key.sign(self._uid)

            self._last_validated_datetime = _get_datetime_now()
            self._last_verified_resource = resource
            self._last_verified_key = testing_key.public_key()
Example #5
0
 def regenerate_uid(self):
     """Regenerate the UUID as there has been a clash"""
     if not self.is_null():
         from Acquire.ObjectStore import create_uuid as _create_uuid
         self._uid = _create_uuid()
    def validate_password(user_uid, username, device_uid, secrets, password,
                          otpcode, remember_device):
        """Validate that the passed password and one-time-code are valid.
           If they are, then return a tuple of the UserAccount of the unlocked
           user, the OTP that is used to generate secrets, and the
           device_uid of the login device

           If 'remember_device' is True and 'device_uid' is None, then
           this creates a new OTP for the login device, which is returned,
           and a new device_uid for that device. The password needed to
           match this device is a MD5 of the normal user password.
        """
        from Acquire.Crypto import PrivateKey as _PrivateKey
        from Acquire.Crypto import OTP as _OTP
        from Acquire.ObjectStore import string_to_bytes as _string_to_bytes

        privkey = _PrivateKey.from_data(data=secrets["private_key"],
                                        passphrase=password)

        # decrypt and validate the OTP code
        data = _string_to_bytes(secrets["otpsecret"])

        otpsecret = privkey.decrypt(data)
        otp = _OTP(secret=otpsecret)
        otp.verify(code=otpcode, once_only=True)

        # everything is ok - we can load the user account via the
        # decrypted primary password
        primary_password = _string_to_bytes(secrets["primary_password"])
        primary_password = privkey.decrypt(primary_password)

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

        data = None
        secrets = None
        key = "%s/uids/%s" % (_user_root, user_uid)

        bucket = _get_service_account_bucket()

        try:
            data = _ObjectStore.get_object_from_json(bucket=bucket, key=key)
        except:
            pass

        if data is None:
            from Acquire.Identity import UserValidationError
            raise UserValidationError(
                "Unable to validate user as no account data is present!")

        from Acquire.Identity import UserAccount as _UserAccount
        user = _UserAccount.from_data(data=data, passphrase=primary_password)

        if user.uid() != user_uid:
            from Acquire.Identity import UserValidationError
            raise UserValidationError(
                "Unable to validate user as mismatch in user_uids!")

        if device_uid is None and remember_device:
            # create a new OTP that is unique for this device
            from Acquire.ObjectStore import create_uuid as _create_uuid
            from Acquire.Client import Credentials as _Credentials
            device_uid = _create_uuid()
            device_password = _Credentials.encode_device_uid(
                                                encoded_password=password,
                                                device_uid=device_uid)

            otp = UserCredentials.create(user_uid=user_uid,
                                         password=device_password,
                                         primary_password=primary_password,
                                         device_uid=device_uid)

            # now save a lookup so that we can find the user_uid from
            # the username and device-specific password
            encoded_password = UserCredentials.hash(
                                        username=username,
                                        password=device_password)

            key = "%s/passwords/%s/%s" % (_user_root, encoded_password,
                                          user_uid)

            from Acquire.ObjectStore import get_datetime_now_to_string \
                as _get_datetime_now_to_string

            _ObjectStore.set_string_object(
                                bucket=bucket, key=key,
                                string_data=_get_datetime_now_to_string())

        return {"user": user, "otp": otp, "device_uid": device_uid}
Example #7
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