def test_put_tiddler_cache_fakey(): [os.unlink('.test_cache/%s' % x) for x in os.listdir('.test_cache')] http_caching = httplib2.Http('.test_cache') http = httplib2.Http() json = simplejson.dumps(dict(text='i fight for the users 2', tags=['tagone','tagtwo'], modifier='', modified='200803030303', created='200803030303')) response, content = http_caching.request('http://our_test_domain:8001/recipes/long/tiddlers/CashForCache', method='PUT', headers={'Content-Type': 'application/json'}, body=json) assert response['status'] == '204' etag_hash = sha('GUEST:application/json').hexdigest() assert response['etag'] == '"bag1/CashForCache/1;%s"' % etag_hash response, content = http_caching.request('http://our_test_domain:8001/recipes/long/tiddlers/CashForCache', method='GET', headers={'Accept': 'application/json'}) assert response['status'] == '200' etag_hash = sha('GUEST:application/json').hexdigest() assert response['etag'] == '"bag1/CashForCache/1;%s"' % etag_hash response, content = http.request('http://our_test_domain:8001/recipes/long/tiddlers/CashForCache', method='PUT', headers={'Content-Type': 'application/json'}, body=json) assert response['status'] == '204' etag_hash = sha('GUEST:application/json').hexdigest() assert response['etag'] == '"bag1/CashForCache/2;%s"' % etag_hash response, content = http_caching.request('http://our_test_domain:8001/recipes/long/tiddlers/CashForCache', method='PUT', headers={'Content-Type': 'application/json'}, body=json) assert response['status'] == '412'
def test_put_tiddler_via_recipe(): http = httplib2.Http() json = simplejson.dumps( dict( text="i fight for the users 2", tags=["tagone", "tagtwo"], modifier="", modified="200803030303", created="200803030303", ) ) response, content = http.request( "http://our_test_domain:8001/recipes/long/tiddlers/FantasticVoyage", method="PUT", headers={"Content-Type": "application/json"}, body=json, ) assert response["status"] == "204" etag_hash = sha("GUEST:application/json").hexdigest() assert response["etag"] == '"bag1/FantasticVoyage/1;%s"' % etag_hash url = response["location"] reponse, content = http.request(url, method="GET", headers={"Accept": "application/json"}) tiddler_dict = simplejson.loads(content) assert tiddler_dict["bag"] == "bag1" etag_hash = sha("GUEST:application/json").hexdigest() assert response["etag"] == '"bag1/FantasticVoyage/1;%s"' % etag_hash
def test_put_tiddler_cache_fakey(): [os.unlink(".test_cache/%s" % x) for x in os.listdir(".test_cache")] http_caching = httplib2.Http(".test_cache") http = httplib2.Http() json = simplejson.dumps( dict( text="i fight for the users 2", tags=["tagone", "tagtwo"], modifier="", modified="200803030303", created="200803030303", ) ) response, content = http_caching.request( "http://our_test_domain:8001/recipes/long/tiddlers/CashForCache", method="PUT", headers={"Content-Type": "application/json"}, body=json, ) assert response["status"] == "204" etag_hash = sha("GUEST:application/json").hexdigest() assert response["etag"] == '"bag1/CashForCache/1;%s"' % etag_hash response, content = http_caching.request( "http://our_test_domain:8001/recipes/long/tiddlers/CashForCache", method="GET", headers={"Accept": "application/json"}, ) assert response["status"] == "200" etag_hash = sha("GUEST:application/json").hexdigest() assert response["etag"] == '"bag1/CashForCache/1;%s"' % etag_hash response, content = http.request( "http://our_test_domain:8001/recipes/long/tiddlers/CashForCache", method="PUT", headers={"Content-Type": "application/json"}, body=json, ) assert response["status"] == "204" etag_hash = sha("GUEST:application/json").hexdigest() assert response["etag"] == '"bag1/CashForCache/2;%s"' % etag_hash response, content = http_caching.request( "http://our_test_domain:8001/recipes/long/tiddlers/CashForCache", method="PUT", headers={"Content-Type": "application/json"}, body=json, ) assert response["status"] == "412"
def test_cookie_set(): """ test that we get a cookie relating to the space we are in """ store = get_store(config) hostname = "foo.0.0.0.0:8080" user = User(u"f\u00F6o") user.set_password("foobar") store.put(user) user_cookie = get_auth(u"f\u00F6o", "foobar") response, content = http.request( "http://foo.0.0.0.0:8080/", method="GET", headers={"Cookie": 'tiddlyweb_user="******"' % user_cookie} ) assert response["status"] == "200", content time = datetime.utcnow().strftime("%Y%m%d%H") cookie = "csrf_token=%s:%s:%s" % ( time, user.usersign, sha("%s:%s:%s:%s" % (user.usersign, time, hostname, config["secret"])).hexdigest(), ) assert response["set-cookie"] == quote(cookie.encode("utf-8"), safe=".!~*'():=")
def validate_mapuser(tiddler, environ): """ If a tiddler is put to the MAPUSER bag clear out the tiddler and set fields['mapped_user'] to the current username. There will always be a current username because the create policy for the bag is set to ANY. """ if tiddler.bag == 'MAPUSER': try: user_cookie = environ['HTTP_COOKIE'] cookie = Cookie.SimpleCookie() cookie.load(user_cookie) cookie_value = cookie['tiddlyweb_secondary_user'].value secret = environ['tiddlyweb.config']['secret'] usersign, cookie_secret = cookie_value.rsplit(':', 1) except KeyError: raise InvalidTiddlerError('secondary cookie not present') if cookie_secret != sha('%s%s' % (usersign, secret)).hexdigest(): raise InvalidTiddlerError('secondary cookie invalid') if usersign != tiddler.title: raise InvalidTiddlerError('secondary cookie mismatch') store = environ['tiddlyweb.store'] # XXX this is a potentially expensive operation but let's not # early optimize if tiddler.title in (user.usersign for user in store.list_users()): raise InvalidTiddlerError('username exists') tiddler.text = '' tiddler.tags = [] tiddler.fields = {} tiddler.fields['mapped_user'] = environ['tiddlyweb.usersign']['name'] return tiddler
def send_entity(environ, start_response, entity): """ Send a bag or recipe out HTTP, first serializing to the correct type. """ username = environ['tiddlyweb.usersign']['name'] try: serialize_type, mime_type = get_serialize_type(environ) serializer = Serializer(serialize_type, environ) serializer.object = entity content = serializer.to_string() except NoSerializationError: raise HTTP415('Content type %s not supported' % mime_type) etag_string = '"%s"' % (sha(_entity_etag(entity) + encode_name(username) + encode_name(mime_type)).hexdigest()) start_response("200 OK", [('Content-Type', mime_type), ('ETag', etag_string), ('Vary', 'Accept')]) if isinstance(content, basestring): return [content] else: return content
def make_cookie(name, value, mac_key=None, path=None, expires=None, httponly=True, domain=None): """ Create a cookie string, optionally with a MAC, path and expires value. If ``expires`` is provided, its value should be in seconds. """ cookie = SimpleCookie() value = encode_name(value) if mac_key: secret_string = sha("%s%s" % (value, mac_key)).hexdigest() cookie[name] = "%s:%s" % (value, secret_string) else: cookie[name] = value if path: cookie[name]["path"] = path if expires: cookie[name]["max-age"] = expires if domain: cookie[name]["domain"] = domain output = cookie.output(header="").lstrip().rstrip() if httponly: output += "; httponly" return output
def test_cookie_set(): """ test that we get a cookie relating to the space we are in """ store = get_store(config) space = 'foo' make_fake_space(store, space) user = User('foo') user.set_password('foobar') store.put(user) user_cookie = get_auth('foo', 'foobar') response, _ = http.request( 'http://foo.0.0.0.0:8080/status', method='GET', headers={'Cookie': 'tiddlyweb_user="******"' % user_cookie}) assert response['status'] == '200' time = datetime.now().strftime('%Y%m%d%H') cookie = 'csrf_token=%s:%s:%s' % ( time, user.usersign, sha('%s:%s:%s:%s' % (user.usersign, time, space, config['secret'])).hexdigest()) assert response['set-cookie'] == cookie
def validate_mapuser(tiddler, environ): """ If a tiddler is put to the MAPUSER bag clear out the tiddler and set fields['mapped_user'] to the current username. There will always be a current username because the create policy for the bag is set to ANY. """ if tiddler.bag == 'MAPUSER': try: user_cookie = environ['HTTP_COOKIE'] cookie = Cookie.SimpleCookie() cookie.load(str(user_cookie)) cookie_value = cookie['tiddlyweb_secondary_user'].value secret = environ['tiddlyweb.config']['secret'] usersign, cookie_secret = cookie_value.rsplit(':', 1) except KeyError: raise InvalidTiddlerError('secondary cookie not present') if cookie_secret != sha('%s%s' % (usersign, secret)).hexdigest(): raise InvalidTiddlerError('secondary cookie invalid') if usersign != tiddler.title: raise InvalidTiddlerError('secondary cookie mismatch') store = environ['tiddlyweb.store'] # XXX this is a potentially expensive operation but let's not # early optimize if tiddler.title in (user.usersign for user in store.list_users()): raise InvalidTiddlerError('username exists') tiddler.text = '' tiddler.tags = [] tiddler.fields = {} tiddler.fields['mapped_user'] = environ['tiddlyweb.usersign']['name'] return tiddler
def test_no_cookie_sent(): """ Test no cookie is sent if one is already present """ store = get_store(config) space = 'foo' make_fake_space(store, space) user = User('foo') user.set_password('foobar') store.put(user) user_cookie = get_auth('foo', 'foobar') time = datetime.now().strftime('%Y%m%d%H') cookie = 'csrf_token=%s:%s:%s' % ( time, user.usersign, sha('%s:%s:%s:%s' % (user.usersign, time, space, config['secret'])).hexdigest()) response, _ = http.request( 'http://foo.0.0.0.0:8080/status', method='GET', headers={'Cookie': 'tiddlyweb_user="******"; %s' % (user_cookie, cookie)}) cookie = response.get('set-cookie') if cookie: assert 'csrf_token' not in cookie
def test_get_tiddler_revision_3(): http = httplib2.Http() response, content = http.request('http://our_test_domain:8001/bags/bag1/tiddlers/TestOne/revisions/3', method='GET') assert response['status'] == '200' etag_hash = sha('GUEST:text/html').hexdigest() assert response['etag'] == '"bag1/TestOne/3;%s"' % etag_hash
def test_validator_nonce_success(): """ test the validator directly ensure that it succeeds when the nonce passed in is correct """ store = get_store(config) username = '******' spacename = 'foo' secret = '12345' timestamp = datetime.now().strftime('%Y%m%d%H') nonce = '%s:%s:%s' % ( timestamp, username, sha('%s:%s:%s:%s' % (username, timestamp, spacename, secret)).hexdigest()) environ = { 'tiddlyweb.usersign': { 'name': username }, 'tiddlyweb.config': { 'secret': secret, 'server_host': { 'host': '0.0.0.0', 'port': '8080' } }, 'HTTP_HOST': 'foo.0.0.0.0:8080' } make_fake_space(store, spacename) csrf = CSRFProtector({}) result = csrf.check_csrf(environ, nonce) assert result == True
def _remotebag_key(environ, uri): """ Generate a tiddler title to use as the key in the REMOTEURI_BAG. """ server_host = environ.get('tiddlyweb.config', {}).get( 'server_host')['host'] return sha(uri + server_host).hexdigest()
def make_cookie(name, value, mac_key=None, path=None, expires=None, httponly=True, domain=None): """ Create a cookie string, optionally with a MAC, path and expires value. Expires is in seconds. """ cookie = Cookie.SimpleCookie() value = value.encode('utf-8') if mac_key: secret_string = sha('%s%s' % (value, mac_key)).hexdigest() cookie[name] = '%s:%s' % (value, secret_string) else: cookie[name] = value if path: cookie[name]['path'] = path if expires: cookie[name]['max-age'] = expires if domain: cookie[name]['domain'] = domain output = cookie.output(header='').lstrip().rstrip() if httponly: output += '; httponly' return output
def send_entity(environ, start_response, entity): """ Send a bag or recipe out HTTP, first serializing to the correct type. """ username = environ['tiddlyweb.usersign']['name'] try: serialize_type, mime_type = get_serialize_type(environ) serializer = Serializer(serialize_type, environ) serializer.object = entity content = serializer.to_string() except NoSerializationError: raise HTTP415('Content type %s not supported' % mime_type) etag_string = '"%s"' % (sha( _entity_etag(entity) + encode_name(username) + encode_name(mime_type)).hexdigest()) start_response("200 OK", [('Content-Type', mime_type), ('ETag', etag_string), ('Vary', 'Accept')]) if isinstance(content, basestring): return [content] else: return content
def _validate_tiddler_list(environ, tiddlers): """ Do Etag and Last modified checks on the collection of tiddlers. If ETag testing is done, no last modified handling is done, even if the ETag testing fails. If no 304 is raised, then just return last-modified and ETag for the caller to use in constructing its HTTP response. """ last_modified_string = http_date_from_timestamp(tiddlers.modified) last_modified = ('Last-Modified', last_modified_string) username = environ.get('tiddlyweb.usersign', {}).get('name', '') try: _, mime_type = get_serialize_type(environ) mime_type = mime_type.split(';', 1)[0].strip() except TypeError: mime_type = '' etag_string = '"%s:%s"' % (tiddlers.hexdigest(), sha('%s:%s' % (username.encode('utf-8'), mime_type)).hexdigest()) etag = ('Etag', etag_string) incoming_etag = check_incoming_etag(environ, etag_string) if not incoming_etag: # only check last modified when no etag check_last_modified(environ, last_modified_string) return last_modified, etag
def test_validator_nonce_success(): """ test the validator directly ensure that it succeeds when the nonce passed in is correct """ store = get_store(config) username = '******' spacename = 'foo' secret = '12345' timestamp = datetime.now().strftime('%Y%m%d%H') nonce = '%s:%s:%s' % (timestamp, username, sha('%s:%s:%s:%s' % (username, timestamp, spacename, secret)). hexdigest()) environ = { 'tiddlyweb.usersign': {'name': username}, 'tiddlyweb.config': { 'secret': secret, 'server_host': { 'host': '0.0.0.0', 'port': '8080' } }, 'HTTP_HOST': 'foo.0.0.0.0:8080' } make_fake_space(store, spacename) csrf = CSRFProtector({}) result = csrf.check_csrf(environ, nonce) assert result == True
def make_user(username, password): user = User(username) user.set_password(password) store.put(user) secret_string = sha('%s%s' % (username, config['secret'])).hexdigest() return secret_string
def test_no_cookie_sent(): """ Test no cookie is sent if one is already present """ store = get_store(config) space = 'foo' make_fake_space(store, space) user = User('foo') user.set_password('foobar') store.put(user) user_cookie = get_auth('foo', 'foobar') time = datetime.now().strftime('%Y%m%d%H') cookie = 'csrf_token=%s:%s:%s' % (time, user.usersign, sha('%s:%s:%s:%s' % (user.usersign, time, space, config['secret'])).hexdigest()) response, _ = http.request('http://foo.0.0.0.0:8080/status', method='GET', headers={ 'Cookie': 'tiddlyweb_user="******"; %s' % (user_cookie, cookie) }) cookie = response.get('set-cookie') if cookie: assert 'csrf_token' not in cookie
def test_cookie_set(): """ test that we get a cookie relating to the space we are in """ store = get_store(config) space = 'foo' make_fake_space(store, space) user = User('foo') user.set_password('foobar') store.put(user) user_cookie = get_auth('foo', 'foobar') response, content = http.request('http://foo.0.0.0.0:8080/status', method='GET', headers={ 'Cookie': 'tiddlyweb_user="******"' % user_cookie }) assert response['status'] == '200', content time = datetime.now().strftime('%Y%m%d%H') cookie = 'csrf_token=%s:%s:%s' % (time, user.usersign, sha('%s:%s:%s:%s' % (user.usersign, time, space, config['secret'])).hexdigest()) assert response['set-cookie'] == cookie
def _validate_tiddler_list(environ, tiddlers): """ Do Etag and Last modified checks on the collection of tiddlers. If ETag testing is done, no last modified handling is done, even if the ETag testing fails. If no 304 is raised, then just return last-modified and ETag for the caller to use in constructing its HTTP response. """ last_modified_string = http_date_from_timestamp(tiddlers.modified) last_modified = ('Last-Modified', last_modified_string) username = environ.get('tiddlyweb.usersign', {}).get('name', '') try: _, mime_type = get_serialize_type(environ) mime_type = mime_type.split(';', 1)[0].strip() except TypeError: mime_type = '' etag_string = '"%s:%s"' % (tiddlers.hexdigest(), sha('%s:%s' % (username, mime_type)).hexdigest()) etag = ('Etag', etag_string) incoming_etag = check_incoming_etag(environ, etag_string, last_modified=last_modified_string) if not incoming_etag: # only check last modified when no etag check_last_modified(environ, last_modified_string, etag=etag_string) return last_modified, etag
def check_password(self, candidate_password): """ Check the password for this user. Return true if correct. """ if self._password is None: return False crypted_thing = sha(candidate_password.strip().encode("utf-8")).hexdigest() return crypted_thing == self._password
def test_put_tiddler_txt_4(): http = httplib2.Http() encoded_body = text_put_body2.encode('utf-8') response, content = http.request('http://our_test_domain:8001/bags/bag1/tiddlers/TestOne', method='PUT', headers={'Content-Type': 'text/plain'}, body=encoded_body) assert response['status'] == '204' etag_hash = sha('GUEST:text/plain').hexdigest() assert response['etag'] == '"bag1/TestOne/4;%s"' % etag_hash
def test_get_tiddler_revision_3(): http = httplib2.Http() response, content = http.request( 'http://our_test_domain:8001/bags/bag1/tiddlers/TestOne/revisions/3', method='GET') assert response['status'] == '200' etag_hash = sha('GUEST:text/html').hexdigest() assert response['etag'] == '"bag1/TestOne/3;%s"' % etag_hash
def check_password(self, candidate_password): """ Check the password for this user. Return ``True`` if correct. """ if not self._password: return False crypted_thing = sha(candidate_password.strip()).hexdigest() return crypted_thing == self._password
def test_put_tiddler_via_recipe(): http = httplib2.Http() json = simplejson.dumps(dict(text='i fight for the users 2', tags=['tagone','tagtwo'], modifier='', modified='200803030303', created='200803030303')) response, content = http.request('http://our_test_domain:8001/recipes/long/tiddlers/FantasticVoyage', method='PUT', headers={'Content-Type': 'application/json'}, body=json) assert response['status'] == '204' etag_hash = sha('GUEST:application/json').hexdigest() assert response['etag'] == '"bag1/FantasticVoyage/1;%s"' % etag_hash url = response['location'] reponse, content = http.request(url, method='GET', headers={'Accept': 'application/json'}) tiddler_dict = simplejson.loads(content) assert tiddler_dict['bag'] == 'bag1' etag_hash = sha('GUEST:application/json').hexdigest() assert response['etag'] == '"bag1/FantasticVoyage/1;%s"' % etag_hash
def set_password(self, password): """ Set the password for this user. """ password = password.strip() # The null password or empty password string never auths if not password: return self._password = sha(password.strip()).hexdigest()
def test_get_sorted_tiddlers(): http = httplib2.Http() response, content = http.request('http://our_test_domain:8001/bags/bag0/tiddlers.json?sort=title', method='GET') etag_hash = sha('GUEST:application/json').hexdigest() assert response['status'] == '200' assert etag_hash in response['etag'] tiddlers = simplejson.loads(content) assert tiddlers[0]['title'] == 'tiddler0'
def test_get_tiddler_etag_bag(): http = httplib2.Http() response, content = http.request("http://our_test_domain:8001/bags/bag28/tiddlers/tiddler8.json", method="GET") etag_hash = sha("GUEST:application/json").hexdigest() assert response["status"] == "200" assert response["etag"] == '"bag28/tiddler8/1;%s"' % etag_hash tiddler_info = simplejson.loads(content) assert tiddler_info["bag"] == "bag28"
def gen_nonce(username, hostname, timestamp, secret): """ generate a hash suitable for using as a nonce. the hash is: timestamp:username:hash(user:time:hostname:secret) """ return '%s:%s:%s' % (timestamp, username, sha('%s:%s:%s:%s' % (username, timestamp, hostname, secret)).hexdigest())
def test_cookie_mac(): string = make_cookie('test4', 'alpha4', mac_key='secret') secret_string = sha('%s%s' % ('alpha4', 'secret')).hexdigest() if sys.version_info[0] == 2: # Cookie changed assert string == 'test4="alpha4:%s"; httponly' % secret_string else: assert string == 'test4=alpha4:%s; httponly' % secret_string
def gen_nonce(username, spacename, timestamp, secret): """ generate a hash suitable for using as a nonce. the hash is: timestamp:username:hash(user:time:space:secret) """ return '%s:%s:%s' % (timestamp, username, sha('%s:%s:%s:%s' % (username, timestamp, spacename, secret)). hexdigest())
def test_post_data_multipart_form(): """ test that a form POST requires a nonce test using multipart/form-data """ store = get_store(config) hostname = "foo.0.0.0.0:8080" user = User(u"f\u00F6o") user.set_password("foobar") store.put(user) timestamp = datetime.utcnow().strftime("%Y%m%d%H") secret = config["secret"] nonce = "%s:%s:%s" % ( timestamp, user.usersign, sha("%s:%s:%s:%s" % (user.usersign, timestamp, hostname, secret)).hexdigest(), ) user_cookie = get_auth(u"f\u00F6o", "foobar") csrf_token = "csrf_token=%s" % nonce data = """---------------------------168072824752491622650073 Content-Disposition: form-data; name="title" foobar ---------------------------168072824752491622650073 Content-Disposition: form-data; name="text" Hello World ---------------------------168072824752491622650073--""" # test success uri = "http://foo.0.0.0.0:8080/bags/foo_public/tiddlers?%s" % csrf_token response, content = http.request( uri, method="POST", headers={ "Content-Type": "multipart/form-data; " "boundary=---------------------------168072824752491622650073", "Cookie": 'tiddlyweb_user="******"' % user_cookie, "Content-Length": "390", }, body=data, ) assert response["status"] == "204", content # test failure response, _ = http.request( "http://foo.0.0.0.0:8080/bags/foo_public/tiddlers", method="POST", headers={ "Content-Type": "multipart/form-data; " "boundary=---------------------------168072824752491622650073", "Cookie": 'tiddlyweb_user="******"' % user_cookie, "Content-Length": "267", }, body=data, ) assert response["status"] == "400"
def test_get_tiddler_etag_bag(): http = httplib2.Http() response, content = http.request('http://our_test_domain:8001/bags/bag28/tiddlers/tiddler8.json', method='GET') etag_hash = sha('GUEST:application/json').hexdigest() assert response['status'] == '200' assert response['etag'] == '"bag28/tiddler8/1;%s"' % etag_hash tiddler_info = simplejson.loads(content) assert tiddler_info['bag'] == 'bag28'
def _generate_secret(): """ create a pseudo-random secret This is used for message authentication. """ digest = sha(str(time())) digest.update(str(random())) digest.update("lorem foo ipsum") return digest.hexdigest()
def test_post_data_multipart_form(): """ test that a form POST requires a nonce test using multipart/form-data """ store = get_store(config) space = 'foo' make_fake_space(store, space) user = User('foo') user.set_password('foobar') store.put(user) timestamp = datetime.now().strftime('%Y%m%d%H') secret = config['secret'] nonce = '%s:%s:%s' % ( timestamp, user.usersign, sha('%s:%s:%s:%s' % (user.usersign, timestamp, space, secret)).hexdigest()) user_cookie = get_auth('foo', 'foobar') csrf_token = 'csrf_token=%s' % nonce data = '''---------------------------168072824752491622650073 Content-Disposition: form-data; name="title" foobar ---------------------------168072824752491622650073 Content-Disposition: form-data; name="text" Hello World ---------------------------168072824752491622650073--''' #test success uri = 'http://foo.0.0.0.0:8080/bags/foo_public/tiddlers?%s' % csrf_token response, content = http.request(uri, method='POST', headers={ 'Content-Type': 'multipart/form-data; ' \ 'boundary=---------------------------168072824752491622650073', 'Cookie': 'tiddlyweb_user="******"' % user_cookie, 'Content-Length': '390' }, body=data) print content assert response['status'] == '204' #test failure response, _ = http.request('http://foo.0.0.0.0:8080/bags/foo_public/tiddlers', method='POST', headers={ 'Content-Type': 'multipart/form-data; ' \ 'boundary=---------------------------168072824752491622650073', 'Cookie': 'tiddlyweb_user="******"' % user_cookie, 'Content-Length': '267' }, body=data) assert response['status'] == '400'
def test_get_tiddler_cached(): [os.unlink('.test_cache/%s' % x) for x in os.listdir('.test_cache')] http = httplib2.Http('.test_cache') response, content = http.request('http://our_test_domain:8001/bags/bag28/tiddlers/tiddler8', headers={'Accept': 'application/json'}, method='GET') assert not response.fromcache assert response['status'] == '200' etag_hash = sha('GUEST:application/json').hexdigest() assert response['etag'] == '"bag28/tiddler8/1;%s"' % etag_hash response, content = http.request('http://our_test_domain:8001/bags/bag28/tiddlers/tiddler8', headers={'Accept': 'application/json'}, method='GET') assert response.fromcache assert response['status'] == '304' etag_hash = sha('GUEST:application/json').hexdigest() assert response['etag'] == '"bag28/tiddler8/1;%s"' % etag_hash response, content = http.request('http://our_test_domain:8001/bags/bag28/tiddlers/tiddler8', headers={'Accept': 'text/html'}, method='GET') etag_hash = sha('GUEST:text/html').hexdigest() assert response['etag'] == '"bag28/tiddler8/1;%s"' % etag_hash assert not response.fromcache assert response['status'] == '200' response, content = http.request('http://our_test_domain:8001/bags/bag28/tiddlers/tiddler8', headers={'Accept': 'application/json'}, method='GET') assert not response.fromcache assert response['status'] == '200' etag_hash = sha('GUEST:application/json').hexdigest() assert response['etag'] == '"bag28/tiddler8/1;%s"' % etag_hash response, content = http.request('http://our_test_domain:8001/bags/bag28/tiddlers/tiddler8', headers={'Accept': 'application/json'}, method='GET') assert response.fromcache assert response['status'] == '304' etag_hash = sha('GUEST:application/json').hexdigest() assert response['etag'] == '"bag28/tiddler8/1;%s"' % etag_hash
def test_get_tiddler_cached(): [os.unlink(".test_cache/%s" % x) for x in os.listdir(".test_cache")] http = httplib2.Http(".test_cache") response, content = http.request( "http://our_test_domain:8001/bags/bag28/tiddlers/tiddler8", headers={"Accept": "application/json"}, method="GET" ) assert not response.fromcache assert response["status"] == "200" etag_hash = sha("GUEST:application/json").hexdigest() assert response["etag"] == '"bag28/tiddler8/1;%s"' % etag_hash response, content = http.request( "http://our_test_domain:8001/bags/bag28/tiddlers/tiddler8", headers={"Accept": "application/json"}, method="GET" ) assert response.fromcache assert response["status"] == "304" etag_hash = sha("GUEST:application/json").hexdigest() assert response["etag"] == '"bag28/tiddler8/1;%s"' % etag_hash response, content = http.request( "http://our_test_domain:8001/bags/bag28/tiddlers/tiddler8", headers={"Accept": "text/html"}, method="GET" ) etag_hash = sha("GUEST:text/html").hexdigest() assert response["etag"] == '"bag28/tiddler8/1;%s"' % etag_hash assert not response.fromcache assert response["status"] == "200" response, content = http.request( "http://our_test_domain:8001/bags/bag28/tiddlers/tiddler8", headers={"Accept": "application/json"}, method="GET" ) assert not response.fromcache assert response["status"] == "200" etag_hash = sha("GUEST:application/json").hexdigest() assert response["etag"] == '"bag28/tiddler8/1;%s"' % etag_hash response, content = http.request( "http://our_test_domain:8001/bags/bag28/tiddlers/tiddler8", headers={"Accept": "application/json"}, method="GET" ) assert response.fromcache assert response["status"] == "304" etag_hash = sha("GUEST:application/json").hexdigest() assert response["etag"] == '"bag28/tiddler8/1;%s"' % etag_hash
def test_put_tiddler_txt_4(): http = httplib2.Http() encoded_body = text_put_body2.encode('utf-8') response, content = http.request( 'http://our_test_domain:8001/bags/bag1/tiddlers/TestOne', method='PUT', headers={'Content-Type': 'text/plain'}, body=encoded_body) assert response['status'] == '204' etag_hash = sha('GUEST:text/plain').hexdigest() assert response['etag'] == '"bag1/TestOne/4;%s"' % etag_hash
def test_post_data_multipart_form(): """ test that a form POST requires a nonce test using multipart/form-data """ store = get_store(config) space = 'foo' make_fake_space(store, space) user = User('foo') user.set_password('foobar') store.put(user) timestamp = datetime.now().strftime('%Y%m%d%H') secret = config['secret'] nonce = '%s:%s:%s' % (timestamp, user.usersign, sha('%s:%s:%s:%s' % (user.usersign, timestamp, space, secret)). hexdigest()) user_cookie = get_auth('foo', 'foobar') csrf_token = 'csrf_token=%s' % nonce data = '''---------------------------168072824752491622650073 Content-Disposition: form-data; name="title" foobar ---------------------------168072824752491622650073 Content-Disposition: form-data; name="text" Hello World ---------------------------168072824752491622650073--''' #test success uri = 'http://foo.0.0.0.0:8080/bags/foo_public/tiddlers?%s' % csrf_token response, content = http.request(uri, method='POST', headers={ 'Content-Type': 'multipart/form-data; ' \ 'boundary=---------------------------168072824752491622650073', 'Cookie': 'tiddlyweb_user="******"' % user_cookie, 'Content-Length': '390' }, body=data) print content assert response['status'] == '204' #test failure response, _ = http.request('http://foo.0.0.0.0:8080/bags/foo_public/tiddlers', method='POST', headers={ 'Content-Type': 'multipart/form-data; ' \ 'boundary=---------------------------168072824752491622650073', 'Cookie': 'tiddlyweb_user="******"' % user_cookie, 'Content-Length': '267' }, body=data) assert response['status'] == '400'
def _tiddler_etag(environ, tiddler): """ Calculate the ETAG of a tiddler, based on bag name, tiddler title and revision. """ try: mime_type = web.get_serialize_type(environ)[1] mime_type = mime_type.split(';', 1)[0].strip() except (TypeError, AttributeError): mime_type = tiddler.type or '' username = environ.get('tiddlyweb.usersign', {}).get('name', '') digest = sha('%s:%s' % (username, mime_type)).hexdigest() return str('"%s/%s/%s;%s"' % (web.encode_name(tiddler.bag), web.encode_name( tiddler.title), tiddler.revision, digest))
def entity_etag(environ, entity): """ Construct an etag from the JSON rep of an entity. """ try: _, mime_type = get_serialize_type(environ) mime_type = mime_type.split(';', 1)[0].strip() except (AttributeError, TypeError): mime_type = '' if 'tiddlyweb.etag_serializer' in environ: serializer = environ['tiddlyweb.etag_serializer'] else: serializer = Serializer('json', environ) environ['tiddlyweb.etag_serializer'] = serializer serializer.object = entity content = serializer.to_string() return '"%s"' % sha(content + mime_type).hexdigest()
def test_etag_generation(): from tiddlyweb.web.handler.tiddler import _tiddler_etag from tiddlyweb.model.bag import Bag from tiddlyweb.model.tiddler import Tiddler from tiddlyweb.config import config tiddler = Tiddler('monkey', 'bar') etag = _tiddler_etag({'tiddlyweb.config': config}, tiddler) etag_hash = sha(':').hexdigest() assert etag == '"bar/monkey/0;%s"' % etag_hash bag = Bag('bar') store.put(bag) store.put(tiddler) etag = _tiddler_etag({'tiddlyweb.config': config}, tiddler) assert etag == '"bar/monkey/1;%s"' % etag_hash
def test_invalid_cookie(): """ Test that an invalid/old cookie causes a new cookie to be sent """ store = get_store(config) space = 'foo' make_fake_space(store, space) user = User('foo') user.set_password('foobar') store.put(user) user_cookie = get_auth('foo', 'foobar') time = datetime.now() - timedelta(hours=3) time = time.strftime('%Y%m%d%H') cookie = 'csrf_token=%s:%s:%s' % ( time, user.usersign, sha('%s:%s:%s:%s' % (user.usersign, time, space, config['secret'])).hexdigest()) response, _ = http.request( 'http://foo.0.0.0.0:8080/status', method='GET', headers={'Cookie': 'tiddlyweb_user="******"; %s' % (user_cookie, cookie)}) assert 'csrf_token' in response['set-cookie'] cookie = 'csrf_token=adiudh9389wefnf98' response, _ = http.request( 'http://foo.0.0.0.0:8080/status', method='GET', headers={'Cookie': 'tiddlyweb_user="******"; %s' % (user_cookie, cookie)}) assert 'csrf_token' in response['set-cookie'] user2 = User('bar') user2.set_password('foobar') store.put(user2) user2_cookie = get_auth('bar', 'foobar') response, _ = http.request( 'http://foo.0.0.0.0:8080/status', method='GET', headers={'Cookie': 'tiddlyweb_user="******"; %s' % (user2_cookie, cookie)}) assert 'csrf_token' in response.get('set-cookie', '')
def test_post_data_form_urlencoded(): """ test that a form POST requires a nonce test using application/x-www-form-urlencoded """ store = get_store(config) space = 'foo' make_fake_space(store, space) user = User('foo') user.set_password('foobar') store.put(user) timestamp = datetime.now().strftime('%Y%m%d%H') secret = config['secret'] nonce = '%s:%s:%s' % ( timestamp, user.usersign, sha('%s:%s:%s:%s' % (user.usersign, timestamp, space, secret)).hexdigest()) user_cookie = get_auth('foo', 'foobar') csrf_token = 'csrf_token="%s"' % nonce data = 'title=foobar&text=hello%20world' #test success response, _ = http.request( 'http://foo.0.0.0.0:8080/bags/foo_public/tiddlers', method='POST', headers={ 'Content-type': 'application/x-www-form-urlencoded', 'Cookie': 'tiddlyweb_user="******"; %s' % (user_cookie, csrf_token) }, body='%s&csrf_token=%s' % (data, nonce)) assert response['status'] == '204' #test failure response, _ = http.request('http://0.0.0.0:8080/bags/foo_public/tiddlers', method='POST', headers={ 'Content-type': 'application/x-www-form-urlencoded', 'Cookie': 'tiddlyweb_user="******"' % user_cookie }, body='%s' % data) assert response['status'] == '400'
def list_entities(environ, start_response, method_name, store_list=None, serializer_list=None): """ Get an optionally :py:mod:`filtered <tiddlyweb.filters>` list of all the :py:class:`bags <tiddlyweb.model.bag.Bag>` or :py:class:`recipes <tiddlyweb.model.recipe.Recipe>` the current ``tiddlyweb.usersign`` can read. """ store = environ['tiddlyweb.store'] serialize_type, mime_type = get_serialize_type(environ, collection=True) serializer = Serializer(serialize_type, environ) filters = environ['tiddlyweb.filters'] if method_name: if not store_list: store_list = getattr(store, method_name) if not serializer_list: serializer_list = getattr(serializer, method_name) try: kept_entities = _filter_readable(environ, store_list(), filters) except FilterError as exc: raise HTTP400(exc) etag_string = '"%s:%s"' % (kept_entities.hexdigest(), sha(mime_type).hexdigest()) check_incoming_etag(environ, etag_string) try: output = serializer_list(kept_entities) except NoSerializationError: raise HTTP415('Content type not supported: %s' % mime_type) start_response("200 OK", [('Content-Type', mime_type), ('Vary', 'Accept'), ('Cache-Control', 'no-cache'), ('Etag', etag_string)]) if isinstance(output, basestring): return [output] else: return output
def _validate_tiddler_list(environ, tiddlers): """ Do Etag and Last modified checks on the collection of tiddlers. If ETag testing is done, no last modified handling is done, even if the ETag testing fails. If no 304 is raised, then just return last-modified and ETag for the caller to use in constructing its HTTP response. """ last_modified_number = tiddlers.modified last_modified_string = http_date_from_timestamp(last_modified_number) last_modified = ('Last-Modified', last_modified_string) username = environ.get('tiddlyweb.usersign', {}).get('name', '') try: _, mime_type = get_serialize_type(environ) mime_type = mime_type.split(';', 1)[0].strip() except TypeError: mime_type = '' etag_string = '"%s:%s;%s"' % (tiddlers.hexdigest(), str(last_modified_number), sha('%s:%s' % (username, mime_type)).hexdigest()) etag = ('Etag', etag_string) incoming_etag = environ.get('HTTP_IF_NONE_MATCH', None) if incoming_etag: if incoming_etag == etag_string: raise HTTP304(incoming_etag) else: incoming_modified = environ.get('HTTP_IF_MODIFIED_SINCE', None) if incoming_modified and \ (datetime_from_http_date(incoming_modified) >= \ datetime_from_http_date(last_modified_string)): raise HTTP304('') return last_modified, etag
def test_nonce_not_left_over(): """ Test that the nonce is not left over in the tiddler after a POST i.e. check that it is removed before the request continues """ store = get_store(config) space = 'foo' make_fake_space(store, space) user = User('foo') user.set_password('foobar') store.put(user) timestamp = datetime.now().strftime('%Y%m%d%H') secret = config['secret'] nonce = '%s:%s:%s' % ( timestamp, user.usersign, sha('%s:%s:%s:%s' % (user.usersign, timestamp, space, secret)).hexdigest()) user_cookie = get_auth('foo', 'foobar') csrf_token = 'csrf_token=%s' % nonce data = 'title=foobar&text=hello%20world&extra_field=baz' #test success response, _ = http.request( 'http://foo.0.0.0.0:8080/bags/foo_public/tiddlers', method='POST', headers={ 'Content-type': 'application/x-www-form-urlencoded', 'Cookie': 'tiddlyweb_user="******"' % user_cookie }, body='%s&csrf_token=%s' % (data, nonce)) assert response['status'] == '204' new_tiddler = Tiddler('foobar') new_tiddler.bag = 'foo_public' new_tiddler = store.get(new_tiddler) assert new_tiddler.title == 'foobar' assert new_tiddler.text == 'hello world' assert new_tiddler.fields.get('extra_field') == 'baz' assert new_tiddler.fields.get('nonce') == None
def extract(self, environ, start_response): """ Extract the cookie, if there, from the headers and attempt to validate its contents. """ try: user_cookie = environ['HTTP_COOKIE'] logging.debug('simple_cookie looking at cookie string: %s', user_cookie) cookie = Cookie.SimpleCookie() cookie.load(user_cookie) cookie_value = cookie['tiddlyweb_user'].value secret = environ['tiddlyweb.config']['secret'] usersign, cookie_secret = cookie_value.rsplit(':', 1) store = environ['tiddlyweb.store'] if cookie_secret == sha('%s%s' % (usersign, secret)).hexdigest(): usersign = usersign.decode('utf-8') user = self.load_user(environ, usersign) return {"name": user.usersign, "roles": user.list_roles()} except Cookie.CookieError, exc: raise HTTP400('malformed cookie: %s' % exc)
def entity_etag(environ, entity): """ Construct an Etag from the digest of the :py:class:`JSON <tiddlyweb.serializations.json.Serialization>` reprepresentation of an entity. The JSON representation provides a reasonably repeatable and unique string of data. """ try: _, mime_type = get_serialize_type(environ) mime_type = mime_type.split(';', 1)[0].strip() except (AttributeError, TypeError): mime_type = '' if 'tiddlyweb.etag_serializer' in environ: serializer = environ['tiddlyweb.etag_serializer'] else: serializer = Serializer('json', environ) environ['tiddlyweb.etag_serializer'] = serializer serializer.object = entity content = serializer.to_string() return '"%s"' % sha(content + mime_type).hexdigest()
def extract(self, environ, start_response): """ Extract the cookie, if there, from the headers and attempt to validate its contents. """ try: user_cookie = environ['HTTP_COOKIE'] LOGGER.debug('simple_cookie looking at cookie string: %s', user_cookie) cookie = SimpleCookie() cookie.load(str(user_cookie)) cookie_value = cookie['tiddlyweb_user'].value secret = environ['tiddlyweb.config']['secret'] usersign, cookie_secret = cookie_value.rsplit(':', 1) if cookie_secret == sha('%s%s' % (usersign, secret)).hexdigest(): usersign = unquote(usersign) user = self.load_user(environ, usersign) return {"name": user.usersign, "roles": user.list_roles()} except CookieError as exc: raise HTTP400('malformed cookie: %s' % exc) except (KeyError, ValueError): pass return False