from yaml import load, SafeLoader
from os import environ
from pytest import raises
from ldap import NO_SUCH_OBJECT, INVALID_CREDENTIALS, OPT_X_TLS_NEVER, OPT_X_TLS_REQUIRE_CERT, SCOPE_SUBTREE, set_option, initialize


def test_ldap_wrong_password():
    conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml'
    with open(conf_file) as yaml:
        data = load(yaml, Loader=SafeLoader)
    set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER)
    l = initialize(f'ldaps://{data["address"]}')
    with raises(INVALID_CREDENTIALS):
        l.simple_bind_s(data['admin_dn'], 'a')


def test_ldap_admin():
    conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml'
    with open(conf_file) as yaml:
        data = load(yaml, Loader=SafeLoader)
    set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER)
    l = initialize(f'ldaps://{data["address"]}')
    l.simple_bind_s(data['admin_dn'], data['admin_password'])

    assert l.search_s(data['base_account_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn'])
    

def test_ldap_accounts():
    conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml'
    with open(conf_file) as yaml:
        data = load(yaml, Loader=SafeLoader)
    set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER)
    l = initialize(f'ldaps://{data["address"]}')
    l.simple_bind_s(data['admin_dn'], data['admin_password'])

    for dn, attrs in l.search_s(data['base_account_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn']):
        cn = attrs['cn'][0].decode()
        assert cn in data['users']
        assert data['users'][cn] == dn
        del data['users'][cn]

    # all users are retrieved
    assert not data['users']


def test_ldap_groups():
    conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml'
    with open(conf_file) as yaml:
        data = load(yaml, Loader=SafeLoader)
    set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER)
    l = initialize(f'ldaps://{data["address"]}')
    l.simple_bind_s(data['admin_dn'], data['admin_password'])

    for dn, attrs in l.search_s(data['base_group_dn'], SCOPE_SUBTREE,'(objectClass=groupOfNames)',['cn', 'member']):
        cn = attrs['cn'][0].decode()
        assert cn in data['groups']
        assert set(data['groups'][cn]) == set([member.decode() for member in attrs['member']])
        del data['groups'][cn]

    # all groups are retrieved
    assert not data['groups']


def test_ldap_user():
    conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml'
    with open(conf_file) as yaml:
        data = load(yaml, Loader=SafeLoader)
    set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER)
    l = initialize(f'ldaps://{data["address"]}')
    l.simple_bind_s(data['user_dn'], data['user_password'])


def test_ldap_migration():
    conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml'
    with open(conf_file) as yaml:
        data = load(yaml, Loader=SafeLoader)
    set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER)
    l = initialize(f'ldaps://{data["address"]}')
    if 'FIRST_RUN' in environ:
        l.simple_bind_s(data['admin_dn'], data['admin_password'])
        l.passwd_s(data['user_family_dn'], data['user_family_password'], data['user_family_password'] + "2")
    try:
        l.simple_bind_s(data['user_family_dn'], data['user_family_password'] + "2")
    except INVALID_CREDENTIALS as err:
        raise Exception(f'cannot find {data["user_family_dn"]} do you run script with FIRST_RUN env variables?')


def test_ldap_remote_auth():
    conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml'
    with open(conf_file) as yaml:
        data = load(yaml, Loader=SafeLoader)
    set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER)
    l = initialize(f'ldaps://{data["address"]}')
    l.simple_bind_s(data['remote0'], data['remote_password0'])
    l.simple_bind_s(data['remote1'], data['remote_password1'])
    l.simple_bind_s(data['remote2'], data['remote_password2'])


def test_ldap_remote_base():
    conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml'
    with open(conf_file) as yaml:
        data = load(yaml, Loader=SafeLoader)
    set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER)
    l = initialize(f'ldaps://{data["address"]}')
    #
    l.simple_bind_s(data['remote0'], data['remote_password0'])
    with raises(NO_SUCH_OBJECT):
        l.search_s(data['base_account_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn'])
    #
    l.simple_bind_s(data['remote1'], data['remote_password1'])
    l.search_s(data['base_account_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn'])
    #
    l.simple_bind_s(data['remote2'], data['remote_password2'])
    with raises(NO_SUCH_OBJECT):
        l.search_s(data['base_account_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn'])


def test_ldap_remote_users():
    conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml'
    with open(conf_file) as yaml:
        data = load(yaml, Loader=SafeLoader)
    set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER)
    l = initialize(f'ldaps://{data["address"]}')
    #
    l.simple_bind_s(data['remote0'], data['remote_password0'])
    l.search_s(data['base_user_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn'])
    #
    l.simple_bind_s(data['remote1'], data['remote_password1'])
    l.search_s(data['base_user_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn'])
    #
    l.simple_bind_s(data['remote2'], data['remote_password2'])
    with raises(NO_SUCH_OBJECT):
        l.search_s(data['base_user_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn'])


def test_ldap_remote_family():
    conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml'
    with open(conf_file) as yaml:
        data = load(yaml, Loader=SafeLoader)
    set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER)
    l = initialize(f'ldaps://{data["address"]}')
    #
    l.simple_bind_s(data['remote0'], data['remote_password0'])
    with raises(NO_SUCH_OBJECT):
        l.search_s(data['base_family_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn'])
    #
    l.simple_bind_s(data['remote1'], data['remote_password1'])
    l.search_s(data['base_family_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn'])
    #
    l.simple_bind_s(data['remote2'], data['remote_password2'])
    l.search_s(data['base_family_dn'], SCOPE_SUBTREE,'(objectClass=inetOrgPerson)',['cn'])


def test_ldap_remote_group():
    conf_file = f'{environ["MACHINE_TEST_DIR"]}/openldap.yml'
    with open(conf_file) as yaml:
        data = load(yaml, Loader=SafeLoader)
    set_option(OPT_X_TLS_REQUIRE_CERT, OPT_X_TLS_NEVER)
    l = initialize(f'ldaps://{data["address"]}')
    #
    l.simple_bind_s(data['remote0'], data['remote_password0'])
    l.search_s(data['base_group_dn'], SCOPE_SUBTREE,'(objectClass=groupOfNames)',['cn'])
    #
    l.simple_bind_s(data['remote1'], data['remote_password1'])
    l.search_s(data['base_group_dn'], SCOPE_SUBTREE,'(objectClass=groupOfNames)',['cn'])
    #
    l.simple_bind_s(data['remote2'], data['remote_password2'])
    l.search_s(data['base_group_dn'], SCOPE_SUBTREE,'(objectClass=groupOfNames)',['cn'])
