Endpoint Resource¶
An endpoint resource maps to a single URL endpoint in a Django app’s urls.py. Each endpoint resource can have one or more EndpointDefinition
that implements the handler code. HTTP verbs such as POST
, GET
, along with parameters present in the request determine which EndpointDefinition
is used.
Set Up Resource Adapter¶
The default resource adapter for Django Declarative APIs is django_declarative_apis.adapters.EndpointResource
defined in settings.py
. To use a custom resource adapter, set DECLARATIVE_ENDPOINT_RESOURCE_ADAPTER
to your own definition of resource adapter.
DECLARATIVE_ENDPOINT_RESOURCE_ADAPTER = (
"django_declarative_apis.adapters.EndpointResource")
Helper Function¶
- django_declarative_apis.adapters.resource_adapter(*args, **kwargs)[source]¶
resource_adapter()
is a helper function that finds the endpoint resource adapter from settings.py and calls that resource adapter.resource_adapter takes two arguments:
- Handler/Resource
Required | The
EndpointDefinition
implementation along with an HTTP verb.- Authentication Handler
Optional | If not specified,
OAuth1.0a
will be used by default.
Example: Handler defined in a separate file named
handlers.py
.TodoEndpoint = resource_adapter( post=resources.TodoUpdateDefinition, get=resources.TodoDefinition, authentication={None: (NoAuth(),)}, )
Django app’s
urls.py
.url( r"^tasks/$", handlers.TodoEndpoint, )
EndpointResource¶
- class django_declarative_apis.adapters.EndpointResource(authentication=None, **kwargs)[source]¶
EndpointResource
is the DDA default resource adapter. It validates the configuration of the authentication handler, and in combination with Django’s native urls.py routes requests (through behavioral routing) to the same URL but to different handlers based on request attributes.
Authentication¶
Authentication is tied to the resource adapter. It is the first step that a request needs to pass through. Requests will not be processed if authentication fails at this stage.
Authentication can be set up in two places
- settings.py
Required | Sets the default authenticator for the entire application. In other words, all endpoint definitions will use the authenticator defined here.
Set
DECLARATIVE_ENDPOINT_AUTHENTICATION_HANDLERS
to point to the authentication handler.- resource_adapter()
Optional | Defines authentication handler for a specific endpoint definition. To implement, set
authentication=<{<AuthenticatorHint>: [Authenticator]}>
in an argument to theresource_adapter()
.Once specified, it will override the default authentication setup in
settings.py
.Default Value |
None
Example
# Define a custom NoAuth class. TodoEndpoint = resource_adapter( post=resources.TodoUpdateDefinition, authentication={None: (NoAuth(),)}, )
The resource_adapter
expects the authentication handler to conform to the following configuration schema.
{
<AuthenticatorHint>: [<Authenticator>, <Authenticator>...],
<AuthenticatorHint>: [<Authenticator>, <Authenticator>...],
}
- <AuthenticatorHint>
Required | It is used to match Authorization headers for quick handler lookup.
Properties
Should be defined as a
<(tuple of header hints)>
Must be an instance of
authentication.AuthenticatorHint
orNone
Example
from django_declarative_apis.authentication import AuthenticatorHint SampleAuthenticatorHint = AuthenticatorHint("OAuth ")
If there are more complexities to the authenticator, catch-alls are allowed by using a key of
None
. The catch-all authenticators are always executed after matched authenticators.{ None: [<Authenticator>] }
- <Authenticator>
Required | Responsible for looking at the request and determining whether it can validate the requester.
Properties
Should point to the implementation of the authenticator →
<implementation>
Must be an instance of
authentication.Authenticator
Example
from django_declarative_apis.authentication import Authenticator class SampleAuthenticator(Authenticator): # your code goes here
Example
For instance, if we want to use OAuth1.0a
, we could use an AuthenticatorHint.header
value of “OAuth” as a key, and [django_declarative_apis.authentication.oauthilib.oauth1.TwoLeggedOauth1()]
as value.
This will ensure that any time an ‘Authorization: OAuth …’ header is seen, the appropriate authenticator is used. In this case, the DDA built-in TwoLeggedOauth1()
will be used.
DECLARATIVE_ENDPOINT_AUTHENTICATION_HANDLERS = [
(
(None, 'django_declarative_apis.authentication.oauthlib.oauth1.TwoLeggedOauth1Hint'),
'django_declarative_apis.authentication.oauthlib.oauth1.TwoLeggedOauth'
),
]
Features of DDA Authentication¶
- class django_declarative_apis.authentication.Authenticator[source]¶
The base class for constructing an authenticator.
The Authenticator class has two methods:
is_authenticated
andchallenge
. Both of these need to be overridden by the authenticator implementation that inherits fromAuthenticator
class. Otherwise, it will throw aNotImplementedError
.Example
from django_declarative_apis.authentication import Authenticator class SampleAuthenticator(Authenticator): def is_authenticated(request): # authentication code def challenge(self, error): # challenge code
- class django_declarative_apis.authentication.AuthenticatorHint(header: str)[source]¶
Tuple to provide hints for authentication implementations.
header_hint: a string used to match the
Authentication:
header.
- class django_declarative_apis.authentication.AuthenticationResult(detail=None, auth_header=None)[source]¶
A class definition that takes in and stores the authentication header and details of the result.
- class django_declarative_apis.authentication.AuthenticationSuccess(detail=None, auth_header=None)[source]¶
An instance of
AuthenticationResult
that returnsTrue
.It can be used as a return response in an authenticator implementation.
- class django_declarative_apis.authentication.AuthenticationFailure(detail=None, auth_header=None)[source]¶
An instance of
AuthenticationResult
that returnsFalse
.It can be used as a return response in an authenticator implementation.
- class django_declarative_apis.authentication.NoAuthentication(detail=None, auth_header=None)[source]¶
Authentication handler that always returns True, so no authentication is needed, nor initiated.
Note
Important: In this implementation the
challenge
method is missing and must be implemented by the user. Otherwise, it will raiseNotImplementedError
.
Example
SampleHint = authentication.AuthenticatorHint("SampleHint")
class SampleAuthenticator(authentication.Authenticator):
def is_authenticated(self, request):
try:
# code for authentication of the requester
return authentication.AuthenticationSuccess()
except Exception as error:
# more code
return authentication.AuthenticationFailure()
Custom Authenticator Class¶
Any authenticator class must be an instance of authentication.Authenticator
.
The built-in Authenticator class requires the user to override the built in is_authenticated
and challenge
methods, and write their own authentication methods. If not implemented, it will raise a NotImplementedError
.
Example
The NoAuth authentication handler is the minimal implementation of that interface.
class NoAuth(authentication.Authenticator):
@staticmethod
def is_authenticated(request):
return True
def challenge(self, error):
super().challenge(error)
Built-in DDA Authenticator Based on OAuth1.0a¶
The current authentication implementation is OAuth 1.0a. This works well for cases where keys and secrets can be treated as secret, such as in server-to-server communication. If other methods of authentication are required, custom methods must be implemented and specified when defining URLs and their corresponding endpoints.
TwoLeggedOauth1¶
- class django_declarative_apis.authentication.oauthlib.oauth1.TwoLeggedOauth1[source]¶
- authenticate_header(request)[source]¶
Returns the authentication header. If it does not exist, returns “Unknown OAuth Error
- challenge(oauth_error=None)[source]¶
Returns a 401 response with a small bit on what OAuth is, and where to learn more about it.
When this was written, browsers did not understand OAuth authentication on the browser side, and hence the helpful template we render. Maybe some day in the future, browsers will take care of this stuff for us and understand the 401 with the realm we give it.
Behavioral Routing¶
In DDA a single HTTP request can take multiple endpoint definitions. DDA will determine which EndpointDefinition to use depending on parameters present in the request and parameters accepted by the different endpoint definitions.
Determining whether an EndpointDefinition
can handle the request happens through an EndpointBinder
.
EndpointBinder¶
The EndpointBinder
performs three important roles.
It checks whether all the required fields for an
EndpointDefinition
are present. If everything binds successfully and all the required fields are present, the EndpointDefinition is going to handle the request. If there are errors and other endpoint definitions are present, then the endpoint binder will try the next endpoint definition. If all the endpoint definitions present give error then the endpoint binder will raise an error.It runs the three standard validators required by the endpoint definition that checks whether the requester should have access to
<endpoint>.resource()
.is_authorized()
is_permitted()
is_valid()
It checks the rate limits defined in the endpoint definition, which are:
rate_limit_key()
rate_limit_period()
Example
post=(
FooCreationEndpoint,
BarCreationEndpoint,
FooBarCreationEndpoint,
)
Helper Functions¶
- class django_declarative_apis.machinery.EndpointResourceAttribute(type=None, filter=None, returns_list=False, **kwargs)[source]¶
Used as a decorator on a resource function. Specifies the attributes of that resource.
- Parameters
type (required) – Specifies the model type. It is used only for documentation generation purposes.
filter (optional) – Defines the class filters. Overrides the default filters. Defaults to
None
.returns_list (optional) – It is used for documentation generation purposes. Defaults to
False
Example
from django_declarative_apis.machinery import endpoint_resource class TodoSingleTaskDefinition( TodoResourceMixin, machinery.ResourceEndpointDefinition ): resource_id = url_field(name='id') # grabs the id from url @endpoint_resource(type=Todo) def resource(self): return Todo.objects.get(id=self.resource_id)
- class django_declarative_apis.machinery.EndpointResponseAttribute(type, filter=None, **kwargs)[source]¶
Used as a decorator on a response function. Specifies the attributes of the response.
- Parameters
type (required) – Specifies the response type, which can be dictionary, list, or type. It is used only for documentation generation purposes.
filter (optional) – Defines the class filters. Overrides the default filters. Defaults to
None
.
Example
from django_declarative_apis.machinery import endpoint_response @endpoint_response(type=dict) def response(self): return http.status.OK