Django: Unable to get user's groups from LDAP user - django

My Django ( Django 1.11) project is using django-auth-ldap 1.2 as authentication backed.
I have no problem to authenticate any user agents LDAP database using:
#login_required(login_url='/accounts/login/')
and in this case, any user from any group can login to the site.
I want to allow only user from 'group1' to be able to access the website.
I used the code listed below
from django.shortcuts import render
from django.template import loader
from django.http import HttpResponse
from django.contrib.auth.decorators import login_required
from django.contrib.auth import views as auth_views
#user_passes_test(
lambda u: hasattr(u, 'ldap_user') and 'group1' in u.ldap_user.group_names,
login_url='/accounts/login/')
def index(request):
template = loader.get_template('main/index.html')
return HttpResponse(template.render())
This is code is not working and user will never pass the test.
According to the model documents django-auth-ldap Document I can use ldap_user.group_names to get group names of a user.
Here is my ldap settings from settings.py:
import os
import django
AUTHENTICATION_BACKENDS = ('django_auth_ldap.backend.LDAPBackend',)
import ldap
from django_auth_ldap.config import LDAPSearch, GroupOfNamesType
AUTH_LDAP_SERVER_URI = "ldap://mydomain.com"
AUTH_LDAP_BIND_DN = "cn=admin,dc=mydomain,dc=com"
AUTH_LDAP_BIND_PASSWORD = "mypass"
AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=ou_org_unit,dc=mydomain,dc=com",
ldap.SCOPE_SUBTREE, "(uid=%(user)s)")
AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=ou_org_unit,cn=group1,cn=group2,dc=mydomain,dc=com",
ldap.SCOPE_SUBTREE, "(objectClass=groupOfNames)"
)
AUTH_LDAP_GROUP_TYPE = GroupOfNamesType()
AUTH_LDAP_USER_ATTR_MAP = {
"first_name": "givenName",
"last_name": "sn",
"email": "mail"
}
AUTH_LDAP_FIND_GROUP_PERMS = True
AUTH_LDAP_CACHE_GROUPS = True
AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600
My question is:
Why I am not able to authenticate any user with this code?

You should be using the AUTH_LDAP_REQUIRE_GROUP setting if you want to restrict logins to a single group.
You will also likely want to use AUTH_LDAP_MIRROR_GROUPS in order to have all of your LDAP groups automatically loaded into your Django database.
As a bonus, you can include multiple groups in the AUTH_LDAP_REQUIRE_GROUP setting, by using the LDAPGroupQuery class. For example (taken from the documentation):
from django_auth_ldap.config import LDAPGroupQuery
AUTH_LDAP_REQUIRE_GROUP = (
(
LDAPGroupQuery("cn=enabled,ou=groups,dc=example,dc=com") |
LDAPGroupQuery("cn=also_enabled,ou=groups,dc=example,dc=com")
) &
~LDAPGroupQuery("cn=disabled,ou=groups,dc=example,dc=com")
)

Related

how can I prevent user to go to login page after successful authentication?

