Show unique and viewable profile page for each user using Flask - flask

I have created unique user pages for each person who registers to this social app by using the following:
#app.route("/profile/<username>", methods=["GET", "POST"])
def profile(username):
user = session["user"] or None
# grab the session user's username from db
username = mongo.db.users.find_one(
{"username": session["user"]})["username"]
user_profile = mongo.db.users.find_one({"username": user})
if session["user"]:
return render_template("profile.html", username=username, profile=user_profile)
return redirect(url_for("login"))
This creates a unique username profile built up from edited data that the user puts in. But I want to be able to view that profile from another user's login and then add them as a friend (if I choose).
Because I already have a URL created /profile/ do I need to create a new #app.route to something such as view_profile/<username>?
From there, I would use the Post and Get methods so if someone wanted to add that user as a friend they could click on that. From there, I think I would need the other user to accept the friend request? Not quite sure how to get to that just yet. But one step at a time I guess.
I started with the idea of a view_profile #app.route but now I am not 100% sure which direction to take it that will make the next stages also easy.

Related

Force Django User To Pick A Username After Verifying Their Email And Logging In For The First Time

I'm looking for ideas on the most elegant way to force a user to define a username immediately after they verify their email and log in for the first time. Alternatively, they click the verification email, are not logged in, and must enter a username first to be able to log in for the first time.
My requirements are that the username not be in the registration form, but instead be on its own template page immediately after the user logs in for the first time. Once they define a username, they would not see this page again.
I'm using class-based views so I think that rules out decorators.
I've researched:
User-level Permissions (can't view any page until you provide a username)
Using the is_active boolean (user is not considered active until they provide a username)
PermissionRequiredMixin (add to every class that a logged-in user could potentially see)
UserPassesTestMixin (add to every class that a logged-in user could potentially see)
AccessMixin (add to every class that a logged-in user could potentially see)
Add my own boolean field to my custom User model
In every view, check if username is null, if it is, redirect to username form page (doesn't seem like an elegant approach)
user_logged_in signal (couldn't someone still bypass the username form page)
Middleware somehow?
My Concern
User verifies email, logs in for the first time, lands on the username page. I want them to create a username on this page before being allowed to go on to any other page. I don't want them logging in, and potentially pasting a different URL in the address bar and side-stepping this step.
I'd like to avoid...
Adding validation to every single view that an authenticated user would have access to.
What I'm trying to do is similar to forcing someone to agree to a "Terms of Service" before continuing to use a website. I need them to choose a username.
Just hoping that someone experienced with this would lend some advice. I'd like to avoid a discussion of "why don't you just add the username field to the registration form". The answer is, it's not what I want.
I fully realize this question is broad and asking for suggestions, not code-specific. I usually ask detailed code-specific questions but this one, I just don't know the best way to approach. Sorry in advance.
The answer was definitely middleware. It basically looked like this. I made a new Python file named middleware.py in my app, made this class, then modified my settings.py MIDDLEWARE area to include this class.
from django.shortcuts import redirect, reverse
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
if request.user.is_authenticated:
if request.user.username is None:
print(request.path)
if not request.path == reverse('choose_username'):
return redirect(reverse('choose_username'))
return response

register user by email or mobile number in django

i have custom user model with email as unique identifier for auth instead of username. i want register a user by email or mobile number. if user enter email address, then register user by activation link, and if user enter phone number then register by SMS OTP.
something like instagram registration:
https://www.instagram.com/accounts/emailsignup/
i found this topic but the answer is not explained well.
A username is required for a User object. But, you can make that username their email too, so it's not a problem (at least in django 2.x, not sure about 1.x). You didn't describe what the app you're making was for, it's purpose, etc., so before you skip to the code, read the following warnings and thought process of why I am giving you a better option.
It's a very bad idea to force the person to have the username equal to their email, because in the future you might want to add some other functionality.
For example: Maybe you'd want to make a message board so people can talk to each other. But because of bad planning from the beginning, everyone would see each other by their email. Technically, you could asign them all a bunch of random usernames they didn't make up, but that's not a good idea cause they're less likely to remember it, and they might not like it. The only good use of a name like sPaRkLe_DaNcEr12 or Poothtaste, is for making people rage quit in video games.
So if you wanted a future ability for users to talk to each other, it would be better to only show their username to other users, but ALLOW people to log in with their email or phone number if they wanted to. This way, now they can login with their (username) or (email) or (phone_number), and they only have to remember one of those. I will show you how soon.
Some downsides of this: It makes more queries to your database, which can make it slower if you have tons of users, but that's your call. Personally, I say it's worth it because it's negligible, and easier for users, and it's them you should cater to. So pay for a faster server, or no??? Ultimately, you always ought to design around having as few queries as possible, while caching certain pages that are heavy on the database so it doesn't have to do the same thing X number of times.
Let's begin:
Remember that the following is an example of what I would do for django 2.x, with a better functionality than you're asking for. If you're using 1.x, just use url() instead of path(), and any other requirements.
Assume we have an app called accounts_app.
Also assume that we put path('accounts/', include('accounts_app.urls')), inside our project level urls.py.
I'm also going to assume you know how to use templates... Now, create a urls.py inside that app:
accounts_app/urls.py:
app_name = 'accounts_app'
urlpatterns = [
...
path('signup/', views.signup, name='signup'),
]
accounts_app/models.py:
The user attribute below enables you to extend the User model, so you can have their phone_number too. In this example, I allowed that be blank in case they don't want to give it. But if they did, you would have make a separate view for it. To make this whole thing much more simple, I'm not going to include that, nor tell you how to overwrite the whole User model. I'm only going to show you how to log in with an email with the regular User model. After that, doing so with a phone number shouldn't be hard at all. A reminder on what you will need is at the very end of this post.
class ExtendedUserExample(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
phone_number = models.IntegerField(blank=True)
settings.py.... NOTE THAT THIS IS IN THE PROJECT LEVEL FOLDER
The order of these backends matters. Always do ModelBackend first, or it will break.
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'accounts_app.authentication.EmailAuthBackend', # to be able to login with email, described next
]
accounts_app/authentication.py:
This is an example to be able to login by their email. If you wanted someone to login by their phone number too, the principle is the same, but in this file, you would also need to import the ExtendedUserExample model above, add that to the above settings AUTHENTICATION_BACKENDS at the bottom of it, and make a new class for PhoneAuthBackend, that searches ExtendedUserExample for the phone_number. Again, to keep this more simple, I am not completely overwriting the User model, rather I only extend it, so if a user made an account and wanted to login by phone number later on, they would have to sign up with a username and email first, and once in, they could add a phone number (with this example, you will need another view for that).
So try this email example first until you get the hang of it. You also don't need to import this file anywhere else because the settings.py file takes care of it.
Here's what's happening: On your template for logging in, it will first search for the username field inside your User model, because your settings.py file has the AUTHENTICATION_BACKENDS variable to check the ModelBackend first.
But let's say a user entered their username as aaa#aaa.com. Now since you didn't allow anyone to sign up with an email as their username, when aaa#aaa.com is not found as a username in the User model, your settings file now says to go check the same User object a second time, but search their input by the email field / column instead. If their input exists in that column, authenticate() logs them in by their email if the password is right.
from django.contrib.auth.models import User
class EmailAuthBackend(object):
""" Authenticate using an email address """
def authenticate(self, request, username=None, password=None):
try:
user = User.objects.get(email=username) # gets the email by the 'username' they entered
if user.check_password(password):
return user
return None
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
acounts_app/views.py:
I'll assume you know how to make django forms. If not, check here for model forms.
from .forms import ExtendedUserForm
def signup(request):
form = ExtendedUserForm(request.POST or None)
if request.method == 'POST':
if request.POST['password1'] == request.POST['password2']:
potential_user = request.POST.get('username', False).lower()
try:
user = User.objects.get(username=potential_user)
return render(request, 'accounts_app/signup.html', {'error': 'Username has already been taken. Please try another'})
except User.DoesNotExist:
if form.is_valid():
new_user = User.objects.create_user(username=potential_user, email=request.POST['email'], password=request.POST['password1'])
# backend argument required cause we are making the ability to LOGIN by email.
# Remember, I only extended the User model.
auth.login(request, new_user, backend='django.contrib.auth.backends.ModelBackend')
return redirect('some_app:some_view')
else:
return render(request, 'accounts_app/signup.html', {'error': "Password's must match."})
return render(request, 'accounts_app/signup.html', {'form': form})
Now that your login by email should be working, it's not that hard to follow these principles to create the same ability for logging in by phone. If you continue with this example, you will need to make a new view to save a phone number to ExtendedUserExample.phone_number. After that, add another line at the bottom of AUTHENTICATION_BACKENDS, write a new class in authentication.py, and you'd be set... So long as you have <input type="text" name="username" required> when they use your login view.

how can I securely perform Rest requests without authentication?

This is more a process logic question than a specific language-framework one.
I am developing a mobile app and want the user to be able to use it without having to login (i.e. try it and offer a plus to the logged users), but I don´t want other persons to make post requests from let´s say Postman or any other platform than the app without having some sort of key, so what would be the approach here?
I am thinking on basic auth with some secret username:password for guests, or some kind of token, but as I am totally new on this I am not sure if it´s the correct approach, I´ve read the authentication and permissions Django Rest Framework tutorial but haven´t found a solution
I am learning Django myself and have gotten to the more advanced topics in the subject. What you could do is create a function in your permissions.py file for this. like so:
from rest_framework import permissions
class specialMobileUserPermissions(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in request.SAFE_METHODS:
return True
if request.user.id == whatever your mobile users id is:
return false
return obj.id == request.user.id # if the user is a subscribed user and they are logged in return true
return false # because we need a way out if none of the above works
So when dealing with permissions classes the permissions.SAFE_PERMISSIONS is a list of permissions that are non-destructive. So the first if statement asks are you a GET, HEAD, or other non data altering method. If so return true.
The second if statement checks the user id of the user that is making the request. And if that user id is equal to the user id you set for the mobile trail user it would return false, denying permissions to whatever this class is used on.
In your viewset you would need to add the permissions_classes variable like below
from . import permissions # your permissions.py file
class FooViewSet(viewsets.ViewSet):
permission_classes = (permissions.specialMobileUserPermissions,)
Unless you need extra functionality, that should be everything you need, all the way down to the imports. I hope I have helped.

Django AllAuth - save session data after login/signup

How do I implement some logic immediately after a user is logged using Django-AllAuth? Before I started implementing AllAuth, my login view contained this extra bit of logic after a user was logged in
...
login(request, user)
# Check if the user has a league in session
if 'league_id' in self.request.session:
# Save the league to this user's user instance
league_id = self.request.session.pop('league_id') # pop removes it from the session
league = League.objects.get(pk = league_id)
league.user = user
league.save()
(The purpose here is that I'm allowing users to create a 'league' instance before logging in, and after they login the league gets associated to their user instance via a league_id stored in session.)
I tried extending the form_valid() method LoginView provided in allauth but it appears as though the form_valid() method never even gets called.
Any ideas how I can handle this?
You could use signals.
There is a signal that is triggered right after an user is logged in: allauth.account.signals.user_logged_in

Django-registration setup without password

I am trying to make a website, where people only put their email addresses and they are logged in with cookies and all. At a later stage, i will ask them provide password and names, but NO username will be used. I am trying to do this with django-registraition, but i get errors and i have a few problems.
First to disable usernames as a login feature, i put str(time()) instead of username - i was looking for something that will change every time.
However, when I skip the authentication (which i currently don't need) i get error:
'RegistrationProfile' object has no attribute 'backend'
Alternatively, i can leave the authentication but then i don't know how to authenticate it only with email and no password. Also, i don't know how to make the next line work:
auth.login(request, ProfileUser)
If anyone can get me out of here, it would be awesome. Here is some code:
my form Class:
class RegistrationFormCustom(forms.Form):
email = forms.EmailField()
def do_save(self):
new_u = User(username=str(time()),email= self.cleaned_data.get('email'),)
new_u.save()
new_p = Profile.objects.create_profile(new_u)
new_p.save()
return new_p
my view:
def registerCustom(request, backend, success_url=None, form_class=None,
disallowed_url='registration_disallowed',
template_name='registration/registration_form.html',
extra_context=None,
initial={}):
form = RegistrationFormCustom(initial=initial)
if request.method == 'POST':
form = RegistrationFormCustom(initial=initial, data=request.POST)
if form.is_valid():
profile = form.do_save()
profile = auth.authenticate(username = profile.user.email, password = form.cleaned_data.get('pass1'))
print(profile)
auth.login(request, profile)
return redirect('/')
else:
pass
return render_jinja(request, 'registration/registration_form.html',
type="register",
form = form
)
and i will post any other snipped required happily
You're getting the 'RegistrationProfile' object has no attribute 'backend' error because the user is not yet authenticated. To log someone in, you have to call the authenticate method first, which requires a password. So, what you can do instead, is this:
from django.contrib.auth import load_backend, login, logout
from django.conf import settings
def _login_user(request, user):
"""
Log in a user without requiring credentials (using ``login`` from
``django.contrib.auth``, first finding a matching backend).
"""
if not hasattr(user, 'backend'):
for backend in settings.AUTHENTICATION_BACKENDS:
if user == load_backend(backend).get_user(user.pk):
user.backend = backend
break
if hasattr(user, 'backend'):
return login(request, user)
Then, to log someone in, just call the _login_user function with the request and User model. (This will be profile.user in your case, probably) Do this instead of calling auth.login. I'm not sure on how you're going to determine whether this is a valid user or not, without a password or username, but I'll leave that to you. If you still have trouble, let me know.
Short Explanation:
What basically happens here is that Django requires a user to be authenticated in order to be logged in via the login function. That authentication is usually done by the authenticate function, which requires a username and password, and checks whether the supplied password matches the hashed version in the database. If it does, it adds an authentication backend to the User model.
So, since you don't have a password and username, you just have to write your own method for adding the authentication backend to the User model. And that's what my _login_user) function does - if the user is already authenticated, it just calls login, otherwise, it first adds the default backend to the User model, without checking for a correct username and password (like authenticate does).
For others reading this thread, I got a similar error message when I was using User.objects.create() instead of User.objects.create_user(). Basically, the first method was setting a clear password whereas create_user encrypts the password. Clear passwords will fail to authenticate. Check your database, if you have passwords set in the clear, then it's likely you need to use create_user() instead.
The author's request could be fixed by simply setting a default user and password using create_user() instead of just user.save().
You can create a known password (put it in settings.py ) and use that as though the user entered it. Create the user with this and authenticate the user with this.