Feature: custom URLPattern and URLResolver

For a project I had to use custom URLPattern and URLResolver. I wanted to resolve on URLs that were not corresponding to my urlpatterns while keeping my patterns untouched when using reverse.
This is quite strange behavior I admit.
However, if we want for some reason to use a custom URLPattern or URLResolver, we have to rewrite the entire _path in django.urls.conf which is not really good for maintenance.

Is there a reason for which we should not customize those two classes ?

Can you be more specific with what the issue is here? Perhaps including an example of the situation you’re trying to address?

My gut reaction is that there’s usually an easier way to handle things like this.

Sure :

My django app has a frontend at / and an admin at /admin/
I have two domains: let’s say frontend.example.com and admin.example.com.
frontend.example.com points to / in my project.
What I would like for admin.example.com is that it points to /admin/: I feel it easier to operate when there is no /admin/ in the url. So I want to reach the myadmin_page with the url admin.example.com/myadmin_page while this view is actually registered on /admin/myadmin_page.

I came up with this setup:
Reverse proxy requests from admin.example.com/myadmin_page to admin.example.com/admin/myadmin_page. When I do only that, every link in admin site is /admin/myother_admin_page which is not what I want, (remember, I want to hide this /admin/). This is because for django, my admin site is located at /admin/ and reverse returns this urlpattern.
To overhaul this, I used :

class PrefixedURLResolver(URLResolver):
    def __init__(
            self,
            pattern,
            urlconf_name,
            prefix,
            default_kwargs=None,
            app_name=None,
            namespace=None):
        self.prefix = prefix
        super().__init__(pattern, urlconf_name,
                         default_kwargs=None, app_name=None, namespace=None)

    def resolve(self, path):
        if not path.startswith(self.prefix):
            return
        else:
            path = path.replace(self.prefix, '')
        return super().resolve(path)

This way, in my urlpattern I have directly /myadmin_page (for using reverse) but actually, it resolves on /admin/myadmin_page.

Thanks :slight_smile:

Are you using the same app/project for both domains?

What web server are you using? (nginx?)

Do you still want frontend.example.com to have access to the admin?

How do you ensure that there aren’t any url conflicts between the two sets of urls, given they’re both starting from the same root?

Personally, I’d be looking at this from a different direction I think. I’d be looking at an nginx-rewrite style situation. (Although, it would scare me a lot to have a production server exposed to the internet where the admin app is the root url. For security purposes, we always have that app buried under a different name - makes it much less likely for a bot to find it as a possible exploit.)

You could just define your admin urls as path("", admin.site.urls),. The admin part of the url is only used because it’s typically defined that way in your urls.py file.

It is the same project and different apps.

I am using gunicorn. The reverse proxy is done by a load balancer.

The load balancer already prevents frontend.example.com from accessing the admin.

I probably was not clear but there cannot be any url conflicts since frontend is at root, admin is at /admin/. It is just that at frontend.example.com I am accessing frontend at root. At admin.example.com I am accessing admin at root (and I cannot access frontend obviously since everything is prefixed by /admin/).

If I do path("", admin.site.urls), I am mixing my frontend and admin urls which is pretty messy. Plus, I give access to frontend user to those urls (for which they will end with a 403).

I precise that my question is not particularly about admin but rather about accessing a path /prefix/something without showing the prefix.

I do not understand why having admin at root is a security issue. For sure I will get more hits by random robots. But my admin site is secure (I suppose that django developpers did a good job for that). What kind of security issues should I be affraid of (and that won’t exist if my site is at /some_random_name/).

I already looked at FORCE_SCRIPT_NAME but this param is doing the opposite of what I would like.

As far as I can tell, you’re already doing that with what you’ve described.

There’s nothing I’m seeing that can tell the difference in what’s being submitted from the browser.

If I had to address this issue, I’d probably want to run two separate instances of your project, one with path("", admin.site.urls) to be accessed by admin.example.com, and the other without any admin path for frontend.example.com. Same code, just a different setting between the two to identify which root url to use.

That’s precisely the point. Bots are dumb. They look for targets of opportunity.

They do the best they can - but they’re not perfect. You won’t know if a vulnerability exists until it’s too late.

Again, attacks by bots. They’re a very real problem.

I run a small personal website. It’s got fewer than 30 users. More than 90% of all requests made to my domain are by bots. My fail2ban list of blocked IP addresses is in the hundreds.

Yes, I keep my server and applications patched. But the lower my surface profile is, the less chance there is of a discovered vulnerability of being exploited.

Information security is not a static “set-and-forget” issue. It’s an ongoing process requiring continual diligence.

Okay I see your point. Thx for information :slight_smile: