I configured my pyramid app in order to have an user object attached to request once it has been authenticated following the official tutorial. So far so good... but while it works perfectly and I can test it using a browser, I don't understand why in webtest tests user is not attached to the request.
I configured my test class in this way:
from my_pyramid_app import main as make_app
from webtest.app import TestApp
from pyramid import testing
class LoginTestCase(TestCase):
def setUp(self):
self.config = testing.setUp()
self.app = TestApp(make_app({}))
And in a test:
# submit valid login data to /login and expect redirect to "next"
response = self.app.post('/login', data, status=302)
redirect = response.follow()
It works as expected, user gets authenticated and redirected to the path specified in "next", but redirect.request does not contain user. Why? What should I do?
ps. the documentation of webtest says:
The best way to simulate authentication is if your application looks
in environ['REMOTE_USER'] to see if someone is authenticated. Then you
can simply set that value, like:
app.get('/secret', extra_environ=dict(REMOTE_USER='bob'))
but honestly it sounds demential to me :/ (I mean if I define a variable manually what is the sense of the test?!)
both webtest and pyramid use webob but this doesn't mean that pyramid's request is the same object than webtest's response.request
the only immutable object shared between webtest and the tested application is the environ dictionary.
This mean that you may be able to retrieve your user if you store it in request.environ with a key like 'myapp.user' (dot and lowercase are important, see PEP333).
Related
I'm using the latest Django OAuth2 Toolkit (0.10.0) with Python 2.7, Django 1.8 and Django REST framework 3.3
While using the grant_type=password, I noticed some weird behavior that any time the user asks for a new access token:
curl -X POST -d "grant_type=password&username=<user_name>&password=<password>" -u"<client_id>:<client_secret>" http://localhost:8000/o/token/
A new access token and refresh token is created. The old access and refresh token are still usable until token timeout!
My Issues:
What I need is that every time a user asks for a new access token,
the old one will become invalid, unusable and will be removed.
Also, is there a way that the password grunt type wont create refresh
token. I don't have any use for that in my application.
One solution I found is that REST Framework OAuth provides a configuration for One Access Token at a time. I'm not eager to use that provider, but I might wont have a choice.
If you like to remove all previous access tokens before issuing a new one, there is a simple solution for this problem: Make your own token view provider!
The code bellow will probably help you to achieve that kind of functionality:
from oauth2_provider.models import AccessToken, Application
from braces.views import CsrfExemptMixin
from oauth2_provider.views.mixins import OAuthLibMixin
from oauth2_provider.settings import oauth2_settings
class TokenView(APIView, CsrfExemptMixin, OAuthLibMixin):
permission_classes = (permissions.AllowAny,)
server_class = oauth2_settings.OAUTH2_SERVER_CLASS
validator_class = oauth2_settings.OAUTH2_VALIDATOR_CLASS
oauthlib_backend_class = oauth2_settings.OAUTH2_BACKEND_CLASS
def post(self, request):
username = request.POST.get('username')
try:
if username is None:
raise User.DoesNotExist
AccessToken.objects.filter(user=User.objects.get(username=username), application=Application.objects.get(name="Website")).delete()
except Exception as e:
return Response(e.message,status=400)
url, headers, body, status = self.create_token_response(request)
return Response(body, status=status, headers=headers)
The part you should notice is the Try-Except block. In there we finding the Access tokens and removing them. All before we creating a new one.
You can look at how to create your own Provider using OAuthLib.
Also, this might be useful as well: TokenView in django-oauth-toolkit. You can see there the original Apiview. As you said, you were using this package.
As for the refresh_token, as previously mentioned in other answers here, you can't do what you are asking. When looking at the code of oauthlib password grunt type, you will see that in its initialization, refresh_token is set to True. Unless you change the Grunt type it self, it can't be done.
But you can do the same thing we did above with the access tokens.
Create the token and then delete the refresh token.
What I need is that every time a user asks for a new access token, the
old one will become invalid, unusable and will be removed.
Giving a new token when you ask for one seems like an expected behavior. Is it not possible for you to revoke the existing one before asking for the new one?
Update
If you are determined to keep just one token -
The class OAuth2Validator inherits OAuthLib's RequestValidator and overrides the method save_bearer_token. In this method before the code related to AccessToken model instance creation and its .save() method you can query (similar to this) to see if there is already an AccessToken saved in DB for this user. If found the existing token can be deleted from database.
I strongly suggest to make this change configurable, in case you change your mind in future (after all multiple tokens are issued for reasons like this)
A more simpler solution is to have your own validator class, probably one that inherits oauth2_provider.oauth2_validators.OAuth2Validator and overrides save_bearer_token. This new class should be given for OAUTH2_VALIDATOR_CLASS in settings.py
Also, is there a way that the password grunt type wont create refresh
token. I don't have any use for that in my application.
Django OAuth Toolkit depends on OAuthLib.
Making refresh_token optional boils down to create_token method in BearerToken class of oAuthLib at this line and the class for password grant is here. As you can see the __init__ method for this class takes refresh_token argument which by default is set to True. This value is used in create_token_response method of the same class at the line
token = token_handler.create_token(request, self.refresh_token)
create_token_response method in OAuthLibCore class of Django OAuth toolkit is the one, I believe, calls the corresponding create_token_response in OAuthLib. Observe the usage of self.server and its initialization in __init__ method of this class, which has just the validator passed as an argument but nothing related to refresh_token.
Compare this with OAuthLib Imlicit grant type's create_token_response method, which explicitly does
token = token_handler.create_token(request, refresh_token=False)
to not create refresh_token at all
So, unless I missed something here, tldr, I don't think Django OAuth toolkit exposes the feature of optional refresh_token.
Here's an example of just making it directly:
from oauthlib.common import generate_token
from oauth2_provider.models import AccessToken, Application
from django.utils import timezone
from dateutil.relativedelta import relativedelta
tok = generate_token()
app = Application.objects.first()
user = User.objects.first()
access_token = AccessToken.objects.create(user=user, application=app, expires=timezone.now() + relativedelta(hours=1), token=tok)
The accepted answer still fails to clear the RefreshToken. Below code should revoke both the refresh and access token.
from oauth2_provider.models import RefreshToken
def clear_token(user):
"""
Clear all user authorized tokens.
"""
for token in RefreshToken.objects.filter(user=user, revoked__isnull=True):
token.revoke()
I am using the Django Test client (django.test.Client) to run view tests. Upon attempting to use the Test Client on my index function that handles post requests for logins, it continually fails the test even though the authentication successfully occurs.
Heres my test:
def test_login(self):
response = self.client.post('/login/', {'username':'user', 'password':'pass'})
print response.content
self.assertIn(SESSION_KEY, self.client.session)
So the reason i know the login process successfully works is because response.content yields HTML data from another view that can only be access if request.user.is_authenticated() is true. In other words, they must be logged in for response.content to yield the "logged in page". So given this, i can tell that the function obviously works for its practical purpose of logging the user in, however, i've been scouring the docs for hours trying to figure out why i can't access SESSION_KEY from the client session. All my reading suggests that the django test client is in fact stateful in nature and should store the session.
Can someone shed some light on this?
Ok after much searching and asking around on #django, i made a working solution for Django 1.6.x
from django.contrib.auth import SESSION_KEY, get_user_model
from django.test import Client
def setUp(self):
self.client = Client()
def test_login_view(self):
user_pk = get_user_model()._default_manager.get(username__exact='test_username_here').pk
response = self.client.post('/login/', {'username':'test_username_here', 'password':'test_password_here'})
self.assertEqual(self.client.session[SESSION_KEY], user_pk)
The test_login_view function will be the one evaluating the view in my app that handles user logins from the template form. First, i grab user_pk which is the real primary key of the given user in the database. I used get_user_model() instead of User.objects.get() because the former allows you to reference regardless of whether the User model is modified or not. Of course you can use the latter as well. Second, i go ahead and send the post request using the test client just like a standard user's browser would. Finally, i discovered that self.client.session[SESSION_KEY] contains the primary key of the logged in user. (If the login was successful, otherwise, it will simply yield a KeyError)
I'm completely new to testing in Django. I have started by installing nose and selenium and now I want to test the following code (below) It sends an SMS message.
This is the actual code:
views.py
#login_required
def process_all(request):
"""
I process the sending for a single or bulk message(s) to a group or single contact.
:param request:
"""
#If we had a POST then get the request post values.
if request.method == 'POST':
batches = Batch.objects.for_user_pending(request.user)
for batch in batches:
ProcessRequests.delay(batch)
batch.complete_update()
return HttpResponseRedirect('/reports/messages/')
So where do I start? This is what I have done so far...
1) created a folder called tests and added init.py.
2) created a new python file called test_views.py (I'm assuming that's correct).
Now, how do I go about writing this test?
Could someone show me with an example of how I write the test for the view above?
Thank you :)
First of all, you don't need selenium for testing views. Selenium is a tool for high-level in-browser testing - it's good and useful when you are writing UI tests simulating a real user.
Nose is a tool that makes testing easier by providing features like automatic test discovery, supplies a number of helper functions etc. The best way to integrate nose with your django project is to use django_nose package. All you have to do is to:
add django_nose to INSTALLED_APPS
define TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
Then, every time you run python manage.py test <project_name> nose will be used to run your tests.
So, speaking about testing this particular view, you should test:
login_required decorator work - in other words, unauthenticated user will be redirected to the login page
if request.method is not POST, no messages sent + redirect to /reports/messages
sending SMS messages when POST method is used + redirect to /reports/messages
Testing first two statements is pretty straight-forward, but in order to test the last statement you need to provide more details on what is Batch, ProcessRequests and how does it work. I mean, you probably don't want to send real SMS messages during testing - this is where mocking will help. Basically, you need to mock (replace with your own implementation on the fly) Batch, ProcessRequests objects. Here's an example of what you should have in test_views.py:
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.test.client import Client
from django.test import TestCase
class ProcessAllTestCase(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user('john', 'lennon#thebeatles.com', 'johnpassword')
def test_login_required(self):
response = self.client.get(reverse('process_all'))
self.assertRedirects(response, '/login')
def test_get_method(self):
self.client.login(username='john', password='johnpassword')
response = self.client.get(reverse('process_all'))
self.assertRedirects(response, '/reports/messages')
# assert no messages were sent
def test_post_method(self):
self.client.login(username='john', password='johnpassword')
# add pending messages, mock sms sending?
response = self.client.post(reverse('process_all'))
self.assertRedirects(response, '/reports/messages')
# assert that sms messages were sent
Also see:
https://docs.djangoproject.com/en/dev/topics/testing/
Django Testing Guide
Getting Started with Python Mock
Hope that helps.
I have formulated test cases in Django framework.
Use Case:
I am using API that register user by sending them an Email and when they click on the link provided in the Email their account get activated.
In my settings.py I am using
EMAIL_FILE_PATH ='django.core.mail.backends.filebased.EmailBackend'
which points to the local directory.
When running PyUnit test case from eclipse everything works file. Text file gets generated for each email sent
But, When i am using
python ./manage.py test <component_name>
the files does not generate.
Any insight what is the difference when I execute test case with ./manage.py and when I use pyUnit ?
It's possible to overwrite this aspect in Django if you want to use a specific email backend.
In django.test.utils, Django will change the e-mail backend to locmem as mentioned in the Django Testing documentation when Django sets up the testing environment:
def setup_test_environment():
...
mail.original_email_backend = settings.EMAIL_BACKEND
settings.EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'
So if you want to enable sending e-mails for a test, you just need to change the setting to what you want.
from django.test.utils import override_settings
#override_settings(EMAIL_BACKEND='django.core.mail.backends.filebased.EmailBackend')
class MyTest(TestCase):
# your test case
The simple answer:
You can't do this without engineering your own email system, but that would probably be silly. I would suggest doing something else to verify that the code was successful without requiring the email to be sent. Like, run the code, assume the user clicks the link and create RequestFactory to get/post the link to run the view code associated with it.
From the Django Testing Application:
Email services
"If any of your Django views send email using Django's email functionality,
you probably don't want to send email each time you run a test using that
view. For this reason, Django's test runner automatically redirects all
Django-sent email to a dummy outbox. This lets you test every aspect of
sending email -- from the number of messages sent to the contents of each
message -- without actually sending the messages."
For somebody (like me) that need to use custom email backend for all tests, another solution would be to override TestRunner class and force settings change.
from django.conf import settings
from django.test.runner import DiscoverRunner
class CustomTestRunner(DiscoverRunner):
def setup_test_environment(self, **kwargs):
super().setup_test_environment(**kwargs)
settings.EMAIL_BACKEND = 'path.to.your.email.backend'
And after that register the test runner in settings:
TEST_RUNNER = 'path.to.CustomTestRunner'
I'm using a web service backend to provide authentication to Django, and the get_user method must retain a cookie provided by the web service in order to associate with a session. Right now, I make my remote calls just by calling urllib2.urlopen(myTargetService) but this doesn't pass the cookie for the current session along.
I have created a session access middleware to store the session in the settings:
class SessionAccessMiddleware:
def process_request(self, request):
settings.current_session = request.session
So, I can access the request session in get_request and post_request, but I don't know how to have urllib2 remember my cookies in a session-specific way.
How do I do this?
Here: http://docs.python.org/library/cookielib.html#examples are examples of doing exactly what you try to do with urllib2 and cookielib. So according to docs you need to create cookielib.CookieJar, set cookie with correct data (from session), build an opener that uses your CookieJar and use it to fetch yourTargetService.
If settings in your middleware code means from django.conf import settings it's not good idea. Look at http://github.com/svetlyak40wt/django-globals/ for a place where you can safely store request-wide data for access from somewhere where request object is unaccessible. Also, it would be probably good idea to write custom authentication backend and use it with django.contrib.auth - instead of rolling your own auth system from scratch - which is covered here: http://docs.djangoproject.com/en/dev/topics/auth/#writing-an-authentication-backend .