Gustavo Narea wrote:
>> There is no other tie between the authentication framework (the security
>> policy) and repoze.who.  The documentation for the security policy lives in
>> repoze.bfg. There is also a RemoteUserSecurityPolicy which does not require
>> repoze.who; instead it just relies on environ['REMOTE_USER'] as its sole
>> principal; everything else works the same.
> 
> In the case of tgext.authorization, it has one repoze.who metadata provider 
> that loads into the identity dict the groups to which the authenticated user 
> belongs and the permissions granted to such groups.
> 
> This metadata provider plugin (called "AuthorizationMetadata") may get the 
> groups and permissions from many sources (databases, ini files, etc).

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).

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).

> 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?

>   3.- Then, developers use authorization assertions in their controllers.
> 
> And for what it's worth, the "glue" between repoze.who and 
> tgext.authorization 
> is its middleware module 
> (http://trac.turbogears.org/browser/projects/tgAuthorization/trunk/tgext/authorization/middleware.py),
> and the "glue" between TG2 and tgext.authorization is its "quickstart" module 
> (http://trac.turbogears.org/browser/projects/tgAuthorization/trunk/tgext/authorization/quickstart.py).

Very nice.

> 
> 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.

> 
> 
>> For each of my customer projects that uses both bfg and repoze.who, I've
>> found it necessary to create one new metadata plugin to get the group
>> information from wherever it lives.  But I've not considered putting these
>> back into repoze.who or starting a project for them, because they are
>> typically very simple (on the order of maybe 10 lines of code each).  OTOH,
>> they are always slightly different from each other, because customers have
>> weird requirements.
> 
> 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).

> 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.

> 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.

> 
> 
>> Are these plugins the bits of code that you believe we should consolidate
>> into a package?
> 
> Not exactly. 
> 
> For example, if I'm developing a WSGI application that uses r.who and 
> tgext.authorization, and I store my users' data, groups and permissions in a 
> database... I would have to configure repoze.who's SQL authenticator and 
> tgext.authorization's SQL groups and permissions "fetchers", separately, 
> while 
> they share some configuration bits.

Yep, see above.

> 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.

> 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/authorization/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.

>> 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.

>> That said, the idea of having a package (ala repoze.what) that contains who
>> metadata providers that provide group info along with authorization
>> decorators that actually use that info sounds fine to me.  I may not use
>> the decorators myself, but I could contribute my little snippets of
>> group-metadata-provider plugins to repoze.what were we to descend on some
>> defacto standard about how those plugins should put that info into the
>> repoze.who identity.
>>
>> Let's take a case at hand... In your LDAP scenario, you've written the LDAP
>> who plugin, now you need to choose where to put an LDAP groups metadata
>> plugin.  It *could* go into the LDAP plugin itself.  Or it could go in some
>> separate package like repoze.what.  In the case of LDAP it *might* be best
>> to put the metadata provider plugin into the LDAP plugin itself, because
>> causing e.g. repoze.what to require python-ldap might be painful due to its
>> requirement that it wants to compile against openldap libs. In fact, now 
>> that I wrote that, I can't think of a reason, for any plugin
>> of significant dependenty complexity, why the metadata plugin shouldn't
>> exist in the package that provides all the other plugins. Maybe we just
>> need to establish a loose standard for metadata provision.  We'd then put
>> the authentication stuff in some other package, and have the authenticators
>> *use* that standard, but each metadata provider which provides it would
>> still live in its respective who plugin package?
> 
> The problem is that we couldn't use an LDAP metadata provider to retrieve 
> Organizational Units as groups, but a tgext.authorization's (or 
> repoze.what's) 
> so-called Group Source Adapter (or Permission Source Adapter). Likewise, 
> groups and permissions stored in databases are not loaded by SQL metadata 
> providers, but by Group/Permission Source Adapters (which also enable you to 
> manage your groups and permissions under a source-independent API).
> 
> Group/Permission source adapters don't deal with repoze.who, they're only 
> used 
> by tgext.authorization, so I'm not sure if including them in a repoze.who 
> plugin would be accurate.
> 
> We could, among other things, unify repoze.who's and tgext.authorization's 
> SQL 
> plugins. The tgext.authorization SQL plugin provides the SQL group source 
> adapter and the SQL permission source adapter:
> http://trac.turbogears.org/browser/projects/tgAuthorization/trunk/tgext/authorization/plugins/sql.py
> 
> What a long email! This is it, for the time being :)

Back atcha. ;-)

- C


> 
> Cheers.

_______________________________________________
Repoze-dev mailing list
Repoze-dev@lists.repoze.org
http://lists.repoze.org/listinfo/repoze-dev

Reply via email to