Change user state in database based on session - python

I have webapp that shows who is currently logged in on it's front page. I did that by having isActive field in user model, if user is logged isActive = True if user is logged out isActive = False. Unfortunately now I needed to implement auto log out on browser close and I've achieved that thanks to
REMEMBER_COOKIE_DURATION = datetime.timedelta(minutes=0)
app.config["SESSION_PERMANENT"] = False
But this obviously doesn't change state in database
My approach was to check active users and check sessions, get the ID's of both and compare them. If user ID is in active_user_list and not in session_id get this user ID and change it's state in database.
def checking_loop():
delay = 180
while True:
active_users = User.query.filter_by(isActive=True).all()
#don't worry about querying database in a loop It's stored locally
#on the PC
active_users_id = []
user_in_session = []
if session.get('_user_id'):
user_in_session.append(session['_user_id'])
for x in active_users:
active_users_id.append(x.id)
users_to_logout = [x for x in active_users_id if x not in user_in_session]
time.sleep(delay)
logout_loop = Thread(target=checking_loop)
logout_loop.start()
So, what's not working? I didn't know that when I call session I only get one ID.
Can I make this work somehow or do you have a better approach for this?

I don't think this will work. Flask session depends on context https://flask.palletsprojects.com/en/2.0.x/reqcontext/#how-the-context-works. I don't know how your code works without view context, but generally global session derives it's value from currently served request (the assumption is only one request can be handled at the time). So in session you'll get current user, not all users.
I'm not sure when session connected to user is deleted or how to iterate over active sessions, but if they are stored in cookies backend won't be able to do that.

Related

storing and accessing users properly in django session

Here I am trying to store selected users in some django session and for this I have tried like this but while retrieving the users from session in another view I am getting empty queryset.How can i get the users which are stored in session in another django fucntion?
storing selected users in session
selected_users = get_user_model().objects.filter(id__in=request.POST.getlist('users'))
initial = {'users':[]}
session = request.session.get('users',initial)
if selected_users:
for user in selected_users:
if not user in session['users']:
session['users'].append(user.email)
print(session['users'])
here trying to retrieve these session's users in another view
sess = request.session.get('users',{'users':[]})
users = get_user_model().objects.filter(pk__in=sess["users"])
print('sess',users) #didn't worked
request.session['users'] # didn't worked either
You are not setting the user list in session. You are setting email in the variable session["user"] but querying id. Also, you are trying to set the session value as a list object but using default value as a dict object.
selected_users = get_user_model().objects.filter(id__in=request.POST.getlist('users'))
session_users = request.session.get('users',[])
if selected_users:
for user in selected_users:
if not user in session_users:
session_users.append(user.id) # append id instead of email
print(session_users)
# set the value in session
request.session["users"] = session_users
Now you can retrieve the list of user.id.
sess_users = request.session.get('users',[])
users = get_user_model().objects.filter(pk__in=sess_users)
print('sess',users)
print request.session['users']

trying to delete inactive users in django

I am writing a site that sends confirmation emails on signup. Until the user clicks the link, the is_active field is set to false. How can I automatically delete any users who do not click the activation link within a certain period of time? Is this even a viable option or is there a better way to work around users who never confirmed their accounts?
Thanks for any help!
I had the same problem, what i did was set a celery task that checks for user that hasn't activated their account after a specified period...
your task.py should be like this
#shared_task(name="delete_unactivated_users")
def delete_unactivated_users(self, *args, **kwargs):
User = get_user_model() # Get User model
inactive_users = User.objects.filter(
date_joined__lt=timezone.now() - timezone.timedelta(seconds=86400),
) # Queryset to get users that have created an account but didn't activate them in a day.
for user in inactive_users:
# Optional line to log deleted users to a file
deleted_users_log(user)
user.delete()
function to write to file
def deleted_users_log(user):
f = open('/deleted_user_log.txt', 'a')
opened_file = File(f)
opened_file.write(f"Unactivated user {user.username} with firstName: {user.first_name}, lastName: {user.last_name}, and email: {user.email} have been flushed down the drain at: {datetime.datetime.now().time()}.\n")
opened_file.close
f.close
I am assuming that your token will expire after a day (24hrs)(86400 seconds)
Reference

GAE users service object - local variable creation or not?

