Beispiel #1
0
def run(args):
    """This function will allow anyone to query the current login
       status of the session with passed UID"""

    status = 0
    message = None
    session_status = None

    session_uid = args["session_uid"]
    username = args["username"]

    # generate a sanitised version of the username
    user_account = UserAccount(username)

    # now log into the central identity account to query
    # the current status of this login session
    bucket = login_to_service_account()

    user_session_key = "sessions/%s/%s" % \
        (user_account.sanitised_name(), session_uid)

    try:
        login_session = LoginSession.from_data(
            ObjectStore.get_object_from_json(bucket, user_session_key))
    except:
        login_session = None

    if login_session is None:
        user_session_key = "expired_sessions/%s/%s" % \
                                (user_account.sanitised_name(),
                                    session_uid)

        login_session = LoginSession.from_data(
            ObjectStore.get_object_from_json(bucket, user_session_key))

    if login_session is None:
        raise InvalidSessionError("Cannot find the session '%s'" % session_uid)

    status = 0
    message = "Success: Status = %s" % login_session.status()
    session_status = login_session.status()

    return_value = create_return_value(status, message)

    if session_status:
        return_value["session_status"] = session_status

    return return_value
Beispiel #2
0
def run(args):
    """This function allows a user to register an account with a
       username and password
       
       Args:
        args (dict): containing the requested username and password
    
        Returns:
            dict: containing the provisioning URI
       
       """

    username = args["username"]
    password = args["password"]

    service = get_this_service(need_private_access=False)
    issuer = "%s@%s" % (service.service_type(), service.hostname())

    (user_uid, otp) = UserAccount.create(username=username,
                                         password=password)

    provisioning_uri = otp.provisioning_uri(username=username, issuer=issuer)

    return_value = {}

    return_value["user_uid"] = user_uid
    return_value["provisioning_uri"] = provisioning_uri

    return return_value
Beispiel #3
0
def run(args):
    """This function will allow the current user to authorise
       a logout from the current session - this will be authorised
       by signing the request to logout"""

    status = 0
    message = None

    session_uid = args["session_uid"]
    username = args["username"]
    permission = args["permission"]
    signature = string_to_bytes(args["signature"])

    # generate a sanitised version of the username
    user_account = UserAccount(username)

    # now log into the central identity account to query
    # the current status of this login session
    bucket = login_to_service_account()

    user_session_key = "sessions/%s/%s" % \
        (user_account.sanitised_name(), session_uid)

    request_session_key = "requests/%s/%s" % (session_uid[:8], session_uid)

    login_session = LoginSession.from_data(
        ObjectStore.get_object_from_json(bucket, user_session_key))

    if login_session:
        # get the signing certificate from the login session and
        # validate that the permission object has been signed by
        # the user requesting the logout
        cert = login_session.public_certificate()

        cert.verify(signature, permission)

        # the signature was correct, so log the user out. For record
        # keeping purposes we change the loginsession to a logout state
        # and move it to another part of the object store
        if login_session.is_approved():
            login_session.logout()

    # only save sessions that were successfully approved
    if login_session:
        if login_session.is_logged_out():
            expired_session_key = "expired_sessions/%s/%s" % \
                                    (user_account.sanitised_name(),
                                     session_uid)

            ObjectStore.set_object_from_json(bucket, expired_session_key,
                                             login_session.to_data())

    try:
        ObjectStore.delete_object(bucket, user_session_key)
    except:
        pass

    try:
        ObjectStore.delete_object(bucket, request_session_key)
    except:
        pass

    status = 0
    message = "Successfully logged out"

    return_value = create_return_value(status, message)

    return return_value
Beispiel #4
0
def setup_this_service(service_type, canonical_url, registry_uid, username,
                       password):
    """Call this function to setup a new
       service that will serve at 'canonical_url', will be of
       the specified service_type. This will be registered at the
       registry at UID registry_uid

        (1) Delete the object store value "_service" if you want to reset
            the actual Service. This will assign a new UID for the service
            which would reset the certificates and keys. This new service
            will need to be re-introduced to other services that need
            to trust it
    """
    assert_running_service()

    from Acquire.Service import get_service_account_bucket as \
        _get_service_account_bucket

    from Acquire.ObjectStore import Mutex as _Mutex
    from Acquire.ObjectStore import ObjectStore as _ObjectStore
    from Acquire.Service import Service as _Service

    bucket = _get_service_account_bucket()

    # ensure that this is the only time the service is set up
    mutex = _Mutex(key=_service_key, bucket=bucket, lease_time=120)

    try:
        service_info = _ObjectStore.get_object_from_json(bucket, _service_key)
    except:
        service_info = None

    service = None
    service_password = _get_service_password()

    user_uid = None
    otp = None

    if service_info:
        try:
            service = _Service.from_data(service_info, service_password)
        except Exception as e:
            from Acquire.Service import ServiceAccountError
            raise ServiceAccountError(
                "Something went wrong reading the Service data. You should "
                "either debug the error or delete the data at key '%s' "
                "to allow the service to be reset and constructed again. "
                "The error was %s" % (_service_key, str(e)))

        if service.uid().startswith("STAGE1"):
            from Acquire.Service import ServiceAccountError
            raise ServiceAccountError(
                "The service is currently under construction. Please "
                "try again later...")

    if service is None:
        # we need to create the new service
        if (service_type is None) or (canonical_url is None):
            from Acquire.Service import ServiceAccountError
            raise ServiceAccountError(
                "You need to supply both the service_type and canonical_url "
                "in order to initialise a new Service")

        # we need to build the service account - first stage 1
        service = _Service.create(service_url=canonical_url,
                                  service_type=service_type)

        # write the stage1 service data, encrypted using the service password.
        # This will be needed to answer the challenge from the registry
        service_data = service.to_data(service_password)
        _ObjectStore.set_object_from_json(bucket, _service_key, service_data)

        # now we can register the service with a registry - this
        # will return the stage2-constructed service
        from Acquire.Registry import register_service as _register_service
        service = _register_service(service=service, registry_uid=registry_uid)

    canonical_url = _Service.get_canonical_url(canonical_url)

    if service.service_type() != service_type or \
            service.canonical_url() != canonical_url:
        from Acquire.Service import ServiceAccountError
        raise ServiceAccountError(
            "The existing service has a different type or URL to that "
            "requested at setup. The request type and URL are %s and %s, "
            "while the actual service type and URL are %s and %s." %
            (service_type, canonical_url, service.service_type(),
             service.canonical_url()))

    # we can add the first admin user
    service_uid = service.uid()
    skelkey = service.skeleton_key().public_key()

    # now register the new admin user account - remembering to
    # encode the password
    from Acquire.Client import Credentials as _Credentials
    password = _Credentials.encode_password(password=password,
                                            identity_uid=service_uid)

    from Acquire.Identity import UserAccount as _UserAccount
    (user_uid, otp) = _UserAccount.create(username=username,
                                          password=password,
                                          _service_uid=service_uid,
                                          _service_public_key=skelkey)

    add_admin_user(service, user_uid)

    # write the service data, encrypted using the service password
    service_data = service.to_data(service_password)
    # reload the data to check it is ok, and also to set the right class
    service = _Service.from_data(service_data, service_password)
    # now it is ok, save this data to the object store
    _ObjectStore.set_object_from_json(bucket, _service_key, service_data)

    mutex.unlock()

    from Acquire.Service import clear_service_cache as _clear_service_cache
    _clear_service_cache()

    return (service, user_uid, otp)
