summaryrefslogtreecommitdiff
path: root/cli/onionshare_cli/web/web.py
diff options
context:
space:
mode:
Diffstat (limited to 'cli/onionshare_cli/web/web.py')
-rw-r--r--cli/onionshare_cli/web/web.py159
1 files changed, 37 insertions, 122 deletions
diff --git a/cli/onionshare_cli/web/web.py b/cli/onionshare_cli/web/web.py
index 04919185..e12fccc7 100644
--- a/cli/onionshare_cli/web/web.py
+++ b/cli/onionshare_cli/web/web.py
@@ -34,7 +34,6 @@ from flask import (
send_file,
__version__ as flask_version,
)
-from flask_httpauth import HTTPBasicAuth
from flask_socketio import SocketIO
from .share_mode import ShareModeWeb
@@ -64,18 +63,16 @@ class Web:
REQUEST_STARTED = 1
REQUEST_PROGRESS = 2
REQUEST_CANCELED = 3
- REQUEST_RATE_LIMIT = 4
- REQUEST_UPLOAD_INCLUDES_MESSAGE = 5
- REQUEST_UPLOAD_FILE_RENAMED = 6
- REQUEST_UPLOAD_SET_DIR = 7
- REQUEST_UPLOAD_FINISHED = 8
- REQUEST_UPLOAD_CANCELED = 9
- REQUEST_INDIVIDUAL_FILE_STARTED = 10
- REQUEST_INDIVIDUAL_FILE_PROGRESS = 11
- REQUEST_INDIVIDUAL_FILE_CANCELED = 12
- REQUEST_ERROR_DATA_DIR_CANNOT_CREATE = 13
- REQUEST_OTHER = 14
- REQUEST_INVALID_PASSWORD = 15
+ REQUEST_UPLOAD_INCLUDES_MESSAGE = 4
+ REQUEST_UPLOAD_FILE_RENAMED = 5
+ REQUEST_UPLOAD_SET_DIR = 6
+ REQUEST_UPLOAD_FINISHED = 7
+ REQUEST_UPLOAD_CANCELED = 8
+ REQUEST_INDIVIDUAL_FILE_STARTED = 9
+ REQUEST_INDIVIDUAL_FILE_PROGRESS = 10
+ REQUEST_INDIVIDUAL_FILE_CANCELED = 11
+ REQUEST_ERROR_DATA_DIR_CANNOT_CREATE = 12
+ REQUEST_OTHER = 13
def __init__(self, common, is_gui, mode_settings, mode="share"):
self.common = common
@@ -92,8 +89,6 @@ class Web:
)
self.app.secret_key = self.common.random_string(8)
self.generate_static_url_path()
- self.auth = HTTPBasicAuth()
- self.auth.error_handler(self.error401)
# Verbose mode?
if self.common.verbose:
@@ -132,9 +127,6 @@ class Web:
]
self.q = queue.Queue()
- self.password = None
-
- self.reset_invalid_passwords()
self.done = False
@@ -199,27 +191,20 @@ class Web:
Common web app routes between all modes.
"""
- @self.auth.get_password
- def get_pw(username):
- if username == "onionshare":
- return self.password
- else:
- return None
-
- @self.app.before_request
- def conditional_auth_check():
- # Allow static files without basic authentication
- if request.path.startswith(self.static_url_path + "/"):
- return None
-
- # If public mode is disabled, require authentication
- if not self.settings.get("general", "public"):
-
- @self.auth.login_required
- def _check_login():
- return None
-
- return _check_login()
+ @self.app.after_request
+ def add_security_headers(r):
+ """
+ Add security headers to a response
+ """
+ for header, value in self.security_headers:
+ r.headers.set(header, value)
+ # Set a CSP header unless in website mode and the user has disabled it
+ if not self.settings.get("website", "disable_csp") or self.mode != "website":
+ r.headers.set(
+ "Content-Security-Policy",
+ "default-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; img-src 'self' data:;",
+ )
+ return r
@self.app.errorhandler(404)
def not_found(e):
@@ -260,37 +245,9 @@ class Web:
f"{self.common.get_resource_path('static')}/img/favicon.ico"
)
- def error401(self):
- auth = request.authorization
- if auth:
- if (
- auth["username"] == "onionshare"
- and auth["password"] not in self.invalid_passwords
- ):
- print(f"Invalid password guess: {auth['password']}")
- self.add_request(Web.REQUEST_INVALID_PASSWORD, data=auth["password"])
-
- self.invalid_passwords.append(auth["password"])
- self.invalid_passwords_count += 1
-
- if self.invalid_passwords_count == 20:
- self.add_request(Web.REQUEST_RATE_LIMIT)
- self.force_shutdown()
- print(
- "Someone has made too many wrong attempts to guess your password, so OnionShare has stopped the server. Start sharing again and send the recipient a new address to share."
- )
-
- r = make_response(
- render_template("401.html", static_url_path=self.static_url_path), 401
- )
- return self.add_security_headers(r)
-
def error403(self):
self.add_request(Web.REQUEST_OTHER, request.path)
- r = make_response(
- render_template("403.html", static_url_path=self.static_url_path), 403
- )
- return self.add_security_headers(r)
+ return render_template("403.html", static_url_path=self.static_url_path), 403
def error404(self, history_id):
mode = self.get_mode()
@@ -302,10 +259,7 @@ class Web:
)
self.add_request(Web.REQUEST_OTHER, request.path)
- r = make_response(
- render_template("404.html", static_url_path=self.static_url_path), 404
- )
- return self.add_security_headers(r)
+ return render_template("404.html", static_url_path=self.static_url_path), 404
def error405(self, history_id):
mode = self.get_mode()
@@ -317,10 +271,7 @@ class Web:
)
self.add_request(Web.REQUEST_OTHER, request.path)
- r = make_response(
- render_template("405.html", static_url_path=self.static_url_path), 405
- )
- return self.add_security_headers(r)
+ return render_template("405.html", static_url_path=self.static_url_path), 405
def error500(self, history_id):
mode = self.get_mode()
@@ -332,24 +283,7 @@ class Web:
)
self.add_request(Web.REQUEST_OTHER, request.path)
- r = make_response(
- render_template("500.html", static_url_path=self.static_url_path), 500
- )
- return self.add_security_headers(r)
-
- def add_security_headers(self, r):
- """
- Add security headers to a request
- """
- for header, value in self.security_headers:
- r.headers.set(header, value)
- # Set a CSP header unless in website mode and the user has disabled it
- if not self.settings.get("website", "disable_csp") or self.mode != "website":
- r.headers.set(
- "Content-Security-Policy",
- "default-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; img-src 'self' data:;",
- )
- return r
+ return render_template("500.html", static_url_path=self.static_url_path), 500
def _safe_select_jinja_autoescape(self, filename):
if filename is None:
@@ -362,21 +296,6 @@ class Web:
"""
self.q.put({"type": request_type, "path": path, "data": data})
- def generate_password(self, saved_password=None):
- self.common.log("Web", "generate_password", f"saved_password={saved_password}")
- if saved_password is not None and saved_password != "":
- self.password = saved_password
- self.common.log(
- "Web",
- "generate_password",
- f'saved_password sent, so password is: "{self.password}"',
- )
- else:
- self.password = self.common.build_password()
- self.common.log(
- "Web", "generate_password", f'built random password: "{self.password}"'
- )
-
def verbose_mode(self):
"""
Turn on verbose mode, which will log flask errors to a file.
@@ -386,10 +305,6 @@ class Web:
log_handler.setLevel(logging.WARNING)
self.app.logger.addHandler(log_handler)
- def reset_invalid_passwords(self):
- self.invalid_passwords_count = 0
- self.invalid_passwords = []
-
def force_shutdown(self):
"""
Stop the flask web server, from the context of the flask app.
@@ -446,18 +361,18 @@ class Web:
# To stop flask, load http://shutdown:[shutdown_password]@127.0.0.1/[shutdown_password]/shutdown
# (We're putting the shutdown_password in the path as well to make routing simpler)
if self.running:
- if self.password:
- requests.get(
- f"http://127.0.0.1:{port}/{self.shutdown_password}/shutdown",
- auth=requests.auth.HTTPBasicAuth("onionshare", self.password),
- )
- else:
+ try:
requests.get(
f"http://127.0.0.1:{port}/{self.shutdown_password}/shutdown"
)
-
- # Reset any password that was in use
- self.password = None
+ except requests.exceptions.ConnectionError as e:
+ # The way flask-socketio stops a connection when running using
+ # eventlet is by raising SystemExit to abort all the processes.
+ # Hence the connections are closed and no response is returned
+ # to the above request. So I am just catching the ConnectionError
+ # to check if it was chat mode, in which case it's okay
+ if self.mode != "chat":
+ raise e
def cleanup(self):
"""