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_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 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 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 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_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 __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
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 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())
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 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())
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(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)))
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 _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 _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
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 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)
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)
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