from ldap3 import Server, Connection, ALL
from tornado.web import create_signed_value, decode_signed_value

import pywebio
from pywebio.input import *
from pywebio.output import *
from pywebio.session import *

LDAP_AUTH_URL = "ldap://localhost:389"
LDAP_AUTH_SEARCH_BASE = "dc=example,dc=org"

SECRET = "encryption salt value"


def ldap_authentication(user_name, user_pwd, auth_url, search_base):
    ldap_user_name = user_name.strip()
    ldap_user_pwd = user_pwd.strip()
    user = f'cn={ldap_user_name},{search_base}'
    server = Server(auth_url, get_info=ALL)
    connection = Connection(server, user=user, password=ldap_user_pwd)
    return connection.bind()


def who():
    user = input_group('Login', [
        input("Username", name='username'),
        input("Password", type="password", name='password'),
    ])
    if ldap_authentication(user['username'], user['password'],
                           auth_url=LDAP_AUTH_URL, search_base=LDAP_AUTH_SEARCH_BASE):
        return user['username']


def persistent_auth(who, salt, expire_days=7):
    """Authenticate users and remember them for {expire_days} days.

    :param callback who: the function to auth user, it should return user name when auth succeed.
    :param str salt: salt value for encryption
    :param int expire_days:
    :return: user name
    """
    # get token from user's web browser
    token = eval_js("localStorage.getItem(key)", key='token')
    # try to decrypt the username from the token, and ignore tokens created more than {expire_days} days ago
    username = decode_signed_value(salt, 'token', token, max_age_days=expire_days).decode("utf-8")
    if not token or not username:  # no token or token validation failed
        while not username:
            username = who()
            if not username:
                toast('Auth failed, try again.', color='error')

        signed = create_signed_value(salt, 'token', username).decode("utf-8")  # encrypt username to token
        run_js("localStorage.setItem(key, value)", key='token', value=signed)  # set token to user's web browser
    return username


def main():
    user = persistent_auth(who, salt=SECRET)

    put_text('Welcome,', user)


if __name__ == '__main__':
    pywebio.start_server(main, port=8080, debug=True)