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