Commit 9e0fd190 authored by Jacotsu's avatar Jacotsu
Browse files

Fixed decodings errors

Added secure referrals management
Added TODO.md
parent 21a8be00
- [X] Url referrals should save only the hostname and the counter
- [X] Change referrals data structure to 'HSET referrers.<short_url> <referrer_url> <count>'
- [ ] Fix javascript
- [ ] Implement refererrers pagination
- [ ] Write tests
......@@ -7,10 +7,12 @@ import crypt
import flask
from flask_httpauth import HTTPBasicAuth
from flask_redis import FlaskRedis
from urllib.parse import urlparse
app = flask.Flask(__name__)
auth = HTTPBasicAuth()
app.config['REDIS_URL'] = os.environ.get('REDIS_URL', 'redis://127.0.0.1:6379/0')
app.config['REDIS_URL'] = os.environ.get('REDIS_URL',
'redis://127.0.0.1:6379/0')
redis_store = FlaskRedis()
redis_store.init_app(app)
pure_string = re.compile(r'^\w+$')
......@@ -29,8 +31,8 @@ def redis_key_for_url(url: str) -> str:
return "url.{}".format(url)
def redis_key_for_referrer_count(url: str, referrer: str) -> str:
return "url.{}.referrers.{}.count".format(url, referrer)
def redis_key_for_url_referrers(url: str) -> str:
return "referrers.{}".format(url)
def redis_key_for_count(url: str) -> str:
......@@ -42,7 +44,7 @@ def redis_key_for_useragent(url: str) -> str:
def redirect_useragent(path: str):
useragent = flask.headers.get('User-Agent')
useragent = flask.request.headers.get('User-Agent')
for agent, match in useragent_match.items():
if match.match(useragent):
redis_store.hincrby(redis_key_for_useragent(path),
......@@ -50,7 +52,22 @@ def redirect_useragent(path: str):
def redirect_referrer(path: str):
pass
raw_referrer = flask.request.headers.get('referer')
if raw_referrer:
safe_referrer = flask.escape(raw_referrer)
if safe_referrer != raw_referrer:
app.logger.warning('XSS injection attempt from {}'
.format(flask.request.remote_addr))
return flask.abort(400)
else:
# The hostname property is empty
referrer_hostname = urlparse(safe_referrer).hostname
if referrer_hostname:
redis_store.hincrby(redis_key_for_url_referrers(path),
referrer_hostname)
app.logger.debug('Increased counter for {}'
.format(referrer_hostname))
def handle_redirect(path):
......@@ -91,9 +108,8 @@ def add_url():
long_url = payload['url']
if not pure_string.match(short_url):
return flask.abort(400)
if not redis_store.exists(redis_key_for_url(short_url)):
redis_store.set(redis_key_for_count(short_url), 0)
redis_store.set(redis_key_for_url(short_url), long_url)
redis_store.set(redis_key_for_url(short_url),
long_url)
return ''
except KeyError:
return flask.abort(400)
......@@ -109,6 +125,7 @@ def del_url():
try:
redis_store.delete(redis_key_for_url(payload['name']))
redis_store.delete(redis_key_for_count(payload['name']))
redis_store.delete(redis_key_for_url_referrers(payload['name']))
return ''
except KeyError:
return flask.abort(400)
......@@ -122,15 +139,24 @@ def list_urls():
return_dict = {}
for key in redis_store.keys('url.*'):
app.logger.debug(key)
real_key = key.replace('url.', '', 1)
real_key = key.decode().replace('url.', '', 1)
click_count = redis_store.get(redis_key_for_count(real_key))
return_dict[real_key] = {'url': redis_store.get(key).decode(),
'count': int(redis_store.get(redis_key_for_count(real_key)).decode())}
'count': int(click_count.decode())
if click_count else 0}
return flask.jsonify(return_dict)
@app.route('/api/v1/listreferrers/<shortedurl>')
@auth.login_required
def list_referrers(shortedurl=None):
if shortedurl:
referers_dict = redis_store\
.hgetall(redis_key_for_url_referrers(shortedurl))
decoded_dict = {key.decode(): value.decode()
for key, value in referers_dict.items()}
app.logger.debug(decoded_dict)
return flask.jsonify(decoded_dict)
return ''
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment