This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
plone.formwidget.captcha/plone/formwidget/captcha/browser/captcha.txt

207 lines
4.6 KiB
Plaintext

========
Captchas
========
Generation
----------
To use the captcha view, simply look up the view using the component architecture.
Here, we'll just import the view directly to demonstrate it's use:
>>> from plone.formwidget.captcha.browser.captcha import Captcha, COOKIE_ID
>>> request = DummyRequest()
>>> context = DummyContext()
>>> view = Captcha(context, request)
We can now use the view to generate an image tag, and a audio_url:
>>> view.image_tag()
'<img src="dummyurl/@@captcha/image" />'
>>> view.audio_url()
'dummyurl/@@captcha/audio'
The request now holds the state in a cookie:
>>> COOKIE_ID in request.response.cookies
True
Verification
------------
With that state, we can verify that the user has given us the correct word. The view
doesn't actually give us the word, but we can replay an existing session for the test:
>>> request = DummyRequest()
>>> request.cookies[COOKIE_ID] = '6552fec8867ee2a85a44784dda007e49efcf50ef'
>>> view = Captcha(context, request)
The verify method, then, tells you if the user entered the correct word:
>>> view.verify('DLXV4XV')
True
Note that the view immediately invalidates the cookie by expiring it:
>>> request.response.cookies[COOKIE_ID].get('expires', '')
'Wed, 31-Dec-97 23:59:59 GMT'
The verify method works case-insensitively:
>>> view.verify('dlxv4xv')
True
The words are valid for a 10 minute period, with a new word for the session every
5 minutes. Thus, the word for the previous 5 minute period should still be valid:
>>> view.verify('YMFRQWT')
True
Verification will fail for both incorrect input and a missing cookie:
>>> view.verify('incorrect')
False
>>> del request.other[COOKIE_ID]
>>> del request.cookies[COOKIE_ID]
>>> view.verify('DLXV4XV')
False
To facilitate displaying a new captcha when verification fails or validation
of a form fails for other reasons, the view makes sure to not expire the
cookie but to set a new value instead:
>>> request = DummyRequest()
>>> request.response.setCookie(COOKIE_ID, '6552fec8867ee2a85a44784dda007e49efcf50ef')
>>> view = Captcha(context, request)
>>> request.response.cookies[COOKIE_ID].get('value', '') == '6552fec8867ee2a85a44784dda007e49efcf50ef'
True
>>> view.audio_url()
'dummyurl/@@captcha/audio'
>>> request.response.cookies[COOKIE_ID].get('expires', False)
False
>>> request.response.cookies[COOKIE_ID].get('value', '') == '6552fec8867ee2a85a44784dda007e49efcf50ef'
False
Displaying
----------
The point of course is that the end-user gets to view the captcha image or listen to the
audio file:
>>> request = DummyRequest()
>>> request.cookies[COOKIE_ID] = '6552fec8867ee2a85a44784dda007e49efcf50ef'
>>> view = Captcha(context, request)
>>> image = view.image()
>>> image.startswith('\x89PNG')
True
>>> request.response.headers['content-type']
'image/png'
>>> audio = view.audio()
>>> audio.startswith('RIFF')
True
>>> request.response.headers['content-type']
'audio/wav'
Bugs
----
view.image() and view.audio() raise exceptions if there is no cookie
>>> request = DummyRequest()
>>> view = Captcha(context, request)
>>> view._session_id is None
True
>>> COOKIE_ID in request
False
>>> COOKIE_ID in request.response.cookies
False
>>> image = view.image()
>>> image.startswith('\x89PNG')
True
>>> request.response.headers['content-type']
'image/png'
After the fix, we get a new session id and cookie instead
>>> view._session_id is None
False
>>> COOKIE_ID in request
True
>>> COOKIE_ID in request.response.cookies
True
Same for audio
>>> request = DummyRequest()
>>> view = Captcha(context, request)
>>> audio = view.audio()
>>> audio.startswith('RIFF')
True
>>> request.response.headers['content-type']
'audio/wav'
>>> view._session_id is None
False
>>> COOKIE_ID in request
True
>>> COOKIE_ID in request.response.cookies
True
Now execute the "impossible" branch, session_id without cookie:
>>> request = DummyRequest()
>>> view = Captcha(context, request)
>>> view._session_id = 'foo'
>>> image = view.image()
>>> image.startswith('\x89PNG')
True
>>> request.response.headers['content-type']
'image/png'
>>> view._session_id
'foo'
>>> COOKIE_ID in request
True
>>> COOKIE_ID in request.response.cookies
True
Same for audio
>>> request = DummyRequest()
>>> view = Captcha(context, request)
>>> view._session_id = 'foo'
>>> audio = view.audio()
>>> audio.startswith('RIFF')
True
>>> request.response.headers['content-type']
'audio/wav'
>>> view._session_id
'foo'
>>> COOKIE_ID in request
True
>>> COOKIE_ID in request.response.cookies
True