I have several files that can be downloaded on my website.
The HTML content refers to this method:
from flask_httpauth import HTTPBasicAuth
auth = HTTPBasicAuth()
...
#app.route('/customer_section/download/<product_id>')
#login_required
def customer_download_file(product_id):
# get the company the currently logged in user belongs to
user_company = current_user.company
# get the product entity
product = Product.query.filter_by(id=product_id).first_or_404()
# now get the company, the product belongs to
product_company = product.project.owner
if current_user.is_admin or (not current_user.is_admin and product_company.id == user_company.id):
print("Splitting Path %s to folder and filename..." % product.path)
# Split the path to folder and filename
folder, filename = os.path.split(product.path)
return send_from_directory(folder, filename, mimetype="application/octet-stream", as_attachment=True)
else:
# Unauthorized
abort(401)
This works great when the user logs in via flask_login in my login view.
So the user logs in via my login view, then somehow navigates through my website, and the clicks on a download link to download a file.
But what if a user tries to download a file directly?
I'd then like to show the user a popup so the user can enter his credentials and may download the file.
Is this possible?
Sorry, I guess my description was not clear enough: I‘m not trying to show a custom popup view of mine when a user calls my download method. I‘d like the default (browser specific) Basic Authentication popup to appear if a non logged in user tries to call that specific url. As if I had a restrictive .htaccess file that ensures that only authorized users continue. If the user reaches the same url via my webpage (after logging in, this method simply returns the file stream).
Ok, after reading this link I solved it like this:
def get_db_user(username):
user = User.query.filter_by(username=username).first()
return user
def check_auth(username, password):
"""This function is called to check if a username /
password combination is valid.
"""
if current_user.is_authenticated:
return True
user = get_db_user(username)
if not user or not user.check_password(password):
return False
else:
return True
#login.unauthorized_handler
def handle_unauthorized_callback():
page_to_load = request.path
print("Tried to access page: %s" % page_to_load)
# this regex match checks whether a specific url was called
if is_file_download_url_pattern.match(page_to_load):
basic_auth = request.authorization
if not basic_auth or not check_auth(basic_auth.username, basic_auth.password):
return authenticate()
else:
user = get_db_user(basic_auth.username)
login_user(user, False)
# extract product id
product_id = re.search(product_id_pattern, page_to_load).group()
return customer_download_file(int(product_id))
else:
return redirect(url_for('login'))
def authenticate():
"""Sends a 401 response that enables basic auth"""
return Response(
'Could not verify your access level for that URL.\n'
'You have to login with proper credentials', 401,
{'WWW-Authenticate': 'Basic realm="Login Required"'}
)
I don't know whether this is an elegant solution, but at least it solves my problem for now.
Thanks.
Related
i have a problem with a functionality of flask-login.
I need to create a system, that prevents multiple users from logging in on one account in the same time. The goal i am trying to achieve, is a way to check if the user is currently logged in, and deny access to people that are trying to log in if the account is already logged in. I was trying to use:
user.is_active
user.is_authenticated
But they always are displaying "True", here is the rest of the code: is there a property that changes after the users logs in/out?
def login():
if request.method == 'GET':
return render_template('login.html')
else:
login = request.form['login']
password = request.form['password'].encode('utf-8')
user = User.query.filter_by(username=f"{login}").first()
if user == None:
return "Non-Existing User"
else:
if bcrypt.checkpw(password,user.password.encode('utf-8')):
login_user(user)
return "Success"
else:
return "Bad creds."
#app.route('/logout')
#login_required
def logout():
logout_user()
return 'You are logged out'```
I don't know about any property but we can do it another way. You can store a token like thing in database with the user details.
class user(db.Model):
[...]
token = db.Column(db.String(20))
when the user login, you can generate a random hex or string and store it in database.
When user logout set token to False
while logging in the user , you can check if token is not False.
user = User.query.filter_by(username=user_input).first()
if user.token != False:
// allow user to login
else:
// don't allow him and show warning message.
I'm using Github-Flask to authenitcate users on my app. I use github.authorize(scope='user:email'). How can I get the logged in user's email?
github = GitHub(app)
user = None
#app.route('/login')
def login():
if user.username:
return redirect(url_for('index'))
return github.authorize(scope='user:email')
#github.access_token_getter
def token_getter():
if user is not None:
return user.github_access_token
#app.route('/github-callback')
#github.authorized_handler
def authorized(oauth_token):
if oauth_token is None:
flask.flash("Authorization failed.")
return redirect(url_for('index'))
global user
user.username = oauth_token
return redirect(url_for('index'))
The login route is only redirecting to GitHub's authorization page, it has no way to access the user data yet because the user isn't logged in. Once you get to the authorized callback, you can make API calls to GitHub.
In the authorized route, use github.get to call the user API endpoint.
data = github.get('user')
email = data['email']
Also, do not use global user to store the logged in user. See Are global variables thread safe in flask? Instead, store the user id in session, and load the user into g, as shown in GitHub-Flask's full example.
I want to login with handler.
I have a code use session but i want to use handler:
I have visit :
https://docs.djangoproject.com/en/1.11/topics/auth/default/
But i don't understand complete.
I want to log user (with username or email and password)
Do you have a code for example or project in stackoverflow or github or . . . ???
login the user is easy if you are using default user model from django.contrib.auth.models
from django.contrib.auth import authenticate, login
def user_login(request):
# check here that request.method is POST or not.
user = authenticate(username=request.POST.get('username'), password=request.POST.get('password'))
if user is not None:
login(request, user)
# send some http response here that login successful or redirect to some other page
else:
# return an error page saying that username password not correct
authenticate function will check for username and password in User table in the database if it founds a user matching query then it returns the user object else it will return None. You might not want to manage sessions as django already sets a cookie for every user that successfully logs in so if user has logged in once then he will not be required to enter password again.
I am using the OAUTH API to verify access to the users google calendar. They are not logging into my system using OAUTH, just accepting my site accessing their data.
The problem is, if the user is logged out of their google account and after they hit verify, it force logs them out of their session and I have no way of linking them back up.
This DOES work if they are already logged into the google account in their browser session and hit accept, they will be redirected to the right page.
I replicate the error when the cache, cookies are clear and they need to relogin into their google account to verify.
I've tried storing the session ID, etc, but the request parameter is not containing the same request data as the initial view, so there is a conflict in the data I am trying to retrieve.
The user is logged in using the standard Django libraries for the credentials model.
CODE
FLOW = flow_from_clientsecrets(
CLIENT_SECRETS,
scope='https://www.googleapis.com/auth/calendar.readonly',
redirect_uri='http://127.0.0.1:8000/oauth2callback')
'''''''''''''''''''''''''''''''''''''''''''''''''''
Main function dealing with auth verification
'''''''''''''''''''''''''''''''''''''''''''''''''''
def index(request):
current_user = User.objects.get(username=request.user.username)
storage = Storage(CredentialsModel, 'id', current_user, 'credential')
credential = storage.get()
if credential is None or credential.invalid == True:
FLOW.params['state'] = xsrfutil.generate_token(settings.SECRET_KEY,
request.user.id)
authorize_url = FLOW.step1_get_authorize_url()
return redirect(authorize_url)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''
User then calls the data function once authenticated
''''''''''''''''''''''''''''''''''''''
def auth_return(request):
print("THE CURRENTLY REQUESTED USER IN THIS SESSION REQUEST IS %s"%(request.user.username))
credential = FLOW.step2_exchange(request.REQUEST)
try:
current_user = User.objects.get(id=request.user.id)
except:
return HttpResponseRedirect("/login")
storage = Storage(CredentialsModel, 'id', current_user, 'credential')
storage.put(credential)
return HttpResponseRedirect("/get_cal")
Ok, so this was a bit more involved than I thought.
I fixed this by adding a state parameter of the currently logged in username.
logged_in_username = request.user.username
user_data = {'var_test' : logged_in_username}
pass_param = urllib.urlencode(user_data)
FLOW.params['state']=pass_param
authorize_url = FLOW.step1_get_authorize_url()
This gave me the ability to query the user from the DB via the User model in Django contrib. I parsed out the state var from the URL:
#Get the currently logged in username
user_variable_data = str(FLOW.params['state'])
#get rid of the var_test= preprended text data for parsing reasons
user_variable_data = user_variable_data[9:]
#Get that user from the database in the form of a user object
current_user = User.objects.get(username=user_variable_data)
and then built a custom backend authentication file to auth the user without a password to maintain the request like nothing weird ever even happened.
user = authenticate(username=user_variable_data)
login(request, user)
print("AUTHENTICATED")
Appended this to settings file
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'procrastinate.auth_backend.PasswordlessAuthBackend',
)
Custom Backend File
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import User
class PasswordlessAuthBackend(ModelBackend):
"""Log in to Django without providing a password.
"""
def authenticate(self, username=None):
try:
return User.objects.get(username=username)
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Specifically, pages using an optional regular expression. By optional, I mean PAGE_RE below.
I am creating a Wiki. If a user searches a term, and that term doesn't already exist, then the user is redirected to an edit page so they can create the new content. This only happens, however, if the user is logged in. To determine if the user is logged in, I check for a cookie. If the user isn't logged in(no cookie), then I redirect, not to the edit page, to create the new content, but to the login page, dealt with by the Login class below. The user logs in, a cookie is created, and then they are redirected to the edit page to create the content they originally searched for. In order to remember what their topic was, so I can redirect after the login, I send the topic(in the form '/topic') to the Login class, where it's received as a parameter by the get and post methods. If a user just comes to the site and logs in normally, they are redirected to the home page, but in this case, because the topic has been received by get and post and is not None, I use the line self.redirect('/edit/%s' % topic[1:]) below, to send them on to their original destination. The problem is, css isn't working for the two urls in below that use PAGE_RE. JsFiddler4 shows that there is a 404 involving /login/css/wiki.css. It suddenly clicked after some time that that url is not the url for the login page when it receives the extra 'topic' param. It is also the case with EditPage. How can I get css to work on these pages/urls when they are sometimes different? I didn't know what was going on for ages, then I downloaded and ran JsFiddler4 and figured it out. I am using Google App Engine, webapp2, jinja2. Any help much appreciated. Apart from those two pages, css works fine.
This code is out of order and incomplete, but I hope it's sufficient
PAGE_RE = r'(/?(?:[A-Za-z0-9_-]+/?)*)'
app = webapp2.WSGIApplication([
('/signup', Register),
('/logout', Logout),
('/login' + PAGE_RE, Login),
('/edit' + PAGE_RE, EditPage),
('/', Front),
(PAGE_RE, WikiPage),
], debug=True)
class Login(Main):
""" Validate form and validate users cookie """
def get(self, topic):
self.render('login.html', error={})
def post(self, topic):
username = self.request.get('username')
password = self.request.get('password')
if not username or not self.valid(user=username):
self.login_error(user=username)
elif not password or not self.valid(pw=password):
self.login_error(user=username)
elif not self.user_exists(username):
self.render('login.html', error={'no_user':'That user does not exist'})
else:
self.login(username, password, topic=topic)
def login(self, name, pw, topic):
user_hash = User.get_user_hash(name)
if self.valid_pw(name, pw, user_hash):
self.create_secure_cookie('user_id', name)
if topic:
self.redirect('/edit/%s' % topic[1:])
else:
self.redirect('/')
else:
self.login_error(user=name)
def login_error(self, user):
self.render('login.html', username=user, error={'login': errors['login']})
def valid_pw(self, name, pw, user_hash):
salt = user_hash.split('|')[0]
return user_hash == self.create_user_hash(name, pw, salt)
Ok, I solved this. Here's what appeared to be happening.
This redirect was sending a parameter to the Login class above:
self.redirect('login/%s' % wiki_topic) #wiki_topic = something like 'topic'
when looking for the css for a page, what seems to happen is that the last part of the path up to the '/' is taken off, and replaced by the path to the css, '/css/wiki.css' in my case.
So I was passing 'login/topic' and just 'topic' was being replaced by the css path to give:
'login/css/wiki.css' instead of the correct just 'css/wiki/css'.
To stop this from happening, I changed the line redirecting to the Login class from:
self.redirect('login/%s' % wiki_topic) to >> self.redirect('login%s' % wiki_topic)
the second version has no slash before the %s.