Skip to content

Commit

Permalink
auth-required API can be auto discovered
Browse files Browse the repository at this point in the history
  • Loading branch information
feifangit committed Mar 9, 2015
1 parent 70e7a85 commit 735cc25
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 50 deletions.
42 changes: 10 additions & 32 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Features
2. Each API key can be associated with a set of API
3. API can be associated with user, your legacy code with ``request.user`` underneath can work smoothly with ``dj-api-auth``
4. Add auth by simply put a decorator on your view.
5. Provide utility functions that can help you register APIs as assignable. Both browser-oriented views(reusing) and dedicated API-oriented views can be marked as assignable.
5. Discover API with auth enabled automatically, these auth-required APIs will display in assignable list when creating API keys
6. A Django command to scan and update API information to database.


Expand Down Expand Up @@ -55,7 +55,13 @@ If you have ``admin`` enabled for your project, you can find these features in `

Add auth for views
----------------------------
- For legacy views, we can add the auth and registration together in URL dispatching file, usually the ``urls.py``, see **Register APIs** for detail.
- For legacy views, we provide utility function ``url_with_auth`` in ``djapiauth.utility``

.. code-block:: python
# add auth for a browser-oriented view
url_with_auth(r'^hello/$', 'djapp.views.index'),
#...
- For API views, simply add ``@api_auth`` for the view after ``from djapiauth.auth import api_auth``

Expand All @@ -66,36 +72,9 @@ Add auth for views
return JsonResponse({"user": "feifan", "boss": "lidan zhou"})
Register APIs
-------------
In order to manage APIs those can be assigned to an API key, we have to mark them first.
We may have two cases:

- For legacy views, we adds auth and register API in URL dispatching file at same time by utility function ``reg_n_protect_api``
- For views dedicate for API-style access, use utility function ``reg_api`` to finish the registration


.. code-block:: python
# e.g.
# 1, add auth for prevapp.views.myview, assign new url myapi/, of course, register the API
# 2, since api_whoami have api_auth decorator in views.py, we only register here,
# and assign url whoami/ for api.myapi.api_whoami
import prevapp.views as app1views # legacy views
from djapiauth.utility import reg_n_protect_api, reg_api
urlpatterns = patterns('api.myapi',
reg_n_protect_api(r'^myapi/$', 'myview', views=app1views), # 1
reg_api(r'^whoami/$', 'api_whoami'), # 2
# ....
)
Scan registered API
Scan API
-------------------
we have a Django command ``reloadentrypoints`` to help you collect all registered API entry points to database.
we have a Django command ``reloadentrypoints`` to help you to collect and save all auth-required APIs to database.


Error messages
Expand Down Expand Up @@ -138,5 +117,4 @@ Thanks
TODO
-----
- performance improvement for entry point matching in API permission check.
- easier solution to register API

1 change: 1 addition & 0 deletions djapiauth/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,6 @@ def wrapped(request, *args, **kwargs):
except Exception as e:
return JsonResponse({"error": str(e)}, status=403)
return func(request, *args, **kwargs)
setattr(wrapped, '__djapiauth__', True)
return wrapped
return real_decorator if not function else real_decorator(function)
32 changes: 14 additions & 18 deletions djapiauth/utility.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import json
import datetime
import decimal
from django.utils import six
from django.http import HttpResponse
from django.core.urlresolvers import RegexURLResolver
from django.conf.urls import url
from django.core.urlresolvers import RegexURLPattern
from django.utils.module_loading import import_by_path


def is_aware(value):
Expand Down Expand Up @@ -67,27 +68,22 @@ def __init__(self, data, encoder=DjangoJSONEncoder, safe=True, **kwargs):
super(JsonResponse, self).__init__(content=data, **kwargs)


# neither reg_api nor reg_n_protect_api take effect on include(...)
def reg_api(regex, viewname, *args, **kwargs): # api_auth added in view, make registration only
urlobj = url(regex, viewname, *args, **kwargs)
if isinstance(urlobj, RegexURLPattern):
setattr(urlobj, "_protected_api", True)
return urlobj


def reg_n_protect_api(regex, viewname, views, *args, **kwargs):
"""reuse an existing view, must provide views"""
from .auth import api_auth
if not isinstance(viewname, (list, tuple)): # not include
urlobj = url(regex, api_auth(getattr(views, viewname)))
setattr(urlobj, "_protected_api", True)
return urlobj
return url(regex, viewname, *args, **kwargs) # ignore include
def url_with_auth(regex, view, kwargs=None, name=None, prefix=''):
"""
if view is string based, must be a full path
"""
from djapiauth.auth import api_auth
if isinstance(view, six.string_types): # view is a string, must be full path
return url(regex, api_auth(import_by_path(prefix + "." + view if prefix else view)))
elif isinstance(view, (list, tuple)): # include
return url(regex, view, name, prefix, **kwargs)
else: # view is an object
return url(regex, api_auth(view))


def is_protected_api(u):
""" if a url is registered as protected """
return hasattr(u, "_protected_api")
return u.callback and getattr(u.callback, "__djapiauth__", False)


def traverse_urls(urlpattern, prefixre=[], prefixname=[], patternFunc=None, resolverFunc=None):
Expand Down

0 comments on commit 735cc25

Please sign in to comment.