I am adding settings.py, root url and views.py. After login user is redirected to respective dashboard. In this situation, if user is pressing back button or changing url to accounts/login, then also it should remain on the dashboard page only. I am using django-registration-redux
settings.py
REGISTRATION_OPEN = True
ACCOUNT_ACTIVATION_DAYS = 7
REGISTRATION_AUTO_LOGIN = False
REGISTRATION_FORM = 'signin.forms.MyRegForm'
LOGIN_REDIRECT_URL = '/signin/user_sign/'
views.py
def user_sign(request):
obj = UserSelection.objects.get(user=request.user)
if obj.user_type == 'candidate':
return redirect('candidate:cand_dash')
else:
return redirect('employer:employer_dash')
urls.py
from django.conf.urls import url, include
from django.contrib import admin
from django.conf import settings
from signin.regbackend import MyRegistrationView
from django.contrib.auth import views as auth_views
urlpatterns = [
url(r'^$', auth_views.LoginView.as_view(template_name='registration/login.html'), name='home'),
url(r'^accounts/register/$', MyRegistrationView.as_view(), name='registration_register'),
url(r'^accounts/', include('registration.backends.default.urls')),
url(r'^candidate/', include('candidate.urls')),
url(r'^employer/', include('employer.urls')),
url(r'^signin/', include('signin.urls')),
]
You could use a Boolean variable authenticated.
Then you should need to set it as False before the user Authentication.
def registration(request):
authenticated = False
...
Then after the user's authentication just change the var as authenticated = True
Finally every time you need to know if user is authenticated just use if user.authenticated
Also, if you need to use authenticated a lot take a look at custom decorators (https://docs.djangoproject.com/en/2.0/topics/http/decorators/) maybe they could help you.

view in one app is not recognized by different view - Django

I have a Django project that I am building. Within the django project I have two apps. one is users and one is accounts. I want to call a view from the acounts app called user_synapse in the users app. I have imported the accounts views python file to import all the methods. I am then calling the method but I am getting an error that says the view method that I am trying to call is not defined. It was working earlier but now it is not working at all. I added the django app name to the settings file and the main urls file. Here is the code I have:
Users app:
views.py file:
#import all references from other apps
from accounts.views import *
from groups.models import *
from groups.views import *
...
# create synapse user
user_synapse(request)
accounts app:
views.py file:
#import all references from other apps
from groups.models import *
from groups.views import *
from users.views import *
from users.models import *
# synapse import statements that are needed
from synapse_pay_rest import Client, Node, Transaction
from synapse_pay_rest import User as SynapseUser
from synapse_pay_rest.models.nodes import AchUsNode
...
# ensure someone is logged in
#login_required
# create a synapse user
def user_synapse(request):
# grab the logged in user
user = request.user
# grab users profile
profile = Profile.objects.get(user = user)
# set user items before creating items
legal_name = profile.first_name + " " + profile.last_name
note = legal_name + " has just created his synapse profile "
supp_id = generate_number()
cip_tag = user.id
# grab the account type
account = profile.business
if account == 'INDIVIDUAL':
account = False
if account == 'BUSINESS':
account = True
# the following is all of the information that is required in order to make a
# new user within the Synapse application
args = {
'email':str(user.email),
'phone_number':str(profile.phone),
'legal_name':str(legal_name),
'note': str(note),
'supp_id':str(supp_id),
'is_business':account,
'cip_tag':cip_tag,
}
# the following is the request to the synapse api as well as the returned
# json that contains information that needs to ba saved in local database
create_user = SynapseUser.create(client, **args)
response = create_user.json
# the following updates the current profile to add the users synapse id within
# the local database.
if response:
synapse_id = response['_id']
updateProfile = profile
updateProfile.synapse_id = synapse_id
updateProfile.save()
the view does exist. why in the world is it saying that the method is not defined:
NameError at /personal/
name 'user_synapse' is not defined
settings.py file:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'general',
'users',
'localflavor',
'groups',
'accounts',
]
It was working earlier but now it is not working and I have no idea why.
The problem is that you have a circular import: in users.views.py
from accounts.views import *
in accounts.views.py
from users.views import *
This is not permitted in Python. See import dependency in Python for more info.

Django restrict/allow access by groups from ldap

