How to change a URL that is configured inside a library? - python

Context: I need to configure OIDC using Django to allow my application to be accessed by users.
Problem: I'm using a library called mozilla-django-oidc that is pretty straight-forward, but when I test the configuration, it keeps returning the following error:
400 - Invalid redirect_uri
Cause: As I tested in Postman, this error occurs because the callback URL is wrong. When I searched more deeply mozilla-django-oidc, I've found it sets a standard URL path for callback (/oidc/callback/) which is not the URL allowed by the OIDC we need to access.
Key Question #1: How can I modify the mozilla-django-oidc code so that the standard callback URL path is what I need it to be?
Key Question #2: Is there anyway I can make these changes in my code, instead of mozilla-django-oidc?
Below are some settings of my Django code:
My Django - views.py
# Mozila OIDC configuration settings
OIDC_OP_AUTHORIZATION_ENDPOINT = "https://XXXXXXXXXXXXXXXXXXXXX"
OIDC_OP_TOKEN_ENDPOINT = "https://XXXXXXXXXXXXXXXXXXXXX"
OIDC_OP_USER_ENDPOINT = "https://XXXXXXXXXXXXXXXXXXXXX"
OIDC_RP_CLIENT_ID = "XXXXXXXXXXXXXXXXXXXXXXX"
OIDC_RP_CLIENT_SECRET = "XXXXXXXXXXXXXXXXXXXXXXX"
LOGIN_REDIRECT_URL = "https://myurl.com"
My Django - settings.py
# Add 'mozilla_django_oidc' to INSTALLED_APPS
INSTALLED_APPS = (
# ...
'django.contrib.auth',
'mozilla_django_oidc', # Load after auth
# ...
)
# Add 'mozilla_django_oidc' authentication backend
AUTHENTICATION_BACKENDS = (
'mozilla_django_oidc.auth.OIDCAuthenticationBackend',
# ...
)
My Django - urls.py
urlpatterns = patterns(
# ...
url(r'^oidc/', include('mozilla_django_oidc.urls')),
# ...
)
mozilla-django-oidc - urls.py
urlpatterns = [
url(r'^callback/$', OIDCCallbackClass.as_view(),
name='oidc_authentication_callback'),
url(r'^authenticate/$', OIDCAuthenticateClass.as_view(),
name='oidc_authentication_init'),
url(r'^logout/$', views.OIDCLogoutView.as_view(), name='oidc_logout'),
]
Other parts of mozilla-django-oidc library can be found at https://github.com/mozilla/mozilla-django-oidc/tree/master/mozilla_django_oidc

Related

Why “accounts/login/?next=/” is coming in url of Django on local development server ? | Way to remove “accounts/login/?next=/” from the url

Why “accounts/login/?next=/” is coming in url of Django on local development server ? | Way to remove “accounts/login/?next=/” from the url .
I have used class based views and used LoginRequiredMixin.
Can anyone tell me why the path changes from /login to accounts/login/?next=/
path('login/', CustomLoginView.as_view(), name='login'),
just add this to your settings.py
LOGIN_URL = 'login'
From Django Docs:
The URL or named URL pattern where requests are redirected for login when using the login_required() decorator...
see: https://docs.djangoproject.com/en/4.1/ref/settings/#login-url
because you have used this in urls.py
path('login/', CustomLoginView.as_view(), name='login')
just remove this url it will be fixed
use this for admin login
path('admin/', admin.site.urls)

How to log in on API-level with django-hosts subdomain and DRF

