Hello, everybody.

[Get a coffee, it's a long email!]

After checking the code for tg.ext.repoze.who, now I have an idea on what 
would need to be changed for its successor (tgext.authorization) to support 
authorization only. So, these are the changes I propose for the replacement of 
tg.ext.repoze.who:

I think we should create a module for authentication and authorization related 
stuff in the TG template (at {package}.lib.auth, for example). And I think we 
should move the 'middleware' module of tg.ext.repoze.who to this new module, 
excluding the SQLMetadataProviderPlugin class (see below to learn why).

On the other hand, I could not find anything on authorize.py that needs to be 
changed.

This way, the extension will be made up of the following modules:
  - authorize: It *may* need some slight modifications.
  - adapters: This module will contain plugins for fetching groups and 
permission data for the authenticated user from different backends. They will 
*not* be repoze.who's metadata providers: tg.ext.authorization will provide 
the only required metadata provider (possibly called 'AuthorizationMetadata'), 
which will fetch groups and permissions metadata from one or more adapters; 
this way, developers will be able to store groups in one place and permissions 
in another (e.g., re-using LDAP groups and storing permissions in an *.ini 
file).

Or in a visual way:

+ tgext.authorization
|
`---+ __init__
|   |
|   `---+ AuthorizationMetadata (class)
|
`---+ authorize
|
`---+ adapters
    |
    `---+ __init__
    |   |
    |   `---+ BaseGroupsAdapter (abstract class or interface)
    |   |
    |   `---+ BasePermissionsAdapter (abstract class or interface)
    |
    `---+ sql
    |   |
    |   `---+ class SQLGroups(BaseGroupsAdapter)
    |   |
    |   `---+ class SQLPermissions(BasePermissionsAdapter)
    |
    `---+ htgroup
    |   |
    |   `---+ class Htgroups(BaseGroupsAdapter)
    |
    `---+ ldap
    |   |
    |   `---+ class LDAPGroups(BaseGroupsAdapter)
    |
    `---+ ini
    |   |
    |   `---+ class IniGroups(BaseGroupsAdapter)
    |   |
    |   `---+ class IniPermissions(BasePermissionsAdapter)
    |
    :


Silverplate and tgext.authorization
===================================

One of the main reasons to use so-called adapters is not to break Silverplate, 
which I think is tied to the only adapter supported by tg.ext.repoze.who, the 
SQL one.

With adapters, Silverplate may work in a adapter-agnostic fashion, as long as 
the adapter supports not only retrieving, but also adding, modifying and 
deleting entries.

How would adaptors would look like
==================================

Roughly, like this:

class BaseGroupsAdapter(object):
    # Abstract
    def get_all_groups(self):
        # Return a tuple for all the groups found in the backend.
    # Abstract
    def get_groups(self, identity):
        # Return a tuple for the groups the user belongs to.
    # Abstract
    def add_group(self, group_name, group_data):
        # Add the group to the backend
    # Abstract
    def edit_group(self, group_name, group_data):
        # Modify the group data
    # Abstract
    def delete_group(self, group_name):
        # Delete the group from the backend

class BasePermissionsAdapter(object):
    # Abstract
    def get_all_permissions(self):
        # Return a tuple for all the permissions found in the backend.
    # Abstract
    def get_permissions(self, group):
        # Return a tuple for the permissions granted to `group`.
    # Abstract
    def get_groups(self, permission):
        # Return a tuple for the groups granted the `permission`.
    # Abstract
    def grant_permission(self, permission, groups):
        # Grant `groups` the `permission` permission
    # Abstract
    def deny_permission(self, permission, groups):
        # Deny `groups` the `permission` permission
    # Abstract
    def delete_permission(self, permission):
        # Delete the `permission` permission from the backend

Then AuthorizationMetadata would look like this:

class AuthorizationMetadata(object):
    implements(IMetadataProvider)
    def __init__(self, group_fetchers, permission_fetchers):
       self.group_fetchers = group_fetchers
       self.permission_fetchers = permission_fetchers
    def add_metadata(self, environ, identity):
        groups = set()
        permissions = set()
        for grp_fetcher in self.group_fetchers:
            groups |= grp_fetcher.get_groups(identity)
        for group in groups:
            for perm_fetcher in self.permission_fetchers:
                permissions |= perm_fetcher.get_permissions(group)
        identity['groups'] = groups
        identity['permissions'] = permissions


How would it be setup in TG2 applications
=========================================

It would be _extremely_ easy to setup tgext.authorization: The developer 
simply has to add the AuthorizationMetadata to the metadata providers of their 
repoze.who setup. That's it.

For example, if I store my groups in an Htgroup file and their permissions in 
an INI file, I would use a code similar to:

    groups_fetcher = Htgroups('/path/to/groups.htgroup')
    permissions_fetcher = IniPermissions('/path/to/permissions.ini')
    authorization = AuthorizationMetadata([groups_fetcher],
                                          [permissions_fetcher])
    mdproviders = [authorization]
    app_with_auth = PluggableAuthenticationMiddleware(
        app,
        identifiers,
        authenticators,
        challengers,
        mdproviders,
        default_request_classifier,
        default_challenge_decider
    )

Please notice that only the first four lines are specific to the setup of 
tgext.authorization; the others are specific to repoze.who itself. So the 
extension will require at least three lines to be configured!

Using its authorization mechanism
=================================
Its API will be just like that of tg.ext.repoze.who, so we will use it the 
same way.

Let's get started!
==================

Please let me know what if think about my proposal.

If we're going to make it, I'll need commit rights in the tgtools repository. 
My Google account is the same as the email address I'm currently using.

Cheers!
-- 
Gustavo Narea.
http://gustavonarea.net/

Get rid of unethical constraints! Switch to Freedomware:
http://softwareliberty.com/


Attachment: signature.asc
Description: This is a digitally signed message part.

Reply via email to