def test_authorisation(): key = PrivateKey() resource = uuid.uuid4() auth = Authorisation(resource=resource, testing_key=key) auth.verify(resource=resource, testing_key=key.public_key()) wrong_key = PrivateKey() with pytest.raises(PermissionError): auth.verify(resource=resource, testing_key=wrong_key.public_key()) wrong_resource = uuid.uuid4() with pytest.raises(PermissionError): auth.verify(resource=wrong_resource, testing_key=key.public_key()) data = auth.to_data() new_auth = Authorisation.from_data(data) new_auth.verify(resource=resource, testing_key=key.public_key()) with pytest.raises(PermissionError): new_auth.verify(resource=resource, testing_key=wrong_key.public_key()) with pytest.raises(PermissionError): new_auth.verify(resource=wrong_resource, testing_key=key.public_key())
def test_filewriterequest(): basedir = os.path.dirname(os.path.abspath(__file__)) filenames = [os.path.abspath(__file__), os.path.abspath("%s/../Accounting/test_account.py" % basedir)] testkey = PrivateKey() r = FileWriteRequest(source=filenames, testing_key=testkey) data = r.to_data() r2 = Request.from_data(data) assert(r == r2) assert(r.uid() == r2.uid()) for i, filename in enumerate(filenames): md5sum = hashlib.md5(open(filename, "rb").read()).hexdigest() assert(md5sum == r.checksums()[i]) r.authorisation().verify(r.resource_key(), testing_key=testkey.public_key()) r2.authorisation().verify(r2.resource_key(), testing_key=testkey.public_key()) testkey2 = PrivateKey() r2 = FileWriteRequest(source=filenames, testing_key=testkey2) assert(r != r2) assert(r.uid() != r2.uid()) for i, filename in enumerate(filenames): md5sum = hashlib.md5(open(filename, "rb").read()).hexdigest() assert(md5sum == r2.checksums()[i]) r2.authorisation().verify(r2.resource_key(), testing_key=testkey2) with pytest.raises(PermissionError): r.authorisation().verify(r.resource_key(), testing_key=testkey2) with pytest.raises(PermissionError): r.authorisation().verify(r2.resource_key(), testing_key=testkey) with pytest.raises(PermissionError): r2.authorisation().verify(r.resource_key(), testing_key=testkey2) with pytest.raises(PermissionError): r2.authorisation().verify(r2.resource_key(), testing_key=testkey)
def authenticated_user(aaai_services): from Acquire.Crypto import PrivateKey, OTP from Acquire.Client import User, Service, Wallet username = str(uuid.uuid4()) password = PrivateKey.random_passphrase() result = User.register(username=username, password=password, identity_url="identity") otpsecret = result["otpsecret"] otp = OTP(otpsecret) # now log the user in user = User(username=username, identity_url="identity", auto_logout=False) result = user.request_login() assert (type(result) is dict) wallet = Wallet() wallet.send_password(url=result["login_url"], username=username, password=password, otpcode=otp.generate(), remember_password=False, remember_device=False) user.wait_for_login() assert (user.is_logged_in()) return user
def validate_password(self, password, otpcode, remember_device=False, device_secret=None): """Validate that the passed password and one-time-code are valid. If they are, then do nothing. Otherwise raise an exception. If 'remember_device' is true, then this returns the provisioning uri needed to initialise the OTP code for this account """ if not self.is_active(): raise UserValidationError( "Cannot validate against an inactive account") # see if we can decrypt the private key using the password privkey = _PrivateKey.read_bytes(self._privkey, password) if device_secret: # decrypt the passed device secret and check the supplied # otpcode for that... otp = _OTP.decrypt(_string_to_bytes(device_secret), privkey) otp.verify(otpcode) else: # now decrypt the secret otp and validate the supplied otpcode otp = _OTP.decrypt(self._otp_secret, privkey) otp.verify(otpcode) if remember_device: # create a new OTP that is unique for this device and return # this together with the provisioning code otp = _OTP() otpsecret = _bytes_to_string(otp.encrypt(privkey.public_key())) return (otpsecret, otp.provisioning_uri(self.username()))
def _get_wallet_key(self): """Return the private key used to encrypt everything in the wallet. This will ask for the users password """ if self._wallet_key: return self._wallet_key wallet_dir = ServiceWallet._wallet_dir() keyfile = "%s/wallet_key.pem" % wallet_dir if not _os.path.exists(keyfile): self._wallet_key = self._create_wallet_key(keyfile) return self._wallet_key # read the keyfile and decrypt with open(keyfile, "rb") as FILE: bytes = FILE.read() # get the user password wallet_key = None while not wallet_key: password = _getpass.getpass( prompt="Please enter your wallet password: "******"Invalid password. Please try again.") self._wallet_key = wallet_key return wallet_key
def from_data(data, password=None): """Deserialise this object from the passed data. This will only deserialise the private key, private certificate, and OTP if a valid password and passcode is supplied """ service = Service() if password: # get the private info... service._privkey = _PrivateKey.from_data(data["private_key"], password) service._privcert = _PrivateKey.from_data( data["private_certificate"], password) service._otpsecret = _string_to_bytes(data["otpsecret"]) service._admin_password = _string_to_bytes(data["admin_password"]) else: service._privkey = None service._privcert = None service._otpsecret = None service._uuid = data["uuid"] service._service_type = data["service_type"] service._service_url = data["service_url"] service._canonical_url = service._service_url service._pubkey = _PublicKey.from_data(data["public_key"]) service._pubcert = _PublicKey.from_data(data["public_certificate"]) if service.is_identity_service(): from Acquire.Identity import IdentityService as _IdentityService return _IdentityService(service) elif service.is_access_service(): from Acquire.Access import AccessService as _AccessService return _AccessService(service) elif service.is_accounting_service(): from Acquire.Accounting import AccountingService \ as _AccountingService return _AccountingService(service) else: return service
def test_json_keys(): privkey = PrivateKey() pubkey = privkey.public_key() args = { "message": "Hello, this is a message", "status": 0, "long": [random.random() for _ in range(1000)] } packed = pack_arguments(args) crypted = pubkey.encrypt(packed) uncrypted = privkey.decrypt(crypted) unpacked = unpack_arguments(uncrypted) assert (args == unpacked)
def run(args): """This function is used to request access to a bucket for data in the object store. The user can request read-only or read-write access. Access is granted based on a permission list """ status = 0 message = None access_token = None user_uuid = args["user_uuid"] identity_service_url = args["identity_service"] # log into the central access account bucket = login_to_service_account() # is the identity service supplied by the user one that we trust? identity_service = Service.from_data( ObjectStore.get_object_from_json(bucket, "services/%s" % identity_service_url)) if not identity_service: raise RequestBucketError( "You cannot request a bucket because " "this access service does not know or trust your supplied " "identity service (%s)" % identity_service_url) if not identity_service.is_identity_service(): raise RequestBucketError( "You cannot request a bucket because " "the passed service (%s) is not an identity service. It is " "a %s" % (identity_service_url, identity_service.service_type())) # Since we trust this identity service, we can ask it to give us the # public certificate and signing certificate for this user. key = PrivateKey() response = call_function(identity_service_url, "get_user_keys", args_key=identity_service.public_key(), response_key=key, user_uuid=user_uuid) status = 0 message = "Success: Status = %s" % str(response) return_value = create_return_value(status, message) if access_token: return_value["access_token"] = access_token return return_value
def test_service_object(tmpdir_factory): bucket = tmpdir_factory.mktemp("test_service") push_testing_objstore(bucket) push_is_running_service() try: service = Service.create(service_type="identity", service_url="identity") assert(service.uid() is not None) assert(service.uid().startswith("STAGE1")) service.create_stage2(service_uid="Z9-Z8", response=service.uid()) assert(service.is_identity_service()) assert(not service.should_refresh_keys()) assert(service.is_unlocked()) assert(not service.is_locked()) passphrase = PrivateKey.random_passphrase() data = service.to_data(passphrase) service2 = IdentityService.from_data(data, passphrase) assert(service2.uid() == service.uid()) assert(service2.is_unlocked()) assert(not service2.is_locked()) assert(service2.is_identity_service()) assert(service.canonical_url() == service2.canonical_url()) assert(not service2.should_refresh_keys()) keys = service.dump_keys() keys = service.load_keys(keys) assert(keys[service.private_key().fingerprint()] == service.private_key()) assert(keys[service.private_certificate().fingerprint()] == service.private_certificate()) service.refresh_keys() assert(service.last_key_update() > service2.last_key_update()) assert(service.last_certificate().public_key() == service2.public_certificate()) assert(service.last_key() == service2.private_key()) except: pop_is_running_service() pop_testing_objstore() raise pop_is_running_service() pop_testing_objstore()
def __init__(self, drive_uid=None, file_uid=None): """Create a new ChunkUploader that uploads the specified file to the specified drive """ self._drive_uid = None self._file_uid = None self._chunk_idx = None self._service = None if drive_uid is not None: self._drive_uid = str(drive_uid) self._file_uid = str(file_uid) from Acquire.Crypto import PrivateKey as _PrivateKey self._secret = _PrivateKey.random_passphrase()
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 from_data(data, passphrase=None): """Return a OSPar constructed from the passed json-deserliased dictionary Args: data (dict): JSON-deserialised dictionary from which to create OSPar Returns: OSPar: OSPar object created from dict """ if data is None or len(data) == 0: return OSPar() from Acquire.ObjectStore import string_to_datetime \ as _string_to_datetime from Acquire.ObjectStore import string_to_bytes \ as _string_to_bytes par = OSPar() par._url = _string_to_bytes(data["url"]) par._key = data["key"] par._uid = data["uid"] if par._key is not None: par._key = str(par._key) par._expires_datetime = _string_to_datetime(data["expires_datetime"]) par._is_readable = data["is_readable"] par._is_writeable = data["is_writeable"] if "service_url" in data: par._service_url = data["service_url"] if "privkey" in data: if passphrase is not None: from Acquire.Crypto import PrivateKey as _PrivateKey par._privkey = _PrivateKey.from_data(data["privkey"], passphrase) # note that we don't load the driver details as this # is stored and loaded separately on the service return par
def from_data(data, passphrase, mangleFunction=None): """Return a UserAccount constructed from the passed data (dictionary) """ user = UserAccount() if data is not None and len(data) > 0: from Acquire.Crypto import PrivateKey as _PrivateKey user._username = data["username"] user._status = data["status"] user._uid = data["uid"] user._privkey = _PrivateKey.from_data( data=data["private_key"], passphrase=passphrase, mangleFunction=mangleFunction) return user
def verify_admin_user(self, password, otpcode, remember_device=False): """Verify that we are the admin user verifying that the passed password and otpcode are correct. This does nothing if they are correct, but raises an exception if they are wrong. If 'remember_device' this this returns the provisioning_uri for the OTP generator""" try: key = _PrivateKey.read_bytes(self._admin_password, password) except Exception as e: raise ServiceError("Could not log into admin account: %s" % str(e)) try: otp = _OTP.decrypt(self._otpsecret, key) otp.verify(otpcode) if remember_device: return otp.provisioning_uri("admin", self.service_url()) except Exception as e: raise ServiceError("Could not log into admin account: %s" % str(e))
def __init__(self, drive_uid=None, file_uid=None): """Create a new ChunkDowloader that downloads the specified file from the specified drive """ self._uid = None self._drive_uid = None self._file_uid = None self._service = None self._next_index = None self._last_filename = None self._downloaded_filename = None self._FILE = None if drive_uid is not None: self._drive_uid = str(drive_uid) self._file_uid = str(file_uid) from Acquire.Crypto import PrivateKey as _PrivateKey self._secret = _PrivateKey.random_passphrase() from Acquire.ObjectStore import create_uid as _create_uid self._uid = _create_uid(short_uid=True)
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 aaai_services(tmpdir_factory): """This function creates mocked versions of all of the main services of the system, returning the json describing each service as a dictionary (which is passed to the test functions as the fixture) """ from Acquire.Identity import Authorisation from Acquire.Crypto import PrivateKey, OTP from Acquire.Service import call_function, Service _services = {} _services["registry"] = tmpdir_factory.mktemp("registry") _services["identity"] = tmpdir_factory.mktemp("identity") _services["accounting"] = tmpdir_factory.mktemp("accounting") _services["access"] = tmpdir_factory.mktemp("access") _services["storage"] = tmpdir_factory.mktemp("storage") _services["userdata"] = tmpdir_factory.mktemp("userdata") _services["compute"] = tmpdir_factory.mktemp("compute") _services["hugs"] = tmpdir_factory.mktemp("hugs") wallet_dir = tmpdir_factory.mktemp("wallet") wallet_password = PrivateKey.random_passphrase() _set_services(_services, wallet_dir, wallet_password) password = PrivateKey.random_passphrase() args = {"password": password} responses = {} os.environ["SERVICE_PASSWORD"] = "******" os.environ["STORAGE_COMPARTMENT"] = str(_services["userdata"]) args["canonical_url"] = "registry" args["service_type"] = "registry" args["registry_uid"] = "Z9-Z9" # UID of testing registry response = call_function("registry", function="admin/setup", args=args) registry_service = Service.from_data(response["service"]) registry_otp = OTP(OTP.extract_secret(response["provisioning_uri"])) registry_user = _login_admin("registry", "admin", password, registry_otp) responses["registry"] = { "service": registry_service, "user": registry_user, "response": response, } assert registry_service.registry_uid() == registry_service.uid() service_uids = [registry_service.uid()] args["canonical_url"] = "identity" args["service_type"] = "identity" response = call_function("identity", function="admin/setup", args=args) identity_service = Service.from_data(response["service"]) identity_otp = OTP(OTP.extract_secret(response["provisioning_uri"])) identity_user = _login_admin("identity", "admin", password, identity_otp) responses["identity"] = { "service": identity_service, "user": identity_user, "response": response, } assert identity_service.registry_uid() == registry_service.uid() assert identity_service.uid() not in service_uids service_uids.append(identity_service.uid()) args["canonical_url"] = "accounting" args["service_type"] = "accounting" response = call_function("accounting", function="admin/setup", args=args) accounting_service = Service.from_data(response["service"]) accounting_otp = OTP(OTP.extract_secret(response["provisioning_uri"])) accounting_user = _login_admin("accounting", "admin", password, accounting_otp) responses["accounting"] = { "service": accounting_service, "user": accounting_user, "response": response, } assert accounting_service.registry_uid() == registry_service.uid() assert accounting_service.uid() not in service_uids service_uids.append(accounting_service.uid()) args["canonical_url"] = "access" args["service_type"] = "access" response = call_function("access", function="admin/setup", args=args) responses["access"] = response access_service = Service.from_data(response["service"]) access_otp = OTP(OTP.extract_secret(response["provisioning_uri"])) access_user = _login_admin("access", "admin", password, access_otp) responses["access"] = { "service": access_service, "user": access_user, "response": response, } assert access_service.registry_uid() == registry_service.uid() assert access_service.uid() not in service_uids service_uids.append(access_service.uid()) args["canonical_url"] = "compute" args["service_type"] = "compute" response = call_function("compute", function="admin/setup", args=args) responses["compute"] = response compute_service = Service.from_data(response["service"]) compute_otp = OTP(OTP.extract_secret(response["provisioning_uri"])) compute_user = _login_admin("compute", "admin", password, compute_otp) responses["compute"] = { "service": compute_service, "user": compute_user, "response": response, } assert compute_service.registry_uid() == registry_service.uid() assert compute_service.uid() not in service_uids service_uids.append(compute_service.uid()) args["canonical_url"] = "storage" args["service_type"] = "storage" response = call_function("storage", function="admin/setup", args=args) storage_service = Service.from_data(response["service"]) storage_otp = OTP(OTP.extract_secret(response["provisioning_uri"])) storage_user = _login_admin("storage", "admin", password, storage_otp) responses["storage"] = { "service": storage_service, "user": storage_user, "response": response, } assert storage_service.registry_uid() == registry_service.uid() assert storage_service.uid() not in service_uids service_uids.append(storage_service.uid()) args["canonical_url"] = "hugs" args["service_type"] = "hugs" response = call_function("hugs", function="admin/setup", args=args) responses["hugs"] = response hugs_service = Service.from_data(response["service"]) hugs_otp = OTP(OTP.extract_secret(response["provisioning_uri"])) hugs_user = _login_admin("hugs", "admin", password, hugs_otp) responses["hugs"] = { "service": hugs_service, "user": hugs_user, "response": response, } resource = "trust_accounting_service %s" % accounting_service.uid() args = { "service_url": accounting_service.canonical_url(), "authorisation": Authorisation(user=access_user, resource=resource).to_data(), } access_service.call_function(function="admin/trust_accounting_service", args=args) responses["_services"] = _services return responses
def validate_password(user_uid, username, device_uid, secrets, password, otpcode, remember_device): """Validate that the passed password and one-time-code are valid. If they are, then return a tuple of the UserAccount of the unlocked user, the OTP that is used to generate secrets, and the device_uid of the login device If 'remember_device' is True and 'device_uid' is None, then this creates a new OTP for the login device, which is returned, and a new device_uid for that device. The password needed to match this device is a MD5 of the normal user password. """ from Acquire.Crypto import PrivateKey as _PrivateKey from Acquire.Crypto import OTP as _OTP from Acquire.ObjectStore import string_to_bytes as _string_to_bytes privkey = _PrivateKey.from_data(data=secrets["private_key"], passphrase=password) # decrypt and validate the OTP code data = _string_to_bytes(secrets["otpsecret"]) otpsecret = privkey.decrypt(data) otp = _OTP(secret=otpsecret) otp.verify(code=otpcode, once_only=True) # everything is ok - we can load the user account via the # decrypted primary password primary_password = _string_to_bytes(secrets["primary_password"]) primary_password = privkey.decrypt(primary_password) from Acquire.ObjectStore import ObjectStore as _ObjectStore from Acquire.Service import get_service_account_bucket \ as _get_service_account_bucket data = None secrets = None key = "%s/uids/%s" % (_user_root, user_uid) bucket = _get_service_account_bucket() try: data = _ObjectStore.get_object_from_json(bucket=bucket, key=key) except: pass if data is None: from Acquire.Identity import UserValidationError raise UserValidationError( "Unable to validate user as no account data is present!") from Acquire.Identity import UserAccount as _UserAccount user = _UserAccount.from_data(data=data, passphrase=primary_password) if user.uid() != user_uid: from Acquire.Identity import UserValidationError raise UserValidationError( "Unable to validate user as mismatch in user_uids!") if device_uid is None and remember_device: # create a new OTP that is unique for this device from Acquire.ObjectStore import create_uuid as _create_uuid from Acquire.Client import Credentials as _Credentials device_uid = _create_uuid() device_password = _Credentials.encode_device_uid( encoded_password=password, device_uid=device_uid) otp = UserCredentials.create(user_uid=user_uid, password=device_password, primary_password=primary_password, device_uid=device_uid) # now save a lookup so that we can find the user_uid from # the username and device-specific password encoded_password = UserCredentials.hash( username=username, password=device_password) key = "%s/passwords/%s/%s" % (_user_root, encoded_password, user_uid) from Acquire.ObjectStore import get_datetime_now_to_string \ as _get_datetime_now_to_string _ObjectStore.set_string_object( bucket=bucket, key=key, string_data=_get_datetime_now_to_string()) return {"user": user, "otp": otp, "device_uid": device_uid}
service_file = "service.json" if os.path.exists(service_file): with open(service_file, "r") as FILE: service_info = json.load(FILE) service_salt = service_info["salt"] while True: password = getpass.getpass( prompt="Please enter the service primary password: "******"key"], passphrase=password) break except: print("Password incorrect. Try again.") old_config = service_key.decrypt(string_to_bytes(service_info["config"])) old_config = json.loads(old_config) keep = True else: while True: password = getpass.getpass( prompt="Please enter the service primary password: "******"Please enter the password again: ") if password == password2: break
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 run(args): """This function will allow a user to register an account with a username and password""" status = 0 message = None provisioning_uri = None username = args["username"] password = args["password"] # generate a sanitised version of the username user_account = UserAccount(username) # generate the encryption keys and otp secret privkey = PrivateKey() pubkey = privkey.public_key() otp = OTP() provisioning_uri = otp.provisioning_uri(username) # save the encrypted private key (encrypted using the user's password) # and encrypted OTP secret (encrypted using the public key) user_account.set_keys(privkey.bytes(password), pubkey.bytes(), otp.encrypt(pubkey)) # remove the key and password from memory privkey = None password = None # now log into the central identity account to either register # the user, or to update to a new password bucket = login_to_service_account() account_key = "accounts/%s" % user_account.sanitised_name() try: existing_data = ObjectStore.get_object_from_json(bucket, account_key) except: existing_data = None message = "Created a new account for '%s'" % username status = 0 if existing_data is None: # save the new account details ObjectStore.set_object_from_json(bucket, account_key, user_account.to_data()) # need to update the "whois" database with the uuid of this user ObjectStore.set_string_object(bucket, "whois/%s" % user_account.uuid(), user_account.username()) else: # The account already exists. See if this is a password change # request old_password = None try: old_password = args["old_password"] except: raise ExistingAccountError( "An account by this name already exists!") if old_password != password: # this is a change of password request - validate that # the existing password unlocks the existing key user_account = UserAccount.from_data(existing_data) testkey = PrivateKey.read_bytes(user_account.private_key(), old_password) # decrypt the old secret old_secret = testkey.decrypt(user_account.otp_secret()) # now encrypt the secret with the new key new_key = PublicKey.read_bytes(pubkey) new_secret = new_key.encrypt(old_secret) user_account.set_keys(privkey, pubkey, new_secret) # save the new account details ObjectStore.set_object_from_json(bucket, account_key, user_account.to_data()) message = "Updated the password for '%s'" % username else: message = "No need to change account '%s'" % username return_value = create_return_value(status, message) if provisioning_uri: return_value["provisioning_uri"] = provisioning_uri return return_value
def __init__(self, location=None, user=None, aclrule=None, expires_datetime=None): """Construct a PAR for the specified location, authorised by the passed user, giving permissions according to the passed 'aclrule' (default is ACLRule.reader()). The passed 'expires_datetime' is the time at which this PAR will expire (by default within 24 hours) """ self._location = None self._uid = None self._expires_datetime = None if location is None: return from Acquire.Client import Location as _Location if not isinstance(location, _Location): raise TypeError("The location must be type Location") if location.is_null(): return from Acquire.Client import User as _User if not isinstance(user, _User): raise TypeError("The user must be type User") if not user.is_logged_in(): raise PermissionError("The passed User must be logged in!") from Acquire.Client import ACLRule as _ACLRule if aclrule is None: aclrule = _ACLRule.reader() elif not isinstance(aclrule, _ACLRule): raise TypeError("The aclrule must be type ACLRule") if expires_datetime is None: from Acquire.ObjectStore import get_datetime_future \ as _get_datetime_future expires_datetime = _get_datetime_future(days=1) else: from Acquire.ObjectStore import datetime_to_datetime \ as _datetime_to_datetime expires_datetime = _datetime_to_datetime(expires_datetime) self._location = location self._expires_datetime = expires_datetime self._aclrule = aclrule from Acquire.Client import Authorisation as _Authorisation auth = _Authorisation(user=user, resource="create_par %s" % self.fingerprint()) from Acquire.Crypto import PrivateKey as _PrivateKey self._secret = _PrivateKey.random_passphrase() args = { "authorisation": auth.to_data(), "par": self.to_data(), "secret": self._secret } service = location.service() result = service.call_function(function="create_par", args=args) self._set_uid(result["par_uid"])
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 get_service_account_bucket(testing_dir=None): """This function logs into the object store account of the service account. Accessing the object store means being able to access all resources and which can authorise the creation of access all resources on the object store. Obviously this is a powerful account, so only log into it if you need it!!! The login information should not be put into a public repository or stored in plain text. In this case, the login information is held in an environment variable (which should be encrypted or hidden in some way...) """ from Acquire.Service import assert_running_service as \ _assert_running_service _assert_running_service() # read the password for the secret key from the filesystem try: with open("secret_key", "r") as FILE: password = FILE.readline()[0:-1] except: password = None # we must be in testing mode... from Acquire.ObjectStore import use_testing_object_store_backend as \ _use_testing_object_store_backend # see if this is running in testing mode... global _current_testing_objstore if testing_dir: _current_testing_objstore = testing_dir return _use_testing_object_store_backend(testing_dir) elif _current_testing_objstore: return _use_testing_object_store_backend(_current_testing_objstore) if password is None: from Acquire.Service import ServiceAccountError raise ServiceAccountError( "You need to supply login credentials via the 'secret_key' " "file, and 'SECRET_KEY' and 'SECRET_CONFIG' environment " "variables! %s" % testing_dir) secret_key = _os.getenv("SECRET_KEY") if secret_key is None: from Acquire.Service import ServiceAccountError raise ServiceAccountError( "You must supply the password used to unlock the configuration " "key in the 'SECRET_KEY' environment variable") try: secret_key = _json.loads(secret_key) except Exception as e: from Acquire.Service import ServiceAccountError raise ServiceAccountError( "Unable to decode valid JSON from the secret key: %s" % str(e)) # use the password to decrypt the SECRET_KEY in the config try: from Acquire.Crypto import PrivateKey as _PrivateKey secret_key = _PrivateKey.from_data(secret_key, password) except Exception as e: from Acquire.Service import ServiceAccountError raise ServiceAccountError( "Unable to open the private SECRET_KEY using the password " "supplied in the 'secret_key' file: %s" % str(e)) config = _os.getenv("SECRET_CONFIG") if config is None: from Acquire.Service import ServiceAccountError raise ServiceAccountError( "You must supply the encrypted config in teh 'SECRET_CONFIG' " "environment variable!") try: from Acquire.ObjectStore import string_to_bytes as _string_to_bytes config = secret_key.decrypt(_string_to_bytes(config)) except Exception as e: from Acquire.Service import ServiceAccountError raise ServiceAccountError( "Cannot decrypt the 'SECRET_CONFIG' with the 'SECRET_KEY'. Are " "you sure that the configuration has been set up correctly? %s " % str(e)) # use the secret_key to decrypt the config in SECRET_CONFIG try: config = _json.loads(config) except Exception as e: from Acquire.Service import ServiceAccountError raise ServiceAccountError( "Unable to decode valid JSON from the config: %s" % str(e)) # get info from this config access_data = config["LOGIN"] bucket_data = config["BUCKET"] # save the service password to the environment _os.environ["SERVICE_PASSWORD"] = config["PASSWORD"] # save any other decrypted config data to environment variables for key in config.keys(): if key not in ["LOGIN", "BUCKET", "PASSWORD"]: _os.environ[key] = config[key] # we have OCI login details, so make sure that we are using # the OCI object store backend from Acquire.ObjectStore import use_oci_object_store_backend as \ _use_oci_object_store_backend _use_oci_object_store_backend() # now login and create/load the bucket for this account try: from ._oci_account import OCIAccount as _OCIAccount account_bucket = _OCIAccount.create_and_connect_to_bucket( access_data, bucket_data["compartment"], bucket_data["bucket"]) except Exception as e: from Acquire.Service import ServiceAccountError raise ServiceAccountError( "Error connecting to the service account: %s" % str(e)) return account_bucket
def test_keys(): privkey = PrivateKey() pubkey = privkey.public_key() message = "Hello World" sig = privkey.sign(message.encode("utf-8")) pubkey.verify(sig, message.encode("utf-8")) c = pubkey.encrypt(message.encode("utf-8")) m = privkey.decrypt(c).decode("utf-8") assert (m == message) privkey2 = PrivateKey() sig2 = privkey2.sign(message.encode("utf-8")) with pytest.raises(SignatureVerificationError): pubkey.verify(sig2, message.encode("utf-8")) bytes = privkey.bytes("testPass32") PrivateKey.read_bytes(bytes, "testPass32") privkey.write("test.pem", "testPass32") PrivateKey.read("test.pem", "testPass32") bytes = pubkey.bytes() pubkey2 = PublicKey.read_bytes(bytes) assert (bytes == pubkey2.bytes()) long_message = str([random.getrandbits(8) for _ in range(4096)]).encode("utf-8") c = pubkey.encrypt(long_message) m = privkey.decrypt(c) assert (m == long_message) os.unlink("test.pem") data = pubkey.to_data() pubkey2 = PublicKey.from_data(data) assert (pubkey.bytes() == pubkey2.bytes()) data = privkey.to_data("testPass33") privkey2 = PrivateKey.from_data(data, "testPass33") assert (privkey == privkey2)
""" This script writes the login information (pem key etc.) that is needed by the identity service to log onto the object store as the identity admin user account """ import json import sys import os from Acquire.Crypto import PrivateKey from Acquire.ObjectStore import bytes_to_string ## Create a key to encrypt the config config_key = PrivateKey() secret_config = {} ## First create the login info to connect to the account """ [DEFAULT] user=ocid1.user.oc1..aaaaaaaasuhkztlrvedgnkqnywkuj7giu7o4nyqohtwyk7zeldpkiktrhosq fingerprint=5d:ac:65:4c:11:69:5b:e7:e8:5c:04:39:93:f0:cd:c8 key_file=~/.oci/oci_api_key.pem pass_phrase=XXXXXX tenancy=ocid1.tenancy.oc1..aaaaaaaa3eiex6fbfj626uwhs3dg24oygknrhhgfj4khqearluf4i74zdt2a region=eu-frankfurt-1 """ data = {} # OCID for the user "bss-auth-service"
import pytest from Acquire.Service import call_function from Acquire.Client import User, Wallet from Acquire.Identity import Authorisation from Acquire.Crypto import OTP, PrivateKey _wallet_password = PrivateKey.random_passphrase() @pytest.mark.parametrize("username, password", [("testuser", "ABCdef12345"), ("something", "!!DDerfld31"), ("someone", "%$(F*Dj4jij43 kdfjdk")]) def test_login(username, password, aaai_services, tmpdir): # register the new user result = User.register(username=username, password=password, identity_url="identity") assert(type(result) is dict) otpsecret = result["otpsecret"] otp = OTP(otpsecret) user = User(username=username, identity_url="identity", auto_logout=False) result = user.request_login()
def login_to_service_account(testing_dir=None): """This function logs into the object store account of the service account. Accessing the object store means being able to access all resources and which can authorise the creation of access all resources on the object store. Obviously this is a powerful account, so only log into it if you need it!!! The login information should not be put into a public repository or stored in plain text. In this case, the login information is held in an environment variable (which should be encrypted or hidden in some way...) """ # read the password for the secret key from the filesystem try: with open("secret_key", "r") as FILE: password = FILE.readline()[0:-1] except: password = None # we must be in testing mode... from Acquire.ObjectStore import use_testing_object_store_backend as \ _use_testing_object_store_backend # see if this is running in testing mode... global _current_testing_objstore if testing_dir: _current_testing_objstore = testing_dir return _use_testing_object_store_backend(testing_dir) elif _current_testing_objstore: return _use_testing_object_store_backend(_current_testing_objstore) if password is None: raise ServiceAccountError( "You need to supply login credentials via the 'secret_key' " "file, and 'SECRET_KEY' and 'SECRET_CONFIG' environment " "variables! %s" % testing_dir) # use the password to decrypt the SECRET_KEY in the config secret_key = _PrivateKey.from_data(_json.loads(_os.getenv("SECRET_KEY")), password) # use the secret_key to decrypt the config in SECRET_CONFIG config = _json.loads( secret_key.decrypt(_string_to_bytes( _os.getenv("SECRET_CONFIG"))).decode("utf-8")) # get info from this config access_data = config["LOGIN"] bucket_data = config["BUCKET"] # save the service password to the environment _os.environ["SERVICE_PASSWORD"] = config["PASSWORD"] # we have OCI login details, so make sure that we are using # the OCI object store backend from Acquire.ObjectStore import use_oci_object_store_backend as \ _use_oci_object_store_backend _use_oci_object_store_backend() # now login and create/load the bucket for this account try: from ._oci_account import OCIAccount as _OCIAccount account_bucket = _OCIAccount.create_and_connect_to_bucket( access_data, bucket_data["compartment"], bucket_data["bucket"]) except Exception as e: raise ServiceAccountError( "Error connecting to the service account: %s" % str(e)) return account_bucket