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 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 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 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 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() assert(type(result) is dict) login_url = result["login_url"] print(login_url) wallet = Wallet() wallet.send_password(url=login_url, username=username, password=password, otpcode=otp.generate(), remember_password=True) user.wait_for_login() assert(user.is_logged_in()) auth = Authorisation(user=user, resource="test") auth.verify("test") user.logout() # now try to log in, using the remembered password user = User(username=username, identity_url="identity", auto_logout=False) result = user.request_login() login_url = result["login_url"] # the test has to specify the username as we can't choose... wallet.send_password(url=login_url, username=username, otpcode=otp.generate(), remember_device=True) user.wait_for_login() assert(user.is_logged_in()) auth = Authorisation(user=user, resource="test") auth.verify("test") user.logout() # now see if the wallet can send all login info # now try to log in, using the remembered password user = User(username=username, identity_url="identity", auto_logout=False) result = user.request_login() login_url = result["login_url"] # the test has to specify the username as we can't choose... wallet.send_password(url=login_url, username=username) user.wait_for_login() assert(user.is_logged_in()) auth = Authorisation(user=user, resource="test") auth.verify("test") user.logout()
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 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 test_login_fails(aaai_services, tmpdir): # register two users username1 = "fail1" password1 = "Fail1!!!" username2 = "fail2" password2 = "Fail2!!!" result = User.register(username=username1, password=password1, identity_url="identity") assert (type(result) is dict) otpsecret1 = result["otpsecret"] otp1 = OTP(otpsecret1) user1 = User(username=username1, identity_url="identity", auto_logout=False) result = User.register(username=username2, password=password2, identity_url="identity") assert (type(result) is dict) otpsecret2 = result["otpsecret"] otp2 = OTP(otpsecret2) user2 = User(username=username2, identity_url="identity", auto_logout=False) result1 = user1.request_login() result2 = user2.request_login() assert (type(result1) is dict) assert (type(result2) is dict) login_url1 = result1["login_url"] login_url2 = result2["login_url"] wallet = Wallet() # try to log in with the wrong user with pytest.raises(LoginError): wallet.send_password(url=login_url1, username=username2, password=password2, otpcode=otp2.generate(), remember_password=False, remember_device=False) with pytest.raises(LoginError): wallet.send_password(url=login_url2, username=username1, password=password1, otpcode=otp1.generate(), remember_password=False, remember_device=False) # now use the right user by the wrong otpcode with pytest.raises(LoginError): wallet.send_password(url=login_url1, username=username1, password=password1, otpcode=otp2.generate(), remember_password=False, remember_device=False) # now use the right user by the wrong otpcode with pytest.raises(LoginError): wallet.send_password(url=login_url2, username=username2, password=password2, otpcode=otp1.generate(), remember_password=False, remember_device=False) # now use the right user by the wrong password with pytest.raises(LoginError): wallet.send_password(url=login_url1, username=username1, password=password2, otpcode=otp1.generate(), remember_password=False, remember_device=False) with pytest.raises(LoginError): wallet.send_password(url=login_url2, username=username2, password=password1, otpcode=otp1.generate(), remember_password=False, remember_device=False) # now, get it right ;-) wallet.send_password(url=login_url1, username=username1, password=password1, otpcode=otp1.generate(), remember_password=False, remember_device=False) wallet.send_password(url=login_url2, username=username2, password=password2, otpcode=otp2.generate(), remember_password=False, remember_device=False) user1.logout() user2.logout()