So ... using the Google App Engine User service.
Should I create a local user object:
my_user = users.get_current_user()
if not my_user:
self.redirect(users.create_login_url(self.request.uri), abort=True)
return
person = Person.get_current(my_user.user_id()) #Here
or access the user object from the users service anytime? :
my_user = users.get_current_user()
if not my_user:
self.redirect(users.create_login_url(self.request.uri), abort=True)
return
#... code ...
person = Person.get_current(users.get_current_user().user_id()) #And here
or something else? :
helping useres :-)
and of course why. Is usage of users service costly in resources?
A locally scoped user object should be fine for each request.
Make sure my_user is local to your thread and the current request:
if it's shared between separate requests, there's no guarantee that it's really the same user issuing the request, unless you have some separate session validation.
different threads can be handling different request, in which case you run into the above problem.
A local call is always better as a call that triggers many method calls. The efficiency gain depends on the frequency your code is calling it. For 2 calls it's okay.

Google App Engine (Python) : use UserProperty with Webapp2 User Model

i have an application where we allow users to use Oauth2 for authentication and even Custom User Registrations. All the Users are saved into the default User entity in the datastore. If the user is logging in using Oauth2 for the first time a new record in the default User entity is created like this:
"""Check if user is already logged in"""
if self.logged_in:
logging.info('User Already Logged In. Updating User Login Information')
u = self.current_user
u.auth_ids.append(auth_id)
u.populate(**self._to_user_model_attrs(data, self.USER_ATTRS[provider]))
u.put()
else:
"""Create a New User"""
logging.info('Creating a New User')
ok, user = self.auth.store.user_model.create_user(auth_id, **self._to_user_model_attrs(data, self.USER_ATTRS[provider]))
if ok:
self.auth.set_session(
self.auth.store.user_to_dict(user)
)
self.redirect(continue_url)
for custom registrations records are inserted through the following handler.
class RegistrationHandler(TemplateHandler, SimpleAuthHandler):
def get(self):
self.render('register.html')
def post(self):
"""Process registration form."""
user = 'appname:%s' % self.request.get('email')
name = '%s %s' % (self.request.get('first_name'), self.request.get('last_name'))
password = self.request.get('password')
avatar = self.request.get('avatar')
act_url = user_activation.Activate(self.request.get('first_name'), self.request.get('email'))
ok, user = User.create_user(auth_id=user, name=name, password_raw=password, email=self.request.get('email'))
if ok:
self.auth.set_session(self.auth.store.user_to_dict(user))
acc = models.Account(display_name=self.request.get('first_name'), act_url=act_url, act_key=act_url.split('activate/')[1], user=users.User(User.get_by_auth_id(self.current_user.auth_ids[0]).email))
acc.put()
if avatar:
avt = models.Picture(is_avatar=True, is_approved=True, image=avatar, user=users.User(User.get_by_auth_id(self.current_user.auth_ids[0]).email))
avt.put()
self.redirect('/')
Now we are using webapp2_extras.sessions for session handling. We have different models like, Comments, Images, Reviews etc in which we want to use db.UserProperty() as the author field. However, the author field shows blank or None whenever we enter a record into any of these models using 'users.get_current_user()'. I think this is because we are handling the sessions through webapp2 sessions.
What we want to achieve is to be able to use the db.UserProperty field in various models and link appropriately to the current user using webapp2 sessions ?
the UserProperty() has to be passed with a User Object in order for it to properly insert the records. Even though we are able to enter the records using the following code :
user = users.User(User.get_by_auth_id(self.current_user.auth_ids[0]).email)
or
user = users.User(User.get_by_auth_id(self.current_user.auth_ids[0]).name)
but then we are not able to get the whole user object by referencing to model.author
Any ideas how we should achieve this ?
OAuth 2.0 is not currently supported by Users service. Supported options are
Google Accounts
OpenId
OAuth 1.0
I don't frankly understand what you're trying to accomplish with introducing db.User in to the codebase. Given there's self.current_user, I assume you're already handling authentication process.
When you do self.auth.store.user_model.create_user - that already gives you a webapp2's user object/entity (it has nothing to do with db.User though). I believe that's what you'll have to use as your author field given OAuth 2.0 constraint.
users.get_current_user() relies on a special cookie (App Engine internal). In fact, it has nothing to do with webapp2's session (or any other "custom" session for that matter). You could hack it by setting the cookie to a value that App Engine internals can understand and be tricked as if a user were logged in with one of the methods I mentioned, but I wouldn't recommend this approach. It is not documented (cookie name, format, etc.) and might be changed at any time.
Instead of using UserProperty to store references to the webapp2 user objects, you should instead store the auth_id as a StringProperty and add a convenience method for fetching the corresponding webapp2 user entity.
Something like this
from webapp2_extras.appengine.auth.models import User
class Comment(db.model):
text = db.StringProperty()
author = db.StringProperty()
def get_author(self):
return User.get_by_auth_id(self.author)

