no background for banner
[wolnelektury.git] / src / basicauth.py
1 # -*- coding: utf-8 -*-
2 #############################################################################
3 # from http://djangosnippets.org/snippets/243/
4
5 from functools import wraps
6 import base64
7
8 from django.http import HttpResponse
9 from django.contrib.auth import authenticate, login
10
11
12 def view_or_basicauth(view, request, test_func, realm="", *args, **kwargs):
13     """
14     This is a helper function used by 'logged_in_or_basicauth' and
15     'has_perm_or_basicauth' (deleted) that does the nitty of determining if they
16     are already logged in or if they have provided proper http-authorization
17     and returning the view if all goes well, otherwise responding with a 401.
18     """
19     if test_func(request.user):
20         # Already logged in, just return the view.
21         #
22         return view(request, *args, **kwargs)
23
24     # They are not logged in. See if they provided login credentials
25     #
26     if 'HTTP_AUTHORIZATION' in request.META:
27         auth = request.META['HTTP_AUTHORIZATION'].split()
28         if len(auth) == 2:
29             # NOTE: We are only support basic authentication for now.
30             #
31             if auth[0].lower() == "basic":
32                 uname, passwd = base64.b64decode(auth[1]).split(':')
33                 user = authenticate(username=uname, password=passwd)
34                 if user is not None:
35                     if user.is_active:
36                         login(request, user)
37                         request.user = user
38                         return view(request, *args, **kwargs)
39
40     # Either they did not provide an authorization header or
41     # something in the authorization attempt failed. Send a 401
42     # back to them to ask them to authenticate.
43     #
44     response = HttpResponse()
45     response.status_code = 401
46     response['WWW-Authenticate'] = 'Basic realm="%s"' % realm
47     return response
48     
49
50 #
51 def logged_in_or_basicauth(realm=""):
52     """
53     A simple decorator that requires a user to be logged in. If they are not
54     logged in the request is examined for a 'authorization' header.
55
56     If the header is present it is tested for basic authentication and
57     the user is logged in with the provided credentials.
58
59     If the header is not present a http 401 is sent back to the
60     requestor to provide credentials.
61
62     The purpose of this is that in several django projects I have needed
63     several specific views that need to support basic authentication, yet the
64     web site as a whole used django's provided authentication.
65
66     The uses for this are for urls that are access programmatically such as
67     by rss feed readers, yet the view requires a user to be logged in. Many rss
68     readers support supplying the authentication credentials via http basic
69     auth (and they do NOT support a redirect to a form where they post a
70     username/password.)
71
72     Use is simple:
73
74     @logged_in_or_basicauth
75     def your_view:
76         ...
77
78     You can provide the name of the realm to ask for authentication within.
79     """
80     def view_decorator(func):
81         def wrapper(request, *args, **kwargs):
82             return view_or_basicauth(func, request,
83                                      lambda u: u.is_authenticated(),
84                                      realm, *args, **kwargs)
85         return wrapper
86     return view_decorator
87
88
89 #############################################################################
90
91
92 def factory_decorator(decorator):
93     """ generates a decorator for a function factory class
94     if A(*) == f, factory_decorator(D)(A)(*) == D(f)
95     """
96     def fac_dec(func):
97         @wraps(func)
98         def wrapper(*args, **kwargs):
99             return decorator(func(*args, **kwargs))
100         return wrapper
101     return fac_dec