# Path: amivapi/ldap.py
# def init_app(app):
# def authenticate_user(cn, password):
# def sync_one(cn):
# def sync_all():
# def _search(query):
# def _escape(query):
# def _process_data(data):
# def _create_or_update_user(ldap_data):
# 
# Path: amivapi/auth/auth.py
# class AmivTokenAuth(BasicAuth):
#     """Amiv authentication and authorization base class.
# 
#     Subclass and overwrite functions if you don't want default behaviour.
#     """
# 
#     def authorized(self, allowed_roles, resource, method):
#         """Authorize Request.
# 
#         This is the method Eve will call if the endpoint is not public.
# 
#         We use this by setting `g.auth_required` to inform auth hook to abort
#         later if user can't be identified.
# 
#         Do NOT overwrite this when subclassing `AmivTokenAuth`.
#         """
#         g.auth_required = True
#         return True
# 
#     def has_resource_write_permission(self, user_id):
#         """Check if the user is alllowed to write to the resource.
# 
#         Implement this function for your resource.
#         Default behaviour: No user has write permission.
# 
#         Args:
#             user_id (str): The if of the user
# 
#         Returns:
#             bool: True if user has permission to write, False otherwise.
#         """
#         return False
# 
#     def has_item_write_permission(self, user_id, item):
#         """Check if the user is allowed to modify the item.
# 
#         Implement this function for your resource.
#         Default behaviour: No user has write permission.
# 
#         Args:
#             user (str): The id of the user that wants to access the item
#             item (dict): The item the user wants to change or delete.
#                 Attention! If they are any ObjectIds in here, Eve will not have
#                 converted them yet, so be sure to cast them to str if you want
#                 to compare them to e.g. g.current_user
# 
#         Returns:
#             bool: True if user has permission to change the item, False if not.
#         """
#         return False
# 
#     def create_user_lookup_filter(self, user_id):
#         """Create a filter for item lookup in GET, PATCH and DELETE.
# 
#         Implement this function for your resource.
#         Default behaviour: No lookup filter.
# 
#         Args:
#             user_id (str): The id of the user
# 
#         Returns:
#             dict: The filter, will be combined with other filters in the hook.
#                 Return empty dict if no filters should be applied.
#                 Return None if no lookup should be possible for the user.
#         """
#         return {}
# 
# Path: amivapi/cron.py
# def periodic(period, *args):
#     """ Decorator to mark a function to be executed periodically.
#     Args:
#         period: timedelta object describing the time between two calls
#         args: arguments to be passed every time
#     """
#     def wrap(func):
#         @wraps(func)
#         def wrapped():
#             schedule_task(datetime.utcnow() + period, wrapped)
#             func(*args)
# 
#         schedulable(wrapped)
# 
#         # if init_app has already run, schedule the first execution
#         if current_app:
#             schedule_once_soon(wrapped)
#         # As this decorator is run very early, there might not be an app yet.
#         # Therefore we save the functions to a list to be scheduled on app init.
#         periodic_functions.append(wrapped)
# 
#         return wrapped
#     return wrap
# 
# Path: amivapi/utils.py
# @contextmanager
# def admin_permissions():
#     """Switch to a context with admin rights and restore state afterwards.
# 
#     Use as context:
#     >> with admin_rights():
#     >>     do_something()
#     """
#     old_admin = g.get('resource_admin')
#     g.resource_admin = True
# 
#     yield
# 
#     if old_admin is not None:  # None means it wasn't set before..
#         g.resource_admin = old_admin
# 
# def get_id(item):
#     """Get the id of a field in a relation. Depending on the embedding clause
#     a field returned by a mongo query may be an ID or an object. This function
#     will get the ID in both cases.
# 
#     Args:
#         item: Either an object from the database as a dict or an object id as
#             str or objectid.
# 
#     Returns:
#         ObjectId with the user ID
#     """
#     try:
#         return ObjectId(item)
#     except TypeError:
#         return ObjectId(item['_id'])


import datetime
from amivapi import ldap
from amivapi.auth import AmivTokenAuth
from amivapi.cron import periodic
from amivapi.utils import admin_permissions, get_id
from bson import ObjectId
from bson.errors import InvalidId
from eve.methods.patch import patch_internal
from eve.utils import debug_error_message
from flask import abort
from flask import current_app as app
from ldap3.core.exceptions import LDAPException
    from secrets import token_urlsafe
    from amivapi.utils import token_urlsafe
        },
    }
}


# Login Hook

def process_login(items):
    """Hook to add token on POST to /sessions.

    Attempts to first login via LDAP (if enabled), then login via database.

    If the login is successful, the fields "username" and "password" are
    removed and the fields "user" and "token" are added, which will be stored
    in the db.

    If the login is unsuccessful, abort(401)

    Args:
        items (list): List of items as passed by EVE to post hooks.
    """
    for item in items:
        username = item['username']
        password = item['password']

        # LDAP
        if (app.config.get('ldap_connector') and
                ldap.authenticate_user(username, password)):
            # Success, sync user and get token
            try:

