Authentication
django-modern-rpc provides a mechanism to check authentication before executing a given RPC method. It implemented at request level and is always checked before executing procedure.
Changed in version 1.0.0: In previous releases, authentication failures caused the view to return a 403 status code on response to standard
(single) request, while batch requests / multicall always returned a 200 status with an error message. For
consistency with XML-RPC specs, an authentication failure now returns a 200 response with a proper
error (`error: -32603, message: Authentication failed when calling "<method_name>"`
).
HTTP Basic Auth
django-modern-rpc comes with a builtin support for HTTP Basic Authentication. It provides a set of decorators to directly extract user information from request and test this user against Django authentication system.
from modernrpc.auth.basic import (
http_basic_auth_login_required,
http_basic_auth_superuser_required,
http_basic_auth_permissions_required,
http_basic_auth_any_of_permissions_required,
http_basic_auth_group_member_required,
http_basic_auth_all_groups_member_required
)
from modernrpc.core import rpc_method
@rpc_method
@http_basic_auth_login_required
def logged_user_required(x):
"""Access allowed only to logged users"""
return x
@rpc_method
@http_basic_auth_superuser_required
def logged_superuser_required(x):
"""Access allowed only to superusers"""
return x
@rpc_method
@http_basic_auth_permissions_required(permissions='auth.delete_user')
def delete_user_perm_required(x):
"""Access allowed only to users with specified permission"""
return x
@rpc_method
@http_basic_auth_any_of_permissions_required(permissions=['auth.add_user', 'auth.change_user'])
def any_permission_required(x):
"""Access allowed only to users with at least 1 of the specified permissions"""
return x
@rpc_method
@http_basic_auth_permissions_required(permissions=['auth.add_user', 'auth.change_user'])
def all_permissions_required(x):
"""Access allowed only to users with all the specified permissions"""
return x
@rpc_method
@http_basic_auth_group_member_required(groups='A')
def in_group_A_required(x):
"""Access allowed only to users contained in specified group"""
return x
@rpc_method
@http_basic_auth_group_member_required(groups=['A', 'B'])
def in_group_A_or_B_required(x):
"""Access allowed only to users contained in at least 1 of the specified group"""
return x
@rpc_method
@http_basic_auth_all_groups_member_required(groups=['A', 'B'])
def in_groups_A_and_B_required_alt(x):
"""Access allowed only to users contained in all the specified group"""
return x
Custom authentication system
To provide authentication features, django-modern-rpc introduce concept of “predicate”. This example will show you how to build a custom authentication system to restrict RPC method execution to clients that present a User-Agent different from a known list of bots.
def forbid_bots_access(request):
"""Return True when request has a User-Agent different from provided list"""
if "User-Agent" not in request.headers:
# No User-Agent provided, the request must be rejected
return False
forbidden_bots = [
'Googlebot', # Google
'Bingbot', # Microsoft
'Slurp', # Yahoo
'DuckDuckBot', # DuckDuckGo
'Baiduspider', # Baidu
'YandexBot', # Yandex
'facebot', # Facebook
]
req_user_agent = request.headers["User-Agent"].lower()
for bot_user_agent in [ua.lower() for ua in forbidden_bots]:
# If we detect the caller is one of the bots listed above...
if bot_user_agent in req_user_agent:
# ... forbid access
return False
# In all other cases, allow access
return True
Note
A predicate always takes a request as argument and returns a boolean value
It is associated with RPC method using @set_authentication_predicate
decorator.
from modernrpc.core import rpc_method
from modernrpc.auth import set_authentication_predicate
from myproject.myapp.auth import forbid_bots_access
@rpc_method
@set_authentication_predicate(forbid_bots_access)
def my_rpc_method(a, b):
return a + b
Now, the RPC method becomes unavailable to callers if User-Agent is not provided or if it has an invalid value.
In addition, you can provide arguments to your predicate using params
:
@rpc_method
@set_authentication_predicate(my_predicate_with_params, params=('param_1', 42))
def my_rpc_method(a, b):
return a + b
It is possible to declare multiple predicates for a single method. In such case, all predicates must return True to allow access to the method.
@rpc_method
@set_authentication_predicate(forbid_bots_access)
@set_authentication_predicate(my_predicate_with_params, params=('param_1', 42))
def my_rpc_method(a, b):
return a + b