#------------------------------------------------------------------------------
# Copyright (c) 2008, Riverbank Computing Limited
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in enthought/LICENSE.txt and may be redistributed only
# under the conditions described in the aforementioned license.  The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
# Thanks for using Enthought open source!
#
# Author: Riverbank Computing Limited
# Description: <Enthought permissions package component>
#------------------------------------------------------------------------------


# Standard library imports.
import errno
import socket

# Enthought library imports.
from apptools.permissions.default.api import IUserStorage, UserStorageError
from traits.api import HasTraits, provides, List, Str

# Local imports.
from proxy_server import ProxyServer


@provides(IUserStorage)
class UserStorage(HasTraits):
    """This implements a user database accessed via XML RPC."""



    #### 'IUserStorage' interface #############################################

    capabilities = List(Str)

    ###########################################################################
    # 'IUserStorage' interface.
    ###########################################################################

    def add_user(self, name, description, password):
        """Add a new user."""

        try:
            ProxyServer.add_user(name, description, password, ProxyServer.key)
        except Exception, e:
            raise UserStorageError(ProxyServer.error(e))

    def authenticate_user(self, name, password):
        """Return the tuple of the user name, description, and blob if the user
        was successfully authenticated."""

        try:
            key, name, description, blob = ProxyServer.authenticate_user(name,
                    password)

            # We don't save the cache because we should be about to read the
            # real permission ids and we do it then.
            ProxyServer.cache = description, blob, []
        except Exception, e:
            # See if we couldn't connect to the server.
            if not isinstance(e, socket.error):
                raise UserStorageError(ProxyServer.error(e))

            err, _ = e.args

            if err != errno.ECONNREFUSED:
                raise UserStorageError(ProxyServer.error(e))

            try:
                ok = ProxyServer.read_cache()
            except Exception, e:
                raise UserStorageError(str(e))

            if not ok:
                raise UserStorageError(ProxyServer.error(e))

            # We are in "disconnect" mode.
            key = None
            description, blob, _ = ProxyServer.cache

        ProxyServer.key = key

        return name, description, blob

    def delete_user(self, name):
        """Delete a new user."""

        try:
            ProxyServer.delete_user(name, ProxyServer.key)
        except Exception, e:
            raise UserStorageError(ProxyServer.error(e))

    def is_empty(self):
        """See if the database is empty."""

        # We leave it to the policy storage to answer this question.
        return False

    def matching_users(self, name):
        """Return the full name and description of all the users that match the
        given name."""

        try:
            return ProxyServer.matching_users(name, ProxyServer.key)
        except Exception, e:
            raise UserStorageError(ProxyServer.error(e))

    def modify_user(self, name, description, password):
        """Update the description and password for the given user."""

        try:
            ProxyServer.modify_user(name, description, password,
                    ProxyServer.key)
        except Exception, e:
            raise UserStorageError(ProxyServer.error(e))

    def unauthenticate_user(self, user):
        """Unauthenticate the given user."""

        if ProxyServer.key is None:
            ok = True
        else:
            try:
                ok = ProxyServer.unauthenticate_user(ProxyServer.key)
            except Exception, e:
                raise UserStorageError(ProxyServer.error(e))

        if ok:
            ProxyServer.key = ''
            ProxyServer.cache = None

        return ok

    def update_blob(self, name, blob):
        """Update the blob for the given user."""

        # Update the cache.
        description, _, perm_ids = ProxyServer.cache
        ProxyServer.cache = description, blob, perm_ids

        if ProxyServer.key is None:
            # Write the cache and tell the user about any errors.
            ProxyServer.write_cache()
        else:
            try:
                ProxyServer.update_blob(name, blob, ProxyServer.key)
            except Exception, e:
                raise UserStorageError(ProxyServer.error(e))

            # Write the cache but ignore any errors.
            try:
                ProxyServer.write_cache()
            except:
                pass

    def update_password(self, name, password):
        """Update the password for the given user."""

        # If the remote server disappeared after the capabilities were read but
        # before the user was authenticated then we could get here.
        if ProxyServer.key is None:
            raise UserStorageError("It is not possible to change password "
                    "when disconnected from the permissions server.")

        try:
            ProxyServer.update_password(name, password, ProxyServer.key)
        except Exception, e:
            raise UserStorageError(ProxyServer.error(e))

    ###########################################################################
    # Trait handlers.
    ###########################################################################

    def _capabilities_default(self):
        """Return the storage capabilities."""

        try:
            caps = ProxyServer.capabilities()
        except:
            caps = []

        return caps