I have Django project that has two apps App1 and App2)
each app has only 1 view.
. My project connected to openldap using django-auth-ldap.
I have two groups(Group1, Group2).
I Added decorators before my views in app1 and app2 (#login_required) and the result as expected that all users from group1 and group2 will be able to login to both apps.
I want to be able to just allow group1 to access app1 only and group2 access app2 only.
I tried many codes but no one work with me.
Here is my code:
app1.views.py
from django.shortcuts import render
from django.template import loader
from django.http import HttpResponse
from django.contrib.auth.decorators import login_required
from django.contrib.auth import views as auth_views
#login_required(login_url='/accounts/login/')
def index(request):
#getting our template
template = loader.get_template('main/index.html')
#rendering the template in HttpResponse
return HttpResponse(template.render())
Here is my ldap settings from settings.py:
#Generated by 'django-admin startproject' using Django 1.11.
import os
import django
AUTHENTICATION_BACKENDS = ('django_auth_ldap.backend.LDAPBackend',)
import ldap
from django_auth_ldap.config import LDAPSearch, GroupOfNamesType
AUTH_LDAP_SERVER_URI = "ldap://mydomain.com"
AUTH_LDAP_BIND_DN = "cn=admin,dc=mydomain,dc=com"
AUTH_LDAP_BIND_PASSWORD = "mypass"
AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=ou_org_unit,dc=mydomain,dc=com",
ldap.SCOPE_SUBTREE, "(uid=%(user)s)")
AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=ou_org_unit,cn=group1,cn=group2,dc=mydomain,dc=com",
ldap.SCOPE_SUBTREE, "(objectClass=groupOfNames)"
)
AUTH_LDAP_GROUP_TYPE = GroupOfNamesType()
AUTH_LDAP_USER_ATTR_MAP = {
"first_name": "givenName",
"last_name": "sn",
"email": "mail"
}
AUTH_LDAP_FIND_GROUP_PERMS = True
AUTH_LDAP_CACHE_GROUPS = True
AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600
First I would map a property to the user object that specifies which group the user is in:
AUTH_LDAP_USER_ATTR_MAP = {
"first_name": "givenName",
"last_name": "sn",
"email": "mail",
"ldap_group": "cn" # not 100% sure if this is what's required, just guessing
}
Then make a decorator with user_passes_test:
from django.contrib.auth.decorators import user_passes_test
def ldap_group_required(group_name):
"""
Checks if a user is in the specified LDAP group.
"""
return user_passes_test(
lambda u: hasattr(u, 'ldap_group') and u.ldap_group == group_name,
login_url='/accounts/login/'
)
Use it on a view like so:
#ldap_group_required('group1')
def index(request):
#getting our template
template = loader.get_template('main/index.html')
#rendering the template in HttpResponse
return HttpResponse(template.render())
If you check out the source code, this is effectively how login_required works.
I would personally recommend a slightly different approach to this, using Django's built-in permissions.
What you can do is create custom permissions, for example, can_access_app1 and can_access_app2. Then, since django-auth-ldap will automatically copy all of your groups into the Django database for you, you can then assign those permissions to the appropriate groups.
Now that your groups and their respective permissions are set up, you would then decorate your views appropriately. For example:
# app1/views.py
from django.contrib.auth.decorators import permission_required
from django.http import HttpResponse
from django.shortcuts import render
from django.template import loader
#permission_required('app1.can_access_app1')
def index(request):
#getting our template
template = loader.get_template('main/index.html')
#rendering the template in HttpResponse
return HttpResponse(template.render())
This approach would be well-documented, not introduce any special trickery having to manipulate your user objects, and would also have the added advantage that you can assign these permissions to individuals as well if you want to give special access. In addition, any superuser account will automatically have both permissions for no extra effort!

How to create a django-allauth regular and social account for testing purposes?

