Example #1
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 #2
0
def _get_account_uids(user, accounting_service=None, accounting_url=None):
    """Return the names and UIDs of all of the accounts that belong
        to the passed user on the passed accounting_service
    """
    if accounting_service is None:
        accounting_service = _get_accounting_service(accounting_url)

    elif not accounting_service.is_accounting_service():
        raise ValueError("You can only query account using "
                         "a valid accounting service")

    if not user.is_logged_in():
        raise PermissionError(
            "You can only get information about about a user's accounts "
            "if they have authenticated their login")

    auth = _Authorisation(user=user)
    args = {"authorisation": auth.to_data()}

    privkey = _PrivateKey()

    result = _call_function(
            accounting_service.service_url(), "get_account_uids",
            args=args,
            args_key=accounting_service.public_key(),
            response_key=privkey,
            public_cert=accounting_service.public_certificate())

    return result["account_uids"]
Example #3
0
    def register(self, password, identity_url=None):
        """Request to register this user with the identity service running
           at 'identity_url', using the supplied 'password'. This will
           return a QR code that you must use immediately to add this
           user on the identity service to a QR code generator"""

        if self._username is None:
            return None

        if identity_url is None:
            identity_url = _get_identity_url()

        privkey = _PrivateKey()

        result = _call_function(
            identity_url,
            "register",
            args_key=self.identity_service().public_key(),
            response_key=privkey,
            public_cert=self.identity_service().public_certificate(),
            username=self._username,
            password=password)

        try:
            provisioning_uri = result["provisioning_uri"]
        except:
            raise UserError("Cannot register the user '%s' on "
                            "the identity service at '%s'!" %
                            (self._username, identity_url))

        # return a QR code for the provisioning URI
        return (provisioning_uri, _create_qrcode(provisioning_uri))
Example #4
0
def deposit(user, value, description=None,
            accounting_service=None, accounting_url=None):
    """Tell the system to allow the user to deposit 'value' from
       their (real) financial account to the system accounts
    """
    authorisation = _Authorisation(user=user)

    if accounting_service is None:
        accounting_service = _get_accounting_service(accounting_url)
    else:
        if not accounting_service.is_accounting_service():
            raise TypeError("You can only deposit funds using an "
                            "accounting service!")

    args = {"authorisation": authorisation.to_data()}

    if description is None:
        args["value"] = str(_create_decimal(value))
    else:
        args["transaction"] = _Transaction(value, description).to_data()

    privkey = _PrivateKey()

    result = _call_function(
                    accounting_service.service_url(), "deposit",
                    args=args,
                    args_key=accounting_service.public_key(),
                    response_key=privkey,
                    public_cert=accounting_service.public_certificate())

    return result
Example #5
0
    def refund(self, credit_note):
        """Refunds the passed credit note that contained a transfer of
           from another account to the passed account
        """
        if not self.is_logged_in():
            raise PermissionError("You cannot refund a credit note as the "
                                  "user has not yet logged in!")

        if credit_note.account_uid() != self.uid():
            raise ValueError(
                "You cannot refund a transaction from a different "
                "account! %s versus %s" % (credit_note.account_uid(),
                                           self.uid()))

        auth = _Authorisation(resource=self._account_uid, user=self._user)

        args = {"credit_note": credit_note.to_data(),
                "authorisation": auth.to_data()}

        privkey = _PrivateKey()

        result = _call_function(
                    self._accounting_service.service_url(), "refund",
                    args=args,
                    args_key=self._accounting_service.public_key(),
                    response_key=privkey,
                    public_cert=self._accounting_service.public_certificate())

        return result["transaction_record"]
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 __init__(self, service_type=None, service_url=None):
        """Construct a new service of the specified type, with
           the specified URL."""
        self._service_type = service_type
        self._service_url = service_url

        if self._service_type:
            if self._service_type not in ["identity", "access", "accounting"]:
                raise ServiceError("Services of type '%s' are not allowed!" %
                                   self._service_type)

            self._uuid = str(_uuid.uuid4())
            self._privkey = _PrivateKey()
            self._pubkey = self._privkey.public_key()
            self._privcert = _PrivateKey()
            self._pubcert = self._privcert.public_key()
            self._admin_password = None
