# --- BEGIN COPYRIGHT BLOCK ---
# Copyright (C) 2022 Red Hat, Inc.
# All rights reserved.
#
# License: GPL (version 3 or any later version).
# See LICENSE for details.
# --- END COPYRIGHT BLOCK ---
#
import pytest
import glob
import base64
import re
from lib389.tasks import *
from lib389.rewriters import *
from lib389.idm.user import UserAccounts
from lib389.utils import *
from lib389.topologies import topology_st

from lib389._constants import DEFAULT_SUFFIX, HOST_STANDALONE, PORT_STANDALONE

samba_missing = False
try:
    from samba.dcerpc import security
    from samba.ndr import ndr_pack, ndr_unpack
except:
    samba_missing = True
    pass

log = logging.getLogger(__name__)
# Skip on versions 1.4.2 and before. Rewriters are expected in 1.4.3
pytestmark = [pytest.mark.tier2,
              pytest.mark.skipif(ds_is_older('1.4.3'), reason="Not implemented")]

PW = 'password'

#
# Necessary because objectcategory relies on cn=xxx RDN
# while userAccount creates uid=xxx RDN
#
def _create_user(inst, schema_container, name, salt):
    dn = 'cn=%s,%s' % (name, schema_container)
    inst.add_s(Entry((
        dn, {
            'objectClass': 'top person extensibleobject'.split(),
            'cn': name,
            'sn': name,
            'objectcategory': dn,
            "description" : salt,
            'userpassword': PW
        })))


def test_adfilter_objectCategory(topology_st):
    """Test adfilter objectCategory rewriter function

    :id: 9c6493c9-1ee0-4bc2-b5db-7253a04fb6f8
    :setup: Standalone instance
    :steps:
         1. Add a objectsid rewriter (from librewriters.so)
         2. Add a dummy schema definition of objectsid to prevent nsslapd-verify-filter-schema
         3. Restart the server (to load the rewriter)
         4. Check EQUALITY filter rewrite
         5. Check SUBSTRING search is not replaced
         6. Check PRESENCE search is not replaced so it selects all entries having objectCategory
    :expectedresults:
         1. Add operation should PASS.
         2. Add operations should PASS.
         3. Restart should PASS
         4. Search returns only one entry
         5. Search returns zero entries
         6. Search returns all (20) entries
    """

    librewriters = os.path.join( topology_st.standalone.ds_paths.lib_dir, 'dirsrv/librewriters.so')
    assert librewriters

    rewriters = AdRewriters(topology_st.standalone)
    ad_rewriter = rewriters.ensure_state(properties={"cn": "adfilter", "nsslapd-libpath": librewriters})
    ad_rewriter.add('nsslapd-filterrewriter', "adfilter_rewrite_objectCategory")
    ad_rewriter.create_containers(DEFAULT_SUFFIX)
    schema_container = ad_rewriter.get_schema_dn()

    objectcategory_attr = '( NAME \'objectCategory\' DESC \'test of objectCategory\' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )'
    topology_st.standalone.schema.add_schema('attributetypes', [ensure_bytes(objectcategory_attr)])

    topology_st.standalone.restart(60)

    # Add a user
    for i in range(0, 20):
        _create_user(topology_st.standalone, schema_container, "user_%d" % i, str(i))

    # Check EQUALITY filter rewrite => it should match only one entry
    for i in range(0, 20):
        ents = topology_st.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, '(objectCategory=user_%d)' % i)
        assert len(ents) == 1

    # Check SUBSTRING search is not replaced
    ents = topology_st.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, '(objectCategory=user_*)')
    assert len(ents) == 0

    # Check PRESENCE search is not replaced so it selects all entries having objectCategory
    ents = topology_st.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, '(objectCategory=*)')
    assert len(ents) == 20

    log.info('Test PASSED')

def sid_to_objectsid(sid):
    return base64.b64encode(ndr_pack(security.dom_sid(sid))).decode('utf-8')

def objectsid_to_sid(objectsid):
    sid = ndr_unpack(security.dom_sid, base64.b64decode(objectsid))
    return str(sid)

@pytest.mark.skipif(samba_missing, reason="It is missing samba python bindings")
def test_adfilter_objectSid(topology_st):
    """Test adfilter objectCategory rewriter function with samba container/users

    :id: fc5880ff-4305-47ba-84fb-38429e264e9e
    :setup: Standalone instance
    :steps:
         1. Add a objectsid rewriter (from librewriters.so)
         2. Add a dummy schema definition of objectsid to prevent nsslapd-verify-filter-schema
         3. Restart the server (to load the rewriter)
         4. Add "samba" container/users
         5. Searches using objectsid in string format
    :expectedresults:
         1. Add operation should PASS.
         2. Add operations should PASS.
         3. Restart should PASS
         4. Add "samba" users should PASS
         5. Search returns only one entry
    """
    librewriters = os.path.join( topology_st.standalone.ds_paths.lib_dir, 'dirsrv/librewriters.so')
    assert librewriters

    rewriters = AdRewriters(topology_st.standalone)
    ad_rewriter = rewriters.ensure_state(properties={"cn": "adfilter", "nsslapd-libpath": librewriters})
    ad_rewriter.add('nsslapd-filterrewriter', "adfilter_rewrite_objectsid")
    ad_rewriter.create_containers(DEFAULT_SUFFIX)
    schema_container = ad_rewriter.get_schema_dn()

    # to prevent nsslapd-verify-filter-schema to reject searches with objectsid
    objectcategory_attr = '( NAME \'objectsid\' DESC \'test of objectsid\' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )'
    topology_st.standalone.schema.add_schema('attributetypes', [ensure_bytes(objectcategory_attr)])

    topology_st.standalone.restart()

    # Contains a list of b64encoded SID from https://github.com/SSSD/sssd/blob/supplier/src/tests/intg/data/ad_data.ldif
    SIDs = ["AQUAAAAAAAUVAAAADcfLTVzC66zo0l8EUAQAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8E9gEAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8EAwIAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8EBAIAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8EBgIAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8EBwIAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8EBQIAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8EAAIAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8EAQIAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8EAgIAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8ECAIAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8EKQIAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8EOwIAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8EPAIAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8ECQIAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8E8gEAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8ETQQAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8ETgQAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8EeUMBAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8EekMBAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8Ee0MBAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8EfEMBAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8ETwQAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8EUQQAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8ESUMBAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8ESkMBAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8ES0MBAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8ETEMBAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8E9AEAAA==",
            "AQUAAAAAAAUVAAAADcfLTVzC66zo0l8E9QEAAA=="]

    # Add a container and "samba" like users containing objectsid
    users = UserAccounts(topology_st.standalone, schema_container, rdn=None)
    i = 0
    for sid in SIDs:
        decoded = base64.b64decode(sid)
        user = users.create_test_user(uid=i)
        user.add('objectclass', 'extensibleobject')
        user.replace('objectsid', decoded)
        user.replace('objectSidString', objectsid_to_sid(sid))
        i = i + 1

    # Check that objectsid rewrite can retrieve the "samba" user
    # using either a string objectsid (i.e. S-1-5...) or a blob objectsid
    for sid_blob in SIDs:
        sid_string = objectsid_to_sid(sid_blob)
        ents_sid_string = topology_st.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, '(objectsid=%s)' % sid_string)
        assert len(ents_sid_string) == 1

