I want to drop all user sessions when user resets his password, but I can't find a way to do that.
My idea was to get all UserTokens of the specific user and delete them, but it seems impossible, because of
user = model.StringProperty(required=True, indexed=False)
in UserToken model
Any ideas how to do that?
I see two ways how to do that.
First is to inherit from the UserToken class making user an indexed property. Then you can set the token_model class property to your new token model in your user class. Here is the code:
class MyToken(UserToken):
user = ndb.StringProperty(required=True)
class MyUser(User):
token_model = MyToken
# etc.
Don't forget to set the user model used by webapp2 to your user class if you do not do it already:
webapp2_config = {
"webapp2_extras.auth": {
"user_model": "models.MyUser"
},
# etc.
}
app = webapp2.WSGIApplication(routes, config=webapp2_config)
The second way is to make a complicated datastore query based on the token key name. Since the key names are of the form <user_id>.<scope>.<random>, it is possible to retrieve all the entities starting with a specific user ID. Have a look at the code:
def query_tokens_by_user(user_id):
min_key = ndb.Key(UserToken, "%s." % user_id)
max_key = ndb.Key(UserToken, "%s/" % user_id) # / is the next ASCII character after .
return UserToken.query(UserToken.key > min_key, UserToken.key < max_key)
This uses the fact that the query by key names works in the lexicographical order.
Related
I'm not sure what is the best/pythonic way of having a User document that automatically hashes its password upon creating.
Consider the following mongoengine model :
class User(Document):
email = EmailField(required=True, primary_key=True)
name = StringField(required=True)
pswd = StringField(required=True)
def check_pswd(self, password):
return verify_password(password, self.pswd)
def hash_pswd(self, password):
return hash_password(password):
def save(self, *args, **kwargs):
self.pswd = self.hash_pswd(self.pswd)
super().save(*args, **kwargs)
When I create a user, it works fine :
user = User()
user.email = 'user#email.com'
user.pswd = 'password'
user.name = 'User'
user.save()
Buf if I update it, it will double hash its password, I don't want that.
#User wants to change his name
user = User.objects(email='user#email.com')
user.name = 'User 2'
user.save()
Is there a way for me to hash its password only when creating or changing the password?
Or maybe I should delegate the responsibility of hashing the password to the View/Controller?
I am not giving you the code sample, you can use Document.update() method which will update the fields that only has changed.
If you still wanna use the save method,
Then you can create a logic along the following lines.
Check if the user has changed the password(by comparing the existing stored hash and new hash if any)
If the new hash is different then user has changed the password in that case you can push an Document.update method.
If not, don't call update on that field.
Alternatively update in Mongoengine accepts a iterable, so you can simply create a list or a dictionary object and convinently choose to Remove the password hash field from it.
As for who should execute this i.e View / Controller, its a Design decision but I would rather keep Representation (GUI / Front End) seperate from logic, so I would delegate this to the controller or even more Intrinsically to the Object who is responsible for handling all database/network related tasks, this way it would be isolated and easy to modify. And would not complexify or slow the View objects process / thread
Link for update using an iterable like Dict.
stackoverflow question for Mongoengine update using iterable
Link discussing save methods deprecation (The Maintainer Has commented below as save method not being deprecated, so trust him/her and proceed at will on this link)
Mongoengine save method deprecated?
Link for update method in mongoengine.
Mongoengine Atomic Update
I am trying to figure out the best method of accessing all the matching microseries found in the Assessment class (table) in my database. I am trying to create a link that will send a user to those matching microseries. I am new and want to better understand the nuts and bolts of front-end engineering. I also have not seen a similar question asked here on Stacks.
I am not using JSON or XML yet, so this is a static HTML process. I have a few routes that access assessments at the moment, e.g.:
config.add_route('assessments', '/assessments')
config.add_route('assessment', '/assessments/{id:\d+}')
What I would like to better understand while implementing a method of finding matching microseries in the Assessment table and sending the user to a new page with those matching series:
How routes work, especially when accessing an attribute of a class, e.g. Assessment.microseries.
The goal of View code to convey the method mentioned above.
Pyramid links and Pyramid on Routes and URL Dispatch
Using: Python 2.7, SQLAlchemy, Pyramid
Assessment table:
class Assessment(Base):
__tablename__ = 'assessments'
id = Column(Integer, primary_key=True)
name = Column(String(50), unique=True)
text = Column(String(2000))
microseries = Column(Integer)
# more code
def __init__(self, name, text, user, video, categories, microseries):
# more code
API for interacting with an assessment is based on CRUD- create, retrieve, update and delete.
View Code:
this is not doing what I need it to do as I don't have a form of link to send the user to the matching series, e.g. Link1 would send a user to a new view with GET for all subseries of Link1: 1a, 1b, 1c....
#view_config(route_name='assessments', request_method='GET', renderer='templates/unique_assessments.jinja2', permission='create')
def view_unique_microseries_group(request):
logged_in_userid = authenticated_userid(request)
if logged_in_userid is None:
raise HTTPForbidden()
all_assessments = api.retrieve_assessments() #all assessments in a list
assessments_by_microseries = {} #dictonary
for x in all_assessments:
if x.microseries in assessments_by_microseries:
print("Already seen this microseries: %s" % x.microseries)
else:
assessments_by_microseries[x.microseries] = x
unique_assessments = sorted(assessments_by_microseries.values()) #.values() method to get the, err, values of the dict.
print 'unique_assessments:', unique_assessments
#a = HTTPSeeOther(location=request.route_url('view_microseries'))
return {'logged_in': logged_in_userid, 'unique_assessments': unique_assessments} #need to send some kind of list that can be shown on the template to send a user to the appropriately matching set of microseries
I'm trying to introduce field-level permissions in my app that would effectively hide/nullify model field values from some users, while showing them to others. A user would need to be able to do something like this:
class MyRestrictedModel(HypotheticalMixin, models.Model):
public = CharField(max_length=128)
restricted = RestrictedCharField(
max_length=128,
permitted_groups=("group1",)
)
user1 = User.objects.get(pk=1) # in group1
user2 = User.objects.get(pk=2) # NOT in group 1
model_instance1 = MyRestrictedModel.objects.get(pk=1).restrict(user1)
model_instance2 = MyRestrictedModel.objects.get(pk=1).restrict(user2)
print(model_instance1.public) # "this is public data"
print(model_instance1.restricted) # "this is restricted data"
print(model_instance2.public) # "this is public data"
print(model_instance2.restricted) # None
I think I might be able to hack something together to get this working, but I'd hate to do that work if something more robust and community-accepted was available, so I thought I'd ask here. Does such a thing exist?
You will want add attribute, method, groups etc so you know if a user is restricted or not. Assuming you have user.is_restricted attribute:
class RestrictManager(models.Manager):
def by_user(self,user):
queryset = super(RestrictManager,self).get_queryset()
if user.is_restricted:
queryset = queryset.annotate(field_to_show=None) # field_to_show is a queryset field (not in any model)
else:
queryset = queryset.annotate(field_to_show=secret_field)
return queryset
class MyRestrictedModel(models.Model):
field1 = models.CharField...
restricted_objects = RestrictManager()
In your code:
q = MyRestrictedModel.restricted_objects.by_user(self.request.user)
# Now use q as usual, q.all(), q.get(...), q.filter(...)
You can of course add more method like by_group etc, and even set objects=RestrictManager() to replace the objects default manager.
Probably you should use a django package that deals specifically with detailed permissions. See here all this kind of packages. The right for you - which has field level permissions management - is django-permissions. But there are others too.
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)
I do not care about concurrency issues.
It is relatively easy to build unique form field:
from django import forms
class UniqueUserEmailField(forms.CharField):
def clean(self, value):
self.check_uniqueness(super(UniqueUserEmailField, self).clean(value))
def check_uniqueness(self, value):
same_user = users.User.all().filter('email', value).get()
if same_user:
raise forms.ValidationError('%s already_registered' % value)
so one could add users on-the-fly. Editing existing user is tricky. This field would not allow to save user having other user email. At the same time it would not allow to save a user with the same email. What code do you use to put a field with uniqueness check into ModelForm?
quick and dirty way would be:
make check_uniqueness classmethod
use custom field check in ModelForm, like this:
class User(forms.ModelForm):
email = forms.EmailField()
def clean_email(self):
data = self.cleaned_data['email']
original = self.instance.email
if original == data:
return data
UniqueUserEmailField.check_uniqueness(data)
return data
better options?