How to define Authorization script

180 views
Skip to first unread message

Vishwajeet

unread,
Apr 9, 2010, 4:25:26 AM4/9/10
to modwsgi
Hi,
I have script which is currently doing authentication and it's working
fine.
My question is how can i define an authorization script to check
access ?
I tried browsing through all directives but none of seems to mention
about authorization.

Regards,
Vishwajeet Singh

Graham Dumpleton

unread,
Apr 9, 2010, 4:52:18 AM4/9/10
to mod...@googlegroups.com

vishwajeet singh

unread,
Apr 9, 2010, 5:00:16 AM4/9/10
to mod...@googlegroups.com
Thanks for the quck response Graham I have gone through these links many times but still fail to understand how it will work for me.
Let me give you some more details
I am not doing either group authorization or host authorization, I have django app and users have different roles in that application, so once user is authenticated I want to look into db if the user is in particular role or not, if he is not a role give him authorization required or you don't have access to this resource. I want to use this authorization to handle access for webdav folders which are not directly part of django app.

Hope that makes me more clear, thank you so much for your response.



--
You received this message because you are subscribed to the Google Groups "modwsgi" group.
To post to this group, send email to mod...@googlegroups.com.
To unsubscribe from this group, send email to modwsgi+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/modwsgi?hl=en.




--
Vishwajeet Singh
+91-9657702154 | dextr...@gmail.com | http://bootstraptoday.com
Twitter: http://twitter.com/vishwajeets | LinkedIn: http://www.linkedin.com/in/singhvishwajeet

Graham Dumpleton

unread,
Apr 9, 2010, 6:32:26 AM4/9/10
to mod...@googlegroups.com
On 9 April 2010 19:00, vishwajeet singh <dextr...@gmail.com> wrote:
> Thanks for the quck response Graham I have gone through these links many
> times but still fail to understand how it will work for me.
> Let me give you some more details
> I am not doing either group authorization or host authorization, I have
> django app and users have different roles in that application, so once user
> is authenticated I want to look into db if the user is in particular role or
> not, if he is not a role give him authorization required or you don't have
> access to this resource. I want to use this authorization to handle access
> for webdav folders which are not directly part of django app.
> Hope that makes me more clear, thank you so much for your response.

Depends on how you are going to do this with Django, but a role is not
really any different to a group or even a Django user permission.

For example, the following might be able to be used (although I have
not tested it).

import os, sys
sys.path.append('/usr/local/django')
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'

from django.contrib.auth.models import User
from django import db

def groups_for_user(environ, user):
db.reset_queries()

kwargs = {'username': user, 'is_active': True}

try:
try:
user = User.objects.get(**kwargs)
except User.DoesNotExist:
return ['']

return user.get_group_permissions()
finally:
db.connection.close()

In other words, just look up user and return permissions associated
with that user through the groups they are in.

I don't actually use Django but I presume this can be used to
designate the roles they have.

Then in Apache configuration you can have:

AuthType Basic
AuthName "Top Secret"
AuthBasicProvider dbm
AuthDBMUserFile /usr/local/wsgi/accounts.dbm
WSGIAuthGroupScript /usr/local/wsgi/scripts/auth.wsgi
Require valid-user

<Location /some/url>
Require group can_do_stuff
</Location>

<Location /some/other/url>
Require group can_do_other_stuff
</Location>

So don't get hung up on the 'group' name used as argument to 'Require'
directive. You can still return a list of permissions and match
against that.

From Apache 2.3 onwards, you will have to actually use 'wsgi-group'
instead of 'group'. Seems I haven't noted this in documentation and
that 'wsgi-group' already works for older Apache and should now be
used in preference to 'group'.

Also note if using check_password() to authenticate user against
Django previously, to avoid second database lookup, you could always
stash the permissions in thread local storage and have the
groups_for_user() look up that, validate is for same user and return
it.

You will need to use mod_wsgi 3.X to use thread local storage like that however.

BTW, if you get this working, post what you use. If I get a working
example from someone with a bit of a description of what you do on
Django admin side to populate permissions, could include it in
documentation as example.

Graham

Vishwajeet

unread,
Apr 9, 2010, 8:19:34 AM4/9/10
to modwsgi

On Apr 9, 3:32 pm, Graham Dumpleton <graham.dumple...@gmail.com>
wrote:

Thanks for an elaborate reply that really helped me to move in the
right direction, I did the suggested changes and It seems to be
working.
though I need to do some more testing before I have something
concrete :)

> Also note if using check_password() to authenticate user against
> Django previously, to avoid second database lookup, you could always
> stash the permissions in thread local storage and have the
> groups_for_user() look up that, validate is for same user and return
> it.