Beispiel #5
0
def run(args):
    """This function is called by the user to log in and validate
       that a session is authorised to connect"""

    status = 0
    message = None
    provisioning_uri = None
    assigned_device_uid = None

    short_uid = args["short_uid"]
    username = args["username"]
    password = args["password"]
    otpcode = args["otpcode"]

    try:
        remember_device = args["remember_device"]
    except:
        remember_device = False

    try:
        device_uid = args["device_uid"]
    except:
        device_uid = None

    # create the user account for the user
    user_account = UserAccount(username)

    # log into the central identity account to query
    # the current status of this login session
    bucket = login_to_service_account()

    # locate the session referred to by this uid
    base_key = "requests/%s" % short_uid
    session_keys = ObjectStore.get_all_object_names(bucket, base_key)

    # try all of the sessions to find the one that the user
    # may be referring to...
    login_session_key = None
    request_session_key = None

    for session_key in session_keys:
        request_session_key = "%s/%s" % (base_key, session_key)
        session_user = ObjectStore.get_string_object(
            bucket, request_session_key)

        # did the right user request this session?
        if user_account.name() == session_user:
            if login_session_key:
                # this is an extremely unlikely edge case, whereby
                # two login requests within a 30 minute interval for the
                # same user result in the same short UID. This should be
                # signified as an error and the user asked to create a
                # new request
                raise LoginError(
                    "You have found an extremely rare edge-case "
                    "whereby two different login requests have randomly "
                    "obtained the same short UID. As we can't work out "
                    "which request is valid, the login is denied. Please "
                    "create a new login request, which will then have a "
                    "new login request UID")
            else:
                login_session_key = session_key

    if not login_session_key:
        raise LoginError(
            "There is no active login request with the "
            "short UID '%s' for user '%s'" % (short_uid, username))

    login_session_key = "sessions/%s/%s" % (user_account.sanitised_name(),
                                            login_session_key)

    # fully load the user account from the object store so that we
    # can validate the username and password
    try:
        account_key = "accounts/%s" % user_account.sanitised_name()
        user_account = UserAccount.from_data(
            ObjectStore.get_object_from_json(bucket, account_key))
    except:
        raise LoginError("No account available with username '%s'" %
                         username)

    if (not remember_device) and device_uid:
        # see if this device has been seen before
        device_key = "devices/%s/%s" % (user_account.sanitised_name(),
                                        device_uid)

        try:
            device_secret = ObjectStore.get_string_object(bucket,
                                                          device_key)
        except:
            device_secret = None

        if device_secret is None:
            raise LoginError(
                "The login device is not recognised. Please try to "
                "log in again using your master one-time-password.")
    else:
        device_secret = None

    # now try to log into this account using the supplied
    # password and one-time-code
    try:
        if device_secret:
            user_account.validate_password(password, otpcode,
                                           device_secret=device_secret)
        elif remember_device:
            (device_secret, provisioning_uri) = \
                        user_account.validate_password(
                                    password, otpcode,
                                    remember_device=True)

            device_uid = str(uuid.uuid4())
            device_key = "devices/%s/%s" % (user_account.sanitised_name(),
                                            device_uid)

            assigned_device_uid = device_uid
        else:
            user_account.validate_password(password, otpcode)
    except:
        # don't leak info about why validation failed
        raise LoginError("The password or OTP code is incorrect")

    # the user is valid - load up the actual login session
    login_session = LoginSession.from_data(
                        ObjectStore.get_object_from_json(bucket,
                                                         login_session_key))

    # we must record the session against which this otpcode has
    # been validated. This is to stop us validating an otpcode more than
    # once (e.g. if the password and code have been intercepted).
    # Any sessions validated using the same code should be treated
    # as immediately suspcious
    otproot = "otps/%s" % user_account.sanitised_name()
    sessions = ObjectStore.get_all_strings(bucket, otproot)

    utcnow = datetime.datetime.utcnow()

    for session in sessions:
        otpkey = "%s/%s" % (otproot, session)
        otpstring = ObjectStore.get_string_object(bucket, otpkey)

        (timestamp, code) = otpstring.split("|||")

        # remove all codes that are more than 10 minutes old. The
        # otp codes are only valid for 3 minutes, so no need to record
        # codes that have been used that are older than that...
        timedelta = utcnow - datetime.datetime.fromtimestamp(
                                                    float(timestamp))

        if timedelta.seconds > 600:
            try:
                ObjectStore.delete_object(bucket, otpkey)
            except:
                pass

        elif code == str(otpcode):
            # Low probability there is some recycling,
            # but very suspicious if the code was validated within the last
            # 10 minutes... (as 3 minute timeout of a code)
            suspect_key = "sessions/%s/%s" % (
                user_account.sanitised_name(), session)

            suspect_session = None

            try:
                suspect_session = LoginSession.from_data(
                        ObjectStore.get_object_from_json(bucket,
                                                         suspect_key))
            except:
                pass

            if suspect_session:
                suspect_session.set_suspicious()
                ObjectStore.set_object_from_json(bucket, suspect_key,
                                                 suspect_session.to_data())

            raise LoginError(
                "Cannot authorise the login as the one-time-code "
                "you supplied has already been used within the last 10 "
                "minutes. The chance of this happening is really low, so "
                "we are treating this as a suspicious event. You need to "
                "try another code. Meanwhile, the other login that used "
                "this code has been put into a 'suspicious' state.")

    # record the value and timestamp of when this otpcode was used
    otpkey = "%s/%s" % (otproot, login_session.uuid())
    otpstring = "%s|||%s" % (datetime.datetime.utcnow().timestamp(),
                             otpcode)

    ObjectStore.set_string_object(bucket, otpkey, otpstring)

    login_session.set_approved()

    # write this session back to the object store
    ObjectStore.set_object_from_json(bucket, login_session_key,
                                     login_session.to_data())

    # save the device secret as everything has now worked
    if assigned_device_uid:
        ObjectStore.set_string_object(bucket, device_key,
                                      device_secret)

    # finally, remove this from the list of requested logins
    try:
        ObjectStore.delete_object(bucket, request_session_key)
    except:
        pass

    status = 0
    message = "Success: Status = %s" % login_session.status()

    return_value = create_return_value(status, message)

    if provisioning_uri:
        return_value["provisioning_uri"] = provisioning_uri
        return_value["device_uid"] = assigned_device_uid

    return return_value
