I am using Django - ldap authentication in my project . Once if the user is authenticated , i need to set a cookie and return as a response to the server .
def post(self,request):
userData = json.loads(request.body)
username = userData.get('username')
password = userData.get('password')
oLdap = LDAPBackend()
if username == "admin" and password == "admin":
User_Grps = "AdminLogin"
else:
try:
User = oLdap.authenticate(username=username,password=password)
if User is not None:
User_Grps = User.ldap_user.group_dns
else:
User_Grps = "Check your username and password"
except ldap.LDAPError:
User_Grps = "Error"
return HttpResponse(User_Grps)
How to add a cookie to the response and send it with a the User_Grps to the client
response = HttpResponse(User_Grps)
response.set_cookie(key, value)
return response
That's it.
Related
I'm using django-tenants for multi-tenancy,All things are good with allauth package.
But the problem is:
I've signup form for "subscribers" they must receive an email confirmation and a custom signup form for every "subscribers" to enable them to create users under their subdomains, But the "created users" must receive an email with an initial password not confirmation email.
Really I tried many things to perform this mission but with no solution and I searched in many threads here but unfortunately useless,
I tried to override
def send_mail(self, template_prefix, email, context):,
def send_confirmation_mail(self, request, emailconfirmation, signup): and
class CustomConfirmEmailView(ConfirmEmailView):.
I knew that I've missed something but I don't know what is it. If I send a confirmation email to "subscribers", "created users" affected , And if I change settings to prevent sending confirmation email the "subscribers" affected
Also I've a custom login form in adapter.py file to enable users to login with username.
class CustomLoginView(LoginView):
template_name = 'account/subdomain/login.html'
This is my settings
ACCOUNT_ADAPTER = 'apps.authentication.adapter.CustomAccountAdapter'
AUTH_USER_MODEL = 'authentication.CustomUser'
ACCOUNT_FORMS = {
'login': 'apps.authentication.forms.CustomLoginForm',
'signup': 'apps.authentication.forms.CustomUserCreationForm'
}
LOGIN_REDIRECT_URL = '/'
ACCOUNT_LOGOUT_REDIRECT_URL = 'account_login' # created users must redirect to their subdomain login page not public login page
ACCOUNT_LOGOUT_ON_GET = True # If True it will logout the user immediately
ACCOUNT_AUTHENTICATION_METHOD = 'username_email'
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_UNIQUE_EMAIL = True #* It's not working for some reason but I don't know why, we have Handled from forms.py
ACCOUNT_USERNAME_REQUIRED = True
#* to solve subdomain users login for now
ACCOUNT_EMAIL_VERIFICATION = 'optional' # to send mail for subscribers not to created users but it doesn't fix the issue
ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = None # To prevent login on verification for created users but the subscribers affected
# ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = '/auth/email/success/'
# ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL = '/'
ACCOUNT_CONFIRM_EMAIL_ON_GET = True # try to solve the issue
ACCOUNT_SESSION_REMEMBER = True
ACCOUNT_LOGIN_ATTEMPTS_LIMIT = 10
#* This setting to enable us to access sign up view when authenticated
ACCOUNT_AUTHENTICATED_LOGIN_REDIRECTS = False
ACCOUNT_SIGNUP_REDIRECT_URL = 'account_login'
Update: Some of my tries to solve the issue
I tried to solve this issue by making redirect to the
return redirect('account_email_verification_sent')
But I failed.
This is the custom signup view in adapter.py
class CustomSignupView(SignupView):
'''
We override this class to
Make Signup form for Customers == Subscribers users from dashboard
'''
print('working done .......')
def form_valid(self, form):
# print('Enter form valid')
self.user = form.save(self.request)
# mobile = self.user.mobile_number
self.user.first_login = False
client = Client.objects.create(
schema_name=self.user.company_name,
type=self.user.industry,
name=self.user.company_name,
description='any for now',
)
print('client is: ', client, self.user.industry)
domain = Domain.objects.create(
domain=self.user.company_name + '.localhost',
tenant=client,
is_primary=True,
)
print('domain: ', domain)
with schema_context(self.user.company_name):
CustomUser.objects.create(
password=self.user.password, username=self.user.username,
first_name=self.user.first_name, last_name=self.user.last_name,
company_name=self.user.company_name , mobile_number=self.user.mobile_number,
email=self.user.email, operating_countries=self.user.operating_countries,
hq_country=self.user.hq_country, operating_cities=self.user.operating_cities,
shipments_per_month=self.user.shipments_per_month, address=self.user.address,
industry=self.user.industry, first_login=False
)
# domain = Domain.objects.prefetch_related('tenant').get(tenant__name=self.user.company_name)
return redirect('account_email_verification_sent')
And this is the signup view for "created users"
def create_user(request):
with schema_context(request.tenant.schema_name):
url = request.get_host() #request.tenant.get_primary_domain()
user_url = url + '/accounts/login/'
# obj = Domain.objects.select_related('tenant').get(domain=url)
print('url= ', url, 'obj= ',request.tenant.type)
if request.method == 'POST':
form = SupplierCreationForm(request.POST or None)
if form.is_valid():
save_form = form.save(commit=False)
save_form.industry = request.tenant.type
save_form.operating_countries = ''
save_form.operating_cities = ''
save_form.hq_country = ''
save_form.first_login = True
save_form.save()
from django.core.mail import send_mail
send_mail(
subject='Action Required',
message='''
Congratulations, Your password is:''' + str(request.POST.get('password1')) + ''' ,
Go to this link (http://''' + url + '''%s''' %reverse('account_login') + ''') to login
''',
from_email=settings.EMAIL_HOST_USER,
recipient_list=[save_form.email]
)
print('sending mail done ... ')
return redirect('create_user')
else:
form = SupplierCreationForm()
context = {
'form': form
}
return render(request, 'account/create_user.html', context)
Any help. If I missed some code to elaborate the issue, please tell me to update my question,
Thanks
I have made a view where I send a Refresh Token to email for activation account purpose. If token is valid everything works fine. The problem is when jwt token expire, I want to be able in backend to extract payload (user_id) from token when jwt.decode throw ExpiredSignatureError and in this way be able to auto resend an Email based on user_id extracted from token.
Here is how I generate the token:
def activation_link(request, user, email):
token = RefreshToken.for_user(user)
curent_site = "localhost:3000"
relative_link="/auth/confirm-email"
link = 'http://' + curent_site + relative_link + "/" + str(token)
html_message = render_to_string('users/email_templates/activate_account.html',{
'activation_link': link,
})
text_content = strip_tags(html_message)
email_subject = 'Activate your account'
from_email = 'notsure#yahoo.com'
to_email = email
#api_view(['POST'])
def ConfirmEmailView(request):
try:
activation_token = request.data['activation_token']
payload = jwt.decode(activation_token,settings.SECRET_KEY, algorithms=['HS256'])
user = User.objects.get(id = payload['user_id'])
if user.is_confirmed:
return Response('Already verified!', status=status.HTTP_200_OK)
user.is_confirmed = True
user.save()
return Response(status=status.HTTP_202_ACCEPTED)
except jwt.ExpiredSignatureError as identifier:
// =>>> Here I want to decode activation_token and extract user_id
return Response("Link- expired!", status=status.HTTP_403_FORBIDDEN)
except Exception as e:
print(e)
return Response(status=status.HTTP_400_BAD_REQUEST)
Well, apparently the solution is easy:
def ConfirmEmailView(request):
try:
activation_token = request.data['activation_token']
payload = jwt.decode(activation_token,settings.SECRET_KEY, algorithms=['HS256'])
user = User.objects.get(id = payload['user_id'])
if user.is_confirmed:
return Response('Already verified!', status=status.HTTP_200_OK)
user.is_confirmed = True
user.save()
return Response(status=status.HTTP_202_ACCEPTED)
except jwt.ExpiredSignatureError as identifier:
# Here we are:
payload = jwt.decode(request.data['activation_token'],settings.SECRET_KEY, algorithms=['HS256'],options={"verify_signature": False})
user_id = payload['user_id'];
return Response({'user_id':user_id, status=status.HTTP_2OO_OK})
By adding options={"verify_signature": False} is posible to decode the token just fine!
I have a problem I am not able to solve. I want to make use of http cookies in flask. This is the code from documentation:
#app.route('/token/auth', methods=['POST'])
def login():
username = request.json.get('username', None)
password = request.json.get('password', None)
if username != 'test' or password != 'test':
return jsonify({'login': False}), 401
# Create the tokens we will be sending back to the user
access_token = create_access_token(identity=username)
refresh_token = create_refresh_token(identity=username)
# Set the JWT cookies in the response
resp = jsonify({'login': True})
set_access_cookies(resp, access_token)
set_refresh_cookies(resp, refresh_token)
return resp, 200
I use flask_restx which automatically turns the response into JSON, so that jsonify in the first example is not needed. However, still still need to jsonify it, because i can not use set_access_cookie on a dictionary. This results at the end in a nested response like this jsonify(jsonify(x))
#api.route("/login")
class UserLogin(Resource):
def post(self):
"""Allows a new user to login with his email and password"""
email = request.get_json()["email"]
password = request.get_json()["password"]
user = User.query.filter_by(email=email).one_or_none()
if user is None:
return {"message": "user does not exist"}, 404
user = user.format()
if bcrypt.check_password_hash(pw_hash=user["password"], password=password):
if user["active"]:
resp = jsonify({"login": True})
access_token = create_access_token(identity=user)
refresh_token = create_refresh_token(user)
set_access_cookies(resp, access_token)
set_refresh_cookies(resp, refresh_token)
return resp, 200
# return (
# {"access_token": access_token, "refresh_token": refresh_token},
# 200,
# )
else:
return {"message": "User not activated"}, 400
else:
return {"message": "Wrong credentials"}, 401
This is the error: TypeError: Object of type Response is not JSON serializable
Any ideas how can I overcome this?
Was able to solve it like this:
data = dict(login=True)
resp = make_response(jsonify(**data), 200)
access_token = create_access_token(identity=user)
refresh_token = create_refresh_token(user)
set_access_cookies(resp, access_token)
set_refresh_cookies(resp, refresh_token)
return resp
I've written a small standalone python script that's calling my django-based backend and everything is working fine with login and calling views requiring auth and so on.
A bit of code
def dostuff():
session = login(username, password)
license = add_license(session)
def _helper(self, url, cookie=None):
http = httplib2.Http()
if cookie:
headers = { "Cookie" : cookie }
else:
headers = {}
response, content = http.request(host + url, "GET", headers=headers, body="")
return response, content
def login(self, username, password):
url = "/license/login?username=%s&password=%s" % (username, password)
response, content = self._helper(url)
sessioncookie = response["set-cookie"]
customer_id = re.search("id=(?P<id>\d+)", content)
if response["status"] == "200":
return sessioncookie, customer_id.group("id")
def add_license(self, session):
cookie = session[0]
customer_id = int(session[1])-1
url = "/license/add_license?customer_id=%s" % customer_id
response, content = self._helper(url, cookie)
content = content[1:-1]
if response["status"] == "200": #ok
data = json.loads(content)
return data["fields"]
If I cahnge "GET" to "POST" I encounter the Django CSRF-error page(CSRF verification failed) in return. How can I send POST data to Django?
My login view in Django, do I need to do anything special to add the csrf token? My plan is to rewrite this to send json once things are working.
def my_login(request):
done, username = get_input(request, "username")
if not done:
return username
done, password = get_input(request, "password")
if not done:
return password
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return HttpResponse("Done, id=%s" % user.pk)
else:
return HttpResponse("User disabled")
else:
return HttpResponse("Invalid login")
I got it working and this is how I did it. Like suggested by toto_tico I worte a dummy view that I retrieve thought GET to get the CSRF token. At first it didn't send the csrf token over GET so I had to add the decorator ensure_csrf_cookie.
#ensure_csrf_cookie
def dummy(request):
return HttpResponse("done")
And then I handle login requests normally.
def my_login(request):
...handle login...
It turned out that just adding the cookie to the POST wasn't enough, I had to write a token to the POST data as well.
def _helper(self, url, method="POST"):
req = urllib2.Request(host + url)
self.cookieMgr.add_cookie_header(req)
try:
if method == "GET":
response = self.opener.open(req)
else:
for cookie in self.cookieMgr:
if cookie.name == "csrftoken":
csrf = cookie.value
values = { "csrfmiddlewaretoken" : csrf}
params = urllib.urlencode(values)
response = self.opener.open(req, params)
code = response.getcode()
info = response.info()
content = response.read()
return code, info, content
except urllib2.HTTPError as ex:
print str(ex)
sys.exit(1)
def get_csrf(self):
url = "/license/dummy"
self._helper(url, method="GET")
def login(self, username, password):
self.get_csrf()
url = "/license/login?username=%s&password=%s" % (username, password)
code, info, content = self._helper(url)
if code == 200:
#done!
You have to add the csrftoken cookie value when you make a request to Django. Alternatively you can add #csrf_exempt to your Django backend to accept those requests.
Start reading about CSFR and ajax. I usually do the following with the code provided:
Create a csfr.js file
Paste the code in the csfr.js file
Reference the code in the template that needs it|
If you are using templates and have something like base.html where you extend from, then you can just reference the script from there and you don't have to worry any more in there rest of your programming. As far as I know, this shouldn't represent any security issue.
I am writing django app that as a beckend is using mongodb. I am curently writing register part. Here is how I connecto to database in settings.py
if socket.gethostname() == "Production server":
CON = Connection()
DB = CON.fish
else:
CON = Connection()
DB = CON.test
DB.user.ensure_index([("username", ASCENDING),("email",ASCENDING)],unique = True)#,drop_dups=True
Here is mye register view:
def register(request):
"""
handle user registration
code variable is for testing purposes
"""
if request.method== 'GET':
form = RegisterForm(auto_id=False)
code = 1
return render_to_response('register_home.html',locals(),context_instance=RequestContext(request))
elif request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
password = form.cleaned_data['password']
password_confirmation = form.cleaned_data['password_confirmation']
if password == password_confirmation:
login = form.cleaned_data['login']
email = form.cleaned_data['email']
newsletter = form.cleaned_data['newsletter']
key = register_user(login,email,password,newsletter)
if key:
#send email
send_mail("Dziękujemy za rejestrację"," Klucz aktywacyjny to " + key,settings.EMAIL_HOST_USER,[email])
request.session['email'] = email
return redirect(register_success)
else:
code = 4
error = "Login/email taken"
return render_to_response('register_home.html',locals(),context_instance=RequestContext(request))
else:
code = 3
error = "invalid password"
return render_to_response('register_home.html',locals(),context_instance=RequestContext(request))
else:
code = 2
return render_to_response('register_home.html',locals(),context_instance=RequestContext(request))
Here is my function I use to register user:
def register_user(login,email,password,newsletter):
"""
This function will return activation key for this user if user was added successfully or none otherwise
"""
key = generate_activation_key()
user = {
"username":login,
"email":email,
"password":crypt_password(password),
"date_join": datetime.now(),
"key": key
}
if newsletter:
user['newsletter'] = True
try:
settings.DB.user.insert(user,safe = True)
except DuplicateKeyError, error:
logging.debug("error raise during saving user")
return None
except OperationFailure, error:
logging.critical("Cannot save to database")
logging.critical(error)
else:
#we have no errors users is registred
return key
And when I test it in the browser it seems to be working. But I write test for it and it isn't working anymore. Here is code for test:
def test_valid_credentials(self):
#now try to register valid user
data = {'login':'test','password':'zaq12wsx','password_confirmation':'zaq12wsx','terms':True,'newsletter':True,'email':'test#test.com'}
response = self.c.post(reverse('register'),data)
#our user should be registred
self.assertEquals(302, response.status_code,'We dont have benn redirected')
self.assertEqual(len(mail.outbox), 1,'No activation email was sent')
#clen email box
mail.outbox = []
#now try to add another user with the same data
response = self.c.post(reverse('register'),data)
#template should be rendered with error message about used login and email
self.assertEquals(200, response.status_code)#this fails
And here is error that i get.
self.assertEquals(200, response.status_code)
AssertionError: 200 != 302
So user was registred with the same username and email which shoudn't happen. Any sugestions? Thanks in advance
Why don't you use https://github.com/django-mongodb-engine/mongodb-engine it works almost perfect with Django ORM. Works like a charm for me.