I managed to create a regular user as happens when signing in with Django-allauth.
I've been trying to do the same for a social account (Github) but I am really struggling. I assume there must be people out here that had to make a social account for testing purposes. Could anyone show how they did that?
Also, if you know a better way to create a regular user this is highly appreciated.
The following snippet from the django-allauth tests shows how to do this:
from allauth.account import app_settings as account_settings
from allauth.account.models import EmailAddress
from allauth.account.utils import user_email
from allauth.socialaccount.helpers import complete_social_login
from allauth.socialaccount.models import SocialApp, SocialAccount, SocialLogin
from allauth.utils import get_user_model
from django.contrib.auth.models import AnonymousUser
from django.contrib.auth.models import User
from django.contrib.messages.middleware import MessageMiddleware
from django.contrib.sessions.middleware import SessionMiddleware
from django.test import TestCase
from django.test.client import Client
from django.test.client import RequestFactory
from django.test.utils import override_settings
class SocialAccountTests(TestCase):
#override_settings(
SOCIALACCOUNT_AUTO_SIGNUP=True,
ACCOUNT_SIGNUP_FORM_CLASS=None,
ACCOUNT_EMAIL_VERIFICATION=account_settings.EmailVerificationMethod.NONE # noqa
)
def test_email_address_created(self):
factory = RequestFactory()
request = factory.get('/accounts/login/callback/')
request.user = AnonymousUser()
SessionMiddleware().process_request(request)
MessageMiddleware().process_request(request)
User = get_user_model()
user = User()
setattr(user, account_settings.USER_MODEL_USERNAME_FIELD, 'test')
setattr(user, account_settings.USER_MODEL_EMAIL_FIELD, 'test#test.com')
account = SocialAccount(user=user, provider='openid', uid='123')
sociallogin = SocialLogin(account)
complete_social_login(request, sociallogin)
user = User.objects.get(
**{account_settings.USER_MODEL_USERNAME_FIELD: 'test'}
)
self.assertTrue(
SocialAccount.objects.filter(user=user, uid=account.uid).exists()
)
self.assertTrue(
EmailAddress.objects.filter(user=user,
email=user_email(user)).exists()
)

Change Django Authentication Backend for Testing

My Django site uses the LDAP backend for authentication in production, but this is not suitable for testing (impossible to create requests from dummy users). How can I disable this backend, solely for tests?
Here is the relevant settings.py section:
AUTHENTICATION_BACKENDS = (
#'crowd.backend.CrowdBackend',
# 'django_auth_ldap.backend.LDAPBackend',
'django.contrib.auth.backends.ModelBackend',
)
AUTH_LDAP_SERVER_URI = "ldap://ldap.cablelabs.com"
import ldap
from django_auth_ldap.config import LDAPSearch
AUTH_LDAP_BIND_DN = "CN=CableLabs Internal,OU=cabletest,OU=Teamwork,OU=community,DC=cablelabs,DC=com"
AUTH_LDAP_BIND_PASSWORD = "UAq,0#ki"
AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=community,dc=cablelabs,dc=com",ldap.SCOPE_SUBTREE, "(sAMAccountName=%(user)s)")
AUTH_LDAP_USER_ATTR_MAP = {"first_name": "givenName", "last_name": "sn","username":"sAMAccountName","email":"mail","photo":"thumbnailPhoto"}
AUTH_LDAP_CONNECTION_OPTIONS = {
ldap.OPT_REFERRALS: 0
}
If you only need/want to disable the backend for certain tests, you can also use the override_settings decorator. You can use this decorator on the test case class:
from django.test.utils import override_settings
#override_settings(AUTHENTICATION_BACKENDS=
('django.contrib.auth.backends.ModelBackend',))
class FooTest(TestCase):
def test_bar(self):
pass
But you can also selectively use it on a test method:
from django.test.utils import override_settings
class FooTest(TestCase):
#override_settings(AUTHENTICATION_BACKENDS=
('django.contrib.auth.backends.ModelBackend',))
def test_bar(self):
pass
Create an alternative settings file, for example myproj/test_settings.py, and specify that settings file when running unit tests.
Write the alternative settings file like this:
from myproj.settings import *
AUTHENTICATION_BACKENDS = (
#'your.ldap.backend',
'django.contrib.auth.backends.ModelBackend',
)
That is, the settings inherits everything from your regular settings, but overrides the AUTHENTICATION_BACKENDS definition, with your LDAP backend commented out.
Then, run your tests like this:
python manage.py test --settings=myproj.test_settings
For future reference, another option to look into for testing is to change the is_authenticated property of the User object to a lambda. For example:
user = User(...)
user.is_authenticated = lambda: True