1#------------------------------------------------------------------------------
2# Copyright (c) 2008, Riverbank Computing Limited
3# All rights reserved.
4#
5# This software is provided without warranty under the terms of the BSD
6# license included in enthought/LICENSE.txt and may be redistributed only
7# under the conditions described in the aforementioned license.  The license
8# is also available online at http://www.enthought.com/licenses/BSD.txt
9# Thanks for using Enthought open source!
10#
11# Author: Riverbank Computing Limited
12# Description: <Enthought permissions package component>
13#------------------------------------------------------------------------------
14
15
16# Standard library imports.
17import errno
18import socket
19
20# Enthought library imports.
21from apptools.permissions.default.api import IUserStorage, UserStorageError
22from traits.api import HasTraits, provides, List, Str
23
24# Local imports.
25from proxy_server import ProxyServer
26
27
28@provides(IUserStorage)
29class UserStorage(HasTraits):
30    """This implements a user database accessed via XML RPC."""
31
32
33
34    #### 'IUserStorage' interface #############################################
35
36    capabilities = List(Str)
37
38    ###########################################################################
39    # 'IUserStorage' interface.
40    ###########################################################################
41
42    def add_user(self, name, description, password):
43        """Add a new user."""
44
45        try:
46            ProxyServer.add_user(name, description, password, ProxyServer.key)
47        except Exception, e:
48            raise UserStorageError(ProxyServer.error(e))
49
50    def authenticate_user(self, name, password):
51        """Return the tuple of the user name, description, and blob if the user
52        was successfully authenticated."""
53
54        try:
55            key, name, description, blob = ProxyServer.authenticate_user(name,
56                    password)
57
58            # We don't save the cache because we should be about to read the
59            # real permission ids and we do it then.
60            ProxyServer.cache = description, blob, []
61        except Exception, e:
62            # See if we couldn't connect to the server.
63            if not isinstance(e, socket.error):
64                raise UserStorageError(ProxyServer.error(e))
65
66            err, _ = e.args
67
68            if err != errno.ECONNREFUSED:
69                raise UserStorageError(ProxyServer.error(e))
70
71            try:
72                ok = ProxyServer.read_cache()
73            except Exception, e:
74                raise UserStorageError(str(e))
75
76            if not ok:
77                raise UserStorageError(ProxyServer.error(e))
78
79            # We are in "disconnect" mode.
80            key = None
81            description, blob, _ = ProxyServer.cache
82
83        ProxyServer.key = key
84
85        return name, description, blob
86
87    def delete_user(self, name):
88        """Delete a new user."""
89
90        try:
91            ProxyServer.delete_user(name, ProxyServer.key)
92        except Exception, e:
93            raise UserStorageError(ProxyServer.error(e))
94
95    def is_empty(self):
96        """See if the database is empty."""
97
98        # We leave it to the policy storage to answer this question.
99        return False
100
101    def matching_users(self, name):
102        """Return the full name and description of all the users that match the
103        given name."""
104
105        try:
106            return ProxyServer.matching_users(name, ProxyServer.key)
107        except Exception, e:
108            raise UserStorageError(ProxyServer.error(e))
109
110    def modify_user(self, name, description, password):
111        """Update the description and password for the given user."""
112
113        try:
114            ProxyServer.modify_user(name, description, password,
115                    ProxyServer.key)
116        except Exception, e:
117            raise UserStorageError(ProxyServer.error(e))
118
119    def unauthenticate_user(self, user):
120        """Unauthenticate the given user."""
121
122        if ProxyServer.key is None:
123            ok = True
124        else:
125            try:
126                ok = ProxyServer.unauthenticate_user(ProxyServer.key)
127            except Exception, e:
128                raise UserStorageError(ProxyServer.error(e))
129
130        if ok:
131            ProxyServer.key = ''
132            ProxyServer.cache = None
133
134        return ok
135
136    def update_blob(self, name, blob):
137        """Update the blob for the given user."""
138
139        # Update the cache.
140        description, _, perm_ids = ProxyServer.cache
141        ProxyServer.cache = description, blob, perm_ids
142
143        if ProxyServer.key is None:
144            # Write the cache and tell the user about any errors.
145            ProxyServer.write_cache()
146        else:
147            try:
148                ProxyServer.update_blob(name, blob, ProxyServer.key)
149            except Exception, e:
150                raise UserStorageError(ProxyServer.error(e))
151
152            # Write the cache but ignore any errors.
153            try:
154                ProxyServer.write_cache()
155            except:
156                pass
157
158    def update_password(self, name, password):
159        """Update the password for the given user."""
160
161        # If the remote server disappeared after the capabilities were read but
162        # before the user was authenticated then we could get here.
163        if ProxyServer.key is None:
164            raise UserStorageError("It is not possible to change password "
165                    "when disconnected from the permissions server.")
166
167        try:
168            ProxyServer.update_password(name, password, ProxyServer.key)
169        except Exception, e:
170            raise UserStorageError(ProxyServer.error(e))
171
172    ###########################################################################
173    # Trait handlers.
174    ###########################################################################
175
176    def _capabilities_default(self):
177        """Return the storage capabilities."""
178
179        try:
180            caps = ProxyServer.capabilities()
181        except:
182            caps = []
183
184        return caps
185