How to redirect user and admin to a different template after login in Django 2 - django-templates

I'm new in Django 2. I try to develop a project where my requirement is from the same login page user and admin can log in but when user login he will view his own dashboard and admin will view his own different dashboard.
My views.py code is
def loginCheck(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(request, username=username, password=password)
admin = authenticate(request, is_staff=1, username=username, password=password)
if user is not None:
login(request, user)
return redirect('home')
elif admin is not None:
login(request, user)
return redirect('admin')
else:
messages.add_message(request, messages.INFO, 'Wrong User Name Or Password')
return redirect('loginView')
messages.add_message(request, messages.INFO, 'You Have To Login First')
return redirect('loginView')
I Was trying to get is_staff value to authenticate admin and user but not getting success. Now how can I login from the same login form and give the user and admin a different dashboard?

In above code you are authenticating a single user twice that's a bad practice. for your requirement we can do like below.
def loginCheck(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(request, username=username, password=password)
if user and user.is_staff is False:
login(request, user)
return redirect('home')
elif user and user.is_staff is True:
login(request, user)
return redirect('admin')
else:
messages.add_message(request, messages.INFO, 'Wrong User Name Or Password')
return redirect('loginView')
messages.add_message(request, messages.INFO, 'You Have To Login First')
return redirect('loginView')

Related

How to override SQLAdmin(SQLAlchemy Admin for FastAPI and Stallete) to show username on successful login into the admin dashboard

I've been a able to integrate SQLAdmin with FastAPI and also overridden the template. How do I serve the template to a route that can display the username when a user is logged in?
I don't know if this is the best option though. What I did was update the request session with the user data in the AuthenticationBackEnd and using the SessionMiddlware.
from starlette.middleware.sessions import SessionMiddleware
app.add_middleware(SessionMiddleware, secret_key=SECRET_KEY, max_age=MAX_AGE)
class MohDeliveryAdmin(AuthenticationBackend):
async def login(self, request: Request) -> bool:
form = await request.form()
username, password = form["username"], form["password"]
user = session.query(User).filter(User.email == username).first()
if not user:
invalid_exception("Invalid username or password!")
# Validate username/password credentials
if not verify_password(password, user.password):
invalid_exception("Invalid Credentials!")
user_data = {
"id": user.id,
"username": user.username or None,
"email": user.email,
"phone_number": user.phone_number,
"user_type": user.user_type,
"company_name": user.company_name or None,
}
# And update session
access_token = auth.create_access_token(data=user_data)
request.session.update({"token": access_token, "user": user_data})
return True

How to login to Flask App when using Locust

First time using Locust. I have a Flask App that requires user to login to access most routes.
I cant get Locust to successfully login to my Flask App.
Here is my Locust.py file:
from locust import HttpLocust, TaskSet, task
import re
class UserBehavior(TaskSet):
def on_start(self):
""" on_start is called when a Locust start before any task is scheduled """
self.client.verify = False
self.get_token()
self.login()
def on_stop(self):
""" on_stop is called when the TaskSet is stopping """
self.logout()
def get_token(self):
response = self.client.get("/login")
# Sample string from response:
# <input id="csrf_token" name="csrf_token" type="hidden" value="REDACTED">
self.csrftoken = re.search(' name="csrf_token" .* value="(.+?)"', response.text).group(1)
print(f"DEBUG: self.csrftoken = {self.csrftoken}")
def login(self):
response = self.client.post("/login",
{"email": "REDACTED", "password": "REDACTED"},
headers={"X-CSRFToken": self.csrftoken})
print(f"DEBUG: login response.status_code = {response.status_code}")
def logout(self):
self.client.get("/logout")
#task(5)
def list_domains(self):
response = self.client.get("/domains", headers={"X-CSRFToken": self.csrftoken})
print(f"DEBUG list: response.status_code = {response.status_code}")
class WebsiteUser(HttpLocust):
task_set = UserBehavior
min_wait = 5000
max_wait = 9000
Here is the login function of my Flask App: (with a few debug statements added)
#users.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST': ##DEBUG
logging.debug(f"debug0: inside login func with method == POST") ##DEBUG
if current_user.is_authenticated:
return redirect(url_for('main.home'))
form = LoginForm()
if form.validate_on_submit():
logging.debug(f"debug0.1: inside validate_on_submit") ##DEBUG
user = Users.query.filter_by(email=form.email.data).first()
if user and user.check_password(form.password.data):
login_user(user, remember=form.remember.data)
next_page = request.args.get('next')
if not next_page or url_parse(next_page).netloc != '':
next_page = url_for('main.home')
logging.debug(f"debug1: Login was successful") ##DEBUG
return redirect(next_page)
else:
logging.debug(f"debug2: Login failed") ##DEBUG
flash(f'Login unsuccessful. Please check email and password!', 'danger')
logging.debug(f"debug3: the end of login func") ##DEBUG
return render_template('login.html', title='Login', form=form)
When i run Locust, I get this output:
[2019-09-16 18:03:06,598] Mac-mini-3.local/INFO/locust.main: Starting web monitor at *:8089
[2019-09-16 18:03:06,598] Mac-mini-3.local/INFO/locust.main: Starting Locust 0.11.0
[2019-09-16 18:03:14,069] Mac-mini-3.local/INFO/locust.runners: Hatching and swarming 2 clients at the rate 1 clients/s...
[2019-09-16 18:03:14,138] Mac-mini-3.local/ERROR/stderr: /Users/myuser/.local/share/virtualenvs/locustio-gB1-mbqd/lib/python3.7/site-packages/urllib3/connectionpool.py:851: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings InsecureRequestWarning)
[2019-09-16 18:03:14,162] Mac-mini-3.local/INFO/stdout: DEBUG: self.csrftoken = REDACTED
[2019-09-16 18:03:14,183] Mac-mini-3.local/INFO/stdout: DEBUG: login response.status_code = 200
[2019-09-16 18:03:14,213] Mac-mini-3.local/INFO/stdout: DEBUG list: response.status_code = 200
[2019-09-16 18:03:15,112] Mac-mini-3.local/INFO/stdout: DEBUG: self.csrftoken = REDACTED
[2019-09-16 18:03:15,137] Mac-mini-3.local/INFO/stdout: DEBUG: login response.status_code = 200
I'm not concerned about the 'InsecureRequestWarning' as this is because I am using a self signed cert and i have disabled verification with 'self.client.verify = False'
The csrftoken looks correct.
From the Flask App itself, I get this output:
DEBUG:user:debug0: inside login func with method == POST
INFO:flask_wtf.csrf:The CSRF token is missing.
DEBUG:user:debug3: the end of login func
DEBUG:user:debug3: the end of login func
DEBUG:user:debug3: the end of login func
DEBUG:user:debug3: the end of login func
So, it's hitting the login function (proven by debug0) but it's not getting into the 'form.validate_on_submit()' conditional.
So far I have spent all day on this, reading articles and trying a lot of things, ie adding the X-CSRFToken headers.
I feel I am missing something fundamental, and would really appreciate some help.
thanks,
WJ
Ok, I solved it, so thought I would share the anwser for anyone else that comes across this. As suggested by #user10788336 its is not a Locust issue.
The issue was that when POSTing to the flask route, the form was not being validated (ie form.validate() was not getting set).
So, I made two changes.
1) changed the POST to have an additional form item which i called "test-mode" and I set the value to "locust-test"
here is the new Locust.py file:
from locust import HttpLocust, TaskSet, task
import re
class UserBehavior(TaskSet):
def on_start(self):
""" on_start is called when a Locust start before any task is scheduled """
self.client.verify = False
self.get_token()
self.login()
def on_stop(self):
""" on_stop is called when the TaskSet is stopping """
self.logout()
def get_token(self):
response = self.client.get("/login")
# Sample string from response:
# <input id="csrf_token" name="csrf_token" type="hidden" value="REDACTED">
self.csrftoken = re.search(' name="csrf_token" .* value="(.+?)"', response.text).group(1)
print(f"DEBUG: self.csrftoken = {self.csrftoken}")
def login(self):
response = self.client.post("/login",
{"email": "REDACTED",
"password": "REDACTED",
"test-mode": "locust-test"
},
headers={"X-CSRFToken": self.csrftoken})
print(f"DEBUG: login response.status_code = {response.status_code}")
def logout(self):
self.client.get("/logout")
#task(5)
def list_domains(self):
response = self.client.get("/domains", headers={"X-CSRFToken": self.csrftoken})
print(f"DEBUG list: response.status_code = {response.status_code}")
class WebsiteUser(HttpLocust):
task_set = UserBehavior
min_wait = 5000
max_wait = 9000
The diff between old and new is:
< {"email": "REDACTED", "password": "REDACTED"},
---
> {"email": "REDACTED",
> "password": "REDACTED",
> "test-mode": "locust-test"
> },
2) I changed my login function the Flask app:
The change is that I don't need the form to be validated, so I skip that ONLY when I detect that I am running in test-mode.
here is the new login function:
#users.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('main.home'))
form = LoginForm()
# shortcut for Locust testing - need to avoid form.validate() (which is within form.validate_on_submit())
form_is_ok = False
if request.method == 'POST':
if request.form.get('test-mode') == 'locust-test':
form_is_ok = True
else:
form_is_ok = form.validate_on_submit()
if form_is_ok:
logging.debug(f"debug0.1: inside validate_on_submit") # DEBUG
user = Users.query.filter_by(email=form.email.data).first()
if user and user.check_password(form.password.data):
login_user(user, remember=form.remember.data)
next_page = request.args.get('next')
if not next_page or url_parse(next_page).netloc != '':
next_page = url_for('main.home')
logging.debug(f"debug1: Login was successful") # DEBUG
return redirect(next_page)
else:
logging.debug(f"debug2: Login failed") # DEBUG
flash(f'Login unsuccessful. Please check email and password!', 'danger')
logging.debug(f"debug3: the end of login func") # DEBUG
return render_template('login.html', title='Login', form=form)
The diff between old and new is:
< if form.validate_on_submit():
---
>
> # shortcut for Locust testing - need to avoid form.validate() (which is within form.validate_on_submit())
> form_is_ok = False
> if request.method == 'POST':
> if request.form.get('test-mode') == 'locust-test':
> form_is_ok = True
> else:
> form_is_ok = form.validate_on_submit()
>
> if form_is_ok:
I think this is still secure ... thoughts on that?
I might add a config variable, that disables/enables this functionality.
And it works!!!!
BTW, Locust is awesome!
Hope this helps.
cheers,
WJ
Can you share the code for what the form.validate_on_submit method is expecting/validating? Chances are there is a hidden field in the login form that is acting as a dynamic token and/or you're missing a required field. Can you also share the HTML source of the login form?
I'd also be curious to see you add an additional debug statement in your login method that outputs the value of the CSRF token, to make sure it is valid, e.g.
def login(self):
print(f"DEBUG: login csrftoken = {self.csrftoken}")
response = self.client.post("/login",
{"email": "REDACTED",
"password": "REDACTED",
"test-mode": "locust-test"
},
headers={"X-CSRFToken": self.csrftoken})
print(f"DEBUG: login response.status_code = {response.status_code}")
I'd also be curious to see if there are any error messages when you submit the form without a valid CSRF token. Does it return a 200 status code and report the invalid token?
My guess is that it's something to do with the CSRF token not being valid or handled correctly.
Is this an older or customized version of Flask login? I'm not seeing an X-CSRFToken header method in the latest version of Flask login docs at https://flask-login.readthedocs.io/en/latest/

How to make Flask Requests module authenticate?

I built my own API with flask, but now I'm trying to write a script to consume the API and the Requests module won't make it past the log in.
The Form
The form has jut a 'username' input, 'password', and I'm also using flask-wtf to implement csrf protection, so there is a 'csrf_token' hidden input.
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField
from wtforms.validators import DataRequired, Length
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(min=1, max=20)])
password = PasswordField('Password', validators=[DataRequired(), Length(min=1, max=25)])
The Route
#auth.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if request.method == 'GET':
return render_template('login.html', form=form)
elif request.method == 'POST':
if form.validate_on_submit():
credentials = {'username': request.form['username'],
'password': request.form['password']}
try:
r = requests.post('http://api.domain.com:80/authenticate', data=credentials)
response = r.json()
except Exception as e:
print('JSON Auth Error: {}'.format(e))
else:
if r.status_code is 200:
session['access_token'] = response['access_token']
session['refresh_token'] = response['refresh_token']
return redirect('/')
return render_template('login.html', form=form, error='invalid')
The API Endpoint
#api.route('/authenticate', methods=['POST'])
def authenticate():
# form data
username = request.form['username']
password = request.form['password']
# check if user exists
try:
user = User.query.filter_by(username=username).first()
except exc.SQLAlchemyError as e:
print(e)
if user and password == user.password:
tokens = generate_tokens()
return jsonify({'access_token': tokens['access'], 'refresh_token': tokens['refresh']}), 200
else:
return jsonify({'message': 'invalid login'}), 401
The API Resource Endpoint I am trying to access
#api.route('/pending', methods=['GET'])
#login_required
def pending():
The #login_required verification method
def tokens_valid():
if 'access_token' and 'refresh_token' in session:
# retrieve access and refresh tokens from session cookie
tokens = {'access_token': session['access_token'],
'refresh_token': session['refresh_token']}
# validate token endpoint
try:
r = requests.post('http://api.domain.com/validate', data=tokens)
response = r.json()
except Exception as e:
print('JSON Token Error: {}'.format(e))
else:
if r.status_code is 200:
if response['message'] == 'valid access token':
return True
elif response['message'] == 'new access token':
#set new tokens
session['access_token'] = response['access_token']
session['refresh_token'] = response['refresh_token']
return True
return False
What I have tried
v.1
def login():
credentials = {'username': 'username',
'password': 'password'}
s = requests.Session()
r = s.post('http://sub.domain.com/auth/login', data=credentials)
return s
v.2
def login():
login_form = requests.get('http://sub.domain.com/auth/login')
soup = BeautifulSoup(login_form.text, "html.parser")
csrf = soup.find('input', id='csrf_token').get('value')
credentials = {'username': 'username',
'password': 'password',
'csrf_token': csrf}
s = requests.Session()
r = s.post('http://sub.domain.com/auth/login', data=credentials)
return s
v.3
from flask import Flask, session
def login():
credentials = {'username': 'username',
'password': 'password'}
r = requests.post('http://api.domain.com/authenticate', data=credentials)
tokens = r.json()
session['access_token'] = tokens['access_token']
session['refresh_token'] = tokens['refresh_token']
So far I can get the '/authenticate' api endpoint to return my session tokens, but i can't access the protected resource because i can't seem to save them to my session any how.
Can anyone help me figure this out?

How can i trigger basic http auth and send user info in realtime?

I have just tried
===============================
# Give user the file requested
url = "http://superhost.gr/data/files/%s" % realfile
username = getpass.getuser()
password = getpass.getpass()
r = requests.get( url, auth = (username, password) ) # ask user for authentication data
r.raise_for_status()
===============================
as well as input() for both user & pass combo but iam not getting in chrome the basic pop-up HTTP auth window.
Any idea why?
How can i ASK the user for http auth data and store them isntead of giving them to the script?
You can try to use below code to ask user for credentials and send them via HTTP request
from requests.auth import HTTPBasicAuth
import requests
import sys
username = sys.argv[1]
password = sys.argv[2]
realfile = sys.argv[3]
url = "http://superhost.gr/data/files/%s" % realfile
r = requests.get( url, auth=HTTPBasicAuth(username, password))
print(r.status_code)
This script user can run as python script.py username password filename
# Give user the file requested
print('''<meta http-equiv="refresh"
content="5;url=http://superhost.gr/data/files/%s">''' % realfile)
authuser = os.environ.get( 'REMOTE_USER', 'Άγνωστος' )
print( authuser )
================================
Trying this, feels liek i'm almost there except that when printing the value of authuser variable it default to "Άγνωστος" meaning not there.
is there any other way i can grab what the user gave a auth login info?

Why isn't Warden using all of the default strategies?

I'm migrating a Rails 3.2 app to greener (higher numbered) pastures. AuthLogic, which served me well forever, apparently doesn't work in 4. So I'm transitioning to Warden.
The warden.rb file below has been migrated from an app written a few years ago ... but the warden docs suggest that the basic interface is the same. Feels like it should work.
I have two authentication strategies, :httpauth and :params. The :params strategy works. Here's a logfile entry showing authentication working:
Started POST "/user_sessions" for 127.0.0.1 at 2016-08-17 16:32:12 -0500
Processing by UserSessionsController#create as HTML
Parameters: {"utf8"=>"✓", "username"=>"...."}
[AUTH] checking params
[AUTH] Authenticating user jw#mustmodify.com from params
...
[AUTH] User jw#mustmodify.com authenticated with a password.
...
Completed 302 Found in 111.5ms (ActiveRecord: 1.0ms)
However, when using httpauth (via curl):
curl http://dev.projectdomain.com/clouds/bmi.svg -u user:password
I'm expecting to see "checking httpauth" then "checking params" ... but instead I see "checking params" twice. I assume I'm asking warden to authenticate twice in the controller... But according to my understanding, I should also be seeing "checking httpauth". Any thoughts about why that isn't working?
Started GET "/clouds/bmi.svg" for 127.0.0.1 at 2016-08-17 16:22:51 -0500
Processing by CloudsController#show as SVG
Parameters: {"id"=>"bmi"}
[AUTH] checking params
[AUTH] checking params
and then that's the end of authentication.
Here's my config/initializers/warden.rb file. One note... since httpauth and params use the same fields and methods to authenticate, I abstracted the "checking of passwords" stuff to a single class: UserCredentialAuthentication. I actually have a third strategy, :token, but it isn't a default strategy and I have excluded it for simplicity. I did verify that the problem still exists with the code as amended:
Rails.application.config.middleware.use Warden::Manager do |manager|
manager.default_strategies :httpauth, :params
end
Warden::Manager.serialize_into_session do |user|
user.persistence_token
end
Warden::Manager.serialize_from_session do |id|
User.where(persistence_token: id).first
end
class UserCredentialAuthentication < ::Warden::Strategies::Base
def verify_against_old_credentials( user, password )
Sha512.matches?( user.sha512_password, password, user.sha512_salt )
end
def transition_from_sha512!( user, password )
user.password = password
user.sha512_password = nil
user.sha512_salt = nil
user.save
end
def authenticate!
Rails.logger.warn("[AUTH] Authenticating user #{username} from #{medium}")
user = User.find_by_username_or_email(username)
if user.blank?
Rails.logger.warn("[AUTH] No Such User")
fail "Invalid email or password"
elsif user.sha512_password.not.blank? && verify_against_old_credentials( user, password )
Rails.logger.warn("[AUTH] User #{user.email} authenticated with a SHA512 password.")
transition_from_sha512!( user, password )
success! user
elsif user.password_digest && user.authenticate( password )
Rails.logger.warn("[AUTH] User #{user.email} authenticated with a password.")
success! user
else
Rails.logger.warn("[AUTH] Bad Password")
fail "Invalid email or password"
end
end
end
Warden::Strategies.add(:httpauth, UserCredentialAuthentication) do
def medium
'httpAuth'
end
def valid?
Rails.logger.warn("[AUTH] checking httpAuth")
auth.provided? && auth.basic?
end
def auth
#auth ||= Rack::Auth::Basic::Request.new(env)
end
def username
auth.credentials[1]
end
def password
auth.credentials[2]
end
end
Warden::Strategies.add(:params, UserCredentialAuthentication) do
def medium
'params'
end
def valid?
Rails.logger.warn("[AUTH] checking params")
credential_params['username'] && credential_params['password']
end
def username
credential_params['username']
end
def password
credential_params['password']
end
def credential_params
p = params.blank? ? post_params : params
p['user_session'] || {}
end
def post_params
#post_params ||= get_post_params
end
def get_post_params
req = Rack::Request.new(env)
if( req.post? )
begin
body = req.body.read
req.body.rewind
JSON.parse( body )
end
else
{}
end
end
end
It seems that two strategies that inherit from the same base class look like the same strategy to Warden. I copy/pasted, fixed a small error in my httpauth strategy, and it worked. Unfortunate.
I used a module instead of a class to abstract the common methods. Final solution:
Rails.application.config.middleware.use Warden::Manager do |manager|
manager.default_strategies [:httpauth, :params]
end
Warden::Manager.serialize_into_session do |user|
user.persistence_token
end
Warden::Manager.serialize_from_session do |id|
User.where(persistence_token: id).first
end
module UserCredentialAuthentication
def verify_against_old_credentials( user, password )
Sha512.matches?( user.sha512_password, password, user.sha512_salt )
end
def transition_from_sha512!( user, password )
user.password = password
user.sha512_password = nil
user.sha512_salt = nil
user.save
end
def authenticate!
Rails.logger.warn("[AUTH] Authenticating user #{username} from #{medium}")
user = User.find_by_username_or_email(username)
if user.blank?
Rails.logger.warn("[AUTH] No Such User")
fail "Invalid email or password"
elsif user.sha512_password.not.blank? && verify_against_old_credentials( user, password )
Rails.logger.warn("[AUTH] User #{user.email} authenticated with a SHA512 password.")
transition_from_sha512!( user, password )
success! user
elsif user.password_digest && user.authenticate( password )
Rails.logger.warn("[AUTH] User #{user.email} authenticated with a password.")
success! user
else
Rails.logger.warn("[AUTH] Bad Password")
fail "Invalid email or password"
end
end
end
Warden::Strategies.add(:httpauth) do
include UserCredentialAuthentication
def medium
'httpAuth'
end
def valid?
Rails.logger.warn("[AUTH] checking httpAuth")
auth.provided? && auth.basic?
end
def username
auth.credentials[0]
end
def password
auth.credentials[1]
end
def auth
#auth ||= Rack::Auth::Basic::Request.new(env)
end
end
Warden::Strategies.add(:params) do
include UserCredentialAuthentication
def medium
'params'
end
def valid?
Rails.logger.warn("[AUTH] checking params")
credential_params['username'] && credential_params['password']
end
def username
credential_params['username']
end
def password
credential_params['password']
end
def credential_params
p = params.blank? ? post_params : params
p['user_session'] || {}
end
def post_params
#post_params ||= get_post_params
end
def get_post_params
if( request.post? )
begin
body = request.body.read
request.body.rewind
JSON.parse( body )
end
else
{}
end
end
end