Example #8
0
    def _refresh(self, force_update=False):
        """Refresh the current status of this account. This fetches
           the latest data, e.g. balance, limits etc. Note that this
           limits you to refreshing at most once every five seconds...
        """
        if self.is_null():
            self._overdraft_limit = _create_decimal(0)
            self._balance = _create_decimal(0)
            self._liability = _create_decimal(0)
            self._receivable = _create_decimal(0)
            self._spent_today = _create_decimal(0)
            return

        if force_update:
            should_refresh = True
        else:
            should_refresh = False

            if self._last_update is None:
                should_refresh = True
            else:
                should_refresh = (_datetime.datetime.now() -
                                  self._last_update).seconds > 5

        if not should_refresh:
            return

        if not self.is_logged_in():
            raise PermissionError(
                "You cannot get information about this account "
                "until after the owner has successfully authenticated.")

        auth = _Authorisation(resource=self._account_uid, user=self._user)

        args = {"authorisation": auth.to_data(),
                "account_name": self.name()}

        privkey = _PrivateKey()

        result = _call_function(
                    self._accounting_service.service_url(), "get_info",
                    args=args,
                    args_key=self._accounting_service.public_key(),
                    response_key=privkey,
                    public_cert=self._accounting_service.public_certificate())

        self._overdraft_limit = _create_decimal(result["overdraft_limit"])
        self._balance = _create_decimal(result["balance"])
        self._liability = _create_decimal(result["liability"])
        self._receivable = _create_decimal(result["receivable"])
        self._spent_today = _create_decimal(result["spent_today"])
        self._description = result["description"]

        self._last_update = _datetime.datetime.now()
Example #9
0
    def set_admin_password(self, admin_password):
        """Set the admin password for this service. This returns the
           provisioning URI for the OTP shared secret"""
        if self._admin_password:
            raise ServiceError("The admin password has already been set!")

        key = _PrivateKey()
        self._admin_password = key.bytes(admin_password)
        otp = _OTP()
        self._otpsecret = otp.encrypt(key.public_key())
        return otp.provisioning_uri("admin", self.service_url())
Example #10
0
def create_account(user, account_name, description=None,
                   accounting_service=None, accounting_url=None):
    """Create an account on the accounting service for the passed
        user, calling the account 'account_name' and optionally
        passing in an account description. Note that the user must
        have authorised the login
    """
    if accounting_service is None:
        accounting_service = _get_accounting_service(accounting_url)

    elif not accounting_service.is_accounting_service():
        raise ValueError("You can only create an account by connecting "
                         "to a valid accounting service")

    if not user.is_logged_in():
        raise PermissionError(
            "You cannot create an account called '%s' for user "
            "'%s' as the user login has not been authenticated." %
            (account_name, user.name()))

    authorisation = _Authorisation(user=user)

    args = {"account_name": str(account_name),
            "authorisation": authorisation.to_data()}

    if description is None:
        args["description"] = "Account '%s' for '%s'" % \
                                (str(account_name), user.name())
    else:
        args["description"] = str(description)

    privkey = _PrivateKey()

    result = _call_function(
                accounting_service.service_url(), "create_account",
                args=args,
                args_key=accounting_service.public_key(),
                response_key=privkey,
                public_cert=accounting_service.public_certificate())

    account_uid = result["account_uid"]

    account = Account()
    account._account_name = account_name
    account._account_uid = account_uid
    account._user = user
    account._accounting_service = accounting_service

    return account
Example #11
0
    def reset_admin_password(self, password, otpcode, new_password):
        """Change the admin password for this service. Note that
           you must pass in a valid password and otpcode to make the change"""

        self.verify_admin_user(password, otpcode)

        if password == new_password:
            return

        key = _PrivateKey.read_bytes(self._admin_password, password)
        otp = _OTP.decrypt(self._otpsecret, key)
        otp.verify(otpcode)

        newkey = _PrivateKey()
        self._admin_password = newkey.bytes(new_password)
        self._otpsecret = otp.encrypt(newkey.public_key())
