(I'm sorry for the delay).

On Tuesday October 28, 2008 00:40:56 you wrote:
> You're *really* not going to like this, because I can tell that you worked
> *extremely* hard on tgext.authorization and the docs (I've now read both),
> and they are really very well done, but here goes:  rather than creating a
> single pluggable plugin that tries to do funnel all authorization metadata
> through it, I'd suggest modeling your AuthorizationMetadata plugin as N
> separate metadata providers, each of which appends to identity['groups']
> and/or
> metadata['permissions'], conventionally.  Doing this would allow you to
> ship "authorization" metadata provider plugins along with their sister
> authenticator plugins (e.g. you could ship
> LDAPAuthenticationMetadataProvider along with LDAPAuthenticator).

Yes, that would be a really big advantage.

> I realize the main driver for the AuthorizationMetadata machinery is to
> reduce configuration duplication.  But maybe a better route would be to
> come up with an alternate front-end to who configuration which deals
> gracefully with duplicate paths and such that need to be shared between
> configuration sections.  (e.g. some variable-setting-and-resolving
> machinery that could be used within who.ini).

Well, I'd say making it easy to configure is the second reason why I decided 
to do it that way. But the main reason is that I realized that under this 
authorization pattern (users, groups and permissions), if we were going to 
have one adapter per supported source type, then we could also make such 
adapters edit the sources they work on (either groups or permissions) -- and 
then a Django Admin-like application for TG could take advantage of this 
ability on every supported source type under a common API.

> > Roughly, this is how it works:
> >   1.- If the user has been authenticated, repoze.who runs the
> > AuthorizationMetadata plugin to load the groups and permissions.
> >   2.- This week I hope to finish support for anonymous (non
> > authenticated) users, using an IAuthenticator plugin which loads such
> > groups and permissions for anonymous users (since a metadata provider
> > would not get called in this case). Of course, the "real" authenticators
> > would have to be called before.
> Isn't the set of groups for an anonymous user a fixed set (typically empty
> or single-valued ("Anonymous") in the 99% case)?  I can imagine a case
> where you might want to add more groups to it, but it's a per-application
> decision, so maybe the policy should live in the application itself rather
> than in some indirection in a plugin that will never be modified for the
> lifetime of 99% of applications?

Right, this is why that set of groups will be empty by default. I don't think 
this will affect applications that don't use that feature. Anyway, I'll take 
it into account when I develop that IAuthenticator, so that people may turn 
that feature off.

> > But I suggest you check the quick introduction to authentication and
> > authorization in TG2 and then the docs for tgext.authorization, which are
> > included in the documentation for TG2; I'm sure you'll get a better
> > overview reading this documentation. However, the online version is not
> > up-to-date yet, so it's temporarily available on my site:
> > http://code.gustavonarea.net/tg2-docs.zip
> Got it.  These are *truly* excellent docs.

Thanks! :)

> > In tgext.authorization you have the following components:
> >  * A "source" is where your groups and/or your permissions are stored. It
> > may be a "group source" or a "permission source".
> >  * A "group source" is a source that stores groups (Htgroups file, a
> > database, ini file, etc).
> >  * A "permission source" is a source that stores permissions (a database,
> > ini file, etc).
> >  * A "source adapter" is a tgext.authorization class that handles a given
> > type of source.
> Yeah.  I don't think this is necessary (as per the above), but I don't
> expect to dissuade you from doing it, given the amount of code and docs
> already produced.
> So I'll just note that calling "permissions" and "groups" out as
> specialized metadata passed down through who is really a TG thing, and not
> an "any framework" thing.  No other framework I've used calls "permissions"
> what you call them in TG2 (in Zope, they might be called "roles", although
> I'm not sure there's a complete one-to-one mapping).

Yes, I agree with you. But I think the "users, groups and permissions" pattern 
is becoming a rather famous pattern, so hopefully many people will feel like 
at home.

