=================== Security Mechanisms =================== :Version: $Revision: 1.9 $ Current situation ================= Current logical controls: ANONYMOUS_ACCESS = 'deny' Deny or allow anonymous access to the web interface ANONYMOUS_REGISTER = 'deny' Deny or allow anonymous users to register through the web interface ANONYMOUS_REGISTER_MAIL = 'deny' Deny or allow anonymous users to register through the mail interface Current user interface authentication and controls: - command-line tool access controlled with passwords, but no logical controls - CGI access is by username and password and has some logical controls - mailgw access is through identification using sender email address, with limited functionality available The web interface implements has specific logical controls, preventing non-admin users from accessing: - other user's details pages - listing the base classes (not issues or their user page) - editing base classes Issues ====== 1. The current implementation is ad-hoc, and not complete for all `use cases`_. 2. Currently it is not possible to allow submission of issues through email but restrict those users from accessing the web interface. 3. Only one user may perform admin functions. 4. There is no verification of users in the mail gateway by any means other than the From address. Support for strong identification through digital signatures should be added. 5. The command-line tool has no logical controls. Possible approaches =================== Security controls in Roundup could be approached in three ways: 1) at the hyperdb level, with read/write/modify permissions on classes, nodes and node properties for all or specific transitions. 2) at the user interface level, with access permissions on CGI interface methods, mailgw methods, roundup-admin methods, and so on. 3) at a logical permission level, checked as needed. In all cases, the security built into roundup assumes restricted access to the hyperdatabase itself, through Operating System controls such as user or group permissions. Hyperdb-level control --------------------- Control is implemented at the Class.get, Class.set and Class.create level. All other methods must access nodes through these methods. Since all accesses go through the database, we can implement deny by default. Pros: - easier to implement as it only affects one module - smaller number of permissions to worry about Cons: - harder to determine the relationship between user interaction and hyperdb permission. - a lot of work to define - must special-case to handle by-node permissions (editing user details, having private messages) User-interface control ---------------------- The user interfaces would have an extra layer between that which parses the request to determine action and the action method. This layer controls access. Since it is possible to require methods be registered with the security mechanisms to be accessed by the user, deny by default is possible. Pros: - much more obvious at the user level what the controls are Cons: - much more work to implement - most user interfaces have multiple uses which can't be covered by a single permission Logical control --------------- At each point that requires an action to be performed, the security mechanisms are asked if the current user has permission. Since code must call the check function to raise a denial, there is no possibility to have automatic default of deny in this situation. Pros: - quite obvious what is going on - is very similar to the current system Cons: - large number of possible permissions that may be defined, possibly mirroring actual user interface controls. - access to the hyperdb must be strictly controlled through program code that implements the logical controls. Applying controls to users ========================== Individual assignment of Permission to User is unwieldy. The concept of a Role, which encompasses several Permissions and may be assigned to many Users, is quite well developed in many projects. Roundup will take this path, and allow the multiple assignment of Roles to Users, and multiple Permissions to Roles. These definitions will be stored in the hyperdb. They don't need to be pushed to the actual database though. There will be two levels of Permission. The Class level permissions define logical permissions associated with all nodes of a particular class (or all classes). The Node level permissions define logical permissions associated with specific nodes by way of their user-linked properties. A permission module defines:: class InMemoryImmutableClass(hyperdb.Class): ''' Don't allow changes to this class's nodes. ''' def __init__(self, db, classname, **properties): ''' Set up an in-memory store for the nodes of this class ''' def create(self, **propvalues): ''' Create a new node in the in-memory store ''' def get(self, nodeid, propname, default=_marker, cache=1): ''' Get the node from the in-memory store ''' def set(self, *args): raise ValueError, "%s are immutable"%self.__class__.__name__ class PermissionClass(InMemoryImmutableClass): ''' Include the default attributes: - name (String) - classname (String) - description (String) The classname may be unset, indicating that this permission is not locked to a particular class. That means there may be multiple Permissions for the same name for different classes. ''' class RoleClass(InMemoryImmutableClass): ''' Include the default attributes: - name (String, key) - description (String) - permissions (PermissionClass Multilink) ''' def hasClassPermission(db, classname, permission, userid): ''' Look through all the Roles, and hence Permissions, and see if "permission" is there for the specified classname. ''' def hasNodePermission(db, classname, nodeid, userid, properties): ''' Check the named properties of the given node to see if the userid appears in them. If it does, then the user is granted this permission check. 'propspec' consists of a list of property names. The property names must be the name of a property of classname, or a KeyError is raised. That property must be a Link or Multilink property, or a TypeError is raised. If the property is a Link, the userid must match the property value. If the property is a Multilink, the userid must appear in the Multilink list. ''' The instance dbinit module then has in ``open()``:: perm = permission.PermissionClass(db, "permission") role = permission.RoleClass(db, "role") # create some Permissions wa = perm.create(name="Web Access", description="User may use the web interface") wr = perm.create(name="Web Registration", description="User may register through the web") ma = perm.create(name="Mail Access", description="User may use the email interface") mr = perm.create(name="Mail Registration", description="User may register through email") ee = perm.create(name="Edit", description="User may edit everthing") ei = perm.create(name="Edit", classname="issue", description="User is allowed to edit issues") ae = perm.create(name="Assign", description="User may be assigned to anything") ai = perm.create(name="Assign", classname="issue", description="User may be assigned to issues") # create some Roles that use the Permissions role.create(name="User", description="A regular user, no privs", permissions=[wa, wr, ma, mr, ei, ai]) role.create(name="Admin", description="An admin user, full privs", permissions=[ee, ae]) role.create(name="No Rego", description="A user who can't register", permissions=[wa, ma]) in ``init()``:: r = db.getclass('role').lookup('Admin') user.create(username="admin", password=Password(adminpw), address=instance_config.ADMIN_EMAIL, roles=[r]) # choose your anonymous user access permission here #r = db.getclass('role').lookup('No Rego') r = db.getclass('role').lookup('User') user.create(username="anonymous", roles=[r]) Then in the code that matters, calls to ``hasPermission`` are made to determine if the user has permission to perform some action:: if security.hasClassPermission('issue', 'Edit', self.user): # all ok if security.hasNodePermission('issue', nodeid, self.user, ['assignedto']): # all ok The htmltemplate will implement a new tag, which has the form:: HTML to display if the user has the permission. HTML to display if the user does not have the permission. where the require attribute gives a comma-separated list of permission names which are required, and the node attribute gives a comma-separated list of node properties whose value must match the current user's id. Either of these tests must pass or the permission check will fail. The section of html within the side of the ```` that fails is remove from processing. Authentication of Users ----------------------- Users must be authenticated correctly for the above controls to work. This is not done in the current mail gateway at all. Use of digital signing of messages could alleviate this problem. The exact mechanism of registering the digital signature should be flexible, with perhaps a level of trust. Users who supply their signature through their first message into the tracker should be at a lower level of trust to those who supply their signature to an admin for submission to their user details. Action ====== The CGI interface must be changed to: - authenticate over a secure connection - use unique tokens as a result of authentication, rather than pass the user's real credentials (username/password) around for each request (this means sessions and hence a session database) - use the new logical control mechanisms - implement the permission module - implement a Role editing interface for users - implement htmltemplate tests on permissions - switch all code over from using config vars for permission checks to using permissions - include config vars for initial Roles for anonymous web, new web and new email users The mail gateway must be changed to: - use digital signatures - use the new logical control mechanisms - switch all code over from using config vars for permission checks to using permissions The command-line tool must be changed to: - use the new logical control mechanisms (only allowing write access by admin users, and read-only by everyone else) Use cases ========= public end users that can submit bugs, request new features, request support developer developers that can fix bugs, implement new features provide support manager approvers/managers that can approve new features and signoff bug fixes admin administrators that can add users and set user's roles system automated request handlers running various report/escalation scripts privacy issues that are only visible to some users