Added IP whitelist support, to block the creation of new links if the page is...

Added IP whitelist support, to block the creation of new links if the page is accessed from a non-whitelisted IP-address.
parent cdb6a883
......@@ -67,3 +67,12 @@ show_github_link: false
# Integer: How many items to show per page in the admin interface
# Default: 20
admin_links_per_page: 20
# List: IP ranges that are allowed to create new links. Everyone is allowed if unset.
# Example:
# allowed_ip_ranges:
# - 127.0.0.1
# - 130.89.0.0/16
# - 2001:67c:2564::/48
# Default: unset
allowed_ip_ranges:
......@@ -2,9 +2,10 @@
# This file is part of liteshort by 132ikl
# This software is license under the MIT license. It should be included in your copy of this software.
# A copy of the MIT license can be obtained at https://mit-license.org/
import ipaddress
from flask import Flask, current_app, flash, g, jsonify, make_response, redirect, render_template, request, \
send_from_directory, url_for, session
send_from_directory, url_for, session, abort
import bcrypt
import os
import random
......@@ -157,6 +158,19 @@ def nested_list_to_dict(l):
def response(rq, result, error_msg="Error: Unknown error"):
# Only allow responses to whitelisted IP ranges
whitelist_disabled = 'allowed_ip_ranges' not in app.config or app.config['allowed_ip_ranges'] is None
if not whitelist_disabled:
is_allowed = False
for range in app.config['allowed_ip_ranges']:
network = ipaddress.ip_network(range)
remote = ipaddress.ip_address(request.remote_addr)
if remote in network:
is_allowed = True
if not is_allowed:
abort(403)
if rq.form.get('api') and not rq.form.get('format') == 'json':
return "Format type HTML (default) not support for API" # Future-proof for non-json return types
if rq.form.get('format') == 'json':
......@@ -234,6 +248,11 @@ app.secret_key = app.config['secret_key']
app.config['SERVER_NAME'] = app.config['site_domain']
@app.errorhandler(403)
def access_denied(e):
return render_template("403_access_denied.html")
@app.route('/favicon.ico', subdomain=app.config['subdomain'])
def favicon():
return send_from_directory(os.path.join(app.root_path, 'static'),
......@@ -323,7 +342,8 @@ def main_post():
@app.route('/login', methods=['POST'])
def login():
if 'admin_hashed_password' not in app.config or app.config['admin_hashed_password'] is None:
if ('admin_hashed_password' not in app.config or app.config['admin_hashed_password'] is None) and (
'admin_password' not in app.config or app.config['admin_password'] is None):
raise AssertionError("Login is disabled.")
if authenticate(request.form['username'], request.form['password']):
......@@ -361,12 +381,13 @@ def admin():
page = request.args.get('page', '1')
try:
page = int(page)
if page > page_count:
return make_response(redirect(url_for('admin')+"?page="+str(page_count)))
if page < 1:
return make_response(redirect(url_for('admin')+"?page=1"))
except ValueError:
page = 1
if page_count != 0:
if page > page_count:
return make_response(redirect(url_for('admin')+"?page="+str(page_count)))
if page < 1:
return make_response(redirect(url_for('admin')+"?page=1"))
urls = list_shortlinks_page(page, app.config['admin_links_per_page'])
......
<!--
Copyright (c) 2019 Steven Spangler <132@ikl.sh>, Kevin Alberts <kevin@kevinalberts.nl>
This file is part of liteshort by 132ikl
This software is license under the MIT license. It should be included in your copy of this software.
A copy of the MIT license can be obtained at https://mit-license.org/
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ config.site_name }}</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='ia.css') }}">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div id="top">
<div id="header">
<div id="logo" class="drop-shadow">
<a href="{{ url_for('main') }}">
<img src="{{ url_for('static', filename='ictsv-logo-white.svg') }}" alt="logo">
</a>
</div>
<div id="title" class="drop-shadow">
<a href="{{ url_for('main') }}">
I.C.T.S.V. URL Shortener
</a>
</div>
</div>
</div>
<div id="middle">
<div id="content">
<div class="clearfix"></div>
<div id="box" class="align-self-center col-xl-4 col-lg-4 col-md-6 col-sm-10 col-xs-12">
<div class="form">
<h3 id="box-title">Access Denied</h3>
<p>
Sorry, but you can only create new links on this URL shortener if you are connected to
the University of Twente network.
</p>
<p>
Please try again later if you are on that network again,
or please activate a VPN connection to create a new link.
</p>
<p>
If you think this is a mistake, please contact the Website committee of Inter-Actief.
</p>
</div>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
{% if category == 'error' %}
<div class="error">
✖ {{ message }}
</div>
{% endif %}
{% endfor %}
{% endif %}
{% endwith %}
<p id="copyright">
<span>This URL shortening service is provided by <a href="https://inter-actief.net/">I.C.T.S.V. Inter-<i>Actief</i></a>.</span>
<span>URLs may be deleted or modified by us if deemed necessary.</span>
<span>No personal information is processed by this application.</span>
<span>&nbsp;</span>
<span>&copy; <a href="https://inter-actief.net/">I.C.T.S.V. Inter-<i>Actief</i></a> and <a href="https://gitlab.ia.utwente.nl/WWW/ia-short">individual contributors</a>. Based on <a href="https://github.com/132ikl/liteshort">liteshort</a>.</span>
</p>
</div>
</div>
</div>
</body>
</html>
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