Jul 21, 2012

Django: Resetting Passwords (with internal tools)

I have had a task recently. It was about adding a forms/mechanism for resetting a password in our Django based project. We have had our own registration system ongoing... It's a corporate sector project. So you can not go and register yourself. Admins (probably via LDAP sync) will register your email/login in system. So you have to go there and only set yourself a password. For security reasons you can not register. One word.

First I've tried to find standart decision. From reviewed by me were: django-registration and django password-reset. These are nice tools to install and give it a go. But I've needed a more complex decision. And the idea was that own bicycle is always better. So I've thought of django admin and that it has all the things you need to do this yourself in no time. (Actually it's django.contrib.auth part of django, but used out of the box in Admin UI) You can find views you need for this in there. they are:
  • password_reset
  • password_reset_done 
  • password_reset_confirm 
  • password_reset_complete 
To use this method you need:

1. Emails config:

Your typical Django project has settings.py variables to be set for enabling SMTP server. Usually you need this on production to send admins emails about 500 errors and/or system warnings. For our needs python has an internal mail server. I prefer to use it for debug purposes. So good method would be to use it only in Debug case. You can do it by adding those lines to your settings.py:
if DEBUG:
    EMAIL_HOST = 'localhost'
    EMAIL_PORT = 1025
    EMAIL_HOST_USER = ''
    EMAIL_HOST_PASSWORD = ''
    EMAIL_USE_TLS = False
    DEFAULT_FROM_EMAIL = 'testing@example.com'
Here you will have this custom email server set to alternative port. For production you should enter your Sendmail (or whatever you use there) parameters...

Now you will be able to start it running the terminal and executing:
python -m smtpd -n -c DebuggingServer localhost:1025
You can also read abut it here. You will be able to copy/paste email links that should be sent via Django email. Now that you have everything setup for experiments...

2. Config Url's:

urls.py
urlpatterns = patterns('',
    url(r'^user/password/reset/$', 
        'django.contrib.auth.views.password_reset', 
        {'post_reset_redirect' : '/user/password/reset/done/'},
        name="password_reset"),
    (r'^user/password/reset/done/$',
        'django.contrib.auth.views.password_reset_done'),
    (r'^user/password/reset/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$', 
        'django.contrib.auth.views.password_reset_confirm', 
        {'post_reset_redirect' : '/user/password/done/'}),
    (r'^user/password/done/$', 
        'django.contrib.auth.views.password_reset_complete'),
    # ...
)

3. Add Templates:

You have templates for your reset password forms already. Django admin templates/css/js are used there. But I think you would want custom one's for your needs. SO basically all you may need is overriding those templates, specified down here and place them on your templates Django path.
You need to override:
Django templates:

  • registration/password_reset_form.html
  • registration/password_reset_done.html
  • registration/password_reset_confirm.html
  • registration/password_reset_complete.html

Email template:

  • registration/password_reset_email.html 

I'll write down here one's I've used, according to Django's "Batteries included" philosophy.

registration/password_reset_form.html 
{% extends "base.html" %}

{% block title %}Reset Password{% endblock %}

{% block content %}
<p>Please specify your email address to receive instructions for resetting it.</p>

<form action="" method="post">
    <div style="display:none">
        <input type="hidden" value="{{ csrf_token }}" name="csrfmiddlewaretoken">
    </div>
     {{ form.email.errors }}
    <p><label for="id_email">E-mail address:</label> {{ form.email }} <input type="submit" value="Reset password" /></p>
</form>
{% endblock %}

registration/password_reset_done.html 
{% extends "base.html" %}

{% block title %}Password reset successful{% endblock %}

{% block content %}
<p>We've e-mailed you instructions for setting your password to the e-mail address you submitted.</p>
<p>You should be receiving it shortly.</p>
{% endblock %}

registration/password_reset_confirm.html 
{% extends "base.html" %}
{% block title %}Setting New password{% endblock %}

{% block content %}
    {% if validlink %}
        <p>Please enter your new password twice.<br />
           So we can verify you typed it in correctly.</p>
        <form action="" method="post">
            <div style="display:none">
                <input type="hidden" value="{{ csrf_token }}" name="csrfmiddlewaretoken">
            </div>
            <table>
                <tr>
                    <td>{{ form.new_password1.errors }}
                        <label for="id_new_password1">New password:</label></td>
                    <td>{{ form.new_password1 }}</td>
                </tr>
                <tr>
                    <td>{{ form.new_password2.errors }}
                        <label for="id_new_password2">Confirm password:</label></td>
                    <td>{{ form.new_password2 }}</td>
                </tr>
                <tr>
                    <td></td>
                    <td><input type="submit" value="Change my password" /></td>
                </tr>
            </table>
        </form>
    {% else %}
        <h1>Password reset unsuccessful</h1>
        <p>The password reset link was invalid, <br />
        possibly because it has already been used. <br />
        Please request a new password reset.</p>
    {% endif %}
{% endblock %}

registration/password_reset_complete.html 
{% extends "base.html" %}

{% block title %}Password reset complete{% endblock %}

{% block content %}
<p>Your password has been set.  You may go ahead and log in now.</p>
<p><a href="{{ login_url }}">Log in</a></p>
{% endblock %}