I am using check_password() but I don't know how to stash the
permissions in thread local storage, can you please let me know how to
do this ?
One more thing is that if the required group is not matched it keeps
on prompting for authentication instead of saying authorization
required.
Once I am done with the implementation I will surely share the script.
Thanks a lot for you help, much appreciated.

> Graham
>
>
>
> > On Fri, Apr 9, 2010 at 2:22 PM, Graham Dumpleton

> > <graham.dumple...@gmail.com> wrote:


>
> >> On 9 April 2010 18:25, Vishwajeet <dextrou...@gmail.com> wrote:
> >> > Hi,
> >> > I have script which is currently doing authentication and it's working
> >> > fine.
> >> > My question is how can i define an authorization script to check
> >> > access ?
> >> > I tried browsing through all directives but none of seems to mention
> >> > about authorization.
>
> >> See:
>

> >>  http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIAut...
>
> >>  http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIAut...


> >>  http://code.google.com/p/modwsgi/wiki/AccessControlMechanisms
>
> >> Graham
>
> >> --
> >> You received this message because you are subscribed to the Google Groups
> >> "modwsgi" group.
> >> To post to this group, send email to mod...@googlegroups.com.
> >> To unsubscribe from this group, send email to
> >> modwsgi+u...@googlegroups.com.
> >> For more options, visit this group at
> >>http://groups.google.com/group/modwsgi?hl=en.
>
> > --
> > Vishwajeet Singh

> > +91-9657702154 | dextrou...@gmail.com |http://bootstraptoday.com

Graham Dumpleton

unread,
Apr 9, 2010, 8:23:41 AM4/9/10
to mod...@googlegroups.com

Will have to be tomorrow, no time tonight now and am logging off.

> One more thing is that if the required group is not matched it keeps
> on prompting for authentication instead of saying authorization
> required.

I would have thought it would actually return FORBIDDEN HTTP status response.

Graham

Graham Dumpleton

unread,
Apr 9, 2010, 9:00:15 AM4/9/10
to mod...@googlegroups.com

Have couple of minutes left. Use something like:

import os, sys
sys.path.append('/usr/local/django')
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'

from django.contrib.auth.models import User
from django import db

import threading

cache = threading.local()

def check_password(environ, username, password):

cache.username = None
cache.permissions = ['']

db.reset_queries()

kwargs = {'username': username, 'is_active': True}

try:
try:
user = User.objects.get(**kwargs)
except User.DoesNotExist:

return None

if user.check_password(password):
cache.username = username
cache.permissions = user.get_group_permissions()
return True
else:
return False
finally:
db.connection.close()

def groups_for_user(environ, username):
if not cache.username or cache.username != username:
cache.username = None
cache.permissions = ['']
return ['']

permissions = cache.permissions
cache.username = None
cache.permissions = ['']
return permissions

Have to do this as only easy way of passing information between the
two Apache phases as no easy way of stashing information back in
Apache request object for passing across.

Note that by WSGIAuthUserScript and WSGIAuthGroupScript must be
delegated to same application-group for this to work as thread locals
are specific to an interpreter.

Graham

Vishwajeet

unread,
Apr 9, 2010, 2:33:11 PM4/9/10
to modwsgi

On Apr 9, 6:00 pm, Graham Dumpleton <graham.dumple...@gmail.com>
wrote:
> On 9 April 2010 22:23, Graham Dumpleton <graham.dumple...@gmail.com> wrote:

Thanks I got it working

> >> One more thing is that if the required group is not matched it keeps
> >> on prompting for authentication instead of saying authorization
> >> required.

But this problem is still bugging me not able to understand why Apache
keeps on returning 401 instead of 403 if some other group is returned
instead of desired one.

Graham Dumpleton

unread,
Apr 10, 2010, 6:59:39 AM4/10/10
to mod...@googlegroups.com

Do you have an explicitly ErrorDocument directive specificied for 403
to map error handler to a URL.

If not explicitly done by you, have you got multi lang error pages in
Apache enabled. Ie.

# Multi-language error messages
Include /private/etc/apache2/extra/httpd-multilang-errordoc.conf

If you have these enabled, it could be redirecting 403 to an error URL
which is getting passed through to Django application, but Django
doesn't have that URL mapped and returns 401.

If this happens, I would actually have expected the end result to be a
500 error though as Apache would treat a 401 for error document to be
an internal server error.

Graham