Beispiel #6
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
    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}
Beispiel #8
0
def run(args):
    """This function is called by the user to log in and validate
       that a session is authorised to connect

       Args:
        args (dict): contains identifying information about the user,
                     short_UID, username, password and OTP code
        Returns:
            dict: contains a URI and a UID for this login
    """
    short_uid = args["short_uid"]
    packed_credentials = args["credentials"]

    try:
        user_uid = args["user_uid"]
    except:
        user_uid = None

    try:
        remember_device = args["remember_device"]

        if remember_device:
            remember_device = True
        else:
            remember_device = False
    except:
        remember_device = False

    # get the session referred to by the short_uid
    sessions = LoginSession.load(short_uid=short_uid, status="pending")

    if isinstance(sessions, LoginSession):
        # we have many sessions to test...
        sessions = [sessions]

    result = None
    login_session = None
    last_error = None
    credentials = None

    for session in sessions:
        try:
            if credentials is None:
                credentials = Credentials.from_data(
                    data=packed_credentials,
                    username=session.username(),
                    short_uid=short_uid)
            else:
                credentials.assert_matching_username(session.username())

            result = UserAccount.login(credentials=credentials,
                                       user_uid=user_uid,
                                       remember_device=remember_device)
            login_session = session

            # success!
            break
        except Exception as e:
            last_error = e

    if result is None or login_session is None:
        # no valid logins
        raise last_error

    # we've successfully logged in
    login_session.set_approved(user_uid=result["user"].uid(),
                               device_uid=result["device_uid"])

    return_value = {}

    return_value["user_uid"] = login_session.user_uid()

    if remember_device:
        try:
            service = get_this_service(need_private_access=False)
            hostname = service.hostname()
            if hostname is None:
                hostname = "acquire"
            issuer = "%s@%s" % (service.service_type(), hostname)
            username = result["user"].name()
            device_uid = result["device_uid"]

            otp = result["otp"]
            provisioning_uri = otp.provisioning_uri(username=username,
                                                    issuer=issuer)

            return_value["provisioning_uri"] = provisioning_uri
            return_value["otpsecret"] = otp.secret()
            return_value["device_uid"] = device_uid
        except:
            pass

    return return_value
