niedziela, 2 sierpnia 2009

ReCaptcha w django w 10 minut

1. Ściągamy recaptcha-django - bardzo małą i przyjemną aplikację, która udostępnia funkcjonalność ReCaptcha w sposób, jakiego nie powstydziliby się twórcy django:

svn checkout http://recaptcha-django.googlecode.com/svn/trunk/ recaptcha-django-read-only


2. Przenosimy folder recaptcha_django do folderu z naszym projektem (niech to będzie mysite), dodajemy 'recaptcha_django' do INSTALLED_APPS w mysite/settings.py. Dodajemy także załączony middleware - do MIDDLEWARE_CLASSES w settings.py dopisujemy na końcu 'recaptcha_django.middleware.ReCaptchaMiddleware'. Ten mały middleware dodaje do POST-a odpowiednich żądań http informacje o ip klienta potrzebną recaptcha. Proponuję odpalić serwer deweloperski. Możesz mieć szczęście i zobaczyć, że brakuje jakiegoś modułu... Wtedy następny punkt jest dla Ciebie.

3. Nie na każdym serwerze jest zainstalowany pythonowy moduł recaptcha-client. Dlatego ściągamy go. Jedynym czego trzeba, by jakiś moduł był dostępny w pythonie, jest obecność tego modułu na ścieżce wyszukiwań modułów [wooow!...], która w przypadku django obejmuje główny katalog projektu (mysite/). Tak więc wrzucamy katalog recaptcha z rozpakowanego archiwum do mysite/.

4. Jeszcze raz odpalamy serwer developerski. Tym razem powinien wstać. Zabieramy się do wzbogacenia naszego formularza (form):


#mysite/forms.py
from django import forms

from recaptcha_django import *

class PortalRegistrationForm(forms.Form):
#tu jakieś mało istotne pola
#...
recaptcha = ReCaptchaField()


Odpalamy serwer, wchodzimy na stronę z naszą formą i widzimy, że brakuje jakichś dziwnych ustawień (RECAPTCHA_PUBLIC_KEY i RECAPTCHA_PRIVATE_KEY). Wpadamy na pomysł, żeby zarejestrować się na ReCaptcha. W nagrodę dostaniemy upragnione wartości zmiennych RECAPTCHA_PUBLIC_KEY i RECAPTCHA_PRIVATE_KEY, które wpisujemy do mysite/settings.py.

I tyle!

Jeśli ktoś chce mieć mniej standardową ReCapchę i chciałby przy okazji poznać kawałek django, to może czytać dalej:

[polecam aplikację django-regitration autorstwa James'a Bennett'a (a.k.a. ubernostrum)]

from registration.forms import RegistrationForm

class PortalRegistrationForm(RegistrationForm):
recaptcha = ReCaptchaField(
label='''Prove that you're a human:'''
#tak oto w django definiuje się niestandardowy opis dla pola formularza

widget=ReCaptchaWidget(
attrs={'theme': 'blackglass'}), #zmieniamy skórkę
# a tak podaje się atrybuty elementom HTMLa do których zostanie
# wyrenderowany formularz. Pisząc 'atrybuty' myślę tu [w przypadku
# standardowych Widget-ów django] o atrybutach HTML,
# np. rows dla textarea:
komentarz = forms.CharField(
widget=forms.Textarea(attrs={'rows': 3})) #krótki komentarz.
)



Naturalnym pytaniem jest: jakie są inne skórki dla ReCaptcha? Odpowiedź tutaj.

EDIT:
Niestety, RecaptchaField wyświetla ochydny error-code zamiast ładnego komunikatu o błędzie. Czytałem trochę ReCaptcha API, ale tam utrzymują, że kody błędów mogą się zmieniać i żeby się tego nie trzymać. Jako że domyślny widget captchy wyświetla komunikat o błędzie, starczy pozbyć się brzydkiego komunikatu o błędzie:

class PortalRegistrationForm(RegistrationForm):
#...jak wyżej...

def clean(self):
cleaned_data = super(PortalRegistrationForm, self).clean()
if 'recaptcha' in self._errors:
self._errors['recaptcha'] = forms.util.ErrorList([])
return cleaned_data
#jak piszą w dokumentacji, "ALWAYS return the cleaned_data" :)


Przeciążamy tutaj metodę odpowiadającą za sprawdzanie poprawnośći formularza tak, by zrobiła to, co zrobiłaby normalnie [super()], a potem zastępujemy listę błędów dla pola 'recaptcha' listą pustą. Krótki, nieinwazyjny, ale dalej hack.

