I customized my user authentication. Using Django 3.
In models.py I have;
class User(AbstractUser):
is_patient = models.BooleanField(default=False)
is_dentist = models.BooleanField(default=False)
is_manager = models.BooleanField(default=False)
In views.py I have below for user creation and login;
def create_account_dentist(request):
if request.method == 'POST':
#Create Dentist Type User
username = request.POST.get('username')
password = request.POST.get('password')
mobile_num = request.POST.get('mobilenum')
user_profile = User()
user_profile.username = username
user_profile.email = username
user_profile.set_password(password)
user_profile.is_dentist = True
user_profile.is_staff = True
user_profile.is_active = True
user_profile.mobile_number = mobile_num
user_profile.save()
login(request, user_profile)
return redirect(reverse('dentist-section'))
My sttings.py : AUTH_USER_MODEL = 'myapp.User'
When i check /admin page i see only groups section.
But i want to see the users, permissions and user groups together.
My DB tables are :
auth_group,
auth_group_permissions,
auth_permission,
django_admin_log,
django_content_type,
django_migrations,
django_session,
myapp_dentist,
myapp_manager,
myapp_patient,
myapp_user,
myapp_user_groups,
myapp_user_user_permissions
Although i created users in myapp_user, i dont see and manage them in django admin page
Below is screeshot from myapp_user table :
You can check the admin.py file which is under the user's model folder, isn't it contain the admin.site.register(YUOR_MODEL_NAME) ,if you want to view the data in /admin page must have registered.
from django.contrib import admin
from YOUR_APP_NAME.models import User
admin.site.register(User)
Related
There are two different types of users, A & B. Users of type A authenticate using username and password while type B have email and password. The project uses token based authentication.
The question is: how to manage both user types simultaneously?
The approaches I have considered:
Separate models for both user types and implement a custom auth backend to authenticate user signing in using email
Single user model to cater for both user types. However, this would mean I have to an add additional field (email) and populate some random value for users of type A
Apart from this, I have to store some custom information for logged in users like device type, location etc. Is it possible to override authtoken_token table to store this info or I have to create a separate model for that?
This following implementation should work (untested):
from rest_framework import serializers as rfs
from rest_framework.decorators import api_view
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
from django.contrib.auth import get_user_model
User = get_user_model()
### serializers.py
class UserSerializer(rfs.ModelSerializer):
class Meta:
model = User
fields = ["username", "email"]
class UserLoginSerializer(rfs.Serializer):
username = rfs.CharField(required=False)
email = rfs.EmailField(required=False)
password = rfs.CharField(style={"input_type": "password")
user = UserSerializer(read_only=True)
def validate(self, data):
username = data.get("username", None)
email = data.get("email", None)
# case: both were given
if username and email:
raise rfs.ValidationError("Only one of email or username should be given.")
# case none were given
elif (not username and not email):
raise rfs.ValidationError("One of email or username is required.")
elif username:
data["user"] = User.objects.get(username=username)
elif email:
data["user"] = User.objects.get(email=email)
return data
### views.py
#api_view(["POST"])
def login_view(request):
serializer = LoginSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({
"token": token.key,
"user_id": user.pk,
})
Explanation:
Basically, we have created a serializer that checks that only of either username or email is POSTed by the user.
For your 2nd question,
I have to store some custom information for logged in users like device type, location etc.
you can probably create a model such as:
from django.db import models
from rest_framework.authtoken.models import Token
class UserTokenClient(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="token_client")
token = models.ForeignKey(Token, related_name="client")
## extra fields you want
device_type = models.CharField()
or, you can use the package I wrote django-rest-durin which tries to solve this problem by associating a Client model between each User and Token.
You can easily subclass the Client model from the models.py to add your own custom fields, like so:
from durin.models import Client
class CustomClient(Client):
## extra fields you want
device_type = models.CharField()
and then override the LoginView similar to how it is done in this project's code.
I currently have an order management app. But the team I am creating this for has two departments the office and the factory who'll be using the same app but with each team having access to what they need. I'm confused if I should create two different apps to login into the same system (but that would redundant code) or if there's any other way I can set permissions. I have tried using the django admin permissions and they don't seem to work.
you can create different html pages for different teams like office team have their own html page and factory team have different html page .
def user_login(request):
if request.method == 'POST':
username=request.POST.get('username')
password = request.POST.get('password')
user = authenticate(username=username,password=password)
if user:
if user.is_active:
if user.is_admin:
login(request,user)
return HttpResponseRedirect(reverse('adminpage'))
#return render(request,'admin.html')
else:
login(request,user)
return HttpResponseRedirect(reverse('userpage'))
#return render(request,'user.html')
else:
return HttpResponseRedirect('Account not active')
else:
#message={"info":"someone tried to login and failed! ","details":"username :{} and password: {}".format(username,password)}
return HttpResponse("someone tried to login and failed ! <br />details: <br />username: {} <br /> password:{} ".format(username,password))
else:
return render(request,'login.html')
you can create models with specified role as active according to condition.(you can create radio button for factory and office and according to radio button you models code work.
class MyUser(AbstractBaseUser):
email = models.EmailField(verbose_name='email address',max_length=255,unique=True,)
full_name = models.CharField(max_length=255,null=True,blank=True)
date_of_birth = models.DateField()
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_agent = models.BooleanField(default=False)
choices are :
USER_CHOICES = (
('is_admin','ADMIN'),
('is_agent', 'AGENT')
)
You can have the same authentication page for both users and staff and redirect them to the same page, (suppose the dashboard ). Now you can add a link in the dashboard which is visible only if the logged in user is a superuser. The link would redirect to an admin interface.
The dashboard could be something common for both users.
Hope this helps.
I'm using django allauth for social login/signup. Also, I've my own signup form as an alternate login/signup. Following are the fields that I'm fetching from the user in the alternate form.
class Profile(models.Model):
col1 = models.CharField(max_length=50, blank=True, null=True)
col2 = models.CharField(max_length=50, blank=True, null=True)
user = models.OneToOneField(User)
So, when the user signs up, it asks for additional fields as well(col1, col2), apart from username, email and password.
Following is the signup view.
user = User.objects.create_user(username, user_email, user_pass)
Profile.objects.create(user=user, col1=col1, col2=col2)
return
So, whenever the user signs up via the alternate form, the above view is called up.
Now, in contrast, when the user signs up from social account FB, it does not ask for extra info, ie col1/col2. It directly signs up without asking for extra info, neither I want it to ask.
I then create a row in Profile model post signup using signals.
#receiver(user_signed_up)
def create_profile(request, user, sociallogin=None, **kwargs):
if sociallogin:
if sociallogin.account.provider == 'facebook':
data = sociallogin.account.extra_data
col1 = data.get('col1')
col2 = data.get('col2')
Profile.objects.create(user=user, col1=col1, col2=col2)
So, (1) my problem is when creating a user using alternate form, no record is inserted in allauth tables, which i find weird.
(2) Consider, I signed up using alternate form using E1 as email id. Now I signup via allauth(FB) with the same id, it throws an error.
(3) How do I send confirmation mail to the users who signed up in alternate form using all_auth.
I played around with the library a bit and finally found the solution to my question. I'm pasting it over here for other's to review.
Add a signal pre_social_login that'll check for conditions.
class MySocialAccountAdapter(DefaultSocialAccountAdapter):
def pre_social_login(self, request, sociallogin=None, **kwargs):
if sociallogin:
user = User.objects.filter(email=email).first()
# If user already exists in custom local form, then login directly.
# Save/Update his details in social login tables.
if user:
# create or update social login tables to maintain the uniformity in the code.
token = sociallogin.token.token
socialaccount = SocialAccount.objects.filter(user=user).first()
if socialaccount: # If it exists, then update social tables.
# updating account.
socialaccount.extra_data = extra_data
socialaccount.save()
# updating token.
SocialToken.objects.filter(account=socialaccount) \
.update(token=token)
else: # else create.
# saving Social EmailAddress.
EmailAddress.objects.create(email=email, user=user)
# saving social account.
provider = 'facebook'
kwargs = {
'uid': extra_data.get('id'),
'provider': provider,
'extra_data': extra_data,
'user': user
}
socialaccount = SocialAccount.objects.create(**kwargs)
# saving social token.
app = SocialApp.objects.get(provider=provider)
kwargs = {
'token': token,
'account': socialaccount,
'app': app
}
SocialToken.objects.create(**kwargs)
# finally login.
user.backend = 'django.contrib.auth.backends.ModelBackend'
login(request, user)
raise ImmediateHttpResponse(redirect(reverse('index')))
So the problem is I have extended User model in django. and I have written views for it.
Here is my models code :-
class StudentProfile(User):
batch = models.CharField(max_length=10)
course = models.CharField(max_length=20)
date_of_birth = models.DateField()
answer = models.CharField(max_length=20)
contact = models.CharField(max_length=20)
here is my auth backend file :-
from quizapp.models import StudentProfile
class StudentAuthenticationBackend(object):
def authenticate(self, username=None, password=None):
try:
student = StudentProfile.objects.get(username=username)
if student.check_password(password):
return student
except StudentProfile.DoesNotExist:
pass
return None
def get_user(self, user_id):
try:
return StudentProfile.objects.get(pk=user_id)
except StudentProfile.DoesNotExist:
return None
And I have made changes in seetings.py
AUTHENTICATION_BACKENDS = (
'quizapp.backends.StudentAuthenticationBackend',
'django.contrib.auth.backends.ModelBackend',
)
I'm printing username,password and authentication user. This is what i got :-
When using django created superuser
>> a = authenticate(username="super",password="super")
>> print(a)
>> super
But when using user created by form,
>> b = authenticate(username="test",password="123")
>> print(b)
>> None
I have cross checked username and password and it's true.
So but in auth_user table, username is super and password is encrypted but for test user, username is user and password is 123.
So the problem must be django is taking 123 is encrypted password and using decrypted version of it to authenticate.
Is there any way to solve this?
I have used OneToOneField and added extra fields in StudentProfile model. Now I'm using forms and registering user with it.
This is the view code :-
def register_page(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
user = StudentProfile.objects.create(
username=form.cleaned_data['username'],
password=form.cleaned_data['password1'],
batch=form.cleaned_data['batch'],
first_name=form.cleaned_data['first_name'],
last_name=form.cleaned_data['last_name'],
course=form.cleaned_data['course'],
date_of_birth=form.cleaned_data['date_of_birth'],
secret_question=form.cleaned_data['secret_question'],
answer=form.cleaned_data['answer'],
contact=form.cleaned_data['contact']
)
return HttpResponseRedirect('/register/success/')
else:
form = RegistrationForm()
variables = RequestContext(request, {'form': form})
return render_to_response('registration/register.html',variables)
And I'm getting IntegrityError at /register/
null value in column "user_id" violates not-null constraint error.
Is there any way to fix this?
From the Django authenication docs section on storing additional information about users:
If you'd like to store additional information related to your users, Django provides a method to specify a site-specific related model -- termed a "user profile" -- for this purpose.
To make use of this feature, define a model with fields for the additional information you'd like to store, or additional methods you'd like to have available, and also add a OneToOneField named user from your model to the User model. This will ensure only one instance of your model can be created for each User.
So you shouldn't subclass User at all -- that's the root of your problem. Instead, you should create another model with a one-to-one relationship with User and add your fields there.
I'm having a problem with a custom Authentication Backend I've built for an Active Directory via LDAP authentication.
The problem is that from the admin login page, after it properly authenticates and creates the new user in the database (or updates their info from the LDAP server), but then returns me to the admin login page indicating that I failed to enter a valid username and password.
Considering it authenticates and creates/updates the user in the django database, what am I doing wrong?
The code:
import ldap
import re
from django.conf import ad_settings
grps = re.compile(r'CN=(\w+)').findall
def anyof(short_group_list, adu):
all_groups_of_user = set(g for gs in adu.get('memberOf',()) for g in grps(gs))
return any(g for g in short_group_list if g in all_groups_of_user)
class ActiveDirectoryBackend(ModelBackend):
"""
This backend utilizes an ActiveDirectory server via LDAP to authenticate
users, creating them in Django if they don't already exist.
"""
def authenticate(self, username=None, password=None):
con = None
ldap.set_option(ldap.OPT_REFERRALS, 0)
try:
con = ldap.initialize('ldap://%s:%s' % (ad_settings.AD_DNS_NAME,
ad_settings.AD_LDAP_PORT))
con.simple_bind_s(username+"#"+ad_settings.AD_DNS_NAME, password)
ADUser = con.search_ext_s(ad_settings.AD_SEARCH_DN,
ldap.SCOPE_SUBTREE,
"sAMAccountName=%s" % username,
ad_settings.AD_SEARCH_FIELDS)[0][1]
con.unbind()
except ldap.LDAPError:
return None
# Does user belong to appropriate AD group?
if not anyof(ad_settings.PROJECTCODE,ADUser):
return None
# Does user already exist in Django?
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
#create Django user
user = User(username=username, is_staff = True, is_superuser = False)
#Update User info from AD
if ADUser.has_key('givenName'):
user.first_name = ADUser.get('givenName')[0]
if ADUser.has_key('sn'):
user.last_name = ADUser.get('sn')[0]
if ADUser.has_key('mail'):
user.email = ADUser.get('mail')[0]
# Does not store password in Django.
user.set_unusable_password()
user.save()
return user
EDIT: Figured out. Users cannot log in unless they are active (even though the documentation does not say that). Therefore, in the code given, the line that creates the new user should look like:
user = User(username=username, is_staff = True, is_Active = True,
is_superuser = False)
Figured out. Users cannot log in unless they are active (even though the documentation does not say that). Therefore, in the code given, the line that creates the new user should look like:
user = User(username=username, is_staff = True, is_Active = True,
is_superuser = False)