# 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
should be used whenever possible.

See [here](http://localhost:5000/docs#section/Authentication-and-Authorization)
for more information on which way to aquire a token is appropriate for you and
how to send it back to the API.

<br />

## LDAP Login

The API can act as a proxy to the ETH LDAP. If the n.ethz shortname and
password are sent in a `POST` request, the API will forward them to the ETH
LDAP and a session will be created if the LDAP login was successful.

> A successful LDAP login will also synchronize the user in the API with the
> ETH LDAP.

<br />

## API Login

If the user has set a password with the API, login is also possible using this
password instead of loging in via LDAP.

In this case, any field that uniquely identifies a user can be sent as
`username`, i.e. `nethz`, `email` or `_id`.

> The API will first attempt to log in a user via LDAP. Only if this fails,
> it will check the API password.


# 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

should be used whenever possible.

See [here](http://localhost:5000/docs#section/Authentication-and-Authorization)
for more information on which way to aquire a token is appropriate for you and
how to send it back to the API.

<br />

## LDAP Login

The API can act as a proxy to the ETH LDAP. If the n.ethz shortname and
password are sent in a `POST` request, the API will forward them to the ETH
LDAP and a session will be created if the LDAP login was successful.

> A successful LDAP login will also synchronize the user in the API with the
> ETH LDAP.

<br />

## API Login

If the user has set a password with the API, login is also possible using this
password instead of loging in via LDAP.

In this case, any field that uniquely identifies a user can be sent as
`username`, i.e. `nethz`, `email` or `_id`.

> The API will first attempt to log in a user via LDAP. Only if this fails,
> it will check the API password.


<br />