Django user impersonation by admin

I have a Django app. When logged in as an admin user, I want to be able to pass a secret parameter in the URL and have the whole site behave as if I were another user.
Let's say I have the URL /my-profile/ which shows the currently logged in user's profile. I want to be able to do something like /my-profile/?__user_id=123 and have the underlying view believe that I am actually the user with ID 123 (thus render that user's profile).
Why do I want that?
Simply because it's much easier to reproduce certain bugs that only appear in a single user's account.
My questions:
What would be the easiest way to implement something like this?
Is there any security concern I should have in mind when doing this? Note that I (obviously) only want to have this feature for admin users, and our admin users have full access to the source code, database, etc. anyway, so it's not really a "backdoor"; it just makes it easier to access a user's account.
I don't have enough reputation to edit or reply yet (I think), but I found that although ionaut's solution worked in simple cases, a more robust solution for me was to use a session variable. That way, even AJAX requests are served correctly without modifying the request URL to include a GET impersonation parameter.
class ImpersonateMiddleware(object):
def process_request(self, request):
if request.user.is_superuser and "__impersonate" in request.GET:
request.session['impersonate_id'] = int(request.GET["__impersonate"])
elif "__unimpersonate" in request.GET:
del request.session['impersonate_id']
if request.user.is_superuser and 'impersonate_id' in request.session:
request.user = User.objects.get(id=request.session['impersonate_id'])
Usage:
log in: http://localhost/?__impersonate=[USERID]
log out (back to admin): http://localhost/?__unimpersonate=True
It looks like quite a few other people have had this problem and have written re-usable apps to do this and at least some are listed on the django packages page for user switching. The most active at time of writing appear to be:
django-hijack puts a "hijack" button in the user list in the admin, along with a bit at the top of page for while you've hijacked an account.
impostor means you can login with username "me as other" and your own password
django-impersonate sets up URLs to start impersonating a user, stop, search etc
I solved this with a simple middleware. It also handles redirects (that is, the GET parameter is preserved during a redirect). Here it is:
class ImpersonateMiddleware(object):
def process_request(self, request):
if request.user.is_superuser and "__impersonate" in request.GET:
request.user = models.User.objects.get(id=int(request.GET["__impersonate"]))
def process_response(self, request, response):
if request.user.is_superuser and "__impersonate" in request.GET:
if isinstance(response, http.HttpResponseRedirect):
location = response["Location"]
if "?" in location:
location += "&"
else:
location += "?"
location += "__impersonate=%s" % request.GET["__impersonate"]
response["Location"] = location
return response
#Charles Offenbacher's answer is great for impersonating users who are not being authenticated via tokens. However, it will not work with clients side apps that use token authentication. To get user impersonation to work with apps using tokens, one has to directly set the HTTP_AUTHORIZATION header in the Impersonate Middleware. My answer basically plagiarizes Charles's answer and adds lines for manually setting said header.
class ImpersonateMiddleware(object):
def process_request(self, request):
if request.user.is_superuser and "__impersonate" in request.GET:
request.session['impersonate_id'] = int(request.GET["__impersonate"])
elif "__unimpersonate" in request.GET:
del request.session['impersonate_id']
if request.user.is_superuser and 'impersonate_id' in request.session:
request.user = User.objects.get(id=request.session['impersonate_id'])
# retrieve user's token
token = Token.objects.get(user=request.user)
# manually set authorization header to user's token as it will be set to that of the admin's (assuming the admin has one, of course)
request.META['HTTP_AUTHORIZATION'] = 'Token {0}'.format(token.key)
i don't see how that is a security hole any more than using su - someuser as root on a a unix machine. root or an django-admin with root/admin access to the database can fake anything if he/she wants to. the risk is only in the django-admin account being cracked at which point the cracker could hide tracks by becoming another user and then faking actions as the user.
yes, it may be called a backdoor, but as ibz says, admins have access to the database anyways. being able to make changes to the database in that light is also a backdoor.
Set up so you have two different host names to the same server. If you are doing it locally, you can connect with 127.0.0.1, or localhost, for example. Your browser will see this as three different sites, and you can be logged in with different users. The same works for your site.
So in addition to www.mysite.com you can set up test.mysite.com, and log in with the user there. I often set up sites (with Plone) so I have both www.mysite.com and admin.mysite.com, and only allow access to the admin pages from there, meaning I can log in to the normal site with the username that has the problems.

Categories

Resources