środa, 13 maja 2009

ssh - automatyczne logowanie - bez generowania kluczy! [linux]

Jak widać bardzo szybko zacząłem używać tego bloga także do innych celów niż wspomniane notkę niżej ;) Jak wpisać hasło bez wpisywania hasła? :) Zwłaszcza, gdy nie ma możliwości użycia klucza publicznego? A no tak: Będzie potrzebny program expect:
sudo apt-get install expect

Jest to program (a właściwie język programowania...), który udaje przed innym programem (np. ssh) użytkownika wpisującego coś z klawiatury. Expect-a można używać tak jak innych języków skryptowych przy pomocy następującej magicznej pierwszej linijki:
#!/usr/bin/expect -f

Całość skryptu wygląda tak:
#!/usr/bin/expect -f

#powiedzieliśmy jaki program ma być interpreterem poleceń dla tego skryptu

spawn ssh twójLogin@twoj.serwer.do.zalogowania.org
#expect uruchomi program do pogawędki

sleep 2
#poczeka chwilkę

send twojeHasło\r
#"wpisze" hasło

interact
#przekaże sterowanie [klawiaturę] użytkownikowi

Ostatnia linijka jest potrzebna, żeby expect nie zakończył się po wykonaniu ostatniego polecenia (co spowodowałoby zamknięcie także naszego programu). Trzeba jeszcze tylko nadać uprawnienia uruchamiania (chmod u+x nazwa.skryptu) i można używać (wywołanie ./nazwa.skryptu zaloguje nas gdzie chcieliśmy).

W moim przypadku motywacją do takiej "gimnastyki" było automatyczne zalogowanie na serwerze w celu uzyskania i utrzymania dostępu internetu (który ma się u nas tak długo, jak jest się zalogowanym na serwerze...). Poza tym serwer nie służy absolutnie do niczego i niczego więcej też nie umożliwia - stąd niespecjalnie zainteresowany byłem dalszą interakcją. Najchętniej zamknąłbym okienko konsoli, w którym wykonałem powyższy skrypt - albo przynajmniej gdzieś schował. Tu z pomocą przychodzi screen.

Screen to emulator konsoli (a.k.a. terminala). Najważniejsze jego funkcje to - jak dla mnie - uruchomienie jakiegoś programu w emulowanym okienku konsoli i ukrycie go - odłączenie (detatch). Wtedy emulowany ekran konsoli (screen) działa sobie w tle, a prawdziwą konsolę, z której screen-a uruchomiliśmy możemy nawet zamknąć.

Grande finale: dodamy do ~/.bashrc ./bash_profile linijki, które automatycznie zalogują nas na serwerze po starcie systemu.
#tego używałem do tej pory
alias internet-recznie='ssh login@serwer'

#a tego używam teraz
alias internet='screen -S internet -d -m $HOME/.nazwa.skryptu'

# -S internet - ustawia nazwę sesji screen na 'internet'. Nieobowiązkowe, dla porządku.
# -d -m - utworzony screen jest od razu odłączany (detatched)
# $HOME/.nazwa.skryptu - skrypt umieściłem w katalogu domowym (dostępnym pod $HOME) i uczyniłem plikiem ukrytym (. na początku nazwy)

# wywołanie całości machinerii
internet

Ponieważ .bashrc jest uruchamiane przy zalogowaniu (czyli najczęściej przy starcie systemu), dobrze jest jeszcze dodać w skrypcie logującym odpowiednio długiego sleep-a, jeszcze przed "spawn ...".

EDIT:
Wbrew temu, co napisałem wcześniej, .bashrc jest uruchamiane w momencie otwarcie konsoli innej niż konsola logowania - czyli np. gdy otworzymy nowy terminal. Tak naprawdę chcielibyśmy wywołanie machinerii zrobić w jednym z plików: ~/.bash_profile, ~/.bash_login, ~/.profile - i to w tej kolejności. Gdy bash jest uruchamiany w konsoli logowania, wykona jeden ze wspomnianych skryptów właśnie w tej kolejności.

Jest jeszcze kilka rzeczy, które należy wiedzieć o screen:
- powrót do odłączonego okienka: screen -d [numer / nazwa sesji, o ile jest więcej niż jedna]
- odłączenie okienka [spoza lini komend]: [ctrl+a, d]
- zamknięcie okienka: [ctrl+a, K]

W następnej notce napiszę jak użyć ~/.bash_logout do wykonania jakiegoś polecenia w okienku podczas zamykania systemu :)

poniedziałek, 11 maja 2009