# Path: invenio_oauthclient/errors.py
# class OAuthCERNRejectedAccountError(OAuthResponseError):
#     """Define exception for not allowed cern accounts."""
# 
# Path: invenio_oauthclient/handlers/rest.py
# def response_handler(remote, url, payload=None):
#     """Handle oauthclient rest response."""
#     if not remote:
#         return current_oauthclient.default_response_handler(url, payload)
#     return current_oauthclient.remote_app_response_handler[remote.name](
#         remote, url, payload)
# 
# Path: invenio_oauthclient/handlers/utils.py
# def require_more_than_one_external_account(f):
#     """Require that the user has more than one external account for login.
# 
#     If the user only has one linked external account and no means for logging
#     in via local credentials, the decorated function won't be executed.
#     This decorator is useful for disconnect handlers, to prevent users from
#     disconnecting their last potential means of authentication.
#     """
#     @wraps(f)
#     def decorated(*args, **kwargs):
#         if current_user.is_anonymous:
#             return f(*args, **kwargs)
# 
#         local_login_enabled = current_app.config.get(
#             "ACCOUNTS_LOCAL_LOGIN_ENABLED", True
#         )
#         password_set = current_user.password is not None
#         local_login_possible = local_login_enabled and password_set
# 
#         remote_apps = current_app.config["OAUTHCLIENT_REMOTE_APPS"]
#         accounts = RemoteAccount.query.filter_by(
#             user_id=current_user.get_id()
#         ).all()
# 
#         # find out all of the linked external accounts for the user
#         # that are currently configured and not hidden
#         consumer_keys = set()
#         remote_apps = current_app.config["OAUTHCLIENT_REMOTE_APPS"]
#         for name, remote_app in remote_apps.items():
#             if not remote_app.get("hide", False):
#                 consumer_keys.add(name)  # backcompat with v1.5.4
#                 remote_app_config = current_app.config[
#                     remote_app["params"]["app_key"]
#                 ]
#                 consumer_keys.add(remote_app_config["consumer_key"])
# 
#         linked_accounts = [
#             acc for acc in accounts if acc.client_id in consumer_keys]
# 
#         # execute the function only if local login is possible, or
#         # there's more than one linked external account
#         if local_login_possible or len(linked_accounts) > 1:
#             return f(*args, **kwargs)
# 
#         else:
#             abort(400)
# 
#     return decorated
# 
# Path: invenio_oauthclient/utils.py
# def oauth_link_external_id(user, external_id=None):
#     """Link a user to an external id.
# 
#     :param user: A :class:`invenio_accounts.models.User` instance.
#     :param external_id: The external id associated with the user.
#         (Default: ``None``)
#     :raises invenio_oauthclient.errors.AlreadyLinkedError: Raised if already
#         exists a link.
#     """
#     try:
#         with db.session.begin_nested():
#             db.session.add(UserIdentity(
#                 id=external_id['id'],
#                 method=external_id['method'],
#                 id_user=user.id
#             ))
#     except IntegrityError:
#         raise AlreadyLinkedError(user, external_id)
# 
# def oauth_unlink_external_id(external_id):
#     """Unlink a user from an external id.
# 
#     :param external_id: The external id associated with the user.
#     """
#     with db.session.begin_nested():
#         UserIdentity.query.filter_by(id=external_id['id'],
#                                      method=external_id['method']).delete()


from datetime import datetime, timedelta
from flask import Blueprint, current_app, flash, g, redirect, session, url_for
from flask_babelex import gettext as _
from flask_login import current_user
from flask_principal import AnonymousIdentity, RoleNeed, UserNeed, \
    identity_changed, identity_loaded
from invenio_db import db
from jwt import decode
from invenio_oauthclient.errors import OAuthCERNRejectedAccountError
from invenio_oauthclient.handlers.rest import response_handler
from invenio_oauthclient.handlers.utils import \
    require_more_than_one_external_account
from invenio_oauthclient.models import RemoteAccount
from invenio_oauthclient.proxies import current_oauthclient
from invenio_oauthclient.utils import oauth_link_external_id, \
    oauth_unlink_external_id
    """Handle unlinking of remote account."""
    if not current_user.is_authenticated:
        return current_app.login_manager.unauthorized()

    account = RemoteAccount.get(
        user_id=current_user.get_id(), client_id=remote.consumer_key
    )
    external_id = account.extra_data.get("external_id")

    if external_id:
        oauth_unlink_external_id(dict(id=external_id, method="cern_openid"))
    if account:
        with db.session.begin_nested():
            account.delete()

    disconnect_identity(g.identity)


def disconnect_handler(remote, *args, **kwargs):
    """Handle unlinking of remote account."""
    _disconnect(remote, *args, **kwargs)
    return redirect(url_for("invenio_oauthclient_settings.index"))


def disconnect_rest_handler(remote, *args, **kwargs):
    """Handle unlinking of remote account."""
    _disconnect(remote, *args, **kwargs)
    redirect_url = current_app.config["OAUTHCLIENT_REST_REMOTE_APPS"][
        remote.name
    ]["disconnect_redirect_url"]

