def test_success(): store = {} def do_callback(message): assert message['To'] == '*****@*****.**' url = message.get_payload().strip() args = parse_args(url) assert url.startswith('http://example/cb/') result = handler.check_callback(url, parse_args(url), {}) LOGGER.info('check_callback(%s,%s): %s', url, args, result) assert isinstance(result, disposition.Verified) store['result'] = result store['is_done'] = result.identity handler = email_addr.EmailAddress(do_callback, 'some data', tokens.DictStore(store), email_template_text='{url}') result = handler.initiate_auth('mailto:[email protected]', 'http://example/cb/', '/redir') LOGGER.info('initiate_auth: %s', result) assert isinstance(result, disposition.Notify) assert result.cdata == 'some data' assert store['result'].identity == 'mailto:[email protected]' assert store['result'].redir == '/redir'
def test_please_wait(mocker): token_store = tokens.DictStore() pending = {} mock_send = mocker.MagicMock() handler = email_addr.EmailAddress(mock_send, "this is data", token_store, expires_time=60, pending_storage=pending) mock_time = mocker.patch('time.time') assert mock_send.call_count == 0 mock_time.return_value = 10 # First auth should call mock_send handler.initiate_auth('mailto:[email protected]', 'http://example/', 'blop') assert mock_send.call_count == 1 assert '*****@*****.**' in pending token_value = pending['*****@*****.**'] # Second auth should not handler.initiate_auth('mailto:[email protected]', 'http://example/', 'blop') assert mock_send.call_count == 1 assert '*****@*****.**' in pending assert token_value == pending['*****@*****.**'] # Using the link should remove the pending item handler.check_callback('http://example/', {'t': pending['*****@*****.**']}, {}) assert '*****@*****.**' not in pending # Next auth should call mock_send again handler.initiate_auth('mailto:[email protected]', 'http://example/', 'blop') assert mock_send.call_count == 2 assert '*****@*****.**' in pending assert token_value != pending['*****@*****.**'] token_value = pending['*****@*****.**'] # Timing out the token should cause it to send again mock_time.return_value = 1000 handler.initiate_auth('mailto:[email protected]', 'http://example/', 'blop') assert mock_send.call_count == 3 assert '*****@*****.**' in pending assert token_value != pending['*****@*****.**'] token_value = pending['*****@*****.**'] # And anything else that removes the token from the token_store should as well token_store.remove(pending['*****@*****.**']) handler.initiate_auth('mailto:[email protected]', 'http://example/', 'blop') assert mock_send.call_count == 4 assert token_value != pending['*****@*****.**'] token_value = pending['*****@*****.**']
def test_basics(): handler = email_addr.EmailAddress(None, None, tokens.DictStore()) assert handler.service_name == 'Email' assert handler.url_schemes assert 'email' in handler.description assert handler.cb_id == 'e' assert handler.logo_html[0][1] == 'Email' assert handler.handles_url('*****@*****.**') == 'mailto:[email protected]' assert handler.handles_url('mailto:[email protected]') == 'mailto:[email protected]' # email addresses must be well-formed assert not handler.handles_url('mailto:foobar.baz') # don't support other schemas assert not handler.handles_url('email:[email protected]') assert not handler.handles_url('@[email protected]') assert not handler.handles_url('https://example.com/') # handle leading/trailing spaces correctly assert handler.handles_url(' [email protected]') == 'mailto:[email protected]' assert handler.handles_url('mailto: [email protected]') == 'mailto:[email protected]' assert handler.handles_url('mailto:[email protected] ') == 'mailto:[email protected]' # but don't allow embedded spaces assert not handler.handles_url(' foo @bar.baz') # email address must be valid assert not handler.handles_url(' asdf[]@poiu_foo.baz!') # don't allow bang-paths assert not handler.handles_url('bang!path!is!fun!bob') assert not handler.handles_url('bang.com!path!is!fun!bob') assert not handler.handles_url('[email protected]') # strip out non-email-address components assert handler.handles_url( 'mailto:[email protected]?subject=pwned') == 'mailto:[email protected]' # handle case correctly assert handler.handles_url( 'MailtO:[email protected]') == 'mailto:[email protected]'
def test_failures(mocker): store = {} pending = {} def accept(message): url = message.get_payload().strip() pending[message['To']] = url handler = email_addr.EmailAddress(accept, 'some data', tokens.DictStore(store), 10, email_template_text='{url}') # must be well-formed mailto: URL for malformed in ('*****@*****.**', 'http://foo.bar/', 'mailto:blahblahblah'): assert 'Malformed' in str( handler.initiate_auth(malformed, 'http://example.cb/', '/malformed')) # check for missing or invalid tokens assert 'Missing token' in str(handler.check_callback('foo', {}, {})) assert 'Invalid token' in str( handler.check_callback('foo', {'t': 'bogus'}, {})) def initiate(addr, redir): result = handler.initiate_auth('mailto:' + addr, 'http://example/', redir) assert isinstance(result, disposition.Notify) assert result.cdata == 'some data' def check_pending(addr): url = pending[addr] return handler.check_callback(url, parse_args(url), {}) # check for timeout failure mock_time = mocker.patch('time.time') mock_time.return_value = 30 assert len(store) == 0 initiate('*****@*****.**', '/timeout') assert len(store) == 1 mock_time.return_value = 20000 result = check_pending('*****@*****.**') assert isinstance(result, disposition.Error) assert 'timed out' in result.message assert result.redir == '/timeout' assert len(store) == 0 # check for replay attacks assert len(store) == 0 initiate('*****@*****.**', '/replay') assert len(store) == 1 result1 = check_pending('*****@*****.**') result2 = check_pending('*****@*****.**') assert len(store) == 0 assert isinstance(result1, disposition.Verified) assert result1.identity == 'mailto:[email protected]' assert result1.redir == '/replay' assert isinstance(result2, disposition.Error) assert 'Invalid token' in str(result2)