Example #12
0
    def perform(self, transaction, credit_account, is_provisional=False):
        """Tell this accounting service to apply the transfer described
           in 'transaction' from this account to the passed account. Note
           that the user must have logged into this account so that they
           have authorised this transaction. This returns the record
           of this transaction
        """
        if not self.is_logged_in():
            raise PermissionError("You cannot transfer value from '%s' to "
                                  "'%s' because you have not authenticated "
                                  "the user who owns this account" %
                                  (str(self), str(credit_account)))

        if not isinstance(transaction, _Transaction):
            raise TypeError("The passed transaction must be of type "
                            "Transaction")

        if not isinstance(credit_account, Account):
            raise TypeError("The passed credit account must be of type "
                            "Account")

        if transaction.is_null():
            return None

        auth = _Authorisation(resource=self._account_uid, user=self._user)

        if is_provisional:
            is_provisional = True
        else:
            is_provisional = False

        args = {"transaction": transaction.to_data(),
                "debit_account_uid": str(self.uid()),
                "credit_account_uid": str(credit_account.uid()),
                "is_provisional": is_provisional,
                "authorisation": auth.to_data()}

        privkey = _PrivateKey()

        result = _call_function(
                    self._accounting_service.service_url(), "perform",
                    args=args,
                    args_key=self._accounting_service.public_key(),
                    response_key=privkey,
                    public_cert=self._accounting_service.public_certificate())

        return result["transaction_records"]
Example #13
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
def get_remote_service_info(service_url):
    """This function returns the service info for the service at
       'service_url'
    """

    key = _PrivateKey()

    try:
        response = _call_function(service_url, response_key=key)
    except Exception as e:
        raise ServiceError("Cannot get information about '%s': %s" %
                           (service_url, str(e)))

    try:
        return _Service.from_data(response["service_info"])
    except Exception as e:
        raise ServiceError(
                "Cannot extract service info for '%s' from '%s': %s" &
                (service_url, str(response), str(e)))
Example #15
0
def _get_account_uid(user, account_name, accounting_service=None,
                     accounting_url=None):
    """Return the UID of the account called 'account_name' that
        belongs to passed user on the passed accounting_service
    """
    if account_name is None:
        # return the UID of the default account for this user
        account_name = "main"

    if accounting_service is None:
        accounting_service = _get_accounting_service(accounting_url)

    elif not accounting_service.is_accounting_service():
        raise ValueError("You can only query account using "
                         "a valid accounting service")

    args = {"user_uid": user.uid(),
            "account_name": str(account_name)}

    if user.is_logged_in():
        auth = _Authorisation(user=user)
        args["authorisation"] = auth.to_data()

    privkey = _PrivateKey()

    result = _call_function(
            accounting_service.service_url(), "get_account_uids",
            args=args,
            args_key=accounting_service.public_key(),
            response_key=privkey,
            public_cert=accounting_service.public_certificate())

    account_uids = result["account_uids"]

    for account_uid in account_uids:
        if account_uids[account_uid] == account_name:
            return account_uid

    raise AccountError("There is no account called '%s' for '%s'" %
                       (account_name, str(user)))
Example #16
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 #17
0
    def _create_wallet_key(self, filename):
        """Create a new wallet key for the user"""

        password = _getpass.getpass(
            prompt="Please enter a password to encrypt your wallet: ")

        key = _PrivateKey()

        bytes = key.bytes(password)

        password2 = _getpass.getpass(prompt="Please confirm the password: "******"The passwords don't match. Please try again.")
            self._create_wallet_key(filename)
            return

        # the passwords match - write this to the file
        with open(filename, "wb") as FILE:
            FILE.write(bytes)

        return key
    def create(user_uid, password, primary_password,
               device_uid=None):
        """Create the credentials for the user with specified
           user_uid, optionally logging in via the specified
           device_uid, using the specified password, to protect
           the passed "primary_password"

           This returns the OTP that has been created to be
           associated with these credentials
        """
        from Acquire.Crypto import PrivateKey as _PrivateKey
        from Acquire.Crypto import OTP as _OTP
        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

        if device_uid is None:
            device_uid = user_uid

        privkey = _PrivateKey(name="user_creds_key %s" % user_uid)
        otp = _OTP()
        otpsecret = otp.encrypt(privkey.public_key())
        primary_password = privkey.encrypt(primary_password)

        data = {"primary_password": _bytes_to_string(primary_password),
                "private_key": privkey.to_data(passphrase=password),
                "otpsecret": _bytes_to_string(otpsecret)
                }

        key = "%s/credentials/%s/%s" % (_user_root, user_uid, device_uid)

        bucket = _get_service_account_bucket()
        _ObjectStore.set_object_from_json(bucket=bucket,
                                          key=key,
                                          data=data)

        return otp