>> Graham
>>
>> >> One more thing is that if the required group is not matched it keeps
>> >> on prompting for authentication instead of saying authorization
>> >> required.
>>
>> > I would have thought it would actually return FORBIDDEN HTTP status response.
>>
>> > Graham
>>
>> >> Once I am done with the implementation I will surely share the script.
>> >> Thanks a lot for you help, much appreciated.
>>
>> >>> Graham
>>
>> >>> > On Fri, Apr 9, 2010 at 2:22 PM, Graham Dumpleton
>> >>> > <graham.dumple...@gmail.com> wrote:
>>
>> >>> >> On 9 April 2010 18:25, Vishwajeet <dextrou...@gmail.com> wrote:
>> >>> >> > Hi,
>> >>> >> > I have script which is currently doing authentication and it's working
>> >>> >> > fine.
>> >>> >> > My question is how can i define an authorization script to check
>> >>> >> > access ?
>> >>> >> > I tried browsing through all directives but none of seems to mention
>> >>> >> > about authorization.
>>
>> >>> >> See:
>>
>> >>> >>  http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIAut...
>>
>> >>> >>  http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIAut...
>> >>> >>  http://code.google.com/p/modwsgi/wiki/AccessControlMechanisms
>>
>> >>> >> Graham

Graham

Vishwajeet

unread,
Apr 10, 2010, 3:16:56 PM4/10/10
to modwsgi
I don't think I have anything like that and I am sure that request is
not going to django.
The configuration which I have is something as shown below

<Directory "/setup/trunk/sspl/src/ssplsite/data/dav/test">

DAV on
AuthType Basic
AuthName "WebDAV Authentication"
AuthBasicProvider wsgi
WSGIAuthUserScript /setup/trunk/sspl/src/ssplsite/data/
repos/svn_wsgi.py application-group=webdav
WSGIAuthGroupScript /setup/trunk/sspl/src/ssplsite/
data/repos/svn_wsgi.py application-group=webdav
Require group allowed
#Require valid-user
AllowOverride None
Order allow,deny
Allow from all

</Directory>
If group allowed is returned all seems to work well but in case of
some other group it keeps on prompting for username password, I tried
it in location block as well.

On Apr 10, 3:59 pm, Graham Dumpleton <graham.dumple...@gmail.com>
wrote:

Graham Dumpleton

unread,
Apr 11, 2010, 12:45:40 AM4/11/10
to mod...@googlegroups.com
On 11 April 2010 05:16, Vishwajeet <dextr...@gmail.com> wrote:
> I don't think I have anything like that and I am sure that request is
> not going to django.
> The configuration which I have is something as shown below
>
>    <Directory "/setup/trunk/sspl/src/ssplsite/data/dav/test">
>
>                DAV on
>                AuthType Basic
>                AuthName "WebDAV Authentication"
>                AuthBasicProvider wsgi
>                WSGIAuthUserScript /setup/trunk/sspl/src/ssplsite/data/
> repos/svn_wsgi.py application-group=webdav
>                WSGIAuthGroupScript /setup/trunk/sspl/src/ssplsite/
> data/repos/svn_wsgi.py application-group=webdav
>                Require group allowed
>                #Require valid-user

Not that it will necessarily make a difference, you still need the
'Require valid-user' line.

BTW, it is 'mod_authz_default' that returns HTTP_UNAUTHORIZED. It
seems that returning 401 is the correct thing to do as you want to
give the user the option of entering different user credentials. If
you return HTTP_FORBIDDEN then the browser will remember the user
credentials and you will not get an option to change them as there is
no concept of logout for Basic authentication. If you say so, it will
even remember the credentials across browser restarts, so restarting
browser doesn't help. I should have remembered this before when
thought that forbidden should be returned, has to be unauthorized.

Graham

Vishwajeet

unread,
Apr 11, 2010, 2:41:16 AM4/11/10
to modwsgi

On Apr 11, 9:45 am, Graham Dumpleton <graham.dumple...@gmail.com>
wrote:


> On 11 April 2010 05:16, Vishwajeet <dextrou...@gmail.com> wrote:
>
>
>
>
>
> > I don't think I have anything like that and I am sure that request is
> > not going to django.
> > The configuration which I have is something as shown below
>
> >    <Directory "/setup/trunk/sspl/src/ssplsite/data/dav/test">
>
> >                DAV on
> >                AuthType Basic
> >                AuthName "WebDAV Authentication"
> >                AuthBasicProvider wsgi
> >                WSGIAuthUserScript /setup/trunk/sspl/src/ssplsite/data/
> > repos/svn_wsgi.py application-group=webdav
> >                WSGIAuthGroupScript /setup/trunk/sspl/src/ssplsite/
> > data/repos/svn_wsgi.py application-group=webdav
> >                Require group allowed
> >                #Require valid-user
>
> Not that it will necessarily make a difference, you still need the
> 'Require valid-user' line.

