Code layout change.
[wolnelektury.git] / src / basicauth.py
diff --git a/src/basicauth.py b/src/basicauth.py
new file mode 100644 (file)
index 0000000..befcc6f
--- /dev/null
@@ -0,0 +1,100 @@
+#############################################################################
+# from http://djangosnippets.org/snippets/243/
+
+from functools import wraps
+import base64
+
+from django.http import HttpResponse
+from django.contrib.auth import authenticate, login
+
+#
+def view_or_basicauth(view, request, test_func, realm = "", *args, **kwargs):
+    """
+    This is a helper function used by 'logged_in_or_basicauth' and
+    'has_perm_or_basicauth' (deleted) that does the nitty of determining if they
+    are already logged in or if they have provided proper http-authorization
+    and returning the view if all goes well, otherwise responding with a 401.
+    """
+    if test_func(request.user):
+        # Already logged in, just return the view.
+        #
+        return view(request, *args, **kwargs)
+
+    # They are not logged in. See if they provided login credentials
+    #
+    if 'HTTP_AUTHORIZATION' in request.META:
+        auth = request.META['HTTP_AUTHORIZATION'].split()
+        if len(auth) == 2:
+            # NOTE: We are only support basic authentication for now.
+            #
+            if auth[0].lower() == "basic":
+                uname, passwd = base64.b64decode(auth[1]).split(':')
+                user = authenticate(username=uname, password=passwd)
+                if user is not None:
+                    if user.is_active:
+                        login(request, user)
+                        request.user = user
+                        return view(request, *args, **kwargs)
+
+    # Either they did not provide an authorization header or
+    # something in the authorization attempt failed. Send a 401
+    # back to them to ask them to authenticate.
+    #
+    response = HttpResponse()
+    response.status_code = 401
+    response['WWW-Authenticate'] = 'Basic realm="%s"' % realm
+    return response
+    
+
+#
+def logged_in_or_basicauth(realm = ""):
+    """
+    A simple decorator that requires a user to be logged in. If they are not
+    logged in the request is examined for a 'authorization' header.
+
+    If the header is present it is tested for basic authentication and
+    the user is logged in with the provided credentials.
+
+    If the header is not present a http 401 is sent back to the
+    requestor to provide credentials.
+
+    The purpose of this is that in several django projects I have needed
+    several specific views that need to support basic authentication, yet the
+    web site as a whole used django's provided authentication.
+
+    The uses for this are for urls that are access programmatically such as
+    by rss feed readers, yet the view requires a user to be logged in. Many rss
+    readers support supplying the authentication credentials via http basic
+    auth (and they do NOT support a redirect to a form where they post a
+    username/password.)
+
+    Use is simple:
+
+    @logged_in_or_basicauth
+    def your_view:
+        ...
+
+    You can provide the name of the realm to ask for authentication within.
+    """
+    def view_decorator(func):
+        def wrapper(request, *args, **kwargs):
+            return view_or_basicauth(func, request,
+                                     lambda u: u.is_authenticated(),
+                                     realm, *args, **kwargs)
+        return wrapper
+    return view_decorator
+
+
+#############################################################################
+
+
+def factory_decorator(decorator):
+    """ generates a decorator for a function factory class
+    if A(*) == f, factory_decorator(D)(A)(*) == D(f)
+    """
+    def fac_dec(func):
+        @wraps(func)
+        def wrapper(*args, **kwargs):
+            return decorator(func(*args, **kwargs))
+        return wrapper
+    return fac_dec