Beispiel #9
0
def run(args):
    """This function will allow anyone to obtain the public
       keys for the passed login session of a user with
       a specified login UID"""

    public_key = None
    public_cert = None
    login_status = None
    logout_timestamp = None

    session_uid = args["session_uid"]
    username = args["username"]

    # generate a sanitised version of the username
    user_account = UserAccount(username)

    # now log into the central identity account to query
    # the current status of this login session
    bucket = login_to_service_account()

    user_session_key = "sessions/%s/%s" % \
            (user_account.sanitised_name(), session_uid)

    try:
        login_session = LoginSession.from_data(
            ObjectStore.get_object_from_json(bucket, user_session_key))
    except:
        login_session = None

    if login_session is None:
        user_session_key = "expired_sessions/%s/%s" % \
                                (user_account.sanitised_name(),
                                 session_uid)

        login_session = LoginSession.from_data(
            ObjectStore.get_object_from_json(bucket, user_session_key))

    if login_session is None:
        raise InvalidSessionError("Cannot find the session '%s'" % session_uid)

    # only send valid keys if the user had logged in!
    if login_session.is_approved():
        public_key = login_session.public_key()
        public_cert = login_session.public_certificate()

    elif login_session.is_logged_out():
        public_cert = login_session.public_certificate()
        logout_timestamp = login_session.logout_time().timestamp()

    else:
        raise InvalidSessionError("You cannot get the keys for a session "
                                  "for which the user has not logged in!")

    login_status = login_session.status()

    status = 0
    message = "Success: Status = %s" % login_session.status()

    return_value = create_return_value(status, message)

    if public_key:
        return_value["public_key"] = public_key.to_data()

    if public_cert:
        return_value["public_cert"] = public_cert.to_data()

    if login_status:
        return_value["login_status"] = str(login_status)

    if logout_timestamp:
        return_value["logout_timestamp"] = logout_timestamp

    return return_value
Beispiel #10
0
def run(args):
    """This function will allow anyone to query who matches
       the passed UID or username (map from one to the other)"""

    status = 0
    message = None
    user_uid = None
    username = None
    public_key = None
    public_cert = None
    logout_timestamp = None
    login_status = None

    try:
        user_uid = args["user_uid"]
    except:
        pass

    try:
        username = args["username"]
    except:
        pass

    try:
        session_uid = args["session_uid"]
    except:
        session_uid = None

    bucket = None
    user_account = None

    if user_uid is None and username is None:
        raise WhoisLookupError(
            "You must supply either a username or user_uid to look up...")

    elif user_uid is None:
        # look up the user_uid from the username
        user_account = UserAccount(username)
        bucket = login_to_service_account()
        user_key = "accounts/%s" % user_account.sanitised_name()

        try:
            user_account = UserAccount.from_data(
                ObjectStore.get_object_from_json(bucket, user_key))
        except:
            raise WhoisLookupError("Cannot find an account for name '%s'" %
                                   username)

        user_uid = user_account.uid()

    elif username is None:
        # look up the username from the uuid
        bucket = login_to_service_account()

        uid_key = "whois/%s" % user_uid

        try:
            username = ObjectStore.get_string_object(bucket, uid_key)
        except:
            raise WhoisLookupError("Cannot find an account for user_uid '%s'" %
                                   user_uid)

    else:
        raise WhoisLookupError("You must only supply one of the username "
                               "or user_uid to look up - not both!")

    if session_uid:
        # now look up the public signing key for this session, if it is
        # a valid login session
        if user_account is None:
            user_account = UserAccount(username)

        user_session_key = "sessions/%s/%s" % \
            (user_account.sanitised_name(), session_uid)

        try:
            login_session = LoginSession.from_data(
                ObjectStore.get_object_from_json(bucket, user_session_key))
        except:
            login_session = None

        if login_session is None:
            user_session_key = "expired_sessions/%s/%s" % \
                                    (user_account.sanitised_name(),
                                     session_uid)

            login_session = LoginSession.from_data(
                ObjectStore.get_object_from_json(bucket, user_session_key))

        if login_session is None:
            raise InvalidSessionError("Cannot find the session '%s'" %
                                      session_uid)

        if login_session.is_approved():
            public_key = login_session.public_key()
            public_cert = login_session.public_certificate()

        elif login_session.is_logged_out():
            public_cert = login_session.public_certificate()
            logout_timestamp = login_session.logout_time().timestamp()

        else:
            raise InvalidSessionError("You cannot get the keys for a session "
                                      "for which the user has not logged in!")

        login_status = login_session.status()

    status = 0
    message = "Success"

    return_value = create_return_value(status, message)

    if user_uid:
        return_value["user_uid"] = str(user_uid)

    if username:
        return_value["username"] = str(username)

    if public_key:
        return_value["public_key"] = public_key.to_data()

    if public_cert:
        return_value["public_cert"] = public_cert.to_data()

    if logout_timestamp:
        return_value["logout_timestamp"] = logout_timestamp

    if login_status:
        return_value["login_status"] = str(login_status)

    return return_value
