Notebook Registration Challenges¶
This module includes support for challenge-response tests posed to users registering for new Sage notebook accounts. These Completely Automated Public Turing tests to tell Computers and Humans Apart, or CAPTCHAs, may be simple math questions, requests for special registration codes, or reCAPTCHAs.
AUTHORS:
- reCAPTCHA is written by Ben Maurer and maintained by Josh
Bronson. It is licensed under a MIT/X11 license. The reCAPTCHA
challenge implemented in
reCAPTCHAChallenge
is adapted from this Python API, which is also available here.
-
class
sagenb.notebook.challenge.
AbstractChallenge
(conf, **kwargs)¶ Bases:
object
An abstract class with a suggested common interface for specific challenge-response schemes.
-
html
(**kwargs)¶ Returns HTML for the challenge, e.g., to insert into a new account registration page.
INPUT:
kwargs
- a dictionary of keywords arguments
OUTPUT:
- a string; HTML form representation of the challenge, including a field for the response, supporting hidden fields, JavaScript code, etc.
TESTS:
sage: from sagenb.notebook.challenge import AbstractChallenge sage: tmp = tmp_dir(ext='.sagenb') sage: import sagenb.notebook.notebook as n sage: nb = n.Notebook(tmp) sage: chal = AbstractChallenge(nb.conf()) sage: chal.html() Traceback (most recent call last): ... NotImplementedError
-
is_valid_response
(**kwargs)¶ Returns the status of a challenge response.
INPUT:
kwargs
- a dictionary of keyword arguments
OUTPUT:
- a
ChallengeResponse
instance
TESTS:
sage: from sagenb.notebook.challenge import AbstractChallenge sage: tmp = tmp_dir(ext='.sagenb') sage: import sagenb.notebook.notebook as n sage: nb = n.Notebook(tmp) sage: chal = AbstractChallenge(nb.conf()) sage: chal.is_valid_response() Traceback (most recent call last): ... NotImplementedError
-
-
class
sagenb.notebook.challenge.
ChallengeDispatcher
(conf, **kwargs)¶ Bases:
object
A simple dispatcher class that provides access to a specific challenge.
-
class
sagenb.notebook.challenge.
ChallengeResponse
(is_valid, error_code=None)¶ Bases:
object
A simple challenge response class that indicates whether a response is empty, correct, or incorrect, and, if it’s incorrect, includes an optional error code.
-
class
sagenb.notebook.challenge.
NotConfiguredChallenge
(conf, **kwargs)¶ Bases:
sagenb.notebook.challenge.AbstractChallenge
A fallback challenge used when an administrator has not configured a specific method.
-
html
(**kwargs)¶ Returns a suggestion to inform the Notebook server’s administrator about the misconfigured challenge.
INPUT:
conf
- aServerConfiguration
; an instance of the server’s configurationkwargs
- a dictionary of keyword arguments
OUTPUT:
- a string
TESTS:
sage: from sagenb.notebook.challenge import NotConfiguredChallenge sage: tmp = tmp_dir(ext='.sagenb') sage: import sagenb.notebook.notebook as n sage: nb = n.Notebook(tmp) sage: chal = NotConfiguredChallenge(nb.conf()) sage: print(chal.html()) Please ask the server administrator to configure a challenge!
-
is_valid_response
(**kwargs)¶ Always reports a failed response, for the sake of security.
INPUT:
kwargs
- a dictionary of keyword arguments
OUTPUT:
- a
ChallengeResponse
instance
TESTS:
sage: from sagenb.notebook.challenge import NotConfiguredChallenge sage: tmp = tmp_dir(ext='.sagenb') sage: import sagenb.notebook.notebook as n sage: nb = n.Notebook(tmp) sage: chal = NotConfiguredChallenge(nb.conf()) sage: chal.is_valid_response().is_valid False sage: chal.is_valid_response().error_code ''
-
-
class
sagenb.notebook.challenge.
SimpleChallenge
(conf, **kwargs)¶ Bases:
sagenb.notebook.challenge.AbstractChallenge
A simple question and answer challenge.
-
html
(**kwargs)¶ Returns a HTML form posing a randomly chosen question.
INPUT:
kwargs
- a dictionary of keyword arguments
OUTPUT:
- a string; the HTML form
TESTS:
sage: from sagenb.notebook.challenge import SimpleChallenge sage: tmp = tmp_dir(ext='.sagenb') sage: import sagenb.notebook.notebook as n sage: nb = n.Notebook(tmp) sage: chal = SimpleChallenge(nb.conf()) sage: chal.html() # random '...What is the largest prime factor of 1001?...'
-
is_valid_response
(req_args={}, **kwargs)¶ Returns the status of a user’s answer to the challenge question.
INPUT:
req_args
- a string:list dictionary; the arguments of the remote client’s HTTP POST requestkwargs
- a dictionary of extra keyword arguments
OUTPUT:
- a
ChallengeResponse
instance
TESTS:
sage: from sagenb.notebook.challenge import SimpleChallenge sage: tmp = tmp_dir(ext='.sagenb') sage: import sagenb.notebook.notebook as n sage: nb = n.Notebook(tmp) sage: chal = SimpleChallenge(nb.conf()) sage: req = {} sage: chal.is_valid_response(req).is_valid sage: chal.is_valid_response(req).error_code '' sage: from sagenb.notebook.challenge import QUESTIONS sage: ques, ans = sorted(QUESTIONS.items())[0] sage: ans = ans.split('|')[0] sage: print(ques) How many bits are in one byte? sage: print(ans) 8 sage: req['simple_response_field'] = ans sage: chal.is_valid_response(req).is_valid False sage: chal.is_valid_response(req).error_code '' sage: req['simple_challenge_field'] = ques sage: chal.is_valid_response(req).is_valid True sage: chal.is_valid_response(req).error_code ''
-
-
sagenb.notebook.challenge.
agree
(response, answer)¶ Returns whether a challenge response agrees with the answer.
INPUT:
response
- a string; the user’s response to a posed challengeanswer
- a string; the challenge’s right answer as a regular expression
OUTPUT:
- a boolean; whether the response agrees with the answer
TESTS:
sage: from sagenb.notebook.challenge import agree sage: agree('0', r'0|zero') True sage: agree('eighty', r'8|eight') False
-
sagenb.notebook.challenge.
challenge
(conf, **kwargs)¶ Wraps an instance of
ChallengeDispatcher
and returns an instance of a specific challenge.INPUT:
conf
- aServerConfiguration
; a server configuration instancekwargs
- a dictionary of keyword arguments
OUTPUT:
- an instantiated subclass of
AbstractChallenge
TESTS:
sage: from sagenb.notebook.challenge import challenge sage: tmp = tmp_dir(ext='.sagenb') sage: import sagenb.notebook.notebook as n sage: nb = n.Notebook(tmp) sage: nb.conf()['challenge_type'] = 'simple' sage: chal = challenge(nb.conf()) sage: chal.html() # random '<p>...'
-
class
sagenb.notebook.challenge.
reCAPTCHAChallenge
(conf, remote_ip='', is_secure=False, lang='en', **kwargs)¶ Bases:
sagenb.notebook.challenge.AbstractChallenge
A reCAPTCHA challenge adapted from this Python API, also hosted here, written by Ben Maurer and maintained by Josh Bronson.
-
html
(error_code=None, **kwargs)¶ Returns HTML and JavaScript for a reCAPTCHA challenge and response field.
INPUT:
error_code
- a string (default: None); an optional error code to embed in the HTML, giving feedback about the user’s previous responsekwargs
- a dictionary of extra keyword arguments
OUTPUT:
- a string; HTML and JavaScript to render the reCAPTCHA challenge
TESTS:
sage: from sagenb.flask_version import base # random output -- depends on warnings issued by other sage packages sage: app = base.create_app(tmp_dir(ext='.sagenb')) sage: ctx = app.app_context() sage: ctx.push() sage: nb = base.notebook sage: from sagenb.notebook.challenge import reCAPTCHAChallenge sage: chal = reCAPTCHAChallenge(nb.conf(), remote_ip = 'localhost') sage: chal.html() u'...recaptcha...' sage: chal.html('incorrect-captcha-sol') u'...incorrect-captcha-sol...'
-
is_valid_response
(req_args={}, **kwargs)¶ Submits a reCAPTCHA request for verification and returns its status.
INPUT:
req_args
- a dictionary; the arguments of the responding user’s HTTP POST requestkwargs
- a dictionary of extra keyword arguments
OUTPUT:
- a
ChallengeResponse
instance; whether the user’s response is empty, accepted, or rejected, with an optional error string
TESTS:
sage: from sagenb.notebook.challenge import reCAPTCHAChallenge sage: tmp = tmp_dir(ext='.sagenb') sage: import sagenb.notebook.notebook as n sage: nb = n.Notebook(tmp) sage: chal = reCAPTCHAChallenge(nb.conf(), remote_ip = 'localhost') sage: req = {} sage: chal.is_valid_response(req).is_valid sage: chal.is_valid_response(req).error_code '' sage: req['recaptcha_response_field'] = ['subplotTimes'] sage: chal.is_valid_response(req).is_valid False sage: chal.is_valid_response(req).error_code 'incorrect-captcha-sol' sage: req['simple_challenge_field'] = ['VBORw0KGgoANSUhEUgAAAB'] sage: chal.is_valid_response(req).is_valid # random False sage: chal.is_valid_response(req).error_code # random 'incorrect-captcha-sol'
-