registration/password_reset_email.html 
{% autoescape off %}
You're receiving this e-mail because you requested a password reset for your user account at {{ site_name }}.

Please go to the following page and choose a new password:
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url django.contrib.auth.views.password_reset_confirm uidb36=uid, token=token %}
{% endblock %}

Your username, in case you've forgotten: {{ user.username }}

Thanks for using our site!

The {{ site_name }} team.

{% endautoescape %}

SO you're getting password reset system out of the box using only Django included tools. In no time.
Comment me...!





Jul 15, 2012

Django: Unittest for HttpResponseRedirect method recipe

I like to invent bicycles in my code. They often come in handy and you basically do not rely on Django version... So upon updating your project's Django version in future times you will not have to refactor half of all the code. So...

How do you test http redirects? I usually restrict my time in thinking and write code like if redirect target is hardcoded. And it really is in most of the times. But imagine if you decide to redirect your view response in several places. Or imagine if you're not the only one who owns you'r project's code and there are many collaborators that can change things without warning... And you need to have a test for that view that will test redirects and complex code behavior.

I used to check redirects like so:
response = self.cliet.get('/myview')
self.assertEqual(response.status_code, 302)
# View redirected... All ok...
But  if you have some view that can redirect to things that you need to check. How would you test that?  E.g. your view:
import random

def myview(request):
    url_end = random.choice([1,2,3,4])
    url = '/someplace/' + str(url_end)
    return HttpResponseRedirect(url)
It can produce many urls and you basically can not predict which one it will produce this time. Maybe it's not the best example. However it's quite simple and understandable IMHO. You're view can redirect you to:
/someplace/1
/someplace/2
...
Imagine if your program results may depend on that and you must check if the page rendering is properly relayed on that URL that you're redirecting.

How would you check that without relying much into Django variables and producing hacks? Exactly! You name it... Regexp's:
class MyTestCase(TestCase)

    def setUp(self):
        # We are using only logged in client in this test
        self.client.login(username=username, password=password)

    def test_mytest(self):
        response = self.client.get(reverse('myview'))
        # Following redirect in tests
        new_url = self._retrieve_redirect_response_url(response)
        response = self.client.get(new_url)
        self.assertEqual(response.status_code, 200)
        # No errors appeared
 
    def _retrieve_redirect_response_url(self, response):
        """
        My handy helper:
        
        parses 302 response object.
        Returns redirect url, parsed by regex.
        """
        self.assertEqual(response.status_code, 302)
        new_url = re.search(
                            # Our REGEXP:
                            "(?P<url>https?://[^\s]+)", 
                            str(response)).group("url"
                            )
        return new_url
A lot of third party logic exists in this test. But it's partially a live tests snippet with names changed. So you may assume it should be like so and concentrate your thoughts on this approach in general, rather than possible inconsistencies.

Main deed does regexp: "(?P<url>https?://[^\s]+)". Other code is only possible solution to use it in a handy way...  

Anyway, now you can use this snippet in your code to check url that view redirected you to. Only concern that I have here that there are much simpler approaches. But this one is almost Django updates independent. It stringifies your response and parses for url there.

If you want to know about re.search more: http://docs.python.org/howto/regex.html
Writing tests in general: https://docs.djangoproject.com/en/dev/topics/testing/?from=olddocs
Hope this will help someone. Comment your ideas on this! Appreciate your every thought...

Jul 9, 2012

Blocking iOS to acces Contacts freely


There are lots of occasions when you want to be private in the net. Now it's common to say "Anonymous". Many people do not even think it's important. I, personaly, dislike facebook, skype, twitter, any else third party... having my contacts list synched to their server without even telling me... I do not want to be truly anonymous, you know... Have nothing to hide really. But I want that really smal confirm that Skype, for e.g. would take my contacts and for what reason. 

Facebook was first, as usual... They've done it. I really deleted their app from my iOS devices first. But instead of taking lessons from their mistakes... All huge companies gone wild about this possibility to copy some new ammount of user data... Skype was second (for me)... Then LinkedIn... However they warned and I've agreed... So nothing really bad to say about them. But MS is such a Microsoft... They want me to "skype" instead of "calling" my friends through my Apple/Android device :)...

Anyway I've decided I really need something like a hardware switch to grant access to my contact list on iDevices... And it is really done for us already...

You need a Jailbreak for this! (As I had one already wont mention it here. I think there are lots of info in the net about it)

ContactPrivacy tweak. For now, that I'm writing this article... It exists in BigBoss repo, that is default set in Cydia AFAIK. And you suppose to have access to it without adding any repositories. So typing in Cydia search: "ContactPrivacy" (without scopes) should reveal it without any problems.

After installing it you will probably see something like this:
So ...... you Skype after installing it. :)

Note: That I think it's good idea to install this tweak even before any other APP is installed. I'd do so if I only knew before.

Heard somewhere about Twitter is going to have my contacts too... Wtf? Why Twitter wants them too... Can't even imagine... Who's next. "Corporation of all good". Nahh... I think they already have them but not so dumb to tell >:) But somehow it does disturb me less...

BTW it's a nice reason to make a jailbreak for this tweak. Why not make it standard in iOS?
How do you think?