I'm trying to make an accesible cache of user data using Pyramid doc's "Making A “User Object” Available as a Request Attribute" example.
They're using this code to return a user object to set_request_property:
from pyramid.security import unauthenticated_userid
def get_user(request):
# the below line is just an example, use your own method of
# accessing a database connection here (this could even be another
# request property such as request.db, implemented using this same
# pattern).
dbconn = request.registry.settings['dbconn']
userid = unauthenticated_userid(request)
if userid is not None:
# this should return None if the user doesn't exist
# in the database
return dbconn['users'].query({'id':userid})
I don't understand why they're using unauthenticated_userid(request) to lookup user info from the database...isn't that insecure? That means that user might not be logged in, so why are you using that ID to get there private info from the database?
Shouldn't
userid = authenticated_userid(request)
be used instead to make sure the user is logged in? What's the advantage of using unauthenticated_userid(request)? Please help me understand what's going on here.
The unauthenticated_userid
call is a cheaper call; it looks up the user id from the request without going through the whole authentication process again.
The key concept there is the word again. You should only use the method in views that have already been authorized. In other words, by the time you reach code that uses unauthenticated_userid
you've already verified the user, and specifically do not want to do this again for this particular call.
Authenticating users against a backend persistent storage can be expensive, especially if such a storage doesn't support caching. The unauthenticated_userid
API method is an optimization where the request is basically your userid
cache.
This is a late reply but it was linked as a source of confusion for some users of Pyramid.
The accepted answer here is not the actual reason that unauthenticated_userid
is used for request.user
. It has nothing to do with performance.
The reason that it uses unauthenticated_userid
is because it makes it easier to reuse the authentication policy between applications with smaller modifications required. Your application needs a "source of truth" for whether the user is allowed to be considered authenticated and usually the policy's internal logic is not enough to make this determination. A valid cookie is nice, but you usually want to verify it with your backend before trusting it. Great, so where do we put that logic? Well unauthenticated_userid
doesn't make sense because it is the reusable part of the policy that focuses specifically on parsing the request headers. You could put it into authenticated_userid
but this method is not the one you normally care about in your application. You normally use request.user
in your apps (rarely do you probably care about request.authenticated_userid
directly) and lastly the request.user
is a superset of functionality - it provides an entire user object, not just an id. It would be silly to verify the id without verifying the entire object in most cases. We can only have one "source of truth" and so the recipe declares it to be request.user
. The groupfinder (and thus authenticated_userid
) can now depend on request.user
and trust that what it gets back from there has been properly verified with the backend. Also request.user
is already reified and thus speeds up subsequent calls to request.authenticated_userid
naturally.
Looks like Martijn Pieters is right.
My micro benchmark to test this (in my project I use Redis as DB for users and everything else):
print ('start test')
t1 = time()
authenticated_userid(self.request)
print ('authenticated: ' + str(time()-t1))
t1 = time()
unauthenticated_userid(self.request)
print ('unauthenticated: ' + str(time()-t1))
print ('test_stop')
Results:
start test
REDIS AUTH! # <-- actually this is query to groups finder in Redis
authenticated: 0.00032901763916
unauthenticated: 7.31945037842e-05
test_stop
It was tested for few times, results are constant :) Do you think I should add Martijn's answer to that article in Pyramid docs to make things more 'clear'? :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With