I had required valid-user but commented it while experimenting, you
are right it does not makes any difference.


>
> BTW, it is 'mod_authz_default' that returns HTTP_UNAUTHORIZED. It
> seems that returning 401 is the correct thing to do as you want to
> give the user the option of entering different user credentials. If
> you return HTTP_FORBIDDEN then the browser will remember the user
> credentials and you will not get an option to change them as there is
> no concept of logout for Basic authentication. If you say so, it will
> even remember the credentials across browser restarts, so restarting
> browser doesn't help. I should have remembered this before when
> thought that forbidden should be returned, has to be unauthorized.

I am not sure about this but Subversion also uses basic auth and
returns forbidden in case you enter right credentials and do not have
access.
Browser restart does clear the Basic authentication as far as I have
seen, I still feel it should give 403 otherwise user will not know
whether his credentials are wrong or he does not have access to
resource.

> ...
>
> read more »

Graham Dumpleton

unread,
Apr 12, 2010, 1:22:03 AM4/12/10
to mod...@googlegroups.com

Sorry, my mistake. Confusing it with firewall proxy passwords which
are remembered.

> I still feel it should give 403 otherwise user will not know
> whether his credentials are wrong or he does not have access to
> resource.

Which subversion can do because it is actually providing the whole
authorization handler.

In mod_wsgi it is implementing what is called an authorization
provider, it is the whole authorization handler so technically it
isn't providing the actual HTTP response code, instead the Apache
authorization handler which uses the authorization provider is.

That said, this authorization provider mechanism will only be
introduced into Apache in version 2.3 and isn't actually a part of
Apache 2.2.

As such, I still present to the user level the same type of interface
as if it was implemented the same way so for Apache 2.2 I do control
the authorization handler.

Hope your not too confused at this point.

Now, where it gets interesting is that for Apache 2.2 I believed I was
returning the same HTTP status codes as what Apache 2.3 would when
real authorization provider mechanisms are used. As such, I was return
HTTP_UNAUTHORIZED.

It appears though that since I did that the Apache 2.3 code has
changed, and they don't always return HTTP_UNAUTHORIZED and can also
return HTTP_FORBIDDEN if certain situations exist.

The code from Apache 2.3 is now:

auth_result = apply_authz_sections(r, conf->section, AUTHZ_LOGIC_AND);

if (auth_result == AUTHZ_GRANTED) {
return OK;
}
else if (auth_result == AUTHZ_DENIED || auth_result == AUTHZ_NEUTRAL) {
if (r->ap_auth_type == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r,
"client denied by server configuration: %s%s",
r->filename ? "" : "uri ",
r->filename ? r->filename : r->uri);

return HTTP_FORBIDDEN;
}
else {
ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, r,
"user %s: authorization failure for \"%s\": ",
r->user, r->uri);

/* If we're returning 403, tell them to try again. */
ap_note_auth_failure(r);

return HTTP_UNAUTHORIZED;
}
}

The specific case where HTTP_FORBIDDEN is returned is where
r->ap_auth_type is not set.

This is an internal variable in Apache which when translated to CGI or
WSGI is what is passed in the AUTH_TYPE variable.

The variable indicates that an Apache handler handled the
authentication. For example, might be set to Basic or Digest.

This thus follows what I described previously, with Apache still
believing that failure of authorization even when authentication has
occurred, should result in HTTP_UNAUTHORIZED.

I guess what it comes down to is the meaning of HTTP_FORBIDDEN. Wikipedia says:

"""403 Forbidden
The request was a legal request, but the server is refusing to respond
to it. Unlike a 401 Unauthorized response, authenticating will make no
difference.
"""

Thus, returning HTTP_FORBIDDEN isn't possibly correct as
'authenticating will make no difference' is not a valid statement,
because if you were to provide a different set of credentials then
which can access that area, you would be allowed. Thus it seems quite
reasonable that HTTP_UNAUTHORIZED is returned. It is saying, you
credentials are wrong for that location, but provide another and we
will let you in.

Now, even if I were to change mod_wsgi code to follow the same logic,
it will as such not help as you are still using Apache to do the
authentication and so r->ap_auth_type will be set.

Is there are a way to get around that if the code change were made.
The answer seems to be no, because even if you do:

WSGIAuthGroupScript /Users/grahamd/Testing/tests/authz.wsgi
AuthType PassThru
AuthDefaultAuthoritative Off
Require wsgi-group xxx

Apache will fail with:

[Mon Apr 12 15:19:26 2010] [crit] [client 127.0.0.1] configuration
error: couldn't check user. No user file?: /echo.wsgi

which is because Apache code says:

if (((access_status = ap_run_access_checker(r)) != 0)) {
if (!ap_some_auth_required(r)) {
return decl_die(access_status, "check access", r);
}

if (((access_status = ap_run_check_user_id(r)) != 0)
|| !ap_auth_type(r)) {
return decl_die(access_status, ap_auth_type(r)
? "check user. No user file?"
: "perform authentication. AuthType not set!",
r);
}

So, even if you can convince it to not do authentication, it will
complain that ap_auth_type wasn't set and will not even get to
authorization phase.

It seems therefore that in Apache 2.2 you just can't do it without
defining a full blown authentication handler, which mod_wsgi doesn't
provide support for doing.

Graham

Vishwajeet

unread,
Apr 12, 2010, 3:45:50 AM4/12/10
to modwsgi

On Apr 12, 10:22 am, Graham Dumpleton <graham.dumple...@gmail.com>
wrote:

Thanks for such a nice and elaborate reply.
I think this would not be possible either but I will ask ss there a
way to do something like this
http://svn.apache.org/repos/asf/subversion/trunk/contrib/server-side/authz_svn_group.py
That script is for mod_python it uses groups in svn authorization file
to authorize users.

> ...
>
> read more »

Graham Dumpleton

unread,
Apr 12, 2010, 5:15:58 AM4/12/10
to mod...@googlegroups.com
On 12 April 2010 17:45, Vishwajeet <dextr...@gmail.com> wrote:
>> It seems therefore that in Apache 2.2 you just can't do it without
>> defining a full blown authentication handler, which mod_wsgi doesn't
>> provide support for doing.
>
> Thanks for such a nice and elaborate reply.
> I think this would not be possible either but I will ask ss there a
> way to do something like this
> http://svn.apache.org/repos/asf/subversion/trunk/contrib/server-side/authz_svn_group.py
> That script is for mod_python it uses groups in svn authorization file
> to authorize users.

How complicated are the restrictions in your svnserve.conf file?

It is possible to implement simple restrictions using standard Apache
directives. I don't have an example right now but can send one when
get back to work.

When I get a chance to look at it properly I respond in a bit more detail.

Graham

Vishwajeet

unread,
Apr 12, 2010, 6:20:56 AM4/12/10
to modwsgi

On Apr 12, 2:15 pm, Graham Dumpleton <graham.dumple...@gmail.com>
wrote:


> On 12 April 2010 17:45, Vishwajeet <dextrou...@gmail.com> wrote:
>
> >> It seems therefore that in Apache 2.2 you just can't do it without
> >> defining a full blown authentication handler, which mod_wsgi doesn't
> >> provide support for doing.
>
> > Thanks for such a nice and elaborate reply.
> > I think this would not be possible either but I will ask ss there a
> > way to do something like this

> >http://svn.apache.org/repos/asf/subversion/trunk/contrib/server-side/...


> > That script is for mod_python it uses groups in svn authorization file
> > to authorize users.
>
> How complicated are the restrictions in your svnserve.conf file?
>

SVN Authorization file is not that complicated and is as shown below
[Project1:/]
vishwajeet = rw

[Project2:/]
vishwajeet = r

> It is possible to implement simple restrictions using standard Apache
> directives. I don't have an example right now but can send one when
> get back to work.
>
> When I get a chance to look at it properly I respond in a bit more detail.
>

Thanks once again for your help much appreciated.

> Graham

Graham Dumpleton

unread,
Apr 12, 2010, 10:36:08 PM4/12/10
to mod...@googlegroups.com

Sorry, forgot about this earlier and am in a hurry to get out door now.

The basic idea is to use something like the following. The second one
may need to change as for what I was doing I was actually allowing a
copy into the area, but then no further commits, so not pure read
only. Don't have time to go back and work out what other methods
should be blocked to make it complete read only.

# [Project1:/]
# vishwajeet = rw

<Location /svn/Project1/!svn/*/*/*>
Require user vishwajeet
</Location>

# [Project2:/]
# vishwajeet = r

<Location /svn/Project2/!svn/*/*/*>
Require user vishwajeet
<LimitExcept OPTIONS GET PROPFIND REPORT COPY>
deny from all
</LimitExcept>
</Location>

Anyway, I have done that in a rush, so may not be totally correct, but
you can get the idea.

Look in your Apache access logs to see the different URLs and methods
subversion repository may receive and adjust as necessary.

Graham

Reply all
Reply to author
Forward
0 new messages