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
def logout(self): """Log out from the current session""" if self.is_logged_in() or self.is_logging_in(): identity_url = self.identity_service_url() if identity_url is None: return # create a permission message that can be signed # and then validated by the user permission = "Log out request for %s" % self._session_uid signature = self.signing_key().sign(permission) print("Logging out %s from session %s" % (self._username, self._session_uid)) result = _call_function( identity_url, "logout", args_key=self.identity_service().public_key(), username=self._username, session_uid=self._session_uid, permission=permission, signature=_bytes_to_string(signature)) print(result) self._status = _LoginStatus.LOGGED_OUT return result
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"]
def _get_identity_service(identity_url=None): """Function to return the identity service for the system""" if identity_url is None: identity_url = _get_identity_url() privkey = _PrivateKey() response = _call_function(identity_url, response_key=privkey) try: service = _Service.from_data(response["service_info"]) except: raise LoginError("Have not received the identity service info from " "the identity service at '%s' - got '%s'" % (identity_url, response)) if not service.is_identity_service(): raise LoginError( "You can only use a valid identity service to log in! " "The service at '%s' is a '%s'" % (identity_url, service.service_type())) if identity_url != service.service_url(): service.update_service_url(identity_url) return service
def get_session_keys(username=None, user_uid=None, session_uid=None, identity_url=None): """Function to return the session keys for the specified user""" if username is None and user_uid is None: raise ValueError("You must supply either the username or user_uid!") if session_uid is None: raise ValueError("You must supply a valid UID for a login session") if identity_url is None: identity_url = _get_identity_url() response = _call_function(identity_url, "whois", username=username, user_uid=user_uid, session_uid=session_uid) try: response["public_key"] = _PublicKey.from_data(response["public_key"]) except: pass try: response["public_cert"] = _PublicKey.from_data(response["public_cert"]) except: pass return response
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"]
def _poll_session_status(self): """Function used to query the identity service for this session to poll for the session status""" identity_url = self.identity_service_url() if identity_url is None: return result = _call_function(identity_url, "get_status", username=self._username, session_uid=self._session_uid) # look for status = 0 try: status = int(result["status"]) except: status = -1 try: message = result["message"] except: message = str(result) if status != 0: error = "Failed to query identity service. Error = %d. " \ "Message = %s" % (status, message) self._set_error_state(error) raise LoginError(error) # now update the status... status = result["session_status"] self._set_status(status)
def _get_access_service(access_url=None): """Function to return the access service for the system""" if access_url is None: access_url = _get_access_url() privkey = _PrivateKey() response = _call_function(access_url, response_key=privkey) try: service = _Service.from_data(response["service_info"]) except: raise LoginError("Have not received the access service info from " "the access service at '%s' - got '%s'" % (access_url, response)) if not service.is_access_service(): raise LoginError( "You can only use a valid access service to access resources! " "The service at '%s' is a '%s'" % (access_url, service.service_type())) if service.service_url() != access_url: service.update_service_url(access_url) return service
def 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))
def username_to_uid(username, identity_url=None): """Function to return the uid for the passed username""" if identity_url is None: identity_url = _get_identity_url() response = _call_function(identity_url, "whois", username=str(username)) return response["user_uid"]
def uid_to_username(user_uid, identity_url=None): """Function to return the username for the passed uid""" if identity_url is None: identity_url = _get_identity_url() response = _call_function(identity_url, "whois", user_uid=str(user_uid)) return response["username"]
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()
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
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 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"]
def _get_service_info(self, identity_service): """Return the service info for the passed identity service""" try: return self._service_info[identity_service] except: pass # can we read this from a file? service_file = "%s/service_%s" % (Wallet._wallet_dir( ), _base64.b64encode(identity_service.encode("utf-8")).decode("utf-8")) try: with open(service_file, "rb") as FILE: service_info = _Service.from_data( _unpack_arguments(FILE.read())) self._service_info[identity_service] = service_info return service_info except: pass try: key = _PrivateKey() response = _call_function(identity_service, response_key=key) service = _Service.from_data(response["service_info"]) except Exception as e: raise LoginError( "Error connecting to the login service %s: Error = %s" % (identity_service, str(e))) if not service.is_identity_service(): raise LoginError("You cannot log into something that is not " "a valid identity service!") self._service_info[identity_service] = service # save this for future reference with open(service_file, "wb") as FILE: FILE.write(_pack_arguments(service.to_data())) return service
def _get_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)))
def challenge_service(self, service): """Send a challenge to the passed service, returning the actual service returned. This will only pass if our copy of the service matches us with the copy returned from the actual service. This verifies that there is a real service sitting at that URL, and that we have the right keys and certs """ from Acquire.Crypto import PrivateKey as _PrivateKey from Acquire.ObjectStore import bytes_to_string as _bytes_to_string from Acquire.Service import Service as _Service challenge = _PrivateKey.random_passphrase() pubkey = service.public_key() encrypted_challenge = pubkey.encrypt(challenge) args = {"challenge": _bytes_to_string(encrypted_challenge), "fingerprint": pubkey.fingerprint()} if service.uid().startswith("STAGE"): # we cannot call using the service directly, as it is # not yet fully constructed from Acquire.Service import get_this_service as _get_this_service from Acquire.Service import call_function as _call_function this_service = _get_this_service(need_private_access=True) result = _call_function(service_url=service.service_url(), function=None, args=args, args_key=service.public_key(), response_key=this_service.private_key(), public_cert=service.public_certificate()) else: result = service.call_function(function=None, args=args) if result["response"] != challenge: raise PermissionError( "Failure of the service %s to correctly respond " "to the challenge!" % service) return _Service.from_data(result["service_info"])
def 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
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
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
def get_trusted_registry_service(registry_uid=None, service_uid=None, service_url=None): """Return the trusted service info for the registry with specified registry_uid, or get any trusted registry service using either 'service_uid' or 'service_url' as a starting hint to locate a suitable registry """ if service_uid is not None: # for the moment, just try to get one registry. Eventually we should # try to get several in case this one is down registry_uid = get_primary_registry_uid(service_uid) return get_trusted_registry_service(registry_uid=registry_uid) if service_url is not None: if service_url.find(".") != -1: # try the main acquire registry first return get_trusted_registry_service(registry_uid="a0-a0") else: # this must be testing... return get_trusted_registry_service(registry_uid="Z9-Z9") if registry_uid is None: raise PermissionError( "You must specify one of registry_uid, service_uid " "or service_url") from Acquire.Service import get_trusted_service as _get_trusted_service try: registry_service = _get_trusted_service(service_uid=registry_uid, autofetch=False) except: registry_service = None if registry_service is not None: if not registry_service.is_registry_service(): from Acquire.Service import ServiceError raise ServiceError("The requested service (%s) for %s is NOT a " "registry service!" % (registry_service, registry_uid)) if registry_service.uid() != registry_uid: from Acquire.Service import ServiceError raise ServiceError( "Disagreement of UID (%s) is NOT the right registry service!" % registry_service) # everything is ok - we have seen this registry before return registry_service # boostrapping from Acquire.Registry import get_registry_details \ as _get_registry_details details = _get_registry_details(registry_uid) from Acquire.Service import call_function as _call_function from Acquire.Service import Service as _Service from Acquire.Crypto import get_private_key as _get_private_key from Acquire.Crypto import PrivateKey as _PrivateKey from Acquire.Crypto import PublicKey as _PublicKey from Acquire.ObjectStore import bytes_to_string as _bytes_to_string from Acquire.ObjectStore import string_to_bytes as _string_to_bytes privkey = _get_private_key(key="registry") pubkey = _PublicKey.from_data(details["public_key"]) pubcert = _PublicKey.from_data(details["public_certificate"]) # ask the registry to return to us their latest details - use # a challenge-response to make sure that the response is # properly returned challenge = _PrivateKey.random_passphrase() encrypted_challenge = _bytes_to_string(pubkey.encrypt(challenge)) args = { "challenge": encrypted_challenge, "fingerprint": pubkey.fingerprint() } result = _call_function(service_url=details["canonical_url"], function=None, args=args, args_key=pubkey, response_key=privkey, public_cert=pubcert) if result["response"] != challenge: from Acquire.Service import ServiceError raise ServiceError( "The requested service (%s) failed to respond to the challenge!" % registry_service) registry_service = _Service.from_data(result["service_info"]) if not registry_service.is_registry_service(): from Acquire.Service import ServiceError raise ServiceError( "The requested service (%s) is NOT a registry service!" % registry_service) if registry_service.uid() != details["uid"]: from Acquire.Service import ServiceError raise ServiceError( "Disagreement of UID (%s) is NOT the right registry service!" % registry_service) # ok - we've got the registry - add this to the set of # trusted services so that we don't need to bootstrap from # the registry details again from Acquire.Service import trust_service as _trust_service _trust_service(registry_service) return registry_service
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
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)