From 75ba8eaf3110ed585d366c09dd7fe10d1e436e7b Mon Sep 17 00:00:00 2001 From: Aaron Ayres Date: Tue, 3 Nov 2020 14:25:03 -0600 Subject: [PATCH] remove permissions subpackage, its docs and examples, and references to it --- README.rst | 3 - apptools/permissions/__init__.py | 5 - apptools/permissions/action/__init__.py | 2 - apptools/permissions/action/api.py | 17 - apptools/permissions/action/login_action.py | 38 -- apptools/permissions/action/logout_action.py | 60 -- .../permissions/action/user_menu_manager.py | 62 -- apptools/permissions/adapter_base.py | 154 ----- apptools/permissions/adapters/__init__.py | 2 - .../permissions/adapters/pyface_action.py | 56 -- apptools/permissions/adapters/qt4_widget.py | 67 -- apptools/permissions/adapters/wx_window.py | 55 -- apptools/permissions/api.py | 22 - apptools/permissions/default/__init__.py | 2 - apptools/permissions/default/api.py | 20 - .../permissions/default/i_policy_storage.py | 70 --- .../permissions/default/i_user_database.py | 78 --- .../permissions/default/i_user_storage.py | 78 --- apptools/permissions/default/persistent.py | 114 ---- apptools/permissions/default/policy_data.py | 55 -- .../permissions/default/policy_manager.py | 147 ----- .../permissions/default/policy_storage.py | 215 ------- .../permissions/default/role_assignment.py | 178 ------ .../permissions/default/role_definition.py | 204 ------- apptools/permissions/default/select_role.py | 75 --- apptools/permissions/default/select_user.py | 72 --- apptools/permissions/default/user_database.py | 518 ---------------- apptools/permissions/default/user_manager.py | 182 ------ apptools/permissions/default/user_storage.py | 196 ------ apptools/permissions/i_policy_manager.py | 42 -- apptools/permissions/i_user.py | 44 -- apptools/permissions/i_user_manager.py | 59 -- apptools/permissions/package_globals.py | 39 -- apptools/permissions/permission.py | 105 ---- apptools/permissions/permissions_manager.py | 104 ---- apptools/permissions/secure_proxy.py | 180 ------ docs/source/index.rst | 4 - docs/source/permissions/ApplicationAPI.rst | 290 --------- .../DefaultPolicyManagerDataAPI.rst | 31 - .../permissions/DefaultUserManagerDataAPI.rst | 72 --- docs/source/permissions/Introduction.rst | 215 ------- examples/permissions/application/example.py | 72 --- .../permissions/application/permissions.py | 25 - examples/permissions/application/person.py | 42 -- .../application/secured_debug_view.py | 24 - .../permissions/application/toolkit_editor.py | 63 -- .../application/workbench_window.py | 133 ---- .../permissions/server/enthought/__init__.py | 0 .../server/enthought/permissions/__init__.py | 10 - .../permissions/external/__init__.py | 10 - .../permissions/external/policy_storage.py | 120 ---- .../permissions/external/proxy_server.py | 126 ---- .../permissions/external/user_storage.py | 184 ------ examples/permissions/server/server.py | 577 ------------------ examples/permissions/server/setup.py | 17 - examples/permissions/server/test_client.py | 284 --------- 56 files changed, 5619 deletions(-) delete mode 100644 apptools/permissions/__init__.py delete mode 100644 apptools/permissions/action/__init__.py delete mode 100644 apptools/permissions/action/api.py delete mode 100644 apptools/permissions/action/login_action.py delete mode 100644 apptools/permissions/action/logout_action.py delete mode 100644 apptools/permissions/action/user_menu_manager.py delete mode 100644 apptools/permissions/adapter_base.py delete mode 100644 apptools/permissions/adapters/__init__.py delete mode 100644 apptools/permissions/adapters/pyface_action.py delete mode 100644 apptools/permissions/adapters/qt4_widget.py delete mode 100644 apptools/permissions/adapters/wx_window.py delete mode 100644 apptools/permissions/api.py delete mode 100644 apptools/permissions/default/__init__.py delete mode 100644 apptools/permissions/default/api.py delete mode 100644 apptools/permissions/default/i_policy_storage.py delete mode 100644 apptools/permissions/default/i_user_database.py delete mode 100644 apptools/permissions/default/i_user_storage.py delete mode 100644 apptools/permissions/default/persistent.py delete mode 100644 apptools/permissions/default/policy_data.py delete mode 100644 apptools/permissions/default/policy_manager.py delete mode 100644 apptools/permissions/default/policy_storage.py delete mode 100644 apptools/permissions/default/role_assignment.py delete mode 100644 apptools/permissions/default/role_definition.py delete mode 100644 apptools/permissions/default/select_role.py delete mode 100644 apptools/permissions/default/select_user.py delete mode 100644 apptools/permissions/default/user_database.py delete mode 100644 apptools/permissions/default/user_manager.py delete mode 100644 apptools/permissions/default/user_storage.py delete mode 100644 apptools/permissions/i_policy_manager.py delete mode 100644 apptools/permissions/i_user.py delete mode 100644 apptools/permissions/i_user_manager.py delete mode 100644 apptools/permissions/package_globals.py delete mode 100644 apptools/permissions/permission.py delete mode 100644 apptools/permissions/permissions_manager.py delete mode 100644 apptools/permissions/secure_proxy.py delete mode 100644 docs/source/permissions/ApplicationAPI.rst delete mode 100644 docs/source/permissions/DefaultPolicyManagerDataAPI.rst delete mode 100644 docs/source/permissions/DefaultUserManagerDataAPI.rst delete mode 100644 docs/source/permissions/Introduction.rst delete mode 100644 examples/permissions/application/example.py delete mode 100644 examples/permissions/application/permissions.py delete mode 100644 examples/permissions/application/person.py delete mode 100644 examples/permissions/application/secured_debug_view.py delete mode 100644 examples/permissions/application/toolkit_editor.py delete mode 100644 examples/permissions/application/workbench_window.py delete mode 100644 examples/permissions/server/enthought/__init__.py delete mode 100644 examples/permissions/server/enthought/permissions/__init__.py delete mode 100644 examples/permissions/server/enthought/permissions/external/__init__.py delete mode 100644 examples/permissions/server/enthought/permissions/external/policy_storage.py delete mode 100644 examples/permissions/server/enthought/permissions/external/proxy_server.py delete mode 100644 examples/permissions/server/enthought/permissions/external/user_storage.py delete mode 100755 examples/permissions/server/server.py delete mode 100644 examples/permissions/server/setup.py delete mode 100755 examples/permissions/server/test_client.py diff --git a/README.rst b/README.rst index 101a86ded..c0d284718 100644 --- a/README.rst +++ b/README.rst @@ -27,9 +27,6 @@ that is commonly needed by many applications - **apptools.logger**: Convenience functions for creating logging handlers - **apptools.naming**: Manages naming contexts, supporting non-string data types and scoped preferences -- **apptools.permissions**: Supports limiting access to parts of an - application unless the user is appropriately authorised (not full-blown - security). - **apptools.persistence**: Supports pickling the state of a Python object to a dictionary, which can then be flexibly applied in restoring the state of the object. diff --git a/apptools/permissions/__init__.py b/apptools/permissions/__init__.py deleted file mode 100644 index e7341f7f6..000000000 --- a/apptools/permissions/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) 2007-2011 by Enthought, Inc. -# All rights reserved. -""" Supports limiting access to parts of an application to authorised users. - Part of the AppTools project of the Enthought Tool Suite. -""" diff --git a/apptools/permissions/action/__init__.py b/apptools/permissions/action/__init__.py deleted file mode 100644 index 9be534293..000000000 --- a/apptools/permissions/action/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Copyright (c) 2008-2011 by Enthought, Inc. -# All rights reserved. diff --git a/apptools/permissions/action/api.py b/apptools/permissions/action/api.py deleted file mode 100644 index a946c1c26..000000000 --- a/apptools/permissions/action/api.py +++ /dev/null @@ -1,17 +0,0 @@ -#------------------------------------------------------------------------------ -# 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: -#------------------------------------------------------------------------------ - -from .login_action import LoginAction -from .logout_action import LogoutAction -from .user_menu_manager import UserMenuManager diff --git a/apptools/permissions/action/login_action.py b/apptools/permissions/action/login_action.py deleted file mode 100644 index caddc68af..000000000 --- a/apptools/permissions/action/login_action.py +++ /dev/null @@ -1,38 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from pyface.action.api import Action -from traits.api import Unicode - -# Local imports. -from apptools.permissions.package_globals import get_permissions_manager - - -class LoginAction(Action): - """An action that authenticates the current user.""" - - #### 'Action' interface ################################################### - - name = Unicode("Log&in...") - - ########################################################################### - # 'Action' interface. - ########################################################################### - - def perform(self, event): - """Perform the action.""" - - get_permissions_manager().user_manager.authenticate_user() diff --git a/apptools/permissions/action/logout_action.py b/apptools/permissions/action/logout_action.py deleted file mode 100644 index 0ff98cd79..000000000 --- a/apptools/permissions/action/logout_action.py +++ /dev/null @@ -1,60 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from pyface.action.api import Action -from traits.api import Bool, Unicode - -# Local imports. -from apptools.permissions.package_globals import get_permissions_manager - - -class LogoutAction(Action): - """An action that unauthenticates the current user.""" - - #### 'Action' interface ################################################### - - enabled = Bool(False) - - name = Unicode("Log&out") - - ########################################################################### - # 'object' interface. - ########################################################################### - - def __init__(self, **traits): - """Initialise the object.""" - - super(LogoutAction, self).__init__(**traits) - - get_permissions_manager().user_manager.on_trait_event(self._refresh_enabled, 'user_authenticated') - - ########################################################################### - # 'Action' interface. - ########################################################################### - - def perform(self, event): - """Perform the action.""" - - get_permissions_manager().user_manager.unauthenticate_user() - - ########################################################################### - # Private interface. - ########################################################################### - - def _refresh_enabled(self, user): - """Invoked whenever the current user's authorisation state changes.""" - - self.enabled = user is not None diff --git a/apptools/permissions/action/user_menu_manager.py b/apptools/permissions/action/user_menu_manager.py deleted file mode 100644 index e18e2d95e..000000000 --- a/apptools/permissions/action/user_menu_manager.py +++ /dev/null @@ -1,62 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from pyface.action.api import Group, MenuManager -from traits.api import Unicode - -# Local imports. -from apptools.permissions.package_globals import get_permissions_manager -from .login_action import LoginAction -from .logout_action import LogoutAction - - -class UserMenuManager(MenuManager): - """A menu that contains all the actions related to users and permissions. - """ - - #### 'MenuManager' interface ############################################## - - id = 'User' - - name = Unicode("&User") - - ########################################################################### - # 'object' interface. - ########################################################################### - - def __init__(self, **traits): - """Initialise the object.""" - - pm = get_permissions_manager() - - # Put them in a group so we can optionally append (because the PyFace - # API doesn't do what you expect with append()). - group = Group() - - group.append(LoginAction()) - - for act in pm.user_manager.user_actions: - group.append(act) - - group.append(LogoutAction()) - - for act in pm.user_manager.management_actions: - group.append(act) - - for act in pm.policy_manager.management_actions: - group.append(act) - - super(UserMenuManager, self).__init__(group, **traits) diff --git a/apptools/permissions/adapter_base.py b/apptools/permissions/adapter_base.py deleted file mode 100644 index f832dd506..000000000 --- a/apptools/permissions/adapter_base.py +++ /dev/null @@ -1,154 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, 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 library imports. -from traits.api import Any, Bool, HasTraits, Instance, List - -# Local imports. -from .package_globals import get_permissions_manager -from .permission import Permission - - -class AdapterBase(HasTraits): - """This is the base class for object specific adapters.""" - - # The object being proxied. - proxied = Any - - # The list of permissions applied to the proxied object. - permissions = List(Instance(Permission)) - - # Set if the proxied object should be shown when it is enabled. - show = Bool - - # The desired enabled state of the proxied object, ie. the state set by the - # application before any permissions were applied. - _desired_enabled = Bool - - # The desired visible state of the proxied object, ie. the state set by the - # application before any permissions were applied. - _desired_visible = Bool - - # The registered adapter types. - _adapter_types = {} - - @classmethod - def register_adapter(cls, adapter, *types): - """Register an adapter type for one or more object types.""" - - for t in types: - cls._adapter_types[t] = adapter - - @classmethod - def factory(cls, proxied, permissions, show): - """Return an adapter for the proxied object. permissions is a list of - permissions to attach to the object. show is set if the proxied object - should be visible when it is disabled. - """ - - # Find a suitable adapter type. - for object_type, adapter_type in cls._adapter_types.items(): - if isinstance(proxied, object_type): - break - else: - raise TypeError("no SecureProxy adapter registered for %s" % proxied) - - adapter = adapter_type(proxied=proxied, permissions=permissions, - show=show) - - # Refresh the state of the object when the authentication state of the - # current user changes. - get_permissions_manager().user_manager.on_trait_event(adapter._refresh, - 'user_authenticated') - - return adapter - - def adapt(self): - """Try and adapt the proxied object and return the adapted object if - successful. If None is returned a proxy wrapper will be created. - """ - - return None - - def setattr(self, name, value): - """The default implementation to set an attribute of the proxied - object. - """ - - setattr(self.proxied, name, value) - - def get_enabled(self): - """Get the enabled state of the proxied object.""" - - raise NotImplementedError - - def set_enabled(self, value): - """Set the enabled state of the proxied object.""" - - raise NotImplementedError - - def update_enabled(self, value): - """Update the proxied object after a possible new value of the enabled - attribute. - """ - - if get_permissions_manager().check_permissions(*self.permissions): - self.set_enabled(value) - - if not self.show: - self.set_visible(self._desired_visible) - else: - self.set_enabled(False) - - if not self.show: - self.set_visible(False) - - # Save the desired value so that it can be checked if the user becomes - # authenticated. - self._desired_enabled = value - - def get_visible(self): - """Get the visible state of the proxied object.""" - - raise NotImplementedError - - def set_visible(self, value): - """Set the visible state of the proxied object.""" - - raise NotImplementedError - - def update_visible(self, value): - """Update the proxied object after a possible new value of the visible - attribute. - """ - - if self.show: - self.set_visible(value) - elif get_permissions_manager().check_permissions(*self.permissions): - self.set_visible(value) - else: - self.set_visible(False) - - # Save the desired value so that it can be checked if the user becomes - # authenticated. - self._desired_visible = value - - def _refresh(self, user): - """Invoked whenever the current user's authorisation state changes.""" - - if not self.show: - self.update_visible(self._desired_visible) - - self.update_enabled(self._desired_enabled) diff --git a/apptools/permissions/adapters/__init__.py b/apptools/permissions/adapters/__init__.py deleted file mode 100644 index 87c9bf0e3..000000000 --- a/apptools/permissions/adapters/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Copyright (c) 2007-2011 by Enthought, Inc. -# All rights reserved. diff --git a/apptools/permissions/adapters/pyface_action.py b/apptools/permissions/adapters/pyface_action.py deleted file mode 100644 index 33ed2aac0..000000000 --- a/apptools/permissions/adapters/pyface_action.py +++ /dev/null @@ -1,56 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, 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 library imports. -from apptools.permissions.adapter_base import AdapterBase -from pyface.action.api import Action - - -class ActionAdapter(AdapterBase): - """This is the adapter for PyFace actions.""" - - def get_enabled(self): - """Get the enabled state of the proxied object.""" - - return self.proxied.enabled - - def set_enabled(self, value): - """Set the enabled state of the proxied object.""" - - self.proxied.enabled = value - - def get_visible(self): - """Get the visible state of the proxied object.""" - - return self.proxied.visible - - def set_visible(self, value): - """Set the visible state of the proxied object.""" - - self.proxied.visible = value - - def setattr(self, name, value): - """Reimplemented to intercept the setting of the enabled and visible - attributes of the proxied action. - """ - - if name == 'enabled': - self.update_enabled(value) - elif name == 'visible': - self.update_visible(value) - else: - super(ActionAdapter, self).setattr(name, value) - -AdapterBase.register_adapter(ActionAdapter, Action) diff --git a/apptools/permissions/adapters/qt4_widget.py b/apptools/permissions/adapters/qt4_widget.py deleted file mode 100644 index f95214eb7..000000000 --- a/apptools/permissions/adapters/qt4_widget.py +++ /dev/null @@ -1,67 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, 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: -#------------------------------------------------------------------------------ - - -# Major library imports. -from pyface.qt import QtGui - -# Enthought library imports. -from apptools.permissions.adapter_base import AdapterBase - - -class QWidgetAdapter(AdapterBase): - """This is the adapter for PyQt QWidget instances.""" - - def adapt(self): - """Reimplemented to adapt the proxied object.""" - - # Replace the real methods. - self.proxied.setEnabled = self.update_enabled - self.proxied.setVisible = self.update_visible - self.proxied.hide = self._hide - self.proxied.show = self._show - - return self.proxied - - def get_enabled(self): - """Get the enabled state of the proxied object.""" - - return self.proxied.isEnabled() - - def set_enabled(self, value): - """Set the enabled state of the proxied object.""" - - QtGui.QWidget.setEnabled(self.proxied, value) - - def get_visible(self): - """Get the visible state of the proxied object.""" - - return self.proxied.isVisible() - - def set_visible(self, value): - """Set the visible state of the proxied object.""" - - QtGui.QWidget.setVisible(self.proxied, value) - - def _hide(self): - """The replacement QWidget.hide() implementation.""" - - self.update_visible(False) - - def _show(self): - """The replacement QWidget.show() implementation.""" - - self.update_visible(True) - -AdapterBase.register_adapter(QWidgetAdapter, QtGui.QWidget) diff --git a/apptools/permissions/adapters/wx_window.py b/apptools/permissions/adapters/wx_window.py deleted file mode 100644 index eda732290..000000000 --- a/apptools/permissions/adapters/wx_window.py +++ /dev/null @@ -1,55 +0,0 @@ -#------------------------------------------------------------------------------ -# 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: -#------------------------------------------------------------------------------ - - -# Major library imports. -import wx - -# Enthought library imports. -from apptools.permissions.adapter_base import AdapterBase - - -class wxWindowAdapter(AdapterBase): - """This is the adapter for wx Window instances.""" - - def adapt(self): - """Reimplemented to adapt the proxied object.""" - - # Replace the real methods. - self.proxied.Enable = self.update_enabled - self.proxied.Show = self.update_visible - - return self.proxied - - def get_enabled(self): - """Get the enabled state of the proxied object.""" - - return self.proxied.IsEnabled() - - def set_enabled(self, value): - """Set the enabled state of the proxied object.""" - - wx.Window.Enable(self.proxied, value) - - def get_visible(self): - """Get the visible state of the proxied object.""" - - return self.proxied.IsShown() - - def set_visible(self, value): - """Set the visible state of the proxied object.""" - - wx.Window.Show(self.proxied, value) - -AdapterBase.register_adapter(wxWindowAdapter, wx.Window) diff --git a/apptools/permissions/api.py b/apptools/permissions/api.py deleted file mode 100644 index 030dd87fe..000000000 --- a/apptools/permissions/api.py +++ /dev/null @@ -1,22 +0,0 @@ -#------------------------------------------------------------------------------ -# 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: -#------------------------------------------------------------------------------ - -from .adapter_base import AdapterBase -from .i_policy_manager import IPolicyManager -from .i_user import IUser -from .i_user_manager import IUserManager -from .package_globals import get_permissions_manager, set_permissions_manager -from .permission import ManagePolicyPermission, ManageUsersPermission, Permission -from .permissions_manager import PermissionsManager -from .secure_proxy import SecureHandler, SecureProxy diff --git a/apptools/permissions/default/__init__.py b/apptools/permissions/default/__init__.py deleted file mode 100644 index 9be534293..000000000 --- a/apptools/permissions/default/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Copyright (c) 2008-2011 by Enthought, Inc. -# All rights reserved. diff --git a/apptools/permissions/default/api.py b/apptools/permissions/default/api.py deleted file mode 100644 index 8214cbf88..000000000 --- a/apptools/permissions/default/api.py +++ /dev/null @@ -1,20 +0,0 @@ -#------------------------------------------------------------------------------ -# 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: -#------------------------------------------------------------------------------ - -from .i_policy_storage import IPolicyStorage, PolicyStorageError -from .i_user_database import IUserDatabase -from .i_user_storage import IUserStorage, UserStorageError -from .policy_manager import PolicyManager -from .user_database import UserDatabase -from .user_manager import UserManager diff --git a/apptools/permissions/default/i_policy_storage.py b/apptools/permissions/default/i_policy_storage.py deleted file mode 100644 index 2c73fc5bd..000000000 --- a/apptools/permissions/default/i_policy_storage.py +++ /dev/null @@ -1,70 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from traits.api import Interface - - -class PolicyStorageError(Exception): - """This is the exception raised by an IPolicyStorage object when an error - occurs accessing the database. Its string representation is displayed as - an error message to the user.""" - - -class IPolicyStorage(Interface): - """This defines the interface expected by a PolicyManager to handle the low - level storage of the policy data.""" - - ########################################################################### - # 'IPolicyStorage' interface. - ########################################################################### - - def add_role(self, name, description, perm_ids): - """Add a new role.""" - - def all_roles(self): - """Return a list of all roles where each element is a tuple of the name - and description.""" - - def delete_role(self, name): - """Delete the role with the given name (which will not be empty).""" - - def get_assignment(self, user_name): - """Return a tuple of the user name and list of role names of the - assignment for the given user name. The tuple will contain an empty - string and list if the user isn't known.""" - - def get_policy(self, user_name): - """Return a tuple of the user name and list of permission names for the - user with the given name. The tuple will contain an empty string and - list if the user isn't known.""" - - def is_empty(self): - """Return True if the user database is empty. It will only ever be - called once.""" - - def matching_roles(self, name): - """Return a list of tuples of the full name, description and list of - permission names, sorted by the full name, of all roles that match the - given name. How the name is interpreted (eg. as a regular expression) - is determined by the storage.""" - - def modify_role(self, name, description, perm_ids): - """Update the description and permissions for the role with the given - name (which will not be empty).""" - - def set_assignment(self, user_name, role_names): - """Save the assignment of the given role names to the given user. Note - that there may or may not be an existing assignment for the user.""" diff --git a/apptools/permissions/default/i_user_database.py b/apptools/permissions/default/i_user_database.py deleted file mode 100644 index 5de6e723f..000000000 --- a/apptools/permissions/default/i_user_database.py +++ /dev/null @@ -1,78 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from traits.api import Bool, Instance, Interface - -# Local imports. -from .i_user_storage import IUserStorage - - -class IUserDatabase(Interface): - """The interface to be implemented by a user database for the default user - manager.""" - - # Set if the implementation supports changing a user's password. - can_change_password = Bool - - # Set if the implementation supports adding users. - can_add_user = Bool - - # Set if the implementation supports modifying users. - can_modify_user = Bool - - # Set if the implementation supports deleting users. - can_delete_user = Bool - - # The user data storage. - user_storage = Instance(IUserStorage) - - def bootstrapping(self): - """Return True if the user database is bootstrapping. Typically this - is when no users have been defined.""" - - def authenticate_user(self, user): - """Authenticate the given user and return True if successful. user - implements IUser.""" - - def unauthenticate_user(self, user): - """Unauthenticate the given user and return True if successful. user - implements IUser.""" - - def change_password(self, user): - """Change a user's password in the database. This only needs to be - reimplemented if 'can_change_password' is True.""" - - def add_user(self): - """Add a user account to the database. This only needs to be - reimplemented if 'can_add_user' is True.""" - - def modify_user(self): - """Modify a user account in the database. This only needs to be - reimplemented if 'can_modify_user' is True.""" - - def delete_user(self): - """Delete a user account from the database. This only needs to be - reimplemented if 'can_delete_user' is True.""" - - def matching_user(self, name): - """Return an object that implements IUser for the user selected based - on the given name. If there was no user selected then return None. - How the name is interpreted (eg. as a regular expression) is determined - by the user database. Note that the blob attribute of the user will - not be set.""" - - def user_factory(self): - """Return a new object that implements the IUser interface.""" diff --git a/apptools/permissions/default/i_user_storage.py b/apptools/permissions/default/i_user_storage.py deleted file mode 100644 index fe478f6dc..000000000 --- a/apptools/permissions/default/i_user_storage.py +++ /dev/null @@ -1,78 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from traits.api import Interface, List, Str - - -class UserStorageError(Exception): - """This is the exception raised by an IUserStorage object when an error - occurs accessing the database. Its string representation is displayed as - an error message to the user.""" - - -class IUserStorage(Interface): - """This defines the interface expected by a UserManager instance to handle - the low level storage of the user data.""" - - #### 'IUserStorage' interface ############################################# - - # A list of strings describing the storage capabilities. 'user_password' - # means a user's password can be changed. 'user_add' means a user can be - # added. 'user_modify' means a user can be modified. 'user_delete' means - # a user can be deleted. - capabilities = List(Str) - - ########################################################################### - # 'IUserStorage' interface. - ########################################################################### - - def add_user(self, name, description, password): - """Add a new user with the given name, description and password.""" - - def authenticate_user(self, name, password): - """Return a tuple of the name, description, and blob of the user with - the given name if they are successfully authenticated with the given - password. A tuple of empty strings will be returned if the - authentication failed.""" - - def delete_user(self, name): - """Delete the user with the given name (which will not be empty).""" - - def is_empty(self): - """Return True if the user database is empty. It will only ever be - called once.""" - - def matching_users(self, name): - """Return a list of tuples of the full name and description of all - users, sorted by the full name, that match the given name. How the - name is interpreted (eg. as a regular expression) is determined by the - storage.""" - - def modify_user(self, name, description, password): - """Update the description and password for the user with the given name - (which will not be empty).""" - - def unauthenticate_user(self, user): - """Unauthenticate the given user (which is an object implementing - IUser) and return True if it was successful.""" - - def update_blob(self, name, blob): - """Update the blob for the user with the given name (which will not be - empty).""" - - def update_password(self, name, password): - """Update the password for the user with the given name (which will not - be empty).""" diff --git a/apptools/permissions/default/persistent.py b/apptools/permissions/default/persistent.py deleted file mode 100644 index 543adc5ce..000000000 --- a/apptools/permissions/default/persistent.py +++ /dev/null @@ -1,114 +0,0 @@ -#------------------------------------------------------------------------------ -# 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: -#------------------------------------------------------------------------------ - - -# Standard library imports. -import errno -import os - -# Third-party imports. -import six.moves.cPickle as pickle - -# Enthought library imports. -from traits.etsconfig.api import ETSConfig - - -class Persistent(object): - """This persists a Traits class to a file. It is used by the default - permissions policy and user manager to implement basic (ie. insecure) - shared data storage.""" - - def __init__(self, factory, file_name, desc): - """Initialise the object. factory is a callable that will create a - new instance if there is no existing data. file_name is the name of - the file in the ETSConfig.application_home directory (or the directory - specified by the ETS_PERMS_DATA_DIR environment variable if set) to - persist the data to. desc is a description of the data used in - exceptions.""" - - self._factory = factory - self._desc = desc - - # Get the name of the file to use. - data_dir = os.environ.get('ETS_PERMS_DATA_DIR', - ETSConfig.application_home) - self._fname = os.path.join(data_dir, file_name) - self._lock = self._fname + '.lock' - - try: - os.makedirs(data_dir) - except OSError: - pass - - def lock(self): - """Obtain a lock on the persisted data.""" - - try: - os.mkdir(self._lock) - except OSError as e: - if e.errno == errno.EEXIST: - msg = "The lock on %s is held by another application or user." % self._desc - else: - msg = "Unable to acquire lock on %s: %s." % (self._desc, e) - - raise PersistentError(msg) - - def unlock(self): - """Release the lock on the persisted data.""" - - try: - os.rmdir(self._lock) - except OSError as e: - raise PersistentError("Unable to release lock on %s: %s." % (self._desc, e)) - - def read(self): - """Read and return the persisted data.""" - - try: - f = open(self._fname, 'r') - - try: - try: - data = pickle.load(f) - except: - raise PersistentError("Unable to read %s." % self._desc) - finally: - f.close() - except IOError as e: - if e.errno == errno.ENOENT: - data = self._factory() - else: - raise PersistentError("Unable to open %s: %s." % (self._desc, e)) - - return data - - def write(self, data): - """Write the persisted data.""" - - try: - f = open(self._fname, 'w') - - try: - try: - pickle.dump(data, f) - except: - raise PersistentError("Unable to write %s." % self._desc) - finally: - f.close() - except IOError as e: - raise PersistentError("Unable to create %s: %s." % (self._desc, e)) - - -class PersistentError(Exception): - """The class used for all persistence related exceptions.""" diff --git a/apptools/permissions/default/policy_data.py b/apptools/permissions/default/policy_data.py deleted file mode 100644 index 83d493442..000000000 --- a/apptools/permissions/default/policy_data.py +++ /dev/null @@ -1,55 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from traits.api import HasTraits, Instance, List, Unicode - -# Local imports. -from apptools.permissions.permission import Permission - - -class Role(HasTraits): - """This represents a role.""" - - # The role name. - name = Unicode - - # The role description. - description = Unicode - - # The permissions that define the role. - permissions = List(Instance(Permission)) - - def __str__(self): - """Return a user friendly representation.""" - - s = self.description - if not s: - s = self.name - - return s - - -class Assignment(HasTraits): - """This represents the assignment of roles to a user.""" - - # The user name. - user_name = Unicode - - # The user description. - description = Unicode - - # The list of assigned roles. - roles = List(Instance(Role)) diff --git a/apptools/permissions/default/policy_manager.py b/apptools/permissions/default/policy_manager.py deleted file mode 100644 index f5d52ba04..000000000 --- a/apptools/permissions/default/policy_manager.py +++ /dev/null @@ -1,147 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from pyface.api import error -from pyface.action.api import Action -from traits.api import Dict, HasTraits, provides, Instance, List - -# Local imports. -from apptools.permissions.i_policy_manager import IPolicyManager -from apptools.permissions.permission import ManagePolicyPermission, Permission -from apptools.permissions.secure_proxy import SecureProxy -from .i_policy_storage import IPolicyStorage, PolicyStorageError -from .role_assignment import role_assignment -from .role_definition import role_definition - - -@provides(IPolicyManager) -class PolicyManager(HasTraits): - """The default policy manager implementation. This policy enforces the use - of roles. Permissions are associated with roles rather than directly with - users. Users are then associated with one or more roles.""" - - - - #### 'IPolicyManager' interface ########################################### - - management_actions = List(Instance(Action)) - - user_permissions = List(Instance(Permission)) - - #### 'PolicyManager' interface ############################################ - - # The dictionary of registered permissions keyed on the permission name. - permissions = Dict - - # The policy data storage. - policy_storage = Instance(IPolicyStorage) - - ########################################################################### - # 'IPolicyManager' interface. - ########################################################################### - - def bootstrapping(self): - """Return True if we are bootstrapping, ie. no roles have been defined - or assigned.""" - - try: - bootstrap = self.policy_storage.is_empty() - except PolicyStorageError: - # Suppress the error and assume it isn't empty. - bootstrap = False - - return bootstrap - - def load_policy(self, user): - """Load the policy for the given user.""" - - self.user_permissions = [] - - # See if the policy is to be unloaded. - if user is None: - return - - # Get the user's policy. - try: - user_name, perm_ids = self.policy_storage.get_policy(user.name) - except PolicyStorageError as e: - error(None, str(e)) - return - - for id in perm_ids: - try: - permission = self.permissions[id] - except KeyError: - # This shouldn't happen if referential integrity is maintained. - continue - - self.user_permissions.append(permission) - - def register_permission(self, permission): - """Register the given permission.""" - - if permission.id in self.permissions: - other = self.permissions[permission.id] - - if other.application_defined: - if permission.application_defined: - raise KeyError('permission "%s" has already been defined' % permission.id) - - # Use the description from the policy manager, if there is - # one, in preference to the application supplied one. - if permission.description: - other.description = permission.description - elif permission.application_defined: - # Again, prefer the policy manager description. - if other.description: - permission.description = other.description - - self.permissions[permission.id] = permission - else: - # This should never happen if the policy manager is working - # properly. - raise KeyError('permission "%s" has already been defined by the same policy manager' % permission.id) - else: - self.permissions[permission.id] = permission - - ########################################################################### - # Trait handlers. - ########################################################################### - - def _management_actions_default(self): - """Return the management actions to manage the policy.""" - - actions = [] - perm = ManagePolicyPermission() - - act = Action(name='&Role Definitions...', on_perform=role_definition) - actions.append(SecureProxy(act, permissions=[perm], show=False)) - - act = Action(name='&Role Assignments...', on_perform=role_assignment) - actions.append(SecureProxy(act, permissions=[perm], show=False)) - - return actions - - def _policy_storage_default(self): - """Return the default storage for the policy data.""" - - # Defer to an external storage manager if there is one. - try: - from apptools.permissions.external.policy_storage import PolicyStorage - except ImportError: - from apptools.permissions.default.policy_storage import PolicyStorage - - return PolicyStorage() diff --git a/apptools/permissions/default/policy_storage.py b/apptools/permissions/default/policy_storage.py deleted file mode 100644 index c94453d26..000000000 --- a/apptools/permissions/default/policy_storage.py +++ /dev/null @@ -1,215 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from traits.api import HasTraits, Instance, provides - -# Local imports. -from .i_policy_storage import IPolicyStorage, PolicyStorageError -from .persistent import Persistent, PersistentError - - -@provides(IPolicyStorage) -class PolicyStorage(HasTraits): - """This implements a policy database that pickles its data in a local file. - """ - - - - #### Private interface #################################################### - - # The persisted database. The database itself is a tuple of the role and - # assignment dictionaries. The role dictionary is keyed by the role name, - # and has a value that is a tuple of the description and the list of - # permission names. The assigment dictionary is keyed by the user name and - # has a value that is the list of role names. - _db = Instance(Persistent) - - ########################################################################### - # 'IPolicyStorage' interface. - ########################################################################### - - def add_role(self, name, description, perm_ids): - """Add a new role.""" - - self._db.lock() - - try: - roles, assigns = self._db.read() - - if name in roles: - raise PolicyStorageError("The role \"%s\" already exists." % name) - - roles[name] = (description, perm_ids) - self._db.write((roles, assigns)) - finally: - self._db.unlock() - - def all_roles(self): - """Return a list of all roles.""" - - roles, _ = self._readonly_copy() - - return [(name, description) for name, (description, _) in roles.items()] - - def delete_role(self, name): - """Delete a role.""" - - self._db.lock() - - try: - roles, assigns = self._db.read() - - if name not in roles: - raise PolicyStorageError("The role \"%s\" does not exist." % name) - - del roles[name] - - # Remove the role from any users who have it. - for user, role_names in assigns.items(): - try: - role_names.remove(name) - except ValueError: - continue - - assigns[user] = role_names - - self._db.write((roles, assigns)) - finally: - self._db.unlock() - - def get_assignment(self, user_name): - """Return the details of the assignment for the given user name.""" - - _, assigns = self._readonly_copy() - - try: - role_names = assigns[user_name] - except KeyError: - return '', [] - - return user_name, role_names - - def get_policy(self, user_name): - """Return the details of the policy for the given user name.""" - - roles, assigns = self._readonly_copy() - - try: - role_names = assigns[user_name] - except KeyError: - return '', [] - - perm_ids = [] - - for r in role_names: - _, perms = roles[r] - perm_ids.extend(perms) - - return user_name, perm_ids - - def is_empty(self): - """See if the database is empty.""" - - roles, assigns = self._readonly_copy() - - # Both have to be non-empty for the whole thing to be non-empty. - return (len(roles) == 0 or len(assigns) == 0) - - def matching_roles(self, name): - """Return the full name, description and permissions of all the roles - that match the given name.""" - - roles, _ = self._readonly_copy() - - # Return any role that starts with the name. - roles = [(full_name, description, perm_ids) - for full_name, (description, perm_ids) in roles.items() - if full_name.startswith(name)] - - return sorted(roles) - - def modify_role(self, name, description, perm_ids): - """Update the description and permissions for the given role.""" - - self._db.lock() - - try: - roles, assigns = self._db.read() - - if name not in roles: - raise PolicyStorageError("The role \"%s\" does not exist." % name) - - roles[name] = (description, perm_ids) - self._db.write((roles, assigns)) - finally: - self._db.unlock() - - def set_assignment(self, user_name, role_names): - """Save the roles assigned to a user.""" - - self._db.lock() - - try: - roles, assigns = self._db.read() - - if len(role_names) == 0: - # Delete the user, but don't worry if there is no current - # assignment. - try: - del assigns[user_name] - except KeyError: - pass - else: - assigns[user_name] = role_names - - self._db.write((roles, assigns)) - finally: - self._db.unlock() - - ########################################################################### - # Trait handlers. - ########################################################################### - - def __db_default(self): - """Return the default persisted database.""" - - return Persistent(self._db_factory, 'ets_perms_policydb', - "the policy database") - - ########################################################################### - # Private interface. - ########################################################################### - - def _readonly_copy(self): - """Return the policy database (which should not be modified).""" - - try: - self._db.lock() - - try: - data = self._db.read() - finally: - self._db.unlock() - except PersistentError as e: - raise PolicyStorageError(str(e)) - - return data - - @staticmethod - def _db_factory(): - """Return an empty policy database.""" - - return ({}, {}) diff --git a/apptools/permissions/default/role_assignment.py b/apptools/permissions/default/role_assignment.py deleted file mode 100644 index f5b3aeb06..000000000 --- a/apptools/permissions/default/role_assignment.py +++ /dev/null @@ -1,178 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from pyface.api import error -from traits.api import Dict, Instance -from traitsui.api import Group, Handler, Item, SetEditor, View -from traitsui.menu import Action, CancelButton - -# Local imports. -from apptools.permissions.package_globals import get_permissions_manager -from .i_policy_storage import PolicyStorageError -from .policy_data import Assignment, Role - - -class _AssignmentView(View): - """The view for handling role assignments.""" - - #### 'View' interface ##################################################### - - kind = 'modal' - - title = "Assign roles" - - ########################################################################### - # 'object' interface. - ########################################################################### - - def __init__(self, all_roles, **traits): - """Initialise the object.""" - - buttons = [Action(name="Search"), Action(name="Save"), CancelButton] - - roles_editor = SetEditor(values=list(all_roles.values()), - left_column_title="Available Roles", - right_column_title="Assigned Roles") - - roles_group = Group(Item(name='roles', editor=roles_editor), - label='Roles', show_border=True, show_labels=False) - - super(_AssignmentView, self).__init__(Item(name='user_name'), - Item(name='description', style='readonly'), roles_group, - buttons=buttons, **traits) - - -class _AssignmentHandler(Handler): - """The view handler for role assignments.""" - - #### Private interface #################################################### - - all_roles = Dict - - ########################################################################### - # Trait handlers. - ########################################################################### - - def _search_clicked(self, info): - """Invoked by the "Search" button.""" - - pm = get_permissions_manager() - assignment = self._assignment(info) - - user = pm.user_manager.matching_user(assignment.user_name) - if user is None: - return - - try: - user_name, role_names = pm.policy_manager.policy_storage.get_assignment(user.name) - except PolicyStorageError as e: - self._ps_error(e) - return - - # Update the viewed object. - assignment.user_name = user.name - assignment.description = user.description - assignment.roles = self._roles_to_list(role_names) - - def _save_clicked(self, info): - """Invoked by the "Save" button.""" - - assignment = self._validate(info) - if assignment is None: - return - - # Update the data in the database. - try: - get_permissions_manager().policy_manager.policy_storage.set_assignment(assignment.user_name, [r.name for r in assignment.roles]) - - info.ui.dispose() - except PolicyStorageError as e: - self._ps_error(e) - - ########################################################################### - # Private interface. - ########################################################################### - - def _validate(self, info): - """Validate the assignment and return it if there were no problems.""" - - assignment = self._assignment(info) - assignment.user_name = assignment.user_name.strip() - - if not assignment.user_name: - self._error("A user name must be given.") - return None - - return assignment - - def _roles_to_list(self, role_names): - """Return a list of Role instances created from the given list of role - names.""" - - rl = [] - - for name in role_names: - try: - r = self.all_roles[name] - except KeyError: - # This shouldn't happen if the policy database is maintaining - # referential integrity. - continue - - rl.append(r) - - return rl - - @staticmethod - def _assignment(info): - """Return the assignment instance being handled.""" - - return info.ui.context['object'] - - @staticmethod - def _error(msg): - """Display an error message to the user.""" - - error(None, msg) - - @staticmethod - def _ps_error(e): - """Display a message to the user after a PolicyStorageError exception - has been raised.""" - - error(None, str(e)) - - -def role_assignment(): - """Implement the role assignment for the current policy manager.""" - - # Create a dictionary of roles keyed by the role name. - all_roles = {} - - try: - roles = get_permissions_manager().policy_manager.policy_storage.all_roles() - except PolicyStorageError as e: - error(None, str(e)) - return - - for name, description in roles: - all_roles[name] = Role(name=name, description=description) - - assignment = Assignment() - view = _AssignmentView(all_roles) - handler = _AssignmentHandler(all_roles=all_roles) - - assignment.edit_traits(view=view, handler=handler) diff --git a/apptools/permissions/default/role_definition.py b/apptools/permissions/default/role_definition.py deleted file mode 100644 index e3b3bbd8c..000000000 --- a/apptools/permissions/default/role_definition.py +++ /dev/null @@ -1,204 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from pyface.api import confirm, error, YES -from traits.api import Instance -from traitsui.api import Group, Handler, Item, SetEditor, View -from traitsui.menu import Action, CancelButton - -# Local imports. -from apptools.permissions.package_globals import get_permissions_manager -from apptools.permissions.permission import Permission -from .i_policy_storage import PolicyStorageError -from .policy_data import Role -from .select_role import select_role - - -class _RoleView(View): - """The view for handling roles.""" - - #### 'View' interface ##################################################### - - kind = 'modal' - - title = "Define roles" - - ########################################################################### - # 'object' interface. - ########################################################################### - - def __init__(self, **traits): - """Initialise the object.""" - - buttons = [Action(name="Search"), Action(name="Add"), - Action(name="Modify"), Action(name="Delete"), CancelButton] - - all_perms = list(get_permissions_manager().policy_manager.permissions.values()) - - perms_editor = SetEditor(values=all_perms, - left_column_title="Available Permissions", - right_column_title="Assigned Permissions") - - perms_group = Group(Item(name='permissions', editor=perms_editor), - label='Permissions', show_border=True, show_labels=False) - - super(_RoleView, self).__init__(Item(name='name'), - Item(name='description'), perms_group, buttons=buttons, - **traits) - - -class _RoleHandler(Handler): - """The view handler for roles.""" - - ########################################################################### - # Trait handlers. - ########################################################################### - - def _search_clicked(self, info): - """Invoked by the "Search" button.""" - - role = self._role(info) - - # Get all roles that satisfy the criteria. - try: - roles = get_permissions_manager().policy_manager.policy_storage.matching_roles(role.name) - except PolicyStorageError as e: - self._ps_error(e) - return - - if len(roles) == 0: - self._error("There is no role that matches \"%s\"." % role.name) - return - - name, description, perm_ids = select_role(roles) - - if name: - # Update the viewed object. - role.name = name - role.description = description - role.permissions = self._perms_to_list(perm_ids) - - def _add_clicked(self, info): - """Invoked by the "Add" button.""" - - role = self._validate(info) - if role is None: - return - - # Add the data to the database. - try: - get_permissions_manager().policy_manager.policy_storage.add_role( - role.name, role.description, - [p.id for p in role.permissions]) - info.ui.dispose() - except PolicyStorageError as e: - self._ps_error(e) - - def _modify_clicked(self, info): - """Invoked by the "Modify" button.""" - - role = self._validate(info) - if role is None: - return - - # Update the data in the database. - try: - get_permissions_manager().policy_manager.policy_storage.modify_role( - role.name, role.description, - [p.id for p in role.permissions]) - info.ui.dispose() - except PolicyStorageError as e: - self._ps_error(e) - - def _delete_clicked(self, info): - """Invoked by the "Delete" button.""" - - role = self._validate(info) - if role is None: - return - - if confirm(None, "Are you sure you want to delete the role \"%s\"?" % role.name) == YES: - # Delete the data from the database. - try: - get_permissions_manager().policy_manager.policy_storage.delete_role(role.name) - - info.ui.dispose() - except PolicyStorageError as e: - self._ps_error(e) - - ########################################################################### - # Private interface. - ########################################################################### - - def _validate(self, info): - """Validate the role and return it if there were no problems.""" - - role = self._role(info) - role.name = role.name.strip() - - if not role.name: - self._error("A role name must be given.") - return None - - return role - - def _perms_to_list(self, perm_ids): - """Return a list of Permission instances created from the given list of - permission ids.""" - - pl = [] - - for id in perm_ids: - try: - p = get_permissions_manager().policy_manager.permissions[id] - except KeyError: - # FIXME: permissions should be populated from the policy - # database - or is it needed at all? Should it just be read - # when managing roles? - p = Permission(id=id, application_defined=False) - - pl.append(p) - - return pl - - @staticmethod - def _role(info): - """Return the role instance being handled.""" - - return info.ui.context['object'] - - @staticmethod - def _error(msg): - """Display an error message to the user.""" - - error(None, msg) - - @staticmethod - def _ps_error(e): - """Display a message to the user after a PolicyStorageError exception - has been raised.""" - - error(None, str(e)) - - -def role_definition(): - """Implement the role definition for the current policy manager.""" - - role = Role() - view = _RoleView() - handler = _RoleHandler() - - role.edit_traits(view=view, handler=handler) diff --git a/apptools/permissions/default/select_role.py b/apptools/permissions/default/select_role.py deleted file mode 100644 index c81eb18b1..000000000 --- a/apptools/permissions/default/select_role.py +++ /dev/null @@ -1,75 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from traits.api import HasTraits, Instance, List, Unicode -from traitsui.api import Item, TableEditor, View -from traitsui.menu import OKCancelButtons -from traitsui.table_column import ObjectColumn - - -class _Role(HasTraits): - """This represents the role model.""" - - #### '_RoleModel' interface ############################################### - - # The role name. - name = Unicode - - # The role description. - description = Unicode - - # The permissions ids. - permissions = List - - -class _RolesView(HasTraits): - """This represents the view used to select a role.""" - - #### '_UsersView' interface ############################################### - - # The list of roles to select from. - model = List(_Role) - - # The selected user. - selection = Instance(_Role) - - # The editor used by the view. - table_editor = TableEditor(columns=[ObjectColumn(name='name'), - ObjectColumn(name='description')], - selected='selection', sort_model=True, configurable=False) - - # The default view. - traits_view = View(Item('model', show_label=False, editor=table_editor), - title="Select a Role", style='readonly', kind='modal', - buttons=OKCancelButtons) - - -def select_role(roles): - """Return a single role from the given list of roles.""" - - # Construct the model. - model = [_Role(name=name, description=description, permissions=permissions) - for name, description, permissions in roles] - - # Construct the view. - view = _RolesView(model=model) - - if view.configure_traits() and view.selection is not None: - role = view.selection.name, view.selection.description, view.selection.permissions - else: - role = '', '', [] - - return role diff --git a/apptools/permissions/default/select_user.py b/apptools/permissions/default/select_user.py deleted file mode 100644 index f41f0e123..000000000 --- a/apptools/permissions/default/select_user.py +++ /dev/null @@ -1,72 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from traits.api import HasTraits, Instance, List, Unicode -from traitsui.api import Item, TableEditor, View -from traitsui.menu import OKCancelButtons -from traitsui.table_column import ObjectColumn - - -class _User(HasTraits): - """This represents the user model.""" - - #### '_User' interface #################################################### - - # The user name. - name = Unicode - - # The user description. - description = Unicode - - -class _UsersView(HasTraits): - """This represents the view used to select a user.""" - - #### '_UsersView' interface ############################################### - - # The list of users to select from. - model = List(_User) - - # The selected user. - selection = Instance(_User) - - # The editor used by the view. - table_editor = TableEditor(columns=[ObjectColumn(name='name'), - ObjectColumn(name='description')], - selected='selection', sort_model=True, configurable=False) - - # The default view. - traits_view = View(Item('model', show_label=False, editor=table_editor), - title="Select a User", style='readonly', kind='modal', - buttons=OKCancelButtons) - - -def select_user(users): - """Return a single user from the given list of users.""" - - # Construct the model. - model = [_User(name=name, description=description) - for name, description in users] - - # Construct the view. - view = _UsersView(model=model) - - if view.configure_traits() and view.selection is not None: - user = view.selection.name, view.selection.description - else: - user = '', '' - - return user diff --git a/apptools/permissions/default/user_database.py b/apptools/permissions/default/user_database.py deleted file mode 100644 index d7d88824a..000000000 --- a/apptools/permissions/default/user_database.py +++ /dev/null @@ -1,518 +0,0 @@ -#------------------------------------------------------------------------------ -# 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: -#------------------------------------------------------------------------------ - - -# Standard library imports. -import os - -# Enthought library imports. -from pyface.api import confirm, error, YES -from traits.api import Bool, Dict, HasTraits, provides, Instance, \ - List, Password, Property, Unicode -from traitsui.api import Handler, Item, View -from traitsui.menu import Action, OKCancelButtons - -# Local imports. -from apptools.permissions.i_user import IUser -from .i_user_database import IUserDatabase -from .i_user_storage import IUserStorage, UserStorageError -from .select_user import select_user - - -@provides(IUser) -class _LoginUser(HasTraits): - """This represents the login data and view.""" - - #### '_LoginUser' interface ############################################### - - # The user name. - name = Unicode - - # The user password. - password = Password - - # The default view. - traits_view = View(Item(name='name'), Item(name='password'), - title="Login", kind='modal', buttons=OKCancelButtons) - - -class _ChangePassword(HasTraits): - """This represents the change password data and view.""" - - #### '_ChangePassword' interface ########################################## - - # The user name. - name = Unicode - - # The new user password. - new_password = Password - - # The confirmed new user password. - confirm_new_password = Password - - # The default view. - traits_view = View(Item(name='name', style='readonly'), - Item(name='new_password'), Item(name='confirm_new_password'), - title="Change password", kind='modal', buttons=OKCancelButtons) - - -class _ViewUserAccount(HasTraits): - """This represents a single account when in a view.""" - - #### '_ViewUserAccount' interface ######################################### - - # The name the user uses to identify themselves. - name = Unicode - - # A description of the user (typically their full name). - description = Unicode - - # The password - password = Password - - # The password confirmation. - confirm_password = Password - - # The user database. - user_db = Instance(IUserDatabase) - - -class _UserAccountHandler(Handler): - """The base traits handler for user account views.""" - - ########################################################################### - # 'Handler' interface. - ########################################################################### - - def close(self, info, is_ok): - """Reimplemented to validate the data.""" - - # If the user cancelled then close the dialog. - if not is_ok: - return True - - # Validate the form, only closing the dialog if the form is valid. - return self.validate(self._user_account(info)) - - ########################################################################### - # '_UserAccountHandler' interface. - ########################################################################### - - def validate(self, vuac): - """Validate the given object and return True if there were no problems. - """ - - if not vuac.name.strip(): - self.error("A user name must be given.") - return False - - return True - - @staticmethod - def error(msg): - """Display an error message to the user.""" - - error(None, msg) - - ########################################################################### - # Trait handlers. - ########################################################################### - - def _search_clicked(self, info): - """Invoked by the "Search" button.""" - - # Get the user name. - vuac = self._user_account(info) - - name, description = vuac.user_db._select_user(vuac.name) - if not name: - return - - # Update the viewed object. - vuac.name = name - vuac.description = description - vuac.password = vuac.confirm_password = '' - - ########################################################################### - # Private interface. - ########################################################################### - - @staticmethod - def _user_account(info): - """Return the user account instance being handled.""" - - return info.ui.context['object'] - - -class _AddUserAccountHandler(_UserAccountHandler): - """The traits handler for the add user account view.""" - - ########################################################################### - # '_UserAccountHandler' interface. - ########################################################################### - - def validate(self, vuac): - """Validate the given object and return True if there were no problems. - """ - - if not super(_AddUserAccountHandler, self).validate(vuac): - return False - - if not _validate_password(vuac.password, vuac.confirm_password): - return False - - return True - - -class _UserAccountView(View): - """The base view for handling user accounts.""" - - #### 'View' interface ##################################################### - - kind = 'modal' - - ########################################################################### - # Trait handlers. - ########################################################################### - - def _buttons_default(self): - """Return the view's buttons.""" - - # Create an action that will search the database. - buttons = [Action(name="Search")] - buttons.extend(OKCancelButtons) - - return buttons - - -class _AddUserAccountView(_UserAccountView): - """A view to handle adding a user account.""" - - #### 'View' interface ##################################################### - - title = "Add a user" - - ########################################################################### - # 'object' interface. - ########################################################################### - - def __init__(self, **traits): - """Initialise the object.""" - - super(_AddUserAccountView, self).__init__(Item(name='name'), - Item(name='description'), Item(name='password'), - Item(name='confirm_password'), **traits) - - -class _ModifyUserAccountView(_AddUserAccountView): - """A view to handle modifying a user account.""" - - #### 'View' interface ##################################################### - - title = "Modify a user" - - -class _DeleteUserAccountView(_UserAccountView): - """A view to handle deleting a user account.""" - - #### 'View' interface ##################################################### - - title = "Delete a user" - - ########################################################################### - # 'object' interface. - ########################################################################### - - def __init__(self, **traits): - """Initialise the object.""" - - super(_DeleteUserAccountView, self).__init__(Item(name='name'), - Item(name='description', style='readonly'), **traits) - - -class User(HasTraits): - """The user implementation. We don't store any extra information other - than that defined by IUser.""" - - - - #### 'IUser' interface #################################################### - - name = Unicode - - authenticated = Bool(False) - - description = Unicode - - blob = Dict - - -@provides(IUserDatabase) -class UserDatabase(HasTraits): - """This implements a user database that supports IUser for the default user - manager (ie. using password authorisation) except that it leaves the actual - access of the data to an implementation of IUserStorage.""" - - - - #### 'IUserDatabase' interface ############################################ - - can_change_password = Property - - can_add_user = Property - - can_modify_user = Property - - can_delete_user = Property - - user_storage = Instance(IUserStorage) - - #### Private interface #################################################### - - # Set if updating the user blob internally. - _updating_blob_internally = Bool(False) - - ########################################################################### - # 'IUserDatabase' interface. - ########################################################################### - - def bootstrapping(self): - """See if we are bootstrapping.""" - - try: - bootstrap = self.user_storage.is_empty() - except UserStorageError: - # Suppress the error and assume it isn't empty. - bootstrap = False - - return bootstrap - - def authenticate_user(self, user): - """Authenticate a user.""" - - # Get the login details. - name = user.name - lu = _LoginUser(name=name) - - if not lu.edit_traits().result: - return False - - # Get the user account and compare passwords. - try: - name, description, blob = self.user_storage.authenticate_user( - lu.name.strip(), lu.password) - except UserStorageError as e: - self._us_error(e) - return False - - # Update the user details. - user.name = name - user.description = description - - # Suppress the trait notification. - self._updating_blob_internally = True - user.blob = blob - self._updating_blob_internally = False - - return True - - def unauthenticate_user(self, user): - """Unauthenticate a user.""" - - return self.user_storage.unauthenticate_user(user) - - def change_password(self, user): - """Change a user's password.""" - - # Get the new password. - name = user.name - np = _ChangePassword(name=name) - - if not np.edit_traits().result: - return - - # Validate the password. - if not _validate_password(np.new_password, np.confirm_new_password): - return - - # Update the password in the database. - try: - self.user_storage.update_password(name, np.new_password) - except UserStorageError as e: - self._us_error(e) - - def add_user(self): - """Add a user.""" - - # Get the data from the user. - vuac = _ViewUserAccount(user_db=self) - view = _AddUserAccountView() - handler = _AddUserAccountHandler() - - if vuac.edit_traits(view=view, handler=handler).result: - # Add the data to the database. - try: - self.user_storage.add_user(vuac.name.strip(), vuac.description, - vuac.password) - except UserStorageError as e: - self._us_error(e) - - def modify_user(self): - """Modify a user.""" - - # Get the data from the user. - vuac = _ViewUserAccount(user_db=self) - view = _ModifyUserAccountView() - handler = _UserAccountHandler() - - if vuac.edit_traits(view=view, handler=handler).result: - # Update the data in the database. - try: - self.user_storage.modify_user(vuac.name.strip(), - vuac.description, vuac.password) - except UserStorageError as e: - self._us_error(e) - - def delete_user(self): - """Delete a user.""" - - # Get the data from the user. - vuac = _ViewUserAccount(user_db=self) - view = _DeleteUserAccountView() - handler = _UserAccountHandler() - - if vuac.edit_traits(view=view, handler=handler).result: - # Make absolutely sure. - name = vuac.name.strip() - - if confirm(None, "Are you sure you want to delete the user \"%s\"?" % name) == YES: - # Delete the data from the database. - try: - self.user_storage.delete_user(name) - except UserStorageError as e: - self._us_error(e) - - def matching_user(self, name): - """Select a user.""" - - name, description = self._select_user(name) - if not name: - return None - - return User(name=name, description=description) - - def user_factory(self): - """Create a new user object.""" - - user = User(name=os.environ.get('USER', '')) - - # Monitor when the blob changes. - user.on_trait_change(self._blob_changed, name='blob') - - return user - - ########################################################################### - # Trait handlers. - ########################################################################### - - def _get_can_change_password(self): - """See if a user can change their password.""" - - return 'user_password' in self.user_storage.capabilities - - def _get_can_add_user(self): - """See if a user can be added.""" - - return 'user_add' in self.user_storage.capabilities - - def _get_can_modify_user(self): - """See if a user can be modified.""" - - return 'user_modify' in self.user_storage.capabilities - - def _get_can_delete_user(self): - """See if a user can be deleted.""" - - return 'user_delete' in self.user_storage.capabilities - - def _user_storage_default(self): - """Return the default storage for the user data.""" - - # Defer to an external storage manager if there is one. - try: - from apptools.permissions.external.user_storage import UserStorage - except ImportError: - from apptools.permissions.default.user_storage import UserStorage - - return UserStorage() - - def _blob_changed(self, user, tname, old, new): - """Invoked when the user's blob data changes.""" - - if not self._updating_blob_internally: - try: - self.user_storage.update_blob(user.name, user.blob) - except UserStorageError as e: - self._us_error(e) - - ########################################################################### - # Private interface. - ########################################################################### - - def _select_user(self, name): - """Select a user returning the data as a tuple of name and description. - """ - - # Get all users that satisfy the criteria. - try: - users = self.user_storage.matching_users(name) - except UserStorageError as e: - self._us_error(e) - return '', '' - - if len(users) == 0: - error(None, "There is no user that matches \"%s\"." % name) - return '', '' - - return select_user(users) - - @staticmethod - def _us_error(e): - """Display a message to the user after a UserStorageError exception has - been raised. If the message is empty then we assume the user has - already been informed.""" - - msg = str(e) - if msg: - error(None, msg) - - -def _validate_password(password, confirmation): - """Validate a password and return True if it is valid.""" - - MIN_PASSWORD_LEN = 6 - - if password != confirmation: - error(None, "The passwords do not match.") - return False - - if not password: - error(None, "A password must be given.") - return False - - if len(password) < MIN_PASSWORD_LEN: - error(None, "The password must be at least %d characters long." % MIN_PASSWORD_LEN) - return False - - return True diff --git a/apptools/permissions/default/user_manager.py b/apptools/permissions/default/user_manager.py deleted file mode 100644 index d720f208a..000000000 --- a/apptools/permissions/default/user_manager.py +++ /dev/null @@ -1,182 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from pyface.action.api import Action -from traits.api import Bool, Event, HasTraits, provides, \ - Instance, List, Unicode - -# Local imports. -from apptools.permissions.i_user import IUser -from apptools.permissions.i_user_manager import IUserManager -from apptools.permissions.package_globals import get_permissions_manager -from apptools.permissions.permission import ManageUsersPermission -from .i_user_database import IUserDatabase - - -@provides(IUserManager) -class UserManager(HasTraits): - """The default user manager implementation.""" - - - - #### 'IUserManager' interface ############################################# - - management_actions = List(Instance(Action)) - - user = Instance(IUser) - - user_actions = List(Instance(Action)) - - user_authenticated = Event(IUser) - - #### 'UserManager' interface ############################################## - - # The user database. - user_db = Instance(IUserDatabase) - - ########################################################################### - # 'IUserManager' interface. - ########################################################################### - - def bootstrapping(self): - """Return True if we are bootstrapping, ie. no users have been defined. - """ - - return self.user_db.bootstrapping() - - def authenticate_user(self): - """Authenticate the user.""" - - if self.user_db.authenticate_user(self.user): - self.user.authenticated = True - - # Tell the policy manager before everybody else. - get_permissions_manager().policy_manager.load_policy(self.user) - - self.user_authenticated = self.user - - def unauthenticate_user(self): - """Unauthenticate the user.""" - - if self.user.authenticated and self.user_db.unauthenticate_user(self.user): - self.user.authenticated = False - - # Tell the policy manager before everybody else. - get_permissions_manager().policy_manager.load_policy(None) - - self.user_authenticated = None - - def matching_user(self, name): - """Select a user.""" - - return self.user_db.matching_user(name) - - ########################################################################### - # Trait handlers. - ########################################################################### - - def _management_actions_default(self): - """Return the list of management actions.""" - - from apptools.permissions.secure_proxy import SecureProxy - - user_db = self.user_db - actions = [] - perm = ManageUsersPermission() - - if user_db.can_add_user: - act = Action(name="&Add a User...", on_perform=user_db.add_user) - actions.append(SecureProxy(act, permissions=[perm], show=False)) - - if user_db.can_modify_user: - act = Action(name="&Modify a User...", - on_perform=user_db.modify_user) - actions.append(SecureProxy(act, permissions=[perm], show=False)) - - if user_db.can_delete_user: - act = Action(name="&Delete a User...", - on_perform=user_db.delete_user) - actions.append(SecureProxy(act, permissions=[perm], show=False)) - - return actions - - def _user_actions_default(self): - """Return the list of user actions.""" - - actions = [] - - if self.user_db.can_change_password: - actions.append(_ChangePasswordAction()) - - return actions - - def _user_default(self): - """Return the default current user.""" - - return self.user_db.user_factory() - - def _user_db_default(self): - """Return the default user database.""" - - # Defer to an external user database if there is one. - try: - from apptools.permissions.external.user_database import UserDatabase - except ImportError: - from apptools.permissions.default.user_database import UserDatabase - - return UserDatabase() - - -class _ChangePasswordAction(Action): - """An action that allows the current user to change their password. It - isn't exported through actions/api.py because it is specific to this user - manager implementation.""" - - #### 'Action' interface ################################################### - - enabled = Bool(False) - - name = Unicode("&Change Password...") - - ########################################################################### - # 'object' interface. - ########################################################################### - - def __init__(self, **traits): - """Initialise the object.""" - - super(_ChangePasswordAction, self).__init__(**traits) - - get_permissions_manager().user_manager.on_trait_event(self._refresh_enabled, 'user_authenticated') - - ########################################################################### - # 'Action' interface. - ########################################################################### - - def perform(self, event): - """Perform the action.""" - - um = get_permissions_manager().user_manager - um.user_db.change_password(um.user) - - ########################################################################### - # Private interface. - ########################################################################### - - def _refresh_enabled(self, user): - """Invoked whenever the current user's authorisation state changes.""" - - self.enabled = user is not None diff --git a/apptools/permissions/default/user_storage.py b/apptools/permissions/default/user_storage.py deleted file mode 100644 index 3b17e5fb7..000000000 --- a/apptools/permissions/default/user_storage.py +++ /dev/null @@ -1,196 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from traits.api import HasTraits, Instance, provides - -# Local imports. -from .i_user_storage import IUserStorage, UserStorageError -from .persistent import Persistent, PersistentError - - -@provides(IUserStorage) -class UserStorage(HasTraits): - """This implements a user database that pickles its data in a local file. - """ - - - - #### 'IUserStorage' interface ############################################# - - capabilities = ['user_password', 'user_add', 'user_modify', 'user_delete'] - - #### Private interface #################################################### - - # The persisted database. The database itself is a dictionary, keyed by - # the user name, and with a value that is a tuple of the description, blob - # and clear text password. - _db = Instance(Persistent) - - ########################################################################### - # 'IUserStorage' interface. - ########################################################################### - - def add_user(self, name, description, password): - """Add a new user.""" - - self._db.lock() - - try: - users = self._db.read() - - if name in users: - raise UserStorageError("The user \"%s\" already exists." % name) - - users[name] = (description, {}, password) - self._db.write(users) - finally: - self._db.unlock() - - def authenticate_user(self, name, password): - """Return the tuple of the user name, description, and blob if the user - was successfully authenticated.""" - - users = self._readonly_copy() - - try: - description, blob, pword = users[name] - except KeyError: - raise UserStorageError("The name or password is invalid.") - - if password != pword: - raise UserStorageError("The name or password is invalid.") - - return name, description, blob - - def delete_user(self, name): - """Delete a user.""" - - self._db.lock() - - try: - users = self._db.read() - - try: - del users[name] - except KeyError: - raise UserStorageError("The user \"%s\" does not exist." % name) - - self._db.write(users) - finally: - self._db.unlock() - - def is_empty(self): - """See if the database is empty.""" - - return (len(self._readonly_copy()) == 0) - - def matching_users(self, name): - """Return the full name and description of all the users that match the - given name.""" - - # Return any user that starts with the name. - users = [(full_name, description) for full_name, (description, _, _) - in self._readonly_copy().items() if full_name.startswith(name)] - - return sorted(users) - - def modify_user(self, name, description, password): - """Update the description and password for the given user.""" - - self._db.lock() - - try: - users = self._db.read() - - try: - _, blob, _ = users[name] - except KeyError: - raise UserStorageError("The user \"%s\" does not exist." % name) - - users[name] = (description, blob, password) - self._db.write(users) - finally: - self._db.unlock() - - def update_blob(self, name, blob): - """Update the blob for the given user.""" - - self._db.lock() - - try: - users = self._db.read() - - try: - description, _, password = users[name] - except KeyError: - raise UserStorageError("The user \"%s\" does not exist." % name) - - users[name] = (description, blob, password) - self._db.write(users) - finally: - self._db.unlock() - - def unauthenticate_user(self, user): - """Unauthenticate the given user.""" - - # There is nothing to do. - return True - - def update_password(self, name, password): - """Update the password for the given user.""" - - self._db.lock() - - try: - users = self._db.read() - - try: - description, blob, _ = users[name] - except KeyError: - raise UserStorageError("The user \"%s\" does not exist." % name) - - users[name] = (description, blob, password) - self._db.write(users) - finally: - self._db.unlock() - - ########################################################################### - # Trait handlers. - ########################################################################### - - def __db_default(self): - """Return the default persisted database.""" - - return Persistent(dict, 'ets_perms_userdb', "the user database") - - ########################################################################### - # Private interface. - ########################################################################### - - def _readonly_copy(self): - """Return the user database (which should not be modified).""" - - try: - self._db.lock() - - try: - data = self._db.read() - finally: - self._db.unlock() - except PersistentError as e: - raise UserStorageError(str(e)) - - return data diff --git a/apptools/permissions/i_policy_manager.py b/apptools/permissions/i_policy_manager.py deleted file mode 100644 index 3f00cf83d..000000000 --- a/apptools/permissions/i_policy_manager.py +++ /dev/null @@ -1,42 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from pyface.action.api import Action -from traits.api import Instance, Interface, List - - -class IPolicyManager(Interface): - """The interface implemented by a policy manager. A policy manager defines - how permissions are assigned to users and stored. A default policy manager - is provided, but it may be replaced using the permissions manager.""" - - # The list of PyFace policy management actions implemented by this policy. - management_actions = List(Instance(Action)) - - # The list of permissions assigned to the current user. - user_permissions = List(Instance('apptools.permissions.api.Permission')) - - def bootstrapping(self): - """Return True if the policy manager is bootstrapping. Typically this - is when no permissions have been assigned.""" - - def load_user(self, user): - """Load the policy for the given user. user is an object that - implements IUser. If it is None then unload the policy for the current - user.""" - - def register_permission(self, permission): - """Register the given permission defined by the application.""" diff --git a/apptools/permissions/i_user.py b/apptools/permissions/i_user.py deleted file mode 100644 index f836f936f..000000000 --- a/apptools/permissions/i_user.py +++ /dev/null @@ -1,44 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, 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 library imports. -from traits.api import Bool, Dict, Interface, Unicode - - -class IUser(Interface): - """The interface implemented by a user (or principal).""" - - # The user's name, ie. how they identified themselves to the permissions - # policy. It is only valid if the authenticated trait is True. - name = Unicode - - # This is set if the user has been authenticated, ie. the name trait is - # valid. - authenticated = Bool(False) - - # An optional description of the user (eg. their full name). The exact - # meaning is defined by the user manager. - description = Unicode - - # This allows application defined, user specific data to be persisted in - # the user database. An application (or plugin) should save the data as a - # single value in the dictionary keyed on the application's (or plugin's) - # unique id. The data will be saved in the user database whenever it is - # changed, and will be read from the user database whenever the user is - # authenticated. - blob = Dict - - def __str__(self): - """Return a user friendly representation of the user.""" diff --git a/apptools/permissions/i_user_manager.py b/apptools/permissions/i_user_manager.py deleted file mode 100644 index 9ad01579a..000000000 --- a/apptools/permissions/i_user_manager.py +++ /dev/null @@ -1,59 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, 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 library imports. -from pyface.action.api import Action -from traits.api import Bool, Event, Instance, Interface, List - -# Local imports. -from .i_user import IUser - - -class IUserManager(Interface): - """The interface implemented by a user manager to manage users.""" - - # The list of PyFace management actions (ie. actions related to all users) - # implemented by this user manager. - management_actions = List(Instance(Action)) - - # The current user. - user = Instance(IUser) - - # The list of PyFace user actions (ie. actions related to the current user) - # implemented by this user manager. - user_actions = List(Instance(Action)) - - # This is fired whenever the currently authenticated user changes. It will - # be None if the current user isn't authenticated. - user_authenticated = Event(IUser) - - def bootstrapping(self): - """Return True if the user manager is bootstrapping. Typically this is - when no users have been defined.""" - - def authenticate_user(self): - """Authenticate (ie. login) the user. If successfully authenticated - all secured objects are re-enabled according to the user's permissions. - """ - - def unauthenticate_user(self): - """Unauthenticate (ie. logout) the user. All secured objects are - disabled.""" - - def select_user(self, name): - """Return an object that implements IUser for the user selected based - on the given name. If there was no user selected then return None. - How the name is interpreted (eg. as a regular expression) is determined - by the user manager.""" diff --git a/apptools/permissions/package_globals.py b/apptools/permissions/package_globals.py deleted file mode 100644 index f99fae2a3..000000000 --- a/apptools/permissions/package_globals.py +++ /dev/null @@ -1,39 +0,0 @@ -#------------------------------------------------------------------------------ -# 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: -#------------------------------------------------------------------------------ - - -# The permissions manager. -_permissions_manager = None - - -def get_permissions_manager(): - """Return the IPermissionsManager implementation, creating a - PermissionsManager instance if no other implementation has been set.""" - - global _permissions_manager - - if _permissions_manager is None: - from .permissions_manager import PermissionsManager - - _permissions_manager = PermissionsManager() - - return _permissions_manager - - -def set_permissions_manager(permissions_manager): - """Set the IPermissionsManager implementation to use.""" - - global _permissions_manager - - _permissions_manager = permissions_manager diff --git a/apptools/permissions/permission.py b/apptools/permissions/permission.py deleted file mode 100644 index c03dcacb3..000000000 --- a/apptools/permissions/permission.py +++ /dev/null @@ -1,105 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from traits.api import Bool, HasTraits, Property, Str, Unicode - -# Locals imports. -from .package_globals import get_permissions_manager - - -class Permission(HasTraits): - """A permission is the link between an application action and the current - user - if the user has a permission attached to the action then the user is - allowed to perform that action.""" - - #### 'Permission' interface ############################################### - - # The id of the permission. By convention a dotted format is used for the - # id with the id of the application being the first part. - id = Str - - # A user friendly description of the permission. - description = Unicode - - # Set if the current user has this permission. This is typically used with - # the enabled_when and visible_when traits of a TraitsUI Item object when - # the permission instance has been placed in the TraitsUI context. - granted = Property - - # Set if the permission should be granted automatically when bootstrapping. - # This is normally only ever set for permissions related to user management - # and permissions. The user manager determines exactly what is meant by - # "bootstrapping" but it is usually when it determines that no user or - # permissions information has been defined. - bootstrap = Bool(False) - - # Set if the permission has been defined by application code rather than as - # a result of loading the policy database. - application_defined = Bool(True) - - ########################################################################### - # 'object' interface. - ########################################################################### - - def __init__(self, **traits): - """Initialise the object.""" - - super(Permission, self).__init__(**traits) - - # Register the permission. - get_permissions_manager().policy_manager.register_permission(self) - - def __str__(self): - """Return a user friendly representation.""" - - s = self.description - if not s: - s = self.id - - return s - - ########################################################################### - # Trait handlers. - ########################################################################### - - def _get_granted(self): - """Check the user has this permission.""" - - return get_permissions_manager().check_permissions(self) - - -class ManagePolicyPermission(Permission): - """The standard permission for managing permissions policies.""" - - #### 'Permission' interface ############################################### - - id = Str('ets.permissions.manage_policy') - - description = Unicode(u"Manage permissions policy") - - bootstrap = Bool(True) - - -class ManageUsersPermission(Permission): - """The standard permission for managing permissions users.""" - - #### 'Permission' interface ############################################### - - id = Str('ets.permissions.manage_users') - - description = Unicode(u"Manage users") - - bootstrap = Bool(True) diff --git a/apptools/permissions/permissions_manager.py b/apptools/permissions/permissions_manager.py deleted file mode 100644 index d972fba01..000000000 --- a/apptools/permissions/permissions_manager.py +++ /dev/null @@ -1,104 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from traits.api import Bool, HasTraits, Instance - -# Local imports. -from .i_policy_manager import IPolicyManager -from .i_user_manager import IUserManager - - -class PermissionsManager(HasTraits): - """A singleton class that provides access to the current policy and user - managers.""" - - #### 'PermissionsManager' interface ####################################### - - # Set if bootstrap permissions should be automatically enabled in a - # bootstrap situation (ie. when no policy or user data has been defined). - # Bootstrap permissions are normally attached to actions used to define - # policy and user data. Normally this is True, unless policy and user data - # is to be managed by an external application. - allow_bootstrap_permissions = Bool(True) - - # The current policy manager. - policy_manager = Instance(IPolicyManager) - - # The current user manager. - user_manager = Instance(IUserManager) - - #### Private interface #################################################### - - # Set if we are bootstrapping. - _bootstrap = Bool - - ########################################################################### - # 'PermissionsManager' interface. - ########################################################################### - - def check_permissions(self, *permissions): - """Check that the current user has one or more of the given permissions - and return True if they have. permissions is a list of Permission - instances.""" - - # Get the current user and their assigned permissions. - user = self.user_manager.user - user_perms = self.policy_manager.user_permissions - - for perm in permissions: - # If this is a bootstrap permission then see if we are in a - # bootstrap situation. - if perm.bootstrap and self._bootstrap: - return True - - if user.authenticated and perm in user_perms: - return True - - return False - - ########################################################################### - # Trait handlers. - ########################################################################### - - def _policy_manager_default(self): - """Provide a default policy manager.""" - - # Defer to an external manager if there is one. - try: - from apptools.permissions.external.policy_manager import PolicyManager - except ImportError: - from apptools.permissions.default.policy_manager import PolicyManager - - return PolicyManager() - - def _user_manager_default(self): - """Provide a default user manager.""" - - # Defer to an external manager if there is one. - try: - from apptools.permissions.external.user_manager import UserManager - except ImportError: - from apptools.permissions.default.user_manager import UserManager - - return UserManager() - - def __bootstrap_default(self): - """Determine whether or not we are bootstrapping. We only want to do - it once as checking may involve one or more remote database queries.""" - - return (self.allow_bootstrap_permissions and - (self.policy_manager.bootstrapping() or - self.user_manager.bootstrapping())) diff --git a/apptools/permissions/secure_proxy.py b/apptools/permissions/secure_proxy.py deleted file mode 100644 index 3d1018921..000000000 --- a/apptools/permissions/secure_proxy.py +++ /dev/null @@ -1,180 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007, 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 library imports. -from traits.etsconfig.api import ETSConfig -from traitsui.api import Handler - -# Local imports. -from .adapter_base import AdapterBase -from .package_globals import get_permissions_manager - - -# Register the bundled adapters. -from .adapters import pyface_action - -if ETSConfig.toolkit == 'wx': - from .adapters import wx_window -elif ETSConfig.toolkit == 'qt4': - from .adapters import qt4_widget - - -class SecureProxy(object): - """The SecureProxy class is a wrapper for an object whose enabled and - visible states can be managed. It attaches one or more permissions to the - object and enables and shows the object only if those permissions allow it. - In all other respects it behaves exactly like the object. It is based on - Tomer Filiba's Object Proxy cookbook recipe.""" - - __slots__ = ('_ets', '__weakref__') - - def __init__(self, proxied, permissions, show=True): - """Initialise the instance. proxied is the object whose enabled and - visible states are managed according to the permissions of the current - user. permissions is a list of permissions to attach to the object. - show is set if the proxied object should be visible when it is - disabled.""" - - adapter = object.__getattribute__(self, '_ets') - - # Correct the current values. - if not show: - adapter.update_visible(adapter.get_visible()) - - adapter.update_enabled(adapter.get_enabled()) - - # Proxying (special cases). - def __getattribute__(self, name): - return getattr(object.__getattribute__(self, '_ets').proxied, name) - - def __delattr__(self, name): - delattr(object.__getattribute__(self, '_ets').proxied, name) - - def __setattr__(self, name, value): - object.__getattribute__(self, '_ets').setattr(name, value) - - def __nonzero__(self): - return bool(object.__getattribute__(self, '_ets').proxied) - - def __str__(self): - return str(object.__getattribute__(self, '_ets').proxied) - - def __repr__(self): - return repr(object.__getattribute__(self, '_ets').proxied) - - # Factories. - _special_names = [ - '__abs__', '__add__', '__and__', '__call__', '__cmp__', '__coerce__', - '__contains__', '__delitem__', '__delslice__', '__div__', '__divmod__', - '__eq__', '__float__', '__floordiv__', '__ge__', '__getitem__', - '__getslice__', '__gt__', '__hash__', '__hex__', '__iadd__', '__iand__', - '__idiv__', '__idivmod__', '__ifloordiv__', '__ilshift__', '__imod__', - '__imul__', '__int__', '__invert__', '__ior__', '__ipow__', - '__irshift__', '__isub__', '__iter__', '__itruediv__', '__ixor__', - '__le__', '__len__', '__long__', '__lshift__', '__lt__', '__mod__', - '__mul__', '__ne__', '__neg__', '__oct__', '__or__', '__pos__', - '__pow__', '__radd__', '__rand__', '__rdiv__', '__rdivmod__', - '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', - '__rfloorfiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', - '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', - '__rxor__', '__setitem__', '__setslice__', '__sub__', '__truediv__', - '__xor__', 'next', - ] - - @classmethod - def _ets_class_proxy(cls, theclass): - """Creates a proxy for the given class.""" - - def make_method(name): - def method(self, *args, **kw): - return getattr(object.__getattribute__(self, '_ets').proxied, name)(*args, **kw) - return method - - namespace = {} - for name in cls._special_names: - if hasattr(theclass, name) and not hasattr(cls, name): - namespace[name] = make_method(name) - - return type("%s(%s)" % (cls.__name__, theclass.__name__), (cls,), - namespace) - - def __new__(cls, proxied, permissions, show=True): - """Apply a set of permissions to an object. This may be done by - creating a proxy or by modifying the object in situ depending on its - type. - """ - - # Create the adapter. - adapter = AdapterBase.factory(proxied, permissions, show) - - # Try and adapt the object itself. - adapted = adapter.adapt() - - if adapted is None: - # Create a wrapper for the object. The cache is unique per - # deriving class to ensure there are no clashes with any - # sub-classes with the same name. - try: - cache = cls.__dict__['_ets_cache'] - except KeyError: - cls._ets_cache = cache = {} - - pclass = proxied.__class__ - try: - theclass = cache[pclass] - except KeyError: - cache[pclass] = theclass = cls._ets_class_proxy(pclass) - - adapted = object.__new__(theclass) - - # Save the adapter in the adapted object. - object.__setattr__(adapted, '_ets', adapter) - else: - # Correct the current values. - if not show: - adapter.update_visible(adapter.get_visible()) - - adapter.update_enabled(adapter.get_enabled()) - - # Save the adapter in the adapted object. - adapted._ets = adapter - - return adapted - - -class SecureHandler(Handler): - """The SecureHandler class is a sub-class of the TraitsUI Handler class - that ensures that the enabled and visible state of the items of a TraitsUI - view are updated when the user's authorisation state changes. - """ - - def __init__(self, **traits): - """Initialise the object.""" - - super(SecureHandler, self).__init__(**traits) - - get_permissions_manager().user_manager.on_trait_event(self._refresh, - 'user_authenticated') - - def init_info(self, info): - """Reimplemented to save the UIInfo object.""" - - self._info = info - - def _refresh(self): - """Invoked whenever the current user's authorisation state changes.""" - - # FIXME: This is (currently) an internal method. - self._info.ui._evaluate_when() diff --git a/docs/source/index.rst b/docs/source/index.rst index 9f69ab94c..3071c5fe2 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -6,10 +6,6 @@ AppTools Documentation :glob: appscripting/* - permissions/Introduction - permissions/ApplicationAPI - permissions/DefaultPolicyManagerDataAPI - permissions/DefaultUserManagerDataAPI preferences/* scripting/* undo/* diff --git a/docs/source/permissions/ApplicationAPI.rst b/docs/source/permissions/ApplicationAPI.rst deleted file mode 100644 index ee67077cd..000000000 --- a/docs/source/permissions/ApplicationAPI.rst +++ /dev/null @@ -1,290 +0,0 @@ -Application API -=============== - -This section provides an overview of the part of the ETS Permissions Framework -API used by application developers. The `Permissions Framework example`_ -demonstrates the API in use. An application typically uses the API to do the -following: - -- define permissions - -- apply permissions - -- user authentication - -- getting and setting user data - -- integrate management actions. - - -Defining Permissions --------------------- - -A permission is the object that determines the user's access to a part of an -application. While it is possible to apply the same permission to more than -one part of an application, it is generally a bad idea to do so as it makes it -difficult to separate them at a later date. - -A permission has an id and a human readable description. Permission ids -must be unique. By convention a dotted notation is used for ids to give -them a structure. Ids should at least be given an application or plugin -specific prefix to ensure their uniqueness. - -Conventionally all an applications permissions are defined in a single -``permissions.py`` module. The following is an extract of the example's -``permissions.py`` module:: - - from apptools.permissions.api import Permission - - # Add a new person. - NewPersonPerm = Permission(id='ets.permissions.example.person.new', - description=u"Add a new person") - - # Update a person's age. - UpdatePersonAgePerm = Permission(id='ets.permissions.example.person.age.update', - description=u"Update a person's age") - - # View or update a person's salary. - PersonSalaryPerm = Permission(id='ets.permissions.example.person.salary', - description=u"View or update a person's salary") - - -Applying Permissions --------------------- - -Permissions are applied to different parts of an applications GUI. When the -user has been granted a permission then the corresponding part of the GUI is -displayed normally. When the user is denied a permission then the -corresponding part of the GUI is disabled or completely hidden. - -Permissions can be applied to TraitsUI view items and to any object which can -be wrapped in a ``SecureProxy``. - - -TraitsUI View Items -................... - -Items in TraitsUI views have ``enabled_when`` and ``visible_when`` traits that -are evaluated to determine if the item should be enabled or visible -respectively. These are used to apply permissions by storing the relevant -permissions in the model so that they are available to the view. The -``enabled_when`` and ``visible_when`` traits then simply reference the -permission's ``granted`` trait. The ``granted`` trait automatically reflects -whether or not the user currently has the corresponding permission. - -In order for the view to be correctly updated when the user's permissions -change (ie. when they become authenticated) the view must use the -``SecureHandler`` handler. This handler is a simple sub-class of the standard -Traits ``Handler`` class. - -The following extract from the example shows a default view of the ``Person`` -object that enables the ``age`` item when the user has the -``UpdatePersonAgePerm`` permission and shows the ``salary`` item when the user -has the ``PersonSalaryPerm`` permission:: - - from apptools.permissions.api import SecureHandler - from traits.api import HasTraits, Int, Unicode - from traitsui.api import Item, View - - from permissions import UpdatePersonAgePerm, PersonSalaryPerm - - class Person(HasTraits): - """A simple example of an object model""" - - # Name. - name = Unicode - - # Age in years. - age = Int - - # Salary. - salary = Int - - # Define the default view with permissions attached. - age_perm = UpdatePersonAgePerm - salary_perm = PersonSalaryPerm - - traits_view = View( - Item(name='name'), - Item(name='age', enabled_when='object.age_perm.granted'), - Item(name='salary', visible_when='object.salary_perm.granted'), - handler=SecureHandler) - - -Wrapping in a SecureProxy -......................... - -Any object can have permissions applied by wrapping it in a ``SecureProxy`` -object. An adapter is used that manages the enabled and visible states of the -proxied object according to the current user's permissions. Otherwise the -proxy behaves just like the object being proxied. - -Adapters are included for the following types of object: - -- PyFace actions - -- PyFace widgets **FIXME:** TODO - -- Qt widgets - -- wx widgets - -See `Writing SecureProxy Adapters`_ for a description of how to write adapters -for other types of objects. - -The following extract from the example shows the wrapping of a standard PyFace -action and the application of the ``NewPersonPerm`` permission:: - - from apptools.permissions.api import SecureProxy - - from permissions import NewPersonPerm - - ... - - def _new_person_action_default(self): - """Trait initializer.""" - - # Create the action and secure it with the appropriate permission. - act = Action(name='New Person', on_perform=self._new_person) - act = SecureProxy(act, permissions=[NewPersonPerm]) - - return act - -A ``SecureProxy`` also accepts a ``show`` argument that, when set to -``False``, hides the object when it becomes disabled. - - -Authenticating the User ------------------------ - -The user manager supports the concept of the current user and is responsible -for authenticating the user (and subsequently unauthorising the user if -required). - -The code fragment to authenticate the current user is:: - - from apptools.permissions.api import get_permissions_manager - - get_permissions_Manager().user_manager.authenticate_user() - -Unauthorising the current user is done using the ``unauthenticate_user()`` -method. - -As a convenience two PyFace actions, called ``LoginAction`` and -``LogoutAction``, are provided that wrap these two methods. - -As a further convenience a PyFace menu manager, called ``UserMenuManager``, is -provided that contains all the user and management actions (see below) in the -permissions framework. This is used by the example. - -The user menu, login and logout actions can be imported from -``apptools.permissions.action.api``. - - -Getting and Setting User Data ------------------------------ - -The user manager has a ``user`` trait that is an object that implements the -``IUser`` interface. It is only valid once the user has been authenticated. - -The ``IUser`` interface has a ``blob`` trait that holds any binary data (as a -Python string). The data will be read when the user is authenticated. The -data will be written whenever it is changed. - - -Integrating Management Actions ------------------------------- - -Both policy and user managers can provide actions that provide access to -various management functions. Both have a ``management_actions`` trait that is -a list of PyFace actions that invoke appropriate dialogs that allow the user to -manage the policy and the user population appropriately. - -User managers also have a ``user_actions`` trait that is a list of PyFace -actions that invoke appropriate dialogs that allow the user to manage -themselves. For example, the default user manager provides an action that -allows a user to change their password. - -The default policy manager provides actions that allows roles to be defined in -terms of sets of permissions, and allows users to be assigned one or more -roles. - -The default user manager provides actions that allows users to be added, -modified and deleted. A user manager that integrates with an enterprise's -secure directory service may not provide any management actions. - -All management actions have appropriate permissions attached to them. - - -Writing SecureProxy Adapters ----------------------------- - -``SecureProxy`` will automatically handle most of the object types you will -want to apply permissions to. However it is possible to implement additional -adapters to support other object types. To do this you need to implement a -sub-class of ``AdapterBase`` and register it. - -Adapters tend to be one of two styles according to how the object's enabled -and visible states are changed. If the states are changed via attributes -(typically Traits based objects) then the adapter will cause a proxy to be -created for the object. If the states are changed via methods (typically -toolkit widgets) then the adapter will probably modify the object itself. We -will refer to these two styles as wrapping adapters and patching adapters -respectively. - -The following gives a brief overview of the ``AdapterBase`` class: - -``proxied`` - This instance attribute is a reference to the original object. - -``register_adapter(adapter, type, type, ...)`` - This is a class method that is used to register your adapter and one or - more object types that it handles. - -``adapt()`` - This is a method that should be reimplemented by patching adapters. (The - default implementation will cause a proxy to be created for wrapping - adapters.) This is where any patching of the ``proxied`` attribute is - done. The object returned will be returned by ``SecureProxy()`` and would - normally be the patched object - but can be any object. - -``setattr(name, value)`` - This method should be reimplemented by wrapping adapters to intercept the - setting of relevant attributes of the ``proxied`` object. The default - implementation should be used as the fallback for irrelevant attributes. - -``get_enabled()`` - This method must be reimplemented to return the current enabled state. - -``set_enabled(value)`` - This method must be reimplemented to set the enabled state to the given - value. - -``update_enabled(value)`` - This method is called by your adapter to set the desired value of the - enabled state. The actual state set will depend on the current user's - permissions. - -``get_visible()`` - This method must be reimplemented to return the current visible state. - -``set_visible(value)`` - This method must be reimplemented to set the visible state to the given - value. - -``update_visible(value)`` - This method is called by your adapter to set the desired value of the - visible state. The actual state set will depend on the current user's - permissions. - -The ``AdapterBase`` class is defined in `adapter_base.py`_. - -The `PyFace action adapter`_ is an example of a wrapping adapter. - -The `PyQt widget adapter`_ is an example of a patching adapter. - - -.. _`Permissions Framework example`: https://svn.enthought.com/enthought/browser/AppTools/trunk/examples/permissions/application/ -.. _`adapter_base.py`: https://svn.enthought.com/enthought/browser/AppTools/trunk/enthought/permissions/adapter_base.py -.. _`PyFace action adapter`: https://svn.enthought.com/enthought/browser/AppTools/trunk/enthought/permissions/adapters/pyface_action.py -.. _`PyQt widget adapter`: https://svn.enthought.com/enthought/browser/AppTools/trunk/enthought/permissions/adapters/qt4_widget.py diff --git a/docs/source/permissions/DefaultPolicyManagerDataAPI.rst b/docs/source/permissions/DefaultPolicyManagerDataAPI.rst deleted file mode 100644 index f56e0fe5e..000000000 --- a/docs/source/permissions/DefaultPolicyManagerDataAPI.rst +++ /dev/null @@ -1,31 +0,0 @@ -Default Policy Manager Data API -=============================== - -This section provides an overview of the part of the ETS Permissions Framework -API used by developers who want to store a policy manager's persistent data in -a more secure location (eg. a remote server) than that provided by the -default implementation. - -The API is defined by the default policy manager which uses roles to make it -easier to assign permissions to users. If this API isn't sufficiently -flexible, or if roles are inappropriate, then an alternative policy manager -should be implemented. - -The API is fully defined by the `IPolicyStorage interface`_. The default -implementation of this interface stores the policy database as a pickle in a -local file. - - -Overview of IPolicyStorage --------------------------- - -The `IPolicyStorage interface`_ defines a number of methods that must be -implemented to read and write to the policy database. The methods are designed -to be implemented using simple SQL statements. - -In the event of an error a method must raise the ``PolicyStorageError`` -exception. The string representation of the exception is used as an error -message that is displayed to the user. - - -.. _`IPolicyStorage interface`: https://svn.enthought.com/enthought/browser/AppTools/trunk/enthought/permissions/default/i_policy_storage.py diff --git a/docs/source/permissions/DefaultUserManagerDataAPI.rst b/docs/source/permissions/DefaultUserManagerDataAPI.rst deleted file mode 100644 index 5f5e9f28b..000000000 --- a/docs/source/permissions/DefaultUserManagerDataAPI.rst +++ /dev/null @@ -1,72 +0,0 @@ -Default User Manager Data API -============================= - -This section provides an overview of the part of the ETS Permissions Framework -API used by developers who want to store a user database in a more secure -location (eg. a remote server) than that provided by the default -implementation. - -The API is defined by the default user manager which uses password based -authorisation. If this API isn't sufficiently flexible, or if another method -of authorisation is used (biometrics for example) then an alternative user -manager should be implemented. - -The API is fully defined by the `IUserDatabase interface`_. This allows user -databases to be implemented that extend the `IUser interface`_ and store -additional user related data. If the user database is being persisted in -secure storage (eg. a remote RDBMS) then this could be used to store sensitive -data (eg. passwords for external systems) that shouldn't be stored as ordinary -preferences. - -In most cases there will be no requirement to store additional user related -data than that defined by ``IUser`` so the supplied `UserDatabase -implementation`_ (which provides all the GUI code required to implement the -`IUserDatabase interface`_) can be used. The `UserDatabase implementation`_ -delegates the access to the user database to an object implementing the -`IUserStorage interface`_. The default implementation of this interface -stores the user database as a pickle in a local file. - - -Overview of IUserStorage ------------------------- - -The `IUserStorage interface`_ defines a number of methods that must be -implemented to read and write to the user database. The methods are designed -to be implemented using simple SQL statements. - -In the event of an error a method must raise the ``UserStorageError`` -exception. The string representation of the exception is used as an error -message that is displayed to the user. - - -Overview of IUserDatabase -------------------------- - -The `IUserDatabase interface`_ defines a set of ``Bool`` traits, all beginning -with ``can_``, that describe the capabilities of a particular implementation. -For example, the ``can_add_user`` trait is set by an implementation if it -supports the ability to add a new user to the database. - -Each of these capability traits has a corresponding method which has the same -name except for the ``can_`` prefix. The method only needs to be implemented -if the corresponding traits is ``True``. The method, for example -``add_user()`` is called by the user manager to implement the capability. - -The interface has two other methods. - -The ``bootstrapping()`` method is called by the user manager to determine if -the database is bootstrapping. Typically this is when the database is empty -and no users have yet been defined. The permissions framework treats this -situation as a special case and is able to relax the enforcement of permissions -to allow users and permissions to be initially defined. - -The ``user_factory()`` method is called by the user manager to create a new -user object, ie. an object that implements the `IUser interface`_. This allows -an implementation to extend the `IUser interface`_ and store additional user -related data in the object if the ``blob`` trait proves insufficient. - - -.. _`IUser interface`: https://svn.enthought.com/enthought/browser/AppTools/trunk/enthought/permissions/i_user.py -.. _`IUserDatabase interface`: https://svn.enthought.com/enthought/browser/AppTools/trunk/enthought/permissions/default/i_user_database.py -.. _`IUserStorage interface`: https://svn.enthought.com/enthought/browser/AppTools/trunk/enthought/permissions/default/i_user_storage.py -.. _`UserDatabase implementation`: https://svn.enthought.com/enthought/browser/AppTools/trunk/enthought/permissions/default/user_database.py diff --git a/docs/source/permissions/Introduction.rst b/docs/source/permissions/Introduction.rst deleted file mode 100644 index 14ae3b623..000000000 --- a/docs/source/permissions/Introduction.rst +++ /dev/null @@ -1,215 +0,0 @@ -Permissions Framework - Introduction -==================================== - -The Permissions Framework is a component of the Enthought Tool Suite that -provides developers with the facility to limit access to parts of an -application unless the user is appropriately authorised. In other words it -enables and disables different parts of the GUI according to the identity of -the user. - -The framework includes an API to allow it to be integrated with an -organisation's existing security infrastructure, for example to look users up -in a corporate LDAP directory. - -The framework is completely configurable. Alternate implementations of all -major components can be provided if necessary. The default implementations -provide a simple local filesystem user database and allows roles to be defined -and assigned to users. - -The framework **does not** provide any facility for protecting access to data. -It is not possible to implement such protection in Python and using the file -security provided by a typical operating system. - - -Framework Concepts ------------------- - -The following are the concepts supported by the framework. - -- Permission - - A permission is the basic tool that a developer uses to specify that access - to a part of the application should be restricted. If the current user has - the permission then access is granted. A permission may be attached to a - PyFace action, to an item of a TraitsUI view, or to a GUI toolkit specific - widget. When the user is denied access, the corresponding GUI control is - disabled or completely hidden. - -- User - - Each application has a current user who is either *authorised* or - *unauthorised*. In order to become authorised a user must identify - themselves and authenticate that identity. - - An arbitrary piece of data (called a blob) can be associated with an - authorised user which (with user manager support) can be stored securely. - This might be used, for example, to store sensitive user preferences, or to - implement a roaming profile. - -- User Manager - - The user manager is responsible for authorising the current user and, - therefore, defines how that is done. It also provides information about the - user population to the policy manager. It may also, optionally, provide the - ability to manage the user population (eg. add or delete users). The user - manager must either maintain a persistent record of the user population, or - interface with an external user database or directory service. - - The default user manager uses password based authorisation. - - The user manager persists its data in a user database. The default user - manager provides an API so that different implementations of the user - database can be used (for example to store the data in an RDBMS, or to - integrate with an existing directory service). A default user database is - provided that pickles the data in a local file. - -- Policy Manager - - The policy manager is responsible for assigning permissions to users and for - determining the permissions assigned to the current user. To do this it must - maintain a persistent record of those assignments. - - The default policy manager supplied with the framework uses roles to make it - easier for an administrator to manage the relationships between permissions - and users. A role is defined as a named set of permissions, and a user may - have one or more roles assigned to them. - - The policy manager persists its data in a policy database. The default - policy manager provides an API so that different implementations of the - policy database can be used (for example to store the data in an RDBMS). A - default policy database is provided that pickles the data in a local file. - -- Permissions Manager - - The permissions manager is a singleton object used to get and set the current - policy and user managers. - - -Framework APIs --------------- - -The APIs provided by the permissions framework can be split into the following -groups. - -- `Application API`_ - - This part of the API is used by application developers. - -- `Policy Manager API`_ - - This is the interface that an alternative policy manager must implement. The - need to implement an alternative is expected to be very rare and so the API - isn't covered further. See the definition of the IPolicyManager interface - for the details. - -- `Default Policy Manager Data API`_ - - This part of the API is used by developers to store the policy's persistent - data in a more secure location (eg. on a remote server) than that provided by - the default implementation. - -- `User Manager API`_ - - This is the interface that an alternative user manager must implement. The - need to implement an alternative is expected to be very rare and so the API - isn't covered further. See the definition of the IUserManager interface for - the details. - -- `Default User Manager Data API`_ - - This part of the API is used by developers to store the user database in a - more secure location (eg. on a remote server) than that provided by the - default implementation. - -The complete API_ documentation is available as endo generated HTML. - - -What Do I Need to Reimplement? ------------------------------- - -The architecture of the permissions framework comprises several layers, each -of which can reimplemented to meet the requirements of a particular -environment. Hopefully the following questions and answers will clarify what -needs to be reimplemented depending on your environment. - -Q: Do you want to use roles to group permissions and assign them to users? - -A: If yes then use the supplied PolicyManager, otherwise provide your own - IPolicyManager implementation. - -Q: Do you want users to be authenticated using a password? - -A: If yes then use the supplied UserManager, otherwise provide your own - IUserManager implementation. - -Q: Does the IUser interface allow you to store all the user specific - information you need? - -A: If yes then use the supplied UserDatabase, otherwise provide your own - IUserDatabase implementation. - -Q: Do you want to store your user accounts as pickled data in a local file? - -A: If yes then use the supplied default, otherwise provide UserDatabase with - your own IUserStorage implementation. - -Q: Do you want to store your policy data (ie. roles and role assignments) as - pickled data in a local file? - -A: If yes then use the supplied default, otherwise provide PolicyManager with - your own IPolicyStorage implementation. - - -Deploying Alternative Managers ------------------------------- - -The permissions framework will first try to import the different managers from -the ``apptools.permissions.external`` namespace. The default managers are -only used if no alternative was found. Therefore, alternative managers should -be deployed as an egg containing that namespace. - -Specifically the framework looks for the following classes: - - ``PolicyManager`` from ``apptools.permissions.external.policy_manager`` - - ``PolicyStorage`` from ``apptools.permissions.external.policy_storage`` - - ``UserDatabase`` from ``apptools.permissions.external.user_database`` - - ``UserManager`` from ``apptools.permissions.external.user_manager`` - - ``UserStorage`` from ``apptools.permissions.external.user_storage`` - -The example server is such a package that provides PolicyStorage and -UserStorage implementations that use an XML-RPC based server to provide remote -(and consequently more secure) policy and user databases. - - -Using the Default Storage Implementations ------------------------------------------ - -The default policy and user managers both (again by default) persist their data -as pickles in local files called ``ets_perms_policydb`` and -``ets_perms_userdb`` respectively. By default these are stored in the -application's home directory (ie. that returned by -``ETSConfig.application_home``). - -Note that this directory is normally in the user's own directory structure -whereas it needs to be available to all users of the application. - -If the ``ETS_PERMS_DATA_DIR`` environment variable is set then its value is -used instead. - -The directory must be writeable by all users of the application. - -It should be restated that the default implementations do *not* provide secure -access to the permissions and user data. They are useful in a cooperative -environment and as working examples. - - -.. _API: api/index.html -.. _`Application API`: ApplicationAPI.html -.. _`Policy Manager API`: https://svn.enthought.com/enthought/browser/AppTools/trunk/enthought/permissions/i_policy_manager.py -.. _`Default Policy Manager Data API`: DefaultPolicyManagerDataAPI.html -.. _`User Manager API`: https://svn.enthought.com/enthought/browser/AppTools/trunk/enthought/permissions/i_user_manager.py -.. _`Default User Manager Data API`: DefaultUserManagerDataAPI.html diff --git a/examples/permissions/application/example.py b/examples/permissions/application/example.py deleted file mode 100644 index 7f7b0b5d2..000000000 --- a/examples/permissions/application/example.py +++ /dev/null @@ -1,72 +0,0 @@ -"""A simple example of using the permissions framework.""" - - -# Standard library imports. -import logging - -# Enthought library imports. -from pyface.api import GUI, YES -from pyface.workbench.api import Workbench - -# Local imports. -from workbench_window import ExampleWorkbenchWindow -from person import Person - - -# Log to stderr. -logger = logging.getLogger() -logger.addHandler(logging.StreamHandler()) -logger.setLevel(logging.DEBUG) - - -class ExampleWorkbench(Workbench): - """A simple example of using the workbench.""" - - #### 'Workbench' interface ################################################ - - # The factory (in this case simply a class) that is used to create - # workbench windows. - window_factory = ExampleWorkbenchWindow - - ########################################################################### - # Private interface. - ########################################################################### - - def _exiting_changed(self, event): - """Called when the workbench is exiting.""" - - if self.active_window.confirm('Ok to exit?') != YES: - event.veto = True - - -def main(argv): - """A simple example of using the workbench.""" - - # Create the GUI. - gui = GUI() - - # Create the workbench. - # - # fixme: I wouldn't really want to specify the state location here. - # Ideally this would be part of the GUI's as DOMs idea, and the state - # location would be an attribute picked up from the DOM hierarchy. This - # would also be the mechanism for doing 'confirm' etc... Let the request - # bubble up the DOM until somebody handles it. - workbench = ExampleWorkbench(state_location=gui.state_location) - - # Create the workbench window. - window = workbench.create_window(position=(300, 300), size=(800, 600)) - window.open() - - # This will cause a TraitsUI editor to be created. - window.edit(Person(name='fred', age=42, salary=50000)) - - # This will cause a toolkit specific editor to be created. - window.edit("This text is implemented by a toolkit specific widget.") - - # Start the GUI event loop. - gui.start_event_loop() - - -if __name__ == '__main__': - import sys; main(sys.argv) diff --git a/examples/permissions/application/permissions.py b/examples/permissions/application/permissions.py deleted file mode 100644 index 3820737cd..000000000 --- a/examples/permissions/application/permissions.py +++ /dev/null @@ -1,25 +0,0 @@ -"""The definitions of all the permissions used in the example.""" - - -from apptools.permissions.api import Permission - - -# Access to the Debug view. -DebugViewPerm = Permission(id='ets.permissions.example.debug.view', - description=u"Use the debug view") - -# Add a new person. -NewPersonPerm = Permission(id='ets.permissions.example.person.new', - description=u"Add a new person") - -# Update a person's age. -UpdatePersonAgePerm = Permission(id='ets.permissions.example.person.age.update', - description=u"Update a person's age") - -# View or update a person's salary. -PersonSalaryPerm = Permission(id='ets.permissions.example.person.salary', - description=u"View or update a person's salary") - -# Enable the example toolkit specific widget. -EnableWidgetPerm = Permission(id='ets.permissions.example.widget', - description=u"Enable the example toolkit specific widget") diff --git a/examples/permissions/application/person.py b/examples/permissions/application/person.py deleted file mode 100644 index 0eb0aceaa..000000000 --- a/examples/permissions/application/person.py +++ /dev/null @@ -1,42 +0,0 @@ -"""A simple example of an object model.""" - - -# Enthought library imports. -from apptools.permissions.api import SecureHandler -from traits.api import HasTraits, Int, Unicode -from traitsui.api import Item, View - -# Local imports. -from permissions import UpdatePersonAgePerm, PersonSalaryPerm - - -class Person(HasTraits): - """A simple example of an object model""" - - # Name. - name = Unicode - - # Age in years. - age = Int - - # Salary. - salary = Int - - # Define the default view with permissions attached. - age_perm = UpdatePersonAgePerm - salary_perm = PersonSalaryPerm - - traits_view = View( - Item(name='name'), - Item(name='age', enabled_when='object.age_perm.granted'), - Item(name='salary', visible_when='object.salary_perm.granted'), - handler=SecureHandler) - - ########################################################################### - # 'object' interface. - ########################################################################### - - def __str__(self): - """Return an informal string representation of the object.""" - - return self.name diff --git a/examples/permissions/application/secured_debug_view.py b/examples/permissions/application/secured_debug_view.py deleted file mode 100644 index 983310fb1..000000000 --- a/examples/permissions/application/secured_debug_view.py +++ /dev/null @@ -1,24 +0,0 @@ -"""The secured DebugView for the permissions framework example.""" - - -# Enthought library imports. -from pyface.workbench.debug.api import DebugView -from apptools.permissions.api import SecureProxy - -# Local imports. -from permissions import DebugViewPerm - - -class SecuredDebugView(DebugView): - """A secured DebugView.""" - - ########################################################################### - # 'IWorkbenchPart' interface. - ########################################################################### - - def create_control(self, parent): - """Reimplemented to secure the created control.""" - - control = DebugView.create_control(self, parent) - - return SecureProxy(control, permissions=[DebugViewPerm]) diff --git a/examples/permissions/application/toolkit_editor.py b/examples/permissions/application/toolkit_editor.py deleted file mode 100644 index 2f3203af3..000000000 --- a/examples/permissions/application/toolkit_editor.py +++ /dev/null @@ -1,63 +0,0 @@ -"""An editor implemented by a toolkit specific widget. This demonstrates the -ability to apply permissions to toolkit specific widgets. -""" - - -# Enthought library imports. -from traits.etsconfig.api import ETSConfig -from apptools.permissions.api import SecureProxy -from pyface.workbench.api import Editor - -# Local imports. -from permissions import EnableWidgetPerm - - -class ToolkitEditor(Editor): - """A workbench editor that displays string with a permission attached to - the toolkit widget. - """ - - def create_control(self, parent): - """Create the toolkit specific control.""" - - tk = ETSConfig.toolkit - - if tk == 'wx': - import wx - - control = wx.StaticText(parent, -1, self.obj, - style=wx.ALIGN_CENTER) - elif tk == 'qt4': - from PyQt4 import QtCore, QtGui - - control = QtGui.QLabel(self.obj, parent) - control.setAlignment(QtCore.Qt.AlignHCenter) - else: - raise ValueError, "unsupported toolkit: %s" % tk - - # Return the wrapped control. - return SecureProxy(control, [EnableWidgetPerm]) - - def destroy_control(self): - """Create the toolkit specific control.""" - - tk = ETSConfig.toolkit - - if tk == 'wx': - self.control.Destroy() - elif tk == 'qt4': - self.control.setParent(None) - - def _name_default(self): - """Show the toolkit in the editor name.""" - - return str(self) - - ########################################################################### - # 'object' interface. - ########################################################################### - - def __str__(self): - """Return an informal string representation of the object.""" - - return ETSConfig.toolkit + " Editor" diff --git a/examples/permissions/application/workbench_window.py b/examples/permissions/application/workbench_window.py deleted file mode 100644 index 5897df653..000000000 --- a/examples/permissions/application/workbench_window.py +++ /dev/null @@ -1,133 +0,0 @@ -"""The workbench window for the permissions framework example.""" - - -# Enthought library imports. -from pyface.action.api import Action, Group, MenuManager -from pyface.workbench.api import EditorManager, WorkbenchWindow -from pyface.workbench.api import Perspective, PerspectiveItem -from pyface.workbench.action.api import MenuBarManager -from pyface.workbench.action.api import ToolBarManager -from pyface.workbench.action.api import ViewMenuManager -from apptools.permissions.api import SecureProxy -from apptools.permissions.action.api import UserMenuManager -from traits.api import Callable, HasTraits, List, Instance - -# Local imports. -from permissions import NewPersonPerm -from person import Person -from toolkit_editor import ToolkitEditor - - -class ExampleEditorManager(EditorManager): - """An editor manager that supports the editor memento protocol.""" - - ####################################################################### - # 'IEditorManager' interface. - ####################################################################### - - def create_editor(self, window, obj, kind): - """Reimplemented to create an editor appropriate to the object being - edited. - """ - - if isinstance(obj, HasTraits): - # The superclass handles Traits objects. - editor = super(ExampleEditorManager, self).create_editor(window, obj, kind) - else: - # Assume it is handled by a toolkit specific editor. - editor = ToolkitEditor(window=window, obj=obj) - - return editor - - -class ExampleWorkbenchWindow(WorkbenchWindow): - """A simple example of using the workbench window.""" - - #### 'WorkbenchWindow' interface ########################################## - - # The available perspectives. - perspectives = [ - Perspective( - name = 'Foo', - contents = [ - PerspectiveItem(id='Black', position='bottom'), - PerspectiveItem(id='Debug', position='left') - ] - ), - - Perspective( - name = 'Bar', - contents = [ - PerspectiveItem(id='Debug', position='left') - ] - ) - ] - - #### Private interface #################################################### - - # The Exit action. - _exit_action = Instance(Action) - - # The New Person action. - _new_person_action = Instance(Action) - - ########################################################################### - # 'ApplicationWindow' interface. - ########################################################################### - - def _editor_manager_default(self): - """ Trait initializer. - - Here we return the replacement editor manager. - """ - - return ExampleEditorManager() - - def _menu_bar_manager_default(self): - """Trait initializer.""" - - file_menu = MenuManager(self._new_person_action, self._exit_action, - name='&File', id='FileMenu') - view_menu = ViewMenuManager(name='&View', id='ViewMenu', window=self) - user_menu = UserMenuManager(id='UserMenu', window=self) - - return MenuBarManager(file_menu, view_menu, user_menu, window=self) - - def _tool_bar_manager_default(self): - """Trait initializer.""" - - return ToolBarManager(self._exit_action, show_tool_names=False) - - ########################################################################### - # 'WorkbenchWindow' interface. - ########################################################################### - - def _views_default(self): - """Trait initializer.""" - - from secured_debug_view import SecuredDebugView - - return [SecuredDebugView(window=self)] - - ########################################################################### - # Private interface. - ########################################################################### - - def __exit_action_default(self): - """Trait initializer.""" - - return Action(name='E&xit', on_perform=self.workbench.exit) - - def __new_person_action_default(self): - """Trait initializer.""" - - # Create the action and secure it with the appropriate permission. - act = Action(name='New Person', on_perform=self._new_person) - act = SecureProxy(act, permissions=[NewPersonPerm]) - - return act - - def _new_person(self): - """Create a new person.""" - - self.workbench.edit(Person(name='New', age=100)) diff --git a/examples/permissions/server/enthought/__init__.py b/examples/permissions/server/enthought/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/permissions/server/enthought/permissions/__init__.py b/examples/permissions/server/enthought/permissions/__init__.py deleted file mode 100644 index 7e84d724d..000000000 --- a/examples/permissions/server/enthought/permissions/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007 by Enthought, Inc. -# All rights reserved. -#------------------------------------------------------------------------------ - -try: - __import__('pkg_resources').declare_namespace(__name__) -except: - pass - diff --git a/examples/permissions/server/enthought/permissions/external/__init__.py b/examples/permissions/server/enthought/permissions/external/__init__.py deleted file mode 100644 index 7e84d724d..000000000 --- a/examples/permissions/server/enthought/permissions/external/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -#------------------------------------------------------------------------------ -# Copyright (c) 2007 by Enthought, Inc. -# All rights reserved. -#------------------------------------------------------------------------------ - -try: - __import__('pkg_resources').declare_namespace(__name__) -except: - pass - diff --git a/examples/permissions/server/enthought/permissions/external/policy_storage.py b/examples/permissions/server/enthought/permissions/external/policy_storage.py deleted file mode 100644 index 59857bae6..000000000 --- a/examples/permissions/server/enthought/permissions/external/policy_storage.py +++ /dev/null @@ -1,120 +0,0 @@ -#------------------------------------------------------------------------------ -# 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 library imports. -from apptools.permissions.default.api import IPolicyStorage, \ - PolicyStorageError -from traits.api import HasTraits, provides - -# Local imports. -from proxy_server import ProxyServer - - -@provides(IPolicyStorage) -class PolicyStorage(HasTraits): - """This implements a policy database accessed via XML RPC.""" - - - - ########################################################################### - # 'IPolicyStorage' interface. - ########################################################################### - - def add_role(self, name, description, perm_ids): - """Add a new role.""" - - try: - ProxyServer.add_role(name, description, perm_ids, ProxyServer.key) - except Exception, e: - raise PolicyStorageError(ProxyServer.error(e)) - - def all_roles(self): - """Return a list of all roles.""" - - try: - return ProxyServer.all_roles(ProxyServer.key) - except Exception, e: - raise PolicyStorageError(ProxyServer.error(e)) - - def delete_role(self, name): - """Delete a role.""" - - try: - ProxyServer.delete_role(name, ProxyServer.key) - except Exception, e: - raise PolicyStorageError(ProxyServer.error(e)) - - def get_assignment(self, user_name): - """Return the details of the assignment for the given user name.""" - - try: - return ProxyServer.get_assignment(user_name, ProxyServer.key) - except Exception, e: - raise PolicyStorageError(ProxyServer.error(e)) - - def get_policy(self, name): - """Return the details of the policy for the given user name.""" - - description, blob, perm_ids = ProxyServer.cache - - if ProxyServer.key is not None: - try: - name, perm_ids = ProxyServer.get_policy(name, ProxyServer.key) - except Exception, e: - raise PolicyStorageError(ProxyServer.error(e)) - - # Save the permissions ids in the persistent cache. - ProxyServer.cache = description, blob, perm_ids - - try: - ProxyServer.write_cache() - except: - pass - - return name, perm_ids - - def is_empty(self): - """See if the database is empty.""" - - try: - return ProxyServer.is_empty_policy() - except Exception, e: - raise PolicyStorageError(ProxyServer.error(e)) - - def matching_roles(self, name): - """Return the full name, description and permissions of all the roles - that match the given name.""" - - try: - return ProxyServer.matching_roles(name, ProxyServer.key) - except Exception, e: - raise PolicyStorageError(ProxyServer.error(e)) - - def modify_role(self, name, description, perm_ids): - """Update the description and permissions for the given role.""" - - try: - ProxyServer.modify_role(name, description, perm_ids, - ProxyServer.key) - except Exception, e: - raise PolicyStorageError(ProxyServer.error(e)) - - def set_assignment(self, user_name, role_names): - """Save the roles assigned to a user.""" - - try: - ProxyServer.set_assignment(user_name, role_names, ProxyServer.key) - except Exception, e: - raise PolicyStorageError(ProxyServer.error(e)) diff --git a/examples/permissions/server/enthought/permissions/external/proxy_server.py b/examples/permissions/server/enthought/permissions/external/proxy_server.py deleted file mode 100644 index c7795fa47..000000000 --- a/examples/permissions/server/enthought/permissions/external/proxy_server.py +++ /dev/null @@ -1,126 +0,0 @@ -#------------------------------------------------------------------------------ -# 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: -#------------------------------------------------------------------------------ - - -# Standard library imports. -import cPickle as pickle -import errno -import os -import socket -import xmlrpclib - -# Enthought library imports. -from traits.etsconfig.api import ETSConfig -from pyface.api import confirm, YES - - -# The default IP address to connect to. -DEFAULT_ADDR = socket.gethostbyname(socket.gethostname()) - -# The default port to connect to. -DEFAULT_PORT = 3800 - - -class ProxyServer(xmlrpclib.ServerProxy): - """This is a thin wrapper around xmlrpclib.ServerProxy that handles the - server address and error reporting.""" - - # The name of the user data cache file. - _cache_file = os.path.join(ETSConfig.application_home, - 'ets_perms_user_cache') - - def __init__(self): - """Initialise the object. The server address and TCP/IP port are taken - from the ETS_PERMS_SERVER environment variable if set.""" - - self._server = os.environ.get('ETS_PERMS_SERVER', - '%s:%d' % (DEFAULT_ADDR, DEFAULT_PORT)) - - xmlrpclib.ServerProxy.__init__(self, uri='http://%s' % self._server) - - # The session key. It is an empty string if the user is not - # authenticated and None if the user is in "disconnected" mode. - self.key = '' - - # The user data cache. This is a tuple of the user description, blob - # and list of permission ids when the session key is not an empty - # string. - self.cache = None - - def write_cache(self): - """Write the user cache to persistent storage.""" - - f = open(self._cache_file, 'w') - pickle.dump(self.cache, f) - f.close() - - def read_cache(self): - """Read the user cache from persistent storage. Returns False if - there was no cache to read.""" - - try: - f = open(self._cache_file, 'r') - - try: - if confirm(None, "It was not possible to connect to the " - "permissions server. Instead you can use the settings " - "used when you last logged in from this system. If " - "you do this then any changes made to settings " - "normally held on the permissions server will be lost " - "when you next login successfully.\n\nDo you want to " - "use the saved settings?") != YES: - raise Exception("") - - try: - self.cache = pickle.load(f) - except: - raise Exception("Unable to read %s." % self._cache_file) - finally: - f.close() - except IOError, e: - if e.errno == errno.ENOENT: - # There is no cache file. - return False - - raise Exception("Unable to open %s: %s." % (self._cache_file, e)) - - return True - - def error(self, e): - """Return a user friendly string describing the given exception.""" - - if isinstance(e, socket.error): - err, msg = e.args - - if err == errno.ECONNREFUSED: - emsg = "Unable to connect to permissions server at %s." % self._server - else: - emsg = "Socket error to permissions server at %s." % self._server - elif isinstance(e, xmlrpclib.Fault): - # Extract the text of the exception. If we don't recognise the - # format then display the lot. - tail = e.faultString.find(':') - if tail < 0: - emsg = "Unexpected exception from permissions server at %s: %s\n" % (self._server, e.faultString) - else: - emsg = e.faultString[tail + 1:] - else: - # Let any existing error handling deal with it. - raise e - - return emsg - - -# Create a singleton. -ProxyServer = ProxyServer() diff --git a/examples/permissions/server/enthought/permissions/external/user_storage.py b/examples/permissions/server/enthought/permissions/external/user_storage.py deleted file mode 100644 index 5efe25a02..000000000 --- a/examples/permissions/server/enthought/permissions/external/user_storage.py +++ /dev/null @@ -1,184 +0,0 @@ -#------------------------------------------------------------------------------ -# 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: -#------------------------------------------------------------------------------ - - -# 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 diff --git a/examples/permissions/server/server.py b/examples/permissions/server/server.py deleted file mode 100755 index 1ae1a401c..000000000 --- a/examples/permissions/server/server.py +++ /dev/null @@ -1,577 +0,0 @@ -#!/usr/bin/env python - -#------------------------------------------------------------------------------ -# 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: -#------------------------------------------------------------------------------ - - -# Standard library imports. -import errno -import logging -import os -import shelve -import SimpleXMLRPCServer -import socket -import sys -import time - - -# Log to stderr. -logger = logging.getLogger() -logger.addHandler(logging.StreamHandler()) -logger.setLevel(logging.INFO) - -# The default IP address to listen on. -DEFAULT_ADDR = socket.gethostbyname(socket.gethostname()) - -# The default port to listen on. -DEFAULT_PORT = 3800 - -# The default data directory. -DEFAULT_DATADIR = os.path.expanduser('~/.ets_perms_server') - -# The session timeout in seconds. -SESSION_TIMEOUT = 90 * 60 - - -class ServerImplementation(object): - """This is a container for all the functions implemented by the server.""" - - def __init__(self, data_dir, insecure, local_user_db): - """Initialise the object.""" - - self._data_dir = data_dir - self._insecure = insecure - self._local_user_db = local_user_db - - # Make sure we can call _close() at any time. - self._keys = self._roles = self._assignments = self._blobs = \ - self._users = None - - # Make sure the data directory exists. - if not os.path.isdir(self._data_dir): - os.mkdir(self._data_dir) - - # Load the data. - self._keys = self._open_shelf('keys') - self._roles = self._open_shelf('roles') - self._assignments = self._open_shelf('assignments') - self._blobs = self._open_shelf('blobs') - - if self._local_user_db: - self._users = self._open_shelf('users') - - # Remove any expired session keys. - now = time.time() - keys = self._keys.keys() - for k in keys: - name, perm_ids, used_at = self._keys[k] - - if now - used_at >= SESSION_TIMEOUT: - logger.info("Expiring session key for user %s" % name) - del self._keys[k] - - self._sync(self._keys) - - def _close(self): - """Close all the databases.""" - - if self._keys is not None: - self._keys.close() - self._keys = None - - if self._roles is not None: - self._roles.close() - self._roles = None - - if self._assignments is not None: - self._assignments.close() - self._assignments = None - - if self._blobs is not None: - self._blobs.close() - self._blobs = None - - if self._users is not None: - if self._local_user_db: - self._users.close() - - self._users = None - - def _open_shelf(self, name): - """Open a shelf.""" - - logger.info("Loading %s" % name) - - fname = os.path.join(self._data_dir, name) - - try: - return shelve.open(fname, writeback=True) - except Exception, e: - logger.error("Unable to open %s: %s" % (fname, e)) - sys.exit(1) - - @staticmethod - def _sync(shelf): - try: - shelf.sync() - except Exception, e: - logger.error("Unable to sync:" % e) - Exception("An error occurred on the permissions server.") - - def _session_data(self, key): - """Validate the session key and return the user name and list of - permission ids.""" - - # Get the session details. - try: - session_name, perm_ids, used_at = self._keys[key] - except KeyError: - # Force the timeout test to fail. - used_at = -SESSION_TIMEOUT - - # See if the session should be timed out. - now = time.time() - if now - used_at >= SESSION_TIMEOUT: - try: - del self._keys[key] - except KeyError: - pass - - self._sync(self._keys) - - raise Exception("Your session has timed out. Please login again.") - - # Update when the session was last used. - self._keys[key] = session_name, perm_ids, now - self._sync(self._keys) - - return session_name, perm_ids - - def _check_user(self, key, name): - """Check that the session is current and the session user matches the - given user and return the permission ids.""" - - session_name, perm_ids = self._session_data(key) - - if session_name != name: - raise Exception("You do not have the appropriate authority.") - - return perm_ids - - def _check_authorisation(self, key, perm_id): - """Check the user has the given permission.""" - - # Handle the easy cases first. - if self._insecure or self.is_empty_policy(): - return - - _, perm_ids = self._session_data(key) - - if perm_id not in perm_ids: - raise Exception("You do not have the appropriate authority.") - - def _check_policy_authorisation(self, key): - """Check that a policy management action is authorised.""" - - self._check_authorisation(key, 'ets.permissions.manage_policy') - - def _check_users_authorisation(self, key): - """Check that a users management action is authorised.""" - - self._check_authorisation(key, 'ets.permissions.manage_users') - - def capabilities(self): - """Return a list of capabilities that the implementation supports. The - full list is 'user_password', 'user_add', 'user_modify', 'user_delete'. - """ - - caps = ['user_password'] - - if self._local_user_db: - caps.extend(['user_add', 'user_modify', 'user_delete']) - - return caps - - def add_user(self, name, description, password, key=None): - """Add a new user.""" - - self._check_users_authorisation(key) - - if self._local_user_db: - if self._users.has_key(name): - raise Exception("The user \"%s\" already exists." % name) - - self._users[name] = (description, password) - self._sync(self._users) - else: - raise Exception("Adding a user isn't supported.") - - # Return a non-None value. - return True - - def authenticate_user(self, name, password): - """Return the tuple of the user name, description, and blob if the user - was successfully authenticated.""" - - if self._local_user_db: - try: - description, pword = self._users[name] - except KeyError: - raise Exception("The name or password is invalid.") - - if password != pword: - raise Exception("The name or password is invalid.") - else: - # FIXME - raise Exception("Authenticating a user isn't yet supported.") - - # Create the session key. The only reason for using a human readable - # string is to make the test client easier to use. We only make - # limited attempts at creating a unique key. - for i in range(5): - key = '' - - for ch in os.urandom(16): - key += '%02x' % ord(ch) - - if not self._keys.has_key(key): - break - else: - # Something is seriously wrong if we get here. - msg = "Unable to create unique session key." - logger.error(msg) - raise Exception(msg) - - # Get the user's permissions. - perm_ids = [] - - for r in self._assignments.get(name, []): - _, perms = self._roles[r] - perm_ids.extend(perms) - - # Save the session data. - self._keys[key] = name, perm_ids, time.time() - - return key, name, description, self._blobs.get(name, {}) - - def matching_users(self, name, key=None): - """Return the full name and description of all the users that match the - given name.""" - - self._check_users_authorisation(key) - - if self._local_user_db: - # Get any user that starts with the name. - users = [(full_name, description) for full_name, (description, _) - in self._users.items() - if full_name.startswith(name)] - - users = sorted(users) - else: - # FIXME - raise Exception("Searching for users isn't yet supported.") - - return users - - def modify_user(self, name, description, password, key=None): - """Update the description and password for the given user.""" - - self._check_users_authorisation(key) - - if self._local_user_db: - if not self._users.has_key(name): - raise Exception("The user \"%s\" does not exist." % name) - - self._users[name] = (description, password) - self._sync(self._users) - else: - raise Exception("Modifying a user isn't supported.") - - # Return a non-None value. - return True - - def delete_user(self, name, key=None): - """Delete a user.""" - - self._check_users_authorisation(key) - - if self._local_user_db: - try: - del self._users[name] - except KeyError: - raise UserStorageError("The user \"%s\" does not exist." % name) - - self._sync(self._users) - else: - raise Exception("Deleting a user isn't supported.") - - # Return a non-None value. - return True - - def unauthenticate_user(self, key=None): - """Unauthenticate the given user (ie. identified by the session key). - """ - - if not self._local_user_db: - # FIXME: LDAP may or may not need anything here. - raise Exception("Unauthenticating a user isn't yet supported.") - - # Invalidate any session key: - if key is not None: - try: - del self._keys[key] - except KeyError: - pass - - self._sync(self._keys) - - # Return a non-None value. - return True - - def update_blob(self, name, blob, key=None): - """Update the blob for the given user.""" - - self._check_user(key, name) - - self._blobs[name] = blob - self._sync(self._blobs) - - # Return a non-None value. - return True - - def update_password(self, name, password, key=None): - """Update the password for the given user.""" - - self._check_user(key, name) - - if not self._local_user_db: - try: - description, _ = self._users[name] - except KeyError: - raise Exception("The user \"%s\" does not exist." % name) - - self._users[name] = (description, password) - self._sync(self._users) - else: - # FIXME - raise Exception("Updating a user password isn't yet supported.") - - # Return a non-None value. - return True - - def add_role(self, name, description, perm_ids, key=None): - """Add a new role.""" - - self._check_policy_authorisation(key) - - if self._roles.has_key(name): - raise Exception("The role \"%s\" already exists." % name) - - self._roles[name] = (description, perm_ids) - self._sync(self._roles) - - # Return a non-None value. - return True - - def all_roles(self, key=None): - """Return a list of all roles.""" - - self._check_policy_authorisation(key) - - return [(name, description) - for name, (description, _) in self._roles.items()] - - def delete_role(self, name, key=None): - """Delete a role.""" - - self._check_policy_authorisation(key) - - if not self._roles.has_key(name): - raise Exception("The role \"%s\" does not exist." % name) - - del self._roles[name] - - # Remove the role from any users who have it. - for user, role_names in self._assignments.items(): - try: - role_names.remove(name) - except ValueError: - continue - - self._assignments[user] = role_names - - self._sync(self._roles) - self._sync(self._assignments) - - # Return a non-None value. - return True - - def get_assignment(self, user_name, key=None): - """Return the details of the assignment for the given user name.""" - - self._check_policy_authorisation(key) - - try: - role_names = self._assignments[user_name] - except KeyError: - return '', [] - - return user_name, role_names - - def get_policy(self, name, key=None): - """Return the details of the policy for the given user name.""" - - return name, self._check_user(key, name) - - def is_empty_policy(self): - """Return True if there is no useful data.""" - - empty = (len(self._roles) == 0 or len(self._assignments) == 0) - - # Include the users as well if the database is local. - if self._local_user_db and len(self._users) == 0: - empty = True - - return empty - - def matching_roles(self, name, key=None): - """Return the full name, description and permissions of all the roles - that match the given name.""" - - self._check_policy_authorisation(key) - - # Return any role that starts with the name. - roles = [(full_name, description, perm_ids) - for full_name, (description, perm_ids) in self._roles.items() - if full_name.startswith(name)] - - return sorted(roles) - - def modify_role(self, name, description, perm_ids, key=None): - """Update an existing role.""" - - self._check_policy_authorisation(key) - - if not self._roles.has_key(name): - raise Exception("The role \"%s\" does not exist." % name) - - self._roles[name] = (description, perm_ids) - self._sync(self._roles) - - # Return a non-None value. - return True - - def set_assignment(self, user_name, role_names, key=None): - """Save the roles assigned to a user.""" - - self._check_policy_authorisation(key) - - if len(role_names) == 0: - # Delete the user, but don't worry if there is no current - # assignment. - try: - del self._assignments[user_name] - except KeyError: - pass - else: - self._assignments[user_name] = role_names - - self._sync(self._assignments) - - # Return a non-None value. - return True - - -class RPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer): - """A thin wrapper around SimpleXMLRPCServer that handles its - initialisation.""" - - def __init__(self, addr=DEFAULT_ADDR, port=DEFAULT_PORT, - data_dir=DEFAULT_DATADIR, insecure=False, local_user_db=False): - """Initialise the object.""" - - SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, (addr, port)) - - self._impl = ServerImplementation(data_dir, insecure, local_user_db) - self.register_instance(self._impl) - - def server_close(self): - """Reimplemented to tidy up the implementation.""" - - SimpleXMLRPCServer.SimpleXMLRPCServer.server_close(self) - - self._impl._close() - - -if __name__ == '__main__': - - # Parse the command line. - import optparse - - p = optparse.OptionParser(description="This is an XML-RPC server that " - "provides user, role and permissions data to a user and policy " - "manager that is part of the ETS Permissions Framework.") - - p.add_option('--data-dir', default=DEFAULT_DATADIR, dest='data_dir', - metavar="DIR", - help="the server's data directory [default: %s]" % DEFAULT_DATADIR) - p.add_option('--insecure', action='store_true', default=False, - dest='insecure', - help="don't require a session key for data changes") - p.add_option('--ip-address', default=DEFAULT_ADDR, dest='addr', - help="the IP address to listen on [default: %s]" % DEFAULT_ADDR) - p.add_option('--local-user-db', action='store_true', default=False, - dest='local_user_db', - help="use a local user database instead of an LDAP directory") - p.add_option('--port', type='int', default=DEFAULT_PORT, dest='port', - help="the TCP port to listen on [default: %d]" % DEFAULT_PORT) - - opts, args = p.parse_args() - - if args: - p.error("unexpected additional arguments: %s" % " ".join(args)) - - # We need a decent RNG for session keys. - if not opts.insecure: - try: - os.urandom(1) - except AttributeError, NotImplementedError: - sys.stderr.write("os.urandom() isn't implemented so the --insecure flag must be used\n") - sys.exit(1) - - # FIXME: Add LDAP support. - if not opts.local_user_db: - sys.stderr.write("Until LDAP support is implemented use the --local-user-db flag\n") - sys.exit(1) - - # Create and start the server. - server = RPCServer(addr=opts.addr, port=opts.port, data_dir=opts.data_dir, - insecure=opts.insecure, local_user_db=opts.local_user_db) - - if opts.insecure: - logger.warn("Server starting in insecure mode") - else: - logger.info("Server starting") - - try: - try: - server.serve_forever() - except KeyboardInterrupt: - pass - else: - raise - finally: - server.server_close() - - logger.info("Server terminated") diff --git a/examples/permissions/server/setup.py b/examples/permissions/server/setup.py deleted file mode 100644 index 3f22fdcb4..000000000 --- a/examples/permissions/server/setup.py +++ /dev/null @@ -1,17 +0,0 @@ -# Major package imports. -from setuptools import setup, find_packages - -setup( - name = 'PermissionsServer', - version = '1.0', - author = 'Riverbank Computing Limited', - author_email = 'info@riverbankcomputing.com', - license = 'BSD', - zip_safe = True, - packages = find_packages(), - include_package_data = True, - - namespace_packages = [ - 'enthought' - ], -) diff --git a/examples/permissions/server/test_client.py b/examples/permissions/server/test_client.py deleted file mode 100755 index a85b42be2..000000000 --- a/examples/permissions/server/test_client.py +++ /dev/null @@ -1,284 +0,0 @@ -#!/usr/bin/env python - -#------------------------------------------------------------------------------ -# 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: -#------------------------------------------------------------------------------ - - -# Standard library imports. -import errno -import optparse -import socket -import sys -import xmlrpclib - - -# The default IP address to connect to. -DEFAULT_ADDR = socket.gethostbyname(socket.gethostname()) - -# The default port to connect to. -DEFAULT_PORT = 3800 - - -def check_blob(opts): - """Check the blob was specified with the --blob flag and return the value. - """ - - if not opts.blob: - raise Exception("the --blob flag to specify a blob") - - return opts.blob - - -def check_name(opts): - """Check the name was specified with the --name flag and return the value. - """ - - if not opts.name: - raise Exception("the --name flag to specify a name") - - return opts.name - - -def check_password(opts): - """Check the password was specified with the --password flag and return the - value.""" - - if not opts.password: - raise Exception("the --password flag to specify a password") - - return opts.password - - -def add_role(opts): - """Add a new role.""" - - return [check_name(opts), opts.description, opts.permissions, opts.key] - - -def add_user(opts): - """Add a new user.""" - - return [check_name(opts), opts.description, check_password(opts), opts.key] - - -def all_roles(opts): - """Return all roles.""" - - return [opts.key] - - -def authenticate_user(opts): - """Authenticate a user and return their details.""" - - return [check_name(opts), check_password(opts)] - - -def delete_role(opts): - """Delete a role.""" - - return [check_name(opts), opts.key] - - -def delete_user(opts): - """Delete a user.""" - - return [check_name(opts), opts.key] - - -def capabilities(opts): - """Get the capabilities of the server.""" - - return [] - - -def matching_roles(opts): - """Get the roles that match a name.""" - - return [opts.name, opts.key] - - -def matching_users(opts): - """Get the users that match a name.""" - - return [opts.name, opts.key] - - -def get_assignment(opts): - """Get the details of a user's assignment.""" - - return [check_name(opts), opts.key] - - -def get_policy(opts): - """Get the details of a user's policy.""" - - return [check_name(opts), opts.key] - - -def is_empty_policy(opts): - """Check if there is any policy data.""" - - return [] - - -def set_assignment(opts): - """Save the details of a user's assignment.""" - - return [check_name(opts), opts.roles, opts.key] - - -def unauthenticate_user(opts): - """Unauthenticate a user.""" - - return [opts.key] - - -def modify_role(opts): - """Modify an existing role.""" - - return [check_name(opts), opts.description, opts.permissions, opts.key] - - -def modify_user(opts): - """Modify an existing user.""" - - return [check_name(opts), opts.description, check_password(opts), opts.key] - - -def update_blob(opts): - """Update the blob for a user.""" - - return [check_name(opts), check_blob(opts)] - - -def update_password(opts): - """Update the password for a user.""" - - return [check_name(opts), check_password(opts)] - - -# The list of actions that can be invoked from the command line. -actions = [ - add_role, - add_user, - all_roles, - authenticate_user, - capabilities, - delete_role, - delete_user, - get_assignment, - get_policy, - is_empty_policy, - matching_roles, - matching_users, - modify_role, - modify_user, - set_assignment, - unauthenticate_user, - update_blob, - update_password, -] - - -def store_list(option, opt_str, value, p): - """An option parser callback that converts space separated words into a - list of strings.""" - - setattr(p.values, option.dest, value.split(' ')) - - -# Parse the command line. -p = optparse.OptionParser(usage="%prog [options] action", description="This " - "is a client used to test the XML-RPC server. The following actions " - "are supported: %s" % ', '.join([a.func_name for a in actions])) - -p.add_option('--ip-address', default=DEFAULT_ADDR, dest='addr', - help="the IP address to connect to [default: %s]" % DEFAULT_ADDR) -p.add_option('-b', '--blob', dest='blob', - help="a blob string (used by update_blob)") -p.add_option('-d', '--description', default='', dest='description', - help="a description (used by add_role, add_user, modify_role, " - "modify_user)") -p.add_option('-k', '--key', default='', dest='key', - help="the session key returned by login (used by add_role, add_user, " - "all_roles, delete_role, delete_user, get_assignment, " - "get_policy, modify_role, modify_user, set_assignment, " - "unauthenticate_user)") -p.add_option('-n', '--name', default='', dest='name', - help="a name (used by add_role, add_user, authenticate_user, " - "delete_role, delete_user, get_assignment, get_policy, " - "matching_roles, matching_users, modify_role, modify_user, " - "set_assignment, update_blob, update_user)") -p.add_option('--permissions', action='callback', callback=store_list, - default=[], dest='permissions', type='string', - help="a space separated list of permission names (used by add_role, " - "modify_role)") -p.add_option('-p', '--password', dest='password', - help="a password (used by add_user, authenticate_user, modify_user " - "update_password)") -p.add_option('--port', type='int', default=DEFAULT_PORT, dest='port', - help="the TCP port to connect to [default: %d]" % DEFAULT_PORT) -p.add_option('--roles', action='callback', callback=store_list, default=[], - dest='roles', type='string', - help="a space separated list of role names (used by set_assignment)") -p.add_option('-v', '--verbose', action='store_true', default=False, - dest='verbose', help="display the progress of the RPC") - -opts, args = p.parse_args() - -if len(args) != 1: - p.error("exactly one action must be given") - -for action in actions: - if action.func_name == args[0]: - break -else: - p.error("unknown action: %s" % args[0]) - -# Get the action's arguments. -try: - action_args = action(opts) -except Exception, e: - sys.stderr.write("The %s action requires %s\n" % (action.func_name, e)) - sys.exit(2) - -# Create the proxy. -proxy = xmlrpclib.ServerProxy(uri='http://%s:%d' % (opts.addr, opts.port), - verbose=opts.verbose) - -try: - result = getattr(proxy, action.func_name)(*action_args) -except socket.error, e: - err, msg = e.args - - if err == errno.ECONNREFUSED: - sys.stderr.write("Unable to connect to permissions server at %s:%d\n" % (opts.addr, opts.port)) - else: - sys.stderr.write("socket error: %s\n" % msg) - - sys.exit(1) -except xmlrpclib.Fault, e: - # Extract the text of the exception. If we don't recognise the format then - # display the lot. - tail = e.faultString.find(':') - if tail < 0: - msg = e.faultString - else: - msg = e.faultString[tail + 1:] - - print("The call raised an exception: %s" % msg) - sys.exit(0) - -# Show the result. -print("Result:", result)