iFrame inside Admin: serving pdf/txt causing X-Frame-Options' to 'deny'

I have found that https://github.com/django/django/raw/stable/3.0.x/django/contrib/admin/templates/admin/change_form.html consists of the code when an object instance is open.

Now, I have a file field inside my model (which shows url and on click It opens a new window with that file).

I want to override it, instead of opening new window, I want to view the same inside an iFrame popup for supported file format (like doc, pdf) or else download the unsupported format on click. Also, highlight file field that ““file is unsupported, click to download””

Any help/guidance will be helpful.

EDIT: I am to do above task my own and I am able to show the image, and but iFrame part is not working.

Code:

class QuestionAdmin(admin.ModelAdmin):
   list_filter = ['group']
   list_display = ['comment', 'supported_format']
   readonly_fields = ['question_file', 'created_at']

   class Meta:
       model = Question

   def question_file(self, obj):
       html = 'No File for the Question'
       filename = obj.question
       if filename:
           _name, ext = os.path.splitext(str(filename))
           if ext in ['.jpg', '.png', '.jpeg', '.webp']:
               html = "<img src='%s'/>" % (obj.question.url)
           elif ext in ['.pdf', '.txt']:
               html = '<iframe src="%s"></iframe>' % (
                   obj.question.url)
           else:
               html = "<a href='%s' download>File Format Not supported, Click to Download</a>" % (
                   obj.question.url)
       return format_html(
           html
       )

Image of other File-


Error Image of iFrame-

So I created the smallest possible test that I could:

<html><head></head>
<body>Hello world
<hr><iframe src="/static/file1.txt"></iframe><hr>
<hr><iframe src="/static/file1.pdf"></iframe><hr>
</body></html>

And it worked exactly as expected.

What I’m guessing is that there may be an issue with the path being requested. Check your console output to see what path is being requested, and whether or not you should be requesting an absolute or relative path.

Ken

No, Error is different (Shown in Console)
Refused to display 'http://127.0.0.1:8000/media/questions/1/IMG_3.pdf' in a frame because it set 'X-Frame-Options' to 'deny'.

Actually I am unable to pass or set xframe_options_exempt

from django.views.decorators.clickjacking import xframe_options_exempt

@xframe_options_exempt

Any idea how to implement it. How could I set decorator?

Try making the settings change to:
X_FRAME_OPTIONS = ‘SAMEORIGIN’

(The default was changed from SAMEORIGIN to DENY in Django 3.0)

My guess is that the static file handler is causing this, and so it probably wouldn’t be an issue in a “real” deployment if you’re handling static files outside of your app.

1 Like

No, it didn’t work. Even I have tried with embed/iFrame.

EDIT- How could I override specific django admin view file to set decorator.? I don’t know exact file.

You’re still getting the same message
Refused to display 'http://127.0.0.1:8000/media/questions/1/IMG_3.pdf' in a frame because it set 'X-Frame-Options' to 'deny'. after adding the X_FRAME_OPTIONS = ‘SAMEORIGIN’ to your settings file?

Is django serving your media files or is some other server/service handling them?

Is this test environment being run using django runserver? (or runserver_plus?)

It’s not the admin form causing this - it’s whatever is serving that PDF file. It’s that service that is checking the origin of the request for the iframe-embedded object.

i have added same in setting file-

I running on local, so django is serving my media files (i don’t know runserver_plus, so i am using runserver).

changed the heading accordingly.

I don’t know how, but it is working now.

EDIT- ONLY for .pdf, BUT not .txt

Here is the code which I have used

if ext == '.pdf':
   html = '<embed src=\'%s\' type=\'application/pdf\' width=\'1000px\' height=\'1000px\' />' % (
       obj.question.url
   )
elif ext == '.txt':
   html = '<embed src=\'%s\' type=\'text/plain\' width=\'1000px\' height=\'1000px\' />' % (
       obj.question.url
   )

Here is the Image for more detail-

Now, getting ‘X-Frame-Options’ to ‘deny’ only on .txt rendering.

What else I have tired that, taking text from a .txt file and including it into html and sending back. But it breaks and I think need lot of coding to improve it.

Only thing I can say at this point is that there’s something else environmentally different between your setup and mine, because as long as I had X_FRAME_OPTIONS = 'SAMEORIGIN' in my settings.py file, it works without the embed tag for both the txt and pdf files.

Ken

1 Like

What is the code without embed tag ??

My little test file from above, changed to use media instead of static in case the handlers were different enough to cause a difference:

<html><head></head>
<body>Hello world
<hr><iframe src="/media/file1.txt"></iframe><hr>
<hr><iframe src="/media/file1.pdf"></iframe><hr>
</body></html>
1 Like

I use your code and open in incognito, and it’s working. Thanks for you all effort you put.

I think it was something related to cache or cookie, but even mine code is working in incognito.

Quite possible. One thing I do get used to doing, when I’m rapidly testing changes like this is to ensure that I do a “shift-refresh” instead of just a “refresh” to make sure that all ancillary files are reloaded. I’m not sure what other effects it may have on the local cache.

I was also facing the same issue and just want to add one more thing… after doing all the settings dont forget to press ctrl+shift+delete to clear all the cookies and caches and it starts working in my case.