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()))
Beispiel #2
0
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
Beispiel #3
0
    def reset_admin_password(self, password, otpcode, new_password):
        """Change the admin password for this service. Note that
           you must pass in a valid password and otpcode to make the change"""

        self.verify_admin_user(password, otpcode)

        if password == new_password:
            return

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

        newkey = _PrivateKey()
        self._admin_password = newkey.bytes(new_password)
        self._otpsecret = otp.encrypt(newkey.public_key())
Beispiel #4
0
    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))
Beispiel #5
0
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()
Beispiel #6
0
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
Beispiel #7
0
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
Beispiel #8
0
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()