Forums

Limiting access to static files

Dear All,

I think I might have misunderstood how this works, but

Is there a way to limit access to the static folder? Ie only allow access if this is through the app/website/login?

I was hoping to use this folder to store private security videos, owned by particular users.

The idea was fairly simple the user logs in and is allowed access to their videos.

But the folders seems completely public and can just be accessed in the browser?

Any help would be much appreciated.

Kind regards

Paul

If you want to have a controlled access to the files served by the web app, you need to serve them via the web app itself, not as static files.

Hi there, that's what I was trying to do, but I'm not sure how to.

I have a folder named static in the app dir, and then redirect to a file in this folder on one of the routes.

If I password protect this route, would that mean that you can only access the file with the password?

Sorry I'm a bit green.

Kind regards

Paul

If you're serving the files as static files through PythonAnywhere, then they will be available at the address that they are served from, even if your route that redirects to them is protected. You will need to actually serve the file from the route and not through the PythonAnywhere static files setting if you want it to be covered by the authentication you have on your route.

Hi again.

From the above, I thought this would work, but its still available by just entering the url?

The static folder is not through PA its just in the app root directory.

It is possible to serve the file directly on the route, rather than using the redirect.

Thank you so much for your help.

Kind regards

Paul

@app.route('/video_login', methods=['GET', 'POST']) def watch(): error = None if request.method == 'POST': if request.form['username'] != 'admin' or request.form['password'] != 'admin': error = 'Invalid Credentials. Please try again.' else: return redirect(url_for('static', filename='Cam1 12.03.41 07-06-2022.mp4')) return render_template('login.html', error=error)

If you are redirecting to somewhere else, you will need to put the authentication there. If you want the authentication in the view above to mean anything, you will need to serve the file from that view. See https://help.pythonanywhere.com/pages/FlaskSendFileBytesIO/

Hello again Glen

I did look at the above, but I struggled to understand what to put in the bytesIO bit.

Having looked around a bit more, I used this which works fine:-

@app.route('/', methods=['GET', 'POST'])
def watch():
    return send_file('Cam1 12.03.41 07-06-2022.mp4')

But, I don't know how to reference the path to file if its not in the root directory. I assumed say "/videos/" but it doesn't work.

The other thing is, as soon as I put the login form on the same route, the video wont play. As in, it looks like its working but the video length is zero and the play button is greyed out:-

   @app.route('/', methods=['GET', 'POST'])
  def login():
      error = None
      if request.method == 'POST':
        if request.form['username'] != 'admin' or request.form['password'] != 'admin':
            error = 'Invalid Credentials. Please try again.'
        else:
            return send_file('Cam1 12.03.41 07-06-2022.mp4')
    return render_template('login.html', error=error)

If you could just point me in the right direction for any of the above I would be vvv grateful.

Kind regards

Paul

That's the good tutorial about paths and files https://realpython.com/python-pathlib/

Hello again

Thanks v much for all your replies, could I just ask one specific thing from the above? Is there any reason why the video plays on the first route, but not on the 2nd?

@app.route('/', methods=['GET', 'POST'])
def show_file():
            return send_file('/home/giffsmith1000/mysite/vids/Cam1 12.03.41 07-06-2022.mp4')

@app.route('/login', methods=['GET', 'POST'])
def login():
    error = None
    if request.method == 'POST':
        if request.form['username'] != 'admin' or request.form['password'] != 'admin':
            error = 'Invalid Credentials. Please try again.'
        else:
            return send_file('/home/giffsmith1000/mysite/vids/Cam1 12.03.41 07-06-2022.mp4')
    return render_template('login.html', error=error)

From my checks, it looks like the browser is handling those differently. I don't know why that might be. Perhaps you would have better luck if you embedded the video in html and served that page instead.

Hi there,

In case it helps anyone, the below solves the problem (you can pass the url argument(s) to the next route). Alternatively if you're using sessions, you could save the variable to the session and then access it from anywhere-

@app.route('/video_viewer', methods=['GET', 'POST'])
def video_player():
            video= request.args.get('video_path') #if key doesn't exist, returns None
            return send_file(video)


@app.route('/login', methods=['GET', 'POST'])
def get_video_path():
           error = None
            video= request.args.get('video_title') #if key doesn't exist, returns None
            if request.method == 'POST':
                if request.form['username'] != 'admin' or request.form['password'] != 'admin':
                    error = 'Invalid Credentials. Please try again.'
            else:


                return redirect(url_for('video_player', video_path = video))

        return render_template('login.html', error=error)

Glad to hear that you made it work!