I'm using django-rest-framework (DRF) to have a browsable API which shall be available under the api subdomain e.g. api.localhost:8000. To serve subdomains, I'm using django-hosts.
I'm having trouble to achieve login and logout functionality on API level (i.e. under the api subdomain).
The subdomains work but I can't get the DRF autentication mechanism to work anymore.
Problem
Visiting api.localhost:8000/ shows the browsable api page which is fine. The DRF Log in button is also available. Using this button shows the log-in form, but when submitting the form, I'm redirected to http://login/ which of course can't be resolved and thus the login fails. The same applies to the logout functionality, which leads to http://logout/ and also fails.
Below are screenshots for these steps.
Django Settings
ROOT_URLCONF = "server.urls"
ROOT_HOSTCONF = "server.hosts"
DEFAULT_HOST = "api"
django-hosts configuration
# server/hosts.py
from django_hosts import patterns, host
from django.conf import settings
host_patterns = patterns(
'',
host(r"api", "server.api_urls", name="api"),
host(r"admin", "server.admin_urls", name="admin"),
host("", settings.ROOT_URLCONF), # resolves to server/urls.py
)
URLS configuration
# server/urls.py
from django.urls import include, path
# this is my root url conf
urlpatterns = [
path("", include("server.api_urls")),
path("", include("rest_framework.urls", namespace='rest_framework')),
path("", admin.site.urls),
]
I'm not happy with this, but even though I serve it at another subdomain, without adding path("", admin.site.urls) to the root urls conf, I always got 'admin' is not a registered namespace. For the same reason I included rest_framework.urls here, because otherwise the namespace rest_framework was not registered.
# server/api_urls.py
urlpatterns = [
path("", include('rest_framework.urls', namespace='rest_framework')),
path("", include("myapp.urls")), # here is the DRF routing
]
In order to use the DRF authentication mechanism and the Log in and Log out button, I include the rest_framework.urls under the namespace I registered before. After that, the usual app urls are included which route the users API endpoint shown in the screenshot below.
# server/admin_urls.py
from django.contrib import admin
urlpatterns = [
path("", admin.site.urls),
]
Screenshots
Visiting api.localhost:8000/ shows the browsable api page.
Using the Log in Button redirects to http://api.localhost:8000/login/?next=/ which is fine.
But after submitting the form, I'm redirected to http://login/ which of course can't be resolved and thus the login fails. The same applies to the logout functionality, which leads to http://logout/ and also fails.
How can I fix the authentication mechanism to work on API-level?
add PARENT_HOST setting.
This will make your redirect correct

Loading URL patterns without activating them?

My Django project has two applications in it: a web tool and a REST interface.
I run the REST interface on my database system (e.g. db.myhost.com). This interface only has URL patterns that correspond to the various REST endpoints:
app_name = "rest"
urlpatterns = [
url(r'^report/id/(?P<rid>[0-9]+)/$', views.ReportByID.as_view()),
url(r'^report/slug/(?P<slug>[a-z0-9-]+)/$', views.ReportBySlug.as_view()),
]
Part of the data that these views ultimately show need links to the other application in my project (which I host on a separate system). That application also has URL patterns:
app_name = "mytool"
urlpatterns = [
url(r'^some/cool/path/$', views.some_cool_path),
]
The REST interface only enables the REST URL patterns, since I only want to serve REST endpoints via that host:
# On my REST system
ROOT_URL = "myproject.rest_urls"
Is there a way that I can get the REST application to load the mytool URL patterns without activating them? I don't want a user to be able to browse to db.myhost.com/some/cool/path/ and get an error because that path isn't served on that host, it's served by the web tool server instead. It would be helpful, however, to be able to use reverse() to get the mytool URLs, even if they are just relative fragments (i.e. /some/cool/path ... I could always prepend the server name, which is unlikely to ever change).
I could hard-code the necessary paths, but I'd like to avoid having to do that in case they need to change in the future.
We can do it using django test utils override_settings decorator. It will use temporary settings so, it will not have any effect on the live site.
settings.py
INSTALLED_APPS = [
# .....
'mytool',
# .....
]
ROOT_URL = "myproject.rest_urls"
mytool/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('news/<slug:slug>/', views.NewsDetailView.as_view(), name='news_detail'),
]
mytool/utils.py
from django.test.utils import override_settings
from django.urls import reverse
def temp_reverse(url_conf, url_name, url_args=(), url_kwargs={}):
#override_settings(ROOT_URLCONF=url_conf)
def get_reverse(url_name, *args, **kwargs):
return reverse(url_name, args=args, kwargs=kwargs)
return get_reverse(url_name, *url_args, **url_kwargs)
accessing the reverse for unregistered urls
from mytool.urils import temp_reverse
url = temp_reverse('mytool.urls', 'news_detail', url_kwargs={'slug': 'django-awesome'})
print(url)
# output: /news/django-awesome/

How can I enable CORS on Django REST Framework

How can I enable CORS on my Django REST Framework?
the reference doesn't help much, it says that I can do by a middleware, but how can I do that?
The link you referenced in your question recommends using django-cors-headers, whose documentation says to install the library
python -m pip install django-cors-headers
and then add it to your installed apps:
INSTALLED_APPS = (
...
'corsheaders',
...
)
You will also need to add a middleware class to listen in on responses:
MIDDLEWARE = [
...,
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...,
]
and specify domains for CORS, e.g.:
CORS_ALLOWED_ORIGINS = [
'http://localhost:3030',
]
Please browse the configuration section of its documentation, paying particular attention to the various CORS_ORIGIN_ settings. You'll need to set some of those based on your needs.
python -m pip install django-cors-headers
and then add it to your installed apps:
INSTALLED_APPS = [
...
'corsheaders',
...
]
You will also need to add a middleware class to listen in on responses:
MIDDLEWARE = [
...,
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...,
]
CORS_ALLOW_ALL_ORIGINS = True # If this is used then `CORS_ALLOWED_ORIGINS` will not have any effect
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOWED_ORIGINS = [
'http://localhost:3030',
] # If this is used, then not need to use `CORS_ALLOW_ALL_ORIGINS = True`
CORS_ALLOWED_ORIGIN_REGEXES = [
'http://localhost:3030',
]
more details: https://github.com/ottoyiu/django-cors-headers/#configuration
read the official documentation can resolve almost all problem
You can do by using a custom middleware, even though knowing that the best option is using the tested approach of the package django-cors-headers. With that said, here is the solution:
create the following structure and files:
-- myapp/middleware/__init__.py
from corsMiddleware import corsMiddleware
-- myapp/middleware/corsMiddleware.py
class corsMiddleware(object):
def process_response(self, req, resp):
resp["Access-Control-Allow-Origin"] = "*"
return resp
add to settings.py the marked line:
MIDDLEWARE_CLASSES = (
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
# Now we add here our custom middleware
'app_name.middleware.corsMiddleware' <---- this line
)
In case anyone is getting back to this question and deciding to write their own middleware, this is a code sample for Django's new style middleware -
class CORSMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
response["Access-Control-Allow-Origin"] = "*"
return response
For Django versions > 1.10, according to the documentation, a custom MIDDLEWARE can be written as a function, let's say in the file: yourproject/middleware.py (as a sibling of settings.py):
def open_access_middleware(get_response):
def middleware(request):
response = get_response(request)
response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Headers"] = "*"
return response
return middleware
and finally, add the python path of this function (w.r.t. the root of your project) to the MIDDLEWARE list in your project's settings.py:
MIDDLEWARE = [
.
.
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'yourproject.middleware.open_access_middleware'
]
Easy peasy!
Below are the working steps without the need for any external modules:
Step 1: Create a module in your app.
E.g, lets assume we have an app called user_registration_app. Explore user_registration_app and create a new file.
Lets call this as custom_cors_middleware.py
Paste the below Class definition:
class CustomCorsMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Headers"] = "*"
# Code to be executed for each request/response after
# the view is called.
return response
Step 2: Register a middleware
In your projects settings.py file, add this line
'user_registration_app.custom_cors_middleware.CustomCorsMiddleware'
E.g:
MIDDLEWARE = [
'user_registration_app.custom_cors_middleware.CustomCorsMiddleware', # ADD THIS LINE BEFORE CommonMiddleware
...
'django.middleware.common.CommonMiddleware',
]
Remember to replace user_registration_app with the name of your app where you have created your custom_cors_middleware.py module.
You can now verify it will add the required response headers to all the views in the project!
Updated 2021 for all those who have the latest version of Django v3.x.x, The steps to allow CORS from any origin are given below.
Step 1: Install required library
pip install django-cors-headers
Step 2: Then add in proper place in your INSTALLED_APPS in settings.py - after the rest_framework and before your application myapp
'rest_framework',
'corsheaders',
'myapp.apps.MyAppConfig',
Step 3: Allow the origins for your api (inside settings.py)
CORS_ORIGIN_WHITELIST = (
'http://localhost:3000', # for localhost (REACT Default)
'http://192.168.10.45:3000', # for network
)
Updated 2022 and adding a new use case
When your using Axios POST with the option withCredentials: true, there are a few additional options to consider.
I used this specific case for authentification over Basic or/and Session login.
To Avoid error messages as:
Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
And the above mentioned by others. I solved the issue in this way.
[IP addresses are from my local example, have in mind to change it]
setting.py
INSTALLED_APPS = [
...
'rest_framework',
'corsheaders',
'rest_framework.authtoken',
...
]
ALLOWED_HOSTS = ["localhost","192.168.0.50"]
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = (
'http://localhost:3000', # for localhost (REACT Default)
'http://192.168.0.50:3000', # for network
'http://localhost:8080', # for localhost (Developlemt)
'http://192.168.0.50:8080', # for network (Development)
)
CSRF_TRUSTED_ORIGINS = [
'http://localhost:3000', # for localhost (REACT Default)
'http://192.168.0.50:3000', # for network
'http://localhost:8080', # for localhost (Developlemt)
'http://192.168.0.50:8080', # for network (Development)
]
MIDDLEWARE = [
...
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'corsheaders.middleware.CorsMiddleware',
...
]
On the browser, the Axios request headers must be send and on the server site the headers must be permitted. If not, the error message will be.
Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response.
Up to this moment play with the headers. You can add more headers if you need them, like:
CORS_ALLOW_HEADERS = [
'accept',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
]
Cheers :)
Well, I don't know guys but:
using here python 3.6 and django 2.2
Renaming MIDDLEWARE_CLASSES to MIDDLEWARE in settings.py worked.
first install django package
pip install django-cors-headers
and add to apps in settings file
INSTALLED_APPS = (
...
'corsheaders',
...
)
and then add cors middle ware to setting file
MIDDLEWARE = [
...,
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...,
]
and finally add cross orgin whitelist
#CORS_ORIGIN_ALLOW_ALL = True
#CORS_ALLOW_CREDENTIALS = True
#CORS_ALLOW_HEADERS = ['*']
CORS_ORIGIN_WHITELIST = ('http://localhost:5000',)
that will solve cors error easily. happy coding
Django=2.2.12 django-cors-headers=3.2.1 djangorestframework=3.11.0
Follow the official instruction doesn't work
Finally use the old way to figure it out.
ADD:
# proj/middlewares.py
from rest_framework.authentication import SessionAuthentication
class CsrfExemptSessionAuthentication(SessionAuthentication):
def enforce_csrf(self, request):
return # To not perform the csrf check previously happening
#proj/settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'proj.middlewares.CsrfExemptSessionAuthentication',
),
}
After trying every suggested solution and nothing seemed to work. I finally fixed the issue after much frustration by clearing my browser cache...
Then the accepted answer works by using django-cors-headers.
Hope this helps someone else!

Turn off caching of static files in Django development server

Is there an easy way to turn off caching of static files in Django's development server?
I'm starting the server with the standard command:
$ python manage.py runserver
I've got settings.py configured to serve up static files from the /static directory of my Django project. I've also got a middleware class that sets the Cache-Control header to must-revalidate, no-cache for development, but that only seems to affect URLs that are not in my /static directory.
#Erik Forsberg's answer worked for me. Here's what I had to do:
Comment out the staticfiles app from INSTALLED_APPS in settings.py:
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
#'django.contrib.staticfiles',
)
Leave my STATIC_URL variable set in settings.py:
STATIC_URL = '/static/'
Add an entry to my project's base urls.py:
# static files w/ no-cache headers
url(r'^static/(?P<path>.*)$', 'django.views.static.serve',
{'document_root': settings.STATIC_ROOT}),
Note that I'm also setting the Cache-Control headers in a middleware class nocache.py:
class NoCache(object):
def process_response(self, request, response):
"""
set the "Cache-Control" header to "must-revalidate, no-cache"
"""
if request.path.startswith('/static/'):
response['Cache-Control'] = 'must-revalidate, no-cache'
return response
And then including that in settings.py:
if DEBUG:
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'nocache.NoCache',
)
Django's contrib.staticfiles app automatically serves staticfiles for you by overriding the runserver command. With this configuration you can't control the way it serves the static files.
You can prevent the staticfiles app from serving the static files by adding the --nostatic option to the runserver command:
./manage.py runserver --nostatic
Then you can write an url config to manually serve the static files with headers that prevent the browser from caching the response:
from django.conf import settings
from django.contrib.staticfiles.views import serve as serve_static
from django.views.decorators.cache import never_cache
urlpatterns = patterns('', )
if settings.DEBUG:
urlpatterns += patterns('',
url(r'^static/(?P<path>.*)$', never_cache(serve_static)),
)
If you want your manage.py runserver command to have the --nostatic option on by default, you can put this in your manage.py:
if '--nostatic' not in sys.argv:
sys.argv.append('--nostatic')
Assuming you're using django.views.static.serve, it doesn't look like it - but writing your own view that just calls django.views.static.serve, adding the Cache-Control header should be rather easy.
Use whitenoise. There's a lot of issues with the static file serving in runserver and they're all already fixed in whitenoise. It's also WAY faster.
They've talked about just replacing the built-in static serving with it, but no one has gotten around to it yet.
Steps to use it in development...
Install with pip install whitenoise
Add the following to the end of settings.py:
if DEBUG:
MIDDLEWARE = [
'whitenoise.middleware.WhiteNoiseMiddleware',
] + MIDDLEWARE
INSTALLED_APPS = [
'whitenoise.runserver_nostatic',
] + INSTALLED_APPS
My very simple solution:
from django.contrib.staticfiles.views import serve
from django.views.decorators.cache import never_cache
static_view = never_cache(serve)
urlpatterns += static_view(settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT)
In newer versions of Django a very simple solution is modify the project urls like so:
from django.conf.urls.static import static
from django.contrib.staticfiles.views import serve
from django.views.decorators.cache import cache_control
from django.conf import settings
# YOUR urlpatterns here...
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, view=cache_control(no_cache=True, must_revalidate=True)(serve))
I arrived at this by looking at how staticfiles modifies the urls automatically and just adding a view decorator. I really don't understand why this isn't the default as this is for development ONLY. The view is able to properly handle a "If-Modified-Since" HTTP header so a request is always made but contents are only transferred on changes (judged by looking at the modification timestamp on the file).
For this to work you must add --nostatic when using runserver, otherwise the above changes are simply ignored.
IMPORTANT EDIT: What I had before didn't work because I wasn't using --nostatic and the never_cache decorator also included no-store which meant unchanged files were always being re-transferred instead of returning 304 Not Modified
For newer Django, the way middleware classes are written has changed a bit.
Follow all the instructions from #aaronstacy above, but for the middleware class, use this:
class NoCache(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
response['Cache-Control'] = 'must-revalidate, no-cache'
return response
It's so simple if you are using Django 2.0+
Step-1 : Make 'django.contrib.staticfiles' as comment in settings.py(Project level)
INSTALLED_APPS = [
# 'django.contrib.staticfiles',
]
Step-2 : Import followings in urls.py (Project level)
from django.conf.urls.static import static
from django.contrib.staticfiles.views import serve
from django.views.decorators.cache import never_cache
from . import settings
Step-3 : Add following line in urls.py(project level) after urlpatterns
urlpatterns = [
]
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, view=never_cache(serve))
Make sure that STATIC_URL is declared in your settings.py
STATIC_URL = '/static/'
The staticfiles app implements serving of static files by overriding the runserver command. We can do the same: override this command again and implement a custom handler which turns off caching.
Note that your django application must be before django.contrib.staticfiles in INSTALLED_APPS, otherwise your command will be ignored.
# your_django_app/management/commands/runserver.py
from django.utils.cache import add_never_cache_headers
from django.contrib.staticfiles.handlers import (
StaticFilesHandler as BaseStaticFilesHandler,
)
from django.contrib.staticfiles.management.commands.runserver import (
Command as RunserverCommand,
)
class StaticFilesHandler(BaseStaticFilesHandler):
def serve(self, request):
response = super().serve(request)
add_never_cache_headers(response)
return response
class Command(RunserverCommand):
def get_handler(self, *args, **options):
handler = super().get_handler(*args, **options)
# Check that serving static files is enabled
if isinstance(handler, BaseStaticFilesHandler):
# If yes, replace the original handler with our custom handler
handler = StaticFilesHandler(handler.application)
return handler
This has nothing with Django, because nothing changed after I reinstall Django using pip.
This is the behavior of browser, so you just need to clear cached images files of your browser.
Ref
Chrome Clear cache and cookies

Categories