On 11 April 2010 16:41, Vishwajeet <[email protected]> wrote:
>
>
> On Apr 11, 9:45 am, Graham Dumpleton <[email protected]>
> wrote:
>> On 11 April 2010 05:16, Vishwajeet <[email protected]> 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,

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

>> Graham
>>
>> >                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 <[email protected]>
>> > wrote:
>> >> On 10 April 2010 04:33, Vishwajeet <[email protected]> wrote:
>>
>> >> > On Apr 9, 6:00 pm, Graham Dumpleton <[email protected]>
>> >> > wrote:
>> >> >> On 9 April 2010 22:23, Graham Dumpleton <[email protected]> 
>> >> >> wrote:
>>
>> >> >> > On 9 April 2010 22:19, Vishwajeet <[email protected]> wrote:
>>
>> >> >> >> On Apr 9, 3:32 pm, Graham Dumpleton <[email protected]>
>> >> >> >> wrote:
>> >> >> >>> On 9 April 2010 19:00, vishwajeet singh <[email protected]> 
>> >> >> >>> 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.
>>
>> >> >> >> 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 ?
>>
>> >> >> > Will have to be tomorrow, no time tonight now and am logging off.
>>
>> >> >> 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.
>>
>> >> > 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.
>>
>> >> 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
>>
>> ...
>>
>> read more »
>
> --
> You received this message because you are subscribed to the Google Groups 
> "modwsgi" group.
> To post to this group, send email to [email protected].
> To unsubscribe from this group, send email to 
> [email protected].
> For more options, visit this group at 
> http://groups.google.com/group/modwsgi?hl=en.
>
>

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

Reply via email to