> > So, you only need one metadata provider that "knows" how to find the
> > groups and permissions from different sources. For example, you could use
> > the code below:
> >     from repoze.who.plugins.htpasswd import HTPasswdPlugin, crypt_check
> >     from tgext.authorization.middleware import setup_auth
> >     # Please note that the plugins below have not been created yet; want
> > to # jump in?
> >     from tgext.authorization.plugins.htgroups import HtgroupsAdapter
> >     from tgext.authorization.plugins.ini import IniPermissionAdapter
> >
> >     # Defining the group adapters; you may add as much as you need:
> >     groups = {'all_groups': HtgroupsAdapter('/path/to/groups.htgroups')}
> >
> >     # Defining the permission adapters; you may add as much as you need:
> >     permissions = {'all_perms':
> > IniPermissionAdapter('/path/to/perms.ini')}
> >
> >     # repoze.who authenticators; you may add as much as you need:
> >     htpasswd_auth = HTPasswdPlugin('/path/to/users.htpasswd',
> > crypt_check) authenticators = [('htpasswd', htpasswd_auth)]
> >
> >     app_with_auth = setup_auth(
> >         app,
> >         groups,
> >         permissions,
> >         authenticators)
> >
> > Where setup_auth() is the function that configures repoze.who by loading
> > tgext.authorization's AuthorizationMetadata.
> Yep.  I might do this instead in who.ini:
> [plugin:ini_permissions]
> use = my.package.ini_permissions:make_ini_permissions
> path = /path/to/perms.ini
> [plugin:htgroups]
> use = my.package.htpasswd_groups:make_htgroups
> path = /path/to/groups.htgroups
> [plugin:htpasswd]
> use = my.package.htpasswd_groups:make_htgroups
> filename = /path/to/passwd
> check_fn = repoze.who.plugins.htpasswd:plain_check
> [mdproviders]
> plugins = htgroups ini_permissions
> [authenticators]
> plugins = htpasswd
> Note that in this configuration there is *no* shared configuration between
> any plugin.  IMO, that's a clue that it's not as common as might justify a
> lot of machinery to elide it.

Hmm, after reading the code above, I think your suggestion of making them 
independent metadata providers will be a great idea. But, at this point, I 
think it's best to make it an alternate way, not the only one.

For example, I would add the following method to the BaseSourceAdapter:
    class BaseSourceAdapter(object):
        """This is the base class for source adapters"""
        # ...
        def add_metadata(self, environ, identity):
            # because the "sections" in a "group source" are the groups
            # defined for the application, find_sections() will load into
            # the identity dict the groups to which the authenticated user
            # belongs
            if 'groups' not in identity:
                identity['groups'] = self.find_sections(identity)
                identity['groups'] |= self.find_sections(identity)

So that groups/permission source adapters can also be used as independent 
repoze.who metadata providers, as in your example.

> > And you also get a bonus: The ability to manage your groups and
> > permissions under a source-independent API, either from your application
> > or from an external program.
> I'd strongly suggest holding off on a "one-API-to-rule-them-all"
> metadata-mutation capability until you have at least four or five backends
> that can be mutated this way.  I've seen some truly horrific things come
> out of conflating authentication / authorization with "member data
> management" early-on, even if the member data being mutated is limited to
> only authentication-and-authorization-related attributes; yes, I'm looking
> at you, Plone. ;-)  It's easy enough to code up an application to change
> these things independently, and by reading the code and docs I can see that
> it's not really a "core" functionality yet.

You make a good point.

But I think it'll work here because it's a very simple API that doesn't try to 
do anything advance at all. It just deals with simple many-to-many relations 
between the following entities:
 * Users <-> groups: It's able to add or remove users from a given group.
 * Groups <-> permissions: It's able to add/grant or remove/deny permissions 
to a given group.

And it can also create, rename and delete groups and permissions. Nothing 

I think all of the actions above will be available on every supported source. 
(When I started working on tgext.authorization, both groups and permissions 
could have descriptions, but later I removed this "feature" because it would 
turn into a headache depending on the source -- what about htgroups files? The 
only thing you can define in such groups are usernames, unless you insert 
descriptions as comments before every group definition, which would be an 
awful solution)