# Path: amivapi/ldap.py
# def init_app(app):
# def authenticate_user(cn, password):
# def sync_one(cn):
# def sync_all():
# def _search(query):
# def _escape(query):
# def _process_data(data):
# def _create_or_update_user(ldap_data):
# 
# Path: amivapi/auth/auth.py
# class AmivTokenAuth(BasicAuth):
#     """Amiv authentication and authorization base class.
# 
#     Subclass and overwrite functions if you don't want default behaviour.
#     """
# 
#     def authorized(self, allowed_roles, resource, method):
#         """Authorize Request.
# 
#         This is the method Eve will call if the endpoint is not public.
# 
#         We use this by setting `g.auth_required` to inform auth hook to abort
#         later if user can't be identified.
# 
#         Do NOT overwrite this when subclassing `AmivTokenAuth`.
#         """
#         g.auth_required = True
#         return True
# 
#     def has_resource_write_permission(self, user_id):
#         """Check if the user is alllowed to write to the resource.
# 
#         Implement this function for your resource.
#         Default behaviour: No user has write permission.
# 
#         Args:
#             user_id (str): The if of the user
# 
#         Returns:
#             bool: True if user has permission to write, False otherwise.
#         """
#         return False
# 
#     def has_item_write_permission(self, user_id, item):
#         """Check if the user is allowed to modify the item.
# 
#         Implement this function for your resource.
#         Default behaviour: No user has write permission.
# 
#         Args:
#             user (str): The id of the user that wants to access the item
#             item (dict): The item the user wants to change or delete.
#                 Attention! If they are any ObjectIds in here, Eve will not have
#                 converted them yet, so be sure to cast them to str if you want
#                 to compare them to e.g. g.current_user
# 
#         Returns:
#             bool: True if user has permission to change the item, False if not.
#         """
#         return False
# 
#     def create_user_lookup_filter(self, user_id):
#         """Create a filter for item lookup in GET, PATCH and DELETE.
# 
#         Implement this function for your resource.
#         Default behaviour: No lookup filter.
# 
#         Args:
#             user_id (str): The id of the user
# 
#         Returns:
#             dict: The filter, will be combined with other filters in the hook.
#                 Return empty dict if no filters should be applied.
#                 Return None if no lookup should be possible for the user.
#         """
#         return {}
# 
# Path: amivapi/cron.py
# def periodic(period, *args):
#     """ Decorator to mark a function to be executed periodically.
#     Args:
#         period: timedelta object describing the time between two calls
#         args: arguments to be passed every time
#     """
#     def wrap(func):
#         @wraps(func)
#         def wrapped():
#             schedule_task(datetime.utcnow() + period, wrapped)
#             func(*args)
# 
#         schedulable(wrapped)
# 
#         # if init_app has already run, schedule the first execution
#         if current_app:
#             schedule_once_soon(wrapped)
#         # As this decorator is run very early, there might not be an app yet.
#         # Therefore we save the functions to a list to be scheduled on app init.
#         periodic_functions.append(wrapped)
# 
#         return wrapped
#     return wrap
# 
# Path: amivapi/utils.py
# @contextmanager
# def admin_permissions():
#     """Switch to a context with admin rights and restore state afterwards.
# 
#     Use as context:
#     >> with admin_rights():
#     >>     do_something()
#     """
#     old_admin = g.get('resource_admin')
#     g.resource_admin = True
# 
#     yield
# 
#     if old_admin is not None:  # None means it wasn't set before..
#         g.resource_admin = old_admin
# 
# def get_id(item):
#     """Get the id of a field in a relation. Depending on the embedding clause
#     a field returned by a mongo query may be an ID or an object. This function
#     will get the ID in both cases.
# 
#     Args:
#         item: Either an object from the database as a dict or an object id as
#             str or objectid.
# 
#     Returns:
#         ObjectId with the user ID
#     """
#     try:
#         return ObjectId(item)
#     except TypeError:
#         return ObjectId(item['_id'])


# Path: amivapi/auth/sessions.py
import datetime
from amivapi import ldap
from amivapi.auth import AmivTokenAuth
from amivapi.cron import periodic
from amivapi.utils import admin_permissions, get_id
from bson import ObjectId
from bson.errors import InvalidId
from eve.methods.patch import patch_internal
from eve.utils import debug_error_message
from flask import abort
from flask import current_app as app
from ldap3.core.exceptions import LDAPException
    from secrets import token_urlsafe
    from amivapi.utils import token_urlsafe

        },
    }
}


# Login Hook

def process_login(items):
    """Hook to add token on POST to /sessions.

    Attempts to first login via LDAP (if enabled), then login via database.

    If the login is successful, the fields "username" and "password" are
    removed and the fields "user" and "token" are added, which will be stored
    in the db.

    If the login is unsuccessful, abort(401)

    Args:
        items (list): List of items as passed by EVE to post hooks.
    """
    for item in items:
        username = item['username']
        password = item['password']

        # LDAP
        if (app.config.get('ldap_connector') and
                ldap.authenticate_user(username, password)):
            # Success, sync user and get token
            try:

                user = ldap.sync_one(username)