Beispiel #11
0
def run(args):
    """This function will allow a user to request a new session
       that will be validated by the passed public key and public
       signing certificate. This will return a URL that the user
       must connect to to then log in and validate that request.
    """

    status = 0
    message = None
    login_url = None
    login_uid = None
    user_uid = None

    username = args["username"]
    public_key = PublicKey.from_data(args["public_key"])
    public_cert = PublicKey.from_data(args["public_certificate"])

    ip_addr = None
    hostname = None
    login_message = None

    try:
        ip_addr = args["ipaddr"]
    except:
        pass

    try:
        hostname = args["hostname"]
    except:
        pass

    try:
        login_message = args["message"]
    except:
        pass

    # generate a sanitised version of the username
    user_account = UserAccount(username)

    # Now generate a login session for this request
    login_session = LoginSession(public_key, public_cert, ip_addr, hostname,
                                 login_message)

    # now log into the central identity account to record
    # that a request to open a login session has been opened
    bucket = login_to_service_account()

    # first, make sure that the user exists...
    account_key = "accounts/%s" % user_account.sanitised_name()

    try:
        existing_data = ObjectStore.get_object_from_json(bucket, account_key)
    except:
        existing_data = None

    if existing_data is None:
        raise InvalidLoginError("There is no user with name '%s'" % username)

    user_account = UserAccount.from_data(existing_data)
    user_uid = user_account.uid()

    # first, make sure that the user doens't have too many open
    # login sessions at once - this prevents denial of service
    user_session_root = "sessions/%s" % user_account.sanitised_name()

    open_sessions = ObjectStore.get_all_object_names(bucket, user_session_root)

    # take the opportunity to prune old user login sessions
    prune_expired_sessions(bucket, user_account, user_session_root,
                           open_sessions)

    # this is the key for the session in the object store
    user_session_key = "%s/%s" % (user_session_root, login_session.uuid())

    ObjectStore.set_object_from_json(bucket, user_session_key,
                                     login_session.to_data())

    # we will record a pointer to the request using the short
    # UUID. This way we can give a simple URL. If there is a clash,
    # then we will use the username provided at login to find the
    # correct request from a much smaller pool (likely < 3)
    request_key = "requests/%s/%s" % (login_session.short_uuid(),
                                      login_session.uuid())

    ObjectStore.set_string_object(bucket, request_key, user_account.name())

    status = 0
    # the login URL is the URL of this identity service plus the
    # short UID of the session
    login_url = "%s/s?id=%s" % (get_service_info().service_url(),
                                login_session.short_uuid())

    login_uid = login_session.uuid()

    message = "Success: Login via %s" % login_url

    return_value = create_return_value(status, message)

    if login_uid:
        return_value["session_uid"] = login_uid

    if login_url:
        return_value["login_url"] = login_url
    else:
        return_value["login_url"] = None

    if user_uid:
        return_value["user_uid"] = user_uid

    return return_value