But you've hit the nail on the head! :) I think this *could* really be a TG-
specific thing.

> > The approach I'm taking with tgext.authorization, which is out of the
> > scope of authorization itself, is to make it also setup authentication
> > via repoze.who so that others won't have to repeat auth settings.
> I'm not sure this is such a great idea.  Conflating configuration between
> the two is a tradeoff: you don't repeat yourself, but it forms bonds
> between plugins that may not stand the test of time.  At least if you keep
> them separate, you don't paint yourself into any *particular* corner from
> the start.  And if the configuration machinery allows for some form of
> substitution, and it's used to prevent duplication during configuration,
> it's far easier to stop using it than to change the authentication
> machinery to not conflate the two things together.

Sorry, my statement above is not complete: Yes, tgext.authorization tries to 
avoid people repeat settings, *but* it does so through optional utilities 
which are used in TG2 unless told otherwise. If fact, it's easy to configure 
everything separately instead of using such quick-start functions -- you can 
even setup repoze.who yourself with support for tgext.authorization, without 
having tgext.authorization doing so for you.

> > See, for example, the
> > function that configures authentication and authorization in the
> > situation described above (users' credentials, groups and permissions
> > stored in a database), called setup_sql_auth():
> > http://trac.turbogears.org/browser/projects/tgAuthorization/trunk/tgext/a
> >uthorization/quickstart.py
> >
> > The same would happen if I want to use LDAP authentication and re-user my
> > LDAP's Organizational Units as groups in my application.
> I'd argue that saying:
> [plugin:ldapauthentication]
> base_dn = o=mycompany,c=US
> [plugin:ldapgroups]
> base_dn = o=mycompany,c=US
> [plugin:ldappermissions]
> base_dn = o=mycompany,c=US
> ... wouldn't really be so bad.  I could live with it.  And needing to
> change it in only one place would be almost solved entirely if you could
> do:
> %define BASE_DN = o=mycompany,c=US
> [plugin:ldapauthentication]
> base_dn = ${BASE_DN}
> [plugin:ldapgroups]
> base_dn = ${BASE_DN}
> I'm not married to who.ini format, BTW.  It's just what we have.

Yes, I think this wouldn't be an issue since group/permission source adapters 
will be able to act as r.who MD providers too.

> >> To be honest, I'd like to keep authentication and authorization (at
> >> least nontrivial-policy-based authorization) in separate packages in
> >> order to avoid the fate of AuthKit, which tries to do both, and fails. 
> >> I think it's a bit too much of a mental jump for some people to
> >> understand the difference between the two without someone spelling it
> >> out for them, and package separation tends to do that.
> >
> > I agree, but my idea is to have repoze.who part of main "WSGI Auth*"
> > project, where it could also be used independently of the authorization
> > framework, while the authorization framework does fully depends on the
> > authentication system.
> Like I mentioned above, the TG assumptions about the components that form
> authorization are not really globally applicable.  If there should be such
> a beast as a "WSGI Auth*" project, I suspect we should try to boil away the
> bits that *are* globally applicable for authentication (if any).  My
> personal suspicion is that we'll wind up with the empty set.

Yes, I think you're right. That crazy idea of the big Auth* project is gone 

Before I wrote this email, I think we didn't agree with two things:
  1.- You preferred to have several group/permission metadata providers, 
instead of one MD provider that loads everything from many places. I think 
this is resolved with so-called "source adapters" being able to act as 
repoze.who MD providers - what do you think?
  2.- You didn't find a good idea to try to have these source adapters able to 
edit the sources under a common API as you thought it'd not be feasible.

So, since the first issue is resolved (I think so) and if I made you change 
your mind regarding the second issue, would you agree with the creation of the 
repoze.what project as the successor of tgext.authorization? 

Gustavo Narea.

Get rid of unethical constraints! Switch to Freedomware:

Repoze-dev mailing list

Reply via email to