# Path: invenio_oauthclient/errors.py
# class OAuthCERNRejectedAccountError(OAuthResponseError):
#     """Define exception for not allowed cern accounts."""
# 
# Path: invenio_oauthclient/handlers/rest.py
# def response_handler(remote, url, payload=None):
#     """Handle oauthclient rest response."""
#     if not remote:
#         return current_oauthclient.default_response_handler(url, payload)
#     return current_oauthclient.remote_app_response_handler[remote.name](
#         remote, url, payload)
# 
# Path: invenio_oauthclient/handlers/utils.py
# def require_more_than_one_external_account(f):
#     """Require that the user has more than one external account for login.
# 
#     If the user only has one linked external account and no means for logging
#     in via local credentials, the decorated function won't be executed.
#     This decorator is useful for disconnect handlers, to prevent users from
#     disconnecting their last potential means of authentication.
#     """
#     @wraps(f)
#     def decorated(*args, **kwargs):
#         if current_user.is_anonymous:
#             return f(*args, **kwargs)
# 
#         local_login_enabled = current_app.config.get(
#             "ACCOUNTS_LOCAL_LOGIN_ENABLED", True
#         )
#         password_set = current_user.password is not None
#         local_login_possible = local_login_enabled and password_set
# 
#         remote_apps = current_app.config["OAUTHCLIENT_REMOTE_APPS"]
#         accounts = RemoteAccount.query.filter_by(
#             user_id=current_user.get_id()
#         ).all()
# 
#         # find out all of the linked external accounts for the user
#         # that are currently configured and not hidden
#         consumer_keys = set()
#         remote_apps = current_app.config["OAUTHCLIENT_REMOTE_APPS"]
#         for name, remote_app in remote_apps.items():
#             if not remote_app.get("hide", False):
#                 consumer_keys.add(name)  # backcompat with v1.5.4
#                 remote_app_config = current_app.config[
#                     remote_app["params"]["app_key"]
#                 ]
#                 consumer_keys.add(remote_app_config["consumer_key"])
# 
#         linked_accounts = [
#             acc for acc in accounts if acc.client_id in consumer_keys]
# 
#         # execute the function only if local login is possible, or
#         # there's more than one linked external account
#         if local_login_possible or len(linked_accounts) > 1:
#             return f(*args, **kwargs)
# 
#         else:
#             abort(400)
# 
#     return decorated
# 
# Path: invenio_oauthclient/utils.py
# def oauth_link_external_id(user, external_id=None):
#     """Link a user to an external id.
# 
#     :param user: A :class:`invenio_accounts.models.User` instance.
#     :param external_id: The external id associated with the user.
#         (Default: ``None``)
#     :raises invenio_oauthclient.errors.AlreadyLinkedError: Raised if already
#         exists a link.
#     """
#     try:
#         with db.session.begin_nested():
#             db.session.add(UserIdentity(
#                 id=external_id['id'],
#                 method=external_id['method'],
#                 id_user=user.id
#             ))
#     except IntegrityError:
#         raise AlreadyLinkedError(user, external_id)
# 
# def oauth_unlink_external_id(external_id):
#     """Unlink a user from an external id.
# 
#     :param external_id: The external id associated with the user.
#     """
#     with db.session.begin_nested():
#         UserIdentity.query.filter_by(id=external_id['id'],
#                                      method=external_id['method']).delete()


# Path: invenio_oauthclient/contrib/cern_openid.py
from datetime import datetime, timedelta
from flask import Blueprint, current_app, flash, g, redirect, session, url_for
from flask_babelex import gettext as _
from flask_login import current_user
from flask_principal import AnonymousIdentity, RoleNeed, UserNeed, \
    identity_changed, identity_loaded
from invenio_db import db
from jwt import decode
from invenio_oauthclient.errors import OAuthCERNRejectedAccountError
from invenio_oauthclient.handlers.rest import response_handler
from invenio_oauthclient.handlers.utils import \
    require_more_than_one_external_account
from invenio_oauthclient.models import RemoteAccount
from invenio_oauthclient.proxies import current_oauthclient
from invenio_oauthclient.utils import oauth_link_external_id, \
    oauth_unlink_external_id

    """Handle unlinking of remote account."""
    if not current_user.is_authenticated:
        return current_app.login_manager.unauthorized()

    account = RemoteAccount.get(
        user_id=current_user.get_id(), client_id=remote.consumer_key
    )
    external_id = account.extra_data.get("external_id")

    if external_id:
        oauth_unlink_external_id(dict(id=external_id, method="cern_openid"))
    if account:
        with db.session.begin_nested():
            account.delete()

    disconnect_identity(g.identity)


def disconnect_handler(remote, *args, **kwargs):
    """Handle unlinking of remote account."""
    _disconnect(remote, *args, **kwargs)
    return redirect(url_for("invenio_oauthclient_settings.index"))


def disconnect_rest_handler(remote, *args, **kwargs):
    """Handle unlinking of remote account."""
    _disconnect(remote, *args, **kwargs)
    redirect_url = current_app.config["OAUTHCLIENT_REST_REMOTE_APPS"][
        remote.name
    ]["disconnect_redirect_url"]

    return response_handler(remote, redirect_url)