Example #19
0
    def send_password(self,
                      url,
                      username=None,
                      remember_password=True,
                      remember_device=None,
                      dryrun=None):
        """Send a password and one-time code to the supplied login url"""

        self._manual_password = False
        self._manual_otpcode = False

        if not remember_password:
            remember_device = False

        # the login URL is of the form "server/code"
        words = url.split("/")
        identity_service = "/".join(words[0:-1])
        short_uid = words[-1]

        # get the public key of this identity service
        service_key = self._get_service_key(identity_service)
        service_cert = self._get_service_cert(identity_service)

        if not username:
            # choose a username from any existing files...
            username = self._get_username()

        print("Logging in using username '%s'" % username)
        password = self._get_user_password(username)
        otpcode = self._get_otpcode(username)

        print("\nLogging in to '%s', session '%s'..." %
              (identity_service, short_uid),
              end="")
        _sys.stdout.flush()

        if dryrun:
            print("Calling %s with username=%s, password=%s, otpcode=%s, "
                  "remember_device=%s, device_uid=%s, short_uid=%s" %
                  (identity_service, username, password, otpcode,
                   remember_device, self._device_uid, short_uid))
            return

        try:
            key = _PrivateKey()
            response = _call_function(identity_service,
                                      "login",
                                      args_key=service_key,
                                      response_key=key,
                                      public_cert=service_cert,
                                      username=username,
                                      password=password,
                                      otpcode=otpcode,
                                      remember_device=remember_device,
                                      device_uid=self._device_uid,
                                      short_uid=short_uid)
            print("SUCCEEDED!")
            _sys.stdout.flush()
        except Exception as e:
            print("FAILED!")
            _sys.stdout.flush()
            raise LoginError("Failed to log in: %s" % str(e))

        if remember_password:
            try:
                provisioning_uri = response["provisioning_uri"]
            except:
                provisioning_uri = None

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

            otpsecret = None

            if provisioning_uri:
                try:
                    otpsecret = _re.search(r"secret=([\w\d+]+)&issuer",
                                           provisioning_uri).groups()[0]
                except:
                    pass

            try:
                user_info = self._read_userinfo(username)
            except:
                user_info = {}

            if user_info is None:
                user_info = {}

            pubkey = self._wallet_key.public_key()

            must_write = self._manual_password

            if otpsecret:
                if self._manual_otpcode:
                    must_write = True

            if must_write:
                user_info["username"] = username.encode("utf-8").decode(
                    "utf-8")
                user_info["password"] = _bytes_to_string(
                    pubkey.encrypt(password.encode("utf-8")))

                if otpsecret:
                    user_info["otpsecret"] = _bytes_to_string(
                        pubkey.encrypt(otpsecret.encode("utf-8")))
                    user_info["device_uid"] = device_uid

                packed_data = _pack_arguments(user_info)

                with open(Wallet._get_userfile(username), "wb") as FILE:
                    FILE.write(packed_data)

        self._manual_password = False
        self._manual_otpcode = False

        return response
Example #20
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 #21
0
    def upload(self, source, destination=None, ignore_hidden=True,
               account=None):
        """Upload a file (or files) from 'source' to 'destination'. If
           'destination is not supplied, then the file(s) will be uploaded
           with 'destination' equals 'source' (i.e. they will have the same
           name on the cloud drive as they do on the drive). If 'destination'
           is supplied then if it ends in a "/" then the destination will
           be treated like a directory. If the number of source files is
           greater than 1 and only a single destination directory is provided
           then all files will be uploaded into that directory.

           If 'ignore_hidden' is true, then hidden files will be ignored
           when uploading directories (but not when specifying files
           manually)

           If you pass in 'account', then
           this account will be used to pay for the storage. The account
           can be authorised from a different user to the owner of the drive,
           although both the user and account must be in the logged-in state.

           If you don't specify the account then the default account for
           the user will be used.

           Note that you cannot overwrite a file that already exists. It has
           to be explicitly removed first.

           Note that this is an atomic function - either all of none
           of the files will be written.

           This will return the list of read-only handles to allow you
           (or anyone else) to read these files.
        """

        if source is None:
            return

        if account is None:
            if not self._user.is_logged_in():
                raise PermissionError(
                    "You cannot upload files unless you have logged into "
                    "your account. Please log in and try again.")

            account = _Account(user=self._user)

        from Acquire.Access import FileWriteRequest as _FileWriteRequest

        request = _FileWriteRequest(source=source, destination=destination,
                                    ignore_hidden=ignore_hidden,
                                    account=account)

        args = {"request": request.to_data()}

        privkey = _PrivateKey()

        result = _call_function(
                    self._access_service.service_url(), "request",
                    args=args,
                    args_key=self._access_service.public_key(),
                    response_key=privkey,
                    public_cert=self._access_service.public_certificate())

        return result
Example #22
0
    def request_login(self, login_message=None):
        """Request to authenticate as this user. This returns a login URL that
           you must connect to to supply your login credentials

           If 'login_message' is supplied, then this is passed to
           the identity service so that it can be displayed
           when the user accesses the login page. This helps
           the user validate that they have accessed the correct
           login page. Note that if the message is None,
           then a random message will be generated.
        """
        self._check_for_error()

        if not self.is_empty():
            raise LoginError("You cannot try to log in twice using the same "
                             "User object. Create another object if you want "
                             "to try to log in again.")

        # first, create a private key that will be used
        # to sign all requests and identify this login
        session_key = _PrivateKey()
        signing_key = _PrivateKey()

        args = {
            "username": self._username,
            "public_key": session_key.public_key().to_data(),
            "public_certificate": signing_key.public_key().to_data(),
            "ipaddr": None
        }

        # get information from the local machine to help
        # the user validate that the login details are correct
        if _has_socket:
            hostname = _socket.gethostname()
            ipaddr = _socket.gethostbyname(hostname)
            args["ipaddr"] = ipaddr
            args["hostname"] = hostname

        if login_message is None:
            login_message = "User '%s' in process '%s' wants to log in..." % \
                              (_os.getlogin(), _os.getpid())

        args["message"] = login_message

        result = _call_function(
            self.identity_service_url(),
            "request_login",
            args_key=self.identity_service().public_key(),
            response_key=session_key,
            public_cert=self.identity_service().public_certificate(),
            username=self._username,
            public_key=session_key.public_key().to_data(),
            public_certificate=signing_key.public_key().to_data(),
            ipaddr=None,
            message=login_message)

        # look for status = 0
        try:
            status = int(result["status"])
        except:
            status = -1

        try:
            message = result["message"]
        except:
            message = str(result)

        try:
            prune_message = result["prune_message"]
            print("Pruning old sessions...\n%s" % "\n".join(prune_message))
        except:
            pass

        if status != 0:
            error = "Failed to login. Error = %d. Message = %s" % \
                                (status, message)
            self._set_error_state(error)
            raise LoginError(error)

        try:
            login_url = result["login_url"]
        except:
            login_url = None

        if login_url is None:
            error = "Failed to login. Could not extract the login URL! " \
                    "Result is %s" % (str(result))
            self._set_error_state(error)
            raise LoginError(error)

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

        if session_uid is None:
            error = "Failed to login. Could not extract the login " \
                    "session UID! Result is %s" % (str(result))

            self._set_error_state(error)
            raise LoginError(error)

        # now save all of the needed data
        self._login_url = result["login_url"]
        self._session_key = session_key
        self._signing_key = signing_key
        self._session_uid = session_uid
        self._status = _LoginStatus.LOGGING_IN
        self._user_uid = result["user_uid"]

        qrcode = None

        if _has_qrcode():
            try:
                self._login_qrcode = _create_qrcode(self._login_url)
                qrcode = self._login_qrcode
            except:
                pass

        return (self._login_url, qrcode)
Example #23
0
    def whois(self, username=None, user_uid=None, session_uid=None):
        """Do a whois lookup to map from username to user_uid or
           vice versa. If 'session_uid' is provided, then also validate
           that this is a correct login session, and return also
           the public key and signing certificate for this login session.

           This should return a dictionary with the following keys
           optionally contained;

           username = name of the user
           user_uid = uid of the user
           public_key = public key for the session with uid 'session_uid'
           public_cert = public certificate for that login session
        """

        if (username is None) and (user_uid is None):
            raise IdentityServiceError("You must supply either a username "
                                       "or a user's UID for a lookup")

        key = _PrivateKey()

        response = None

        if session_uid is None:
            args = {}
        else:
            args = {"session_uid": str(session_uid)}

        try:
            if username:
                args["username"] = str(username)
                response = _call_function(
                    self.service_url(),
                    "whois",
                    public_cert=self.public_certificate(),
                    response_key=key,
                    args=args)
                lookup_uid = response["user_uid"]
            else:
                lookup_uid = None

            if user_uid:
                args["user_uid"] = str(user_uid)
                response = _call_function(
                    self.service_url(),
                    "whois",
                    public_cert=self.public_certificate(),
                    response_key=key,
                    args=args)
                lookup_username = response["username"]
            else:
                lookup_username = None

        except Exception as e:
            raise IdentityServiceError("Failed whois lookup: %s" % str(e))

        if username is None:
            username = lookup_username

        elif (lookup_username is not None) and (username != lookup_username):
            raise IdentityServiceError(
                "Disagreement of the user who matches "
                "UID=%s. We think '%s', but the identity service says '%s'" %
                (user_uid, username, lookup_username))

        if user_uid is None:
            user_uid = lookup_uid

        elif (lookup_uid is not None) and (user_uid != lookup_uid):
            raise IdentityServiceError(
                "Disagreement of the user's UID for user "
                "'%s'. We think %s, but the identity service says %s" %
                (username, user_uid, lookup_uid))

        result = response

        try:
            result["public_key"] = _PublicKey.from_data(response["public_key"])
        except:
            pass

        try:
            result["public_cert"] = _PublicKey.from_data(
                response["public_cert"])
        except:
            pass

        return result
Example #24
0
    def call_admin_function(self,
                            function,
                            args={},
                            service_url=None,
                            remember_password=True,
                            remember_device=None):
        """Call the admin function 'function' using supplied arguments 'args',
           on the service at 'service_url'
        """

        self._manual_password = False
        self._manual_otpcode = False

        if not remember_password:
            remember_device = False

        if not service_url:
            # choose a service_url from any existing files...
            service_url = self._get_service_url()

        # get the public key of this identity service
        service_key = self._get_service_key(service_url)
        service_cert = self._get_service_cert(service_url)

        password = self._get_admin_password(service_url)
        otpcode = self._get_otpcode(service_url)

        strargs = str(args)

        args["password"] = password
        args["otpcode"] = otpcode
        args["remember_device"] = remember_device

        print("\nCalling '%s' with %s... " % (function, strargs), end="")
        _sys.stdout.flush()

        try:
            key = _PrivateKey()
            response = _call_function(service_url,
                                      function,
                                      args_key=service_key,
                                      response_key=key,
                                      public_cert=service_cert,
                                      args=args)
            print("SUCCEEDED!")
            _sys.stdout.flush()
        except Exception as e:
            print("FAILED!")
            _sys.stdout.flush()
            raise LoginError("Failed to log in: %s" % str(e))

        if remember_password:
            try:
                provisioning_uri = response["provisioning_uri"]
            except:
                provisioning_uri = None

            otpsecret = None

            if provisioning_uri:
                try:
                    otpsecret = _re.search(r"secret=([\w\d+]+)&issuer",
                                           provisioning_uri).groups()[0]
                except:
                    pass

            try:
                service_info = self._read_service_info(service_url)
            except:
                service_info = {}

            if service_info is None:
                service_info = {}

            pubkey = self._wallet_key.public_key()

            must_write = self._manual_password

            if otpsecret:
                if self._manual_otpcode:
                    must_write = True

            if must_write:
                service_info["service_url"] = service_url.encode(
                    "utf-8").decode("utf-8")
                service_info["password"] = _bytes_to_string(
                    pubkey.encrypt(password.encode("utf-8")))

                if otpsecret:
                    service_info["otpsecret"] = _bytes_to_string(
                        pubkey.encrypt(otpsecret.encode("utf-8")))

                packed_data = _pack_arguments(service_info)

                with open(ServiceWallet._get_service_file(service_url),
                          "wb") as FILE:
                    FILE.write(packed_data)

        self._manual_password = False
        self._manual_otpcode = False

        return response