diff options
Diffstat (limited to 'cli')
-rw-r--r-- | cli/README.md | 61 | ||||
-rw-r--r-- | cli/onionshare_cli/__init__.py | 101 | ||||
-rw-r--r-- | cli/onionshare_cli/mode_settings.py | 6 | ||||
-rw-r--r-- | cli/onionshare_cli/onion.py | 144 | ||||
-rw-r--r-- | cli/onionshare_cli/onionshare.py | 4 | ||||
-rw-r--r-- | cli/onionshare_cli/resources/templates/401.html | 21 | ||||
-rw-r--r-- | cli/onionshare_cli/resources/templates/send.html | 4 | ||||
-rw-r--r-- | cli/onionshare_cli/resources/version.txt | 2 | ||||
-rw-r--r-- | cli/onionshare_cli/settings.py | 32 | ||||
-rw-r--r-- | cli/onionshare_cli/web/chat_mode.py | 7 | ||||
-rw-r--r-- | cli/onionshare_cli/web/receive_mode.py | 18 | ||||
-rw-r--r-- | cli/onionshare_cli/web/send_base_mode.py | 4 | ||||
-rw-r--r-- | cli/onionshare_cli/web/share_mode.py | 18 | ||||
-rw-r--r-- | cli/onionshare_cli/web/web.py | 159 | ||||
-rw-r--r-- | cli/poetry.lock | 432 | ||||
-rw-r--r-- | cli/pyproject.toml | 7 | ||||
-rw-r--r-- | cli/setup.py | 2 | ||||
-rw-r--r-- | cli/tests/test_cli_common.py | 2 | ||||
-rw-r--r-- | cli/tests/test_cli_settings.py | 2 | ||||
-rw-r--r-- | cli/tests/test_cli_web.py | 94 |
20 files changed, 519 insertions, 601 deletions
diff --git a/cli/README.md b/cli/README.md index 744ece4d..00c175a7 100644 --- a/cli/README.md +++ b/cli/README.md @@ -22,14 +22,69 @@ ## Installing OnionShare CLI -First, make sure you have `tor` installed. In Linux, install it through your package manager. In macOS, install it with [Homebrew](https://brew.sh): `brew install tor`. +First, make sure you have `tor` and `python3` installed. In Linux, install it through your package manager. In macOS, install it with [Homebrew](https://brew.sh): `brew install tor`. Second, OnionShare is written in python, and you can install the command line version use python's package manager `pip`. -Then install OnionShare CLI: +### Requirements +Debian/Ubuntu (APT): ```sh -pip install onionshare-cli +sudo apt-get install tor python3-pip ``` +Arch (Pacman): +```sh +sudo pacman -S tor python-pip +``` + +CentOS, Red Hat, and Fedora (Yum): +```sh +sudo yum install tor python3 python3-wheel +``` + +macOS (Homebrew): +```sh +brew install tor python +sudo easy_install pip +``` + +### Main + +#### Installation + +Install OnionShare CLI: + +```sh +pip install --user onionshare-cli +``` + +#### Set path + +When you install programs with pip and use the --user flag, it installs them into ~/.local/bin, which isn't in your path by default. To add ~/.local/bin to your path automatically for the next time you reopen the terminal or source your shell configuration file, do the following: + +First, discover what shell you are using: + +```sh +echo $SHELL +``` + +Then apply the path to your shell file: + +bash: + +```sh +echo "PATH=\$PATH:~/.local/bin" >> ~/.bashrc +source ~/.bashrc +``` + +zsh: + +```sh +echo "PATH=\$PATH:~/.local/bin" >> ~/.zshrc +source ~/.zshrc +``` + +#### Usage + Then run it with: ```sh diff --git a/cli/onionshare_cli/__init__.py b/cli/onionshare_cli/__init__.py index c046e472..4bc00929 100644 --- a/cli/onionshare_cli/__init__.py +++ b/cli/onionshare_cli/__init__.py @@ -38,14 +38,6 @@ from .onionshare import OnionShare from .mode_settings import ModeSettings -def build_url(mode_settings, app, web): - # Build the URL - if mode_settings.get("general", "public"): - return f"http://{app.onion_host}" - else: - return f"http://onionshare:{web.password}@{app.onion_host}" - - def main(cwd=None): """ The main() function implements all of the logic that the command-line version of @@ -113,7 +105,7 @@ def main(cwd=None): action="store_true", dest="public", default=False, - help="Don't use a password", + help="Don't use a private key", ) parser.add_argument( "--auto-start-timer", @@ -129,20 +121,6 @@ def main(cwd=None): default=0, help="Stop onion service at schedule time (N seconds from now)", ) - parser.add_argument( - "--legacy", - action="store_true", - dest="legacy", - default=False, - help="Use legacy address (v2 onion service, not recommended)", - ) - parser.add_argument( - "--client-auth", - action="store_true", - dest="client_auth", - default=False, - help="Use client authorization (requires --legacy)", - ) # Share args parser.add_argument( "--no-autostop-sharing", @@ -215,8 +193,6 @@ def main(cwd=None): public = bool(args.public) autostart_timer = int(args.autostart_timer) autostop_timer = int(args.autostop_timer) - legacy = bool(args.legacy) - client_auth = bool(args.client_auth) autostop_sharing = not bool(args.no_autostop_sharing) data_dir = args.data_dir webhook_url = args.webhook_url @@ -225,25 +201,9 @@ def main(cwd=None): disable_csp = bool(args.disable_csp) verbose = bool(args.verbose) - if receive: - mode = "receive" - elif website: - mode = "website" - elif chat: - mode = "chat" - else: - mode = "share" - # Verbose mode? common.verbose = verbose - # client_auth can only be set if legacy is also set - if client_auth and not legacy: - print( - "Client authentication (--client-auth) is only supported with with legacy onion services (--legacy)" - ) - sys.exit() - # Re-load settings, if a custom config was passed in if config_filename: common.load_settings(config_filename) @@ -254,18 +214,26 @@ def main(cwd=None): if persistent_filename: mode_settings = ModeSettings(common, persistent_filename) mode_settings.set("persistent", "enabled", True) - mode_settings.set("persistent", "mode", mode) else: mode_settings = ModeSettings(common) + if receive: + mode = "receive" + elif website: + mode = "website" + elif chat: + mode = "chat" + else: + mode = "share" + if mode_settings.just_created: # This means the mode settings were just created, not loaded from disk mode_settings.set("general", "title", title) mode_settings.set("general", "public", public) mode_settings.set("general", "autostart_timer", autostart_timer) mode_settings.set("general", "autostop_timer", autostop_timer) - mode_settings.set("general", "legacy", legacy) - mode_settings.set("general", "client_auth", client_auth) + if persistent_filename: + mode_settings.set("persistent", "mode", mode) if mode == "share": mode_settings.set("share", "autostop_sharing", autostop_sharing) if mode == "receive": @@ -352,11 +320,6 @@ def main(cwd=None): try: common.settings.load() - if mode_settings.get("general", "public"): - web.password = None - else: - web.generate_password(mode_settings.get("onion", "password")) - # Receive mode needs to know the tor proxy details for webhooks if mode == "receive": if local_only: @@ -381,7 +344,7 @@ def main(cwd=None): sys.exit() app.start_onion_service(mode, mode_settings, False) - url = build_url(mode_settings, app, web) + url = f"http://{app.onion_host}" schedule = datetime.now() + timedelta(seconds=autostart_timer) if mode == "receive": print( @@ -394,21 +357,21 @@ def main(cwd=None): "what you are doing." ) print("") - if mode_settings.get("general", "client_auth"): + if not mode_settings.get("general", "public"): print( - f"Give this address and HidServAuth lineto your sender, and tell them it won't be accessible until: {schedule.strftime('%I:%M:%S%p, %b %d, %y')}" + f"Give this address and private key to your sender, and tell them it won't be accessible until: {schedule.strftime('%I:%M:%S%p, %b %d, %y')}" ) - print(app.auth_string) + print(f"Private key: {app.auth_string}") else: print( f"Give this address to your sender, and tell them it won't be accessible until: {schedule.strftime('%I:%M:%S%p, %b %d, %y')}" ) else: - if mode_settings.get("general", "client_auth"): + if not mode_settings.get("general", "public"): print( - f"Give this address and HidServAuth line to your recipient, and tell them it won't be accessible until: {schedule.strftime('%I:%M:%S%p, %b %d, %y')}" + f"Give this address and private key to your recipient, and tell them it won't be accessible until: {schedule.strftime('%I:%M:%S%p, %b %d, %y')}" ) - print(app.auth_string) + print(f"Private key: {app.auth_string}") else: print( f"Give this address to your recipient, and tell them it won't be accessible until: {schedule.strftime('%I:%M:%S%p, %b %d, %y')}" @@ -426,7 +389,6 @@ def main(cwd=None): sys.exit() except (TorTooOldEphemeral, TorTooOldStealth, TorErrorProtocolError) as e: print("") - print(e.args[0]) sys.exit() if mode == "website": @@ -458,21 +420,14 @@ def main(cwd=None): t.start() try: # Trap Ctrl-C - # Wait for web.generate_password() to finish running time.sleep(0.2) # start auto-stop timer thread if app.autostop_timer > 0: app.autostop_timer_thread.start() - # Save the web password if we are using a persistent private key - if mode_settings.get("persistent", "enabled"): - if not mode_settings.get("onion", "password"): - mode_settings.set("onion", "password", web.password) - # mode_settings.save() - # Build the URL - url = build_url(mode_settings, app, web) + url = f"http://{app.onion_host}" print("") if autostart_timer > 0: @@ -490,21 +445,21 @@ def main(cwd=None): ) print("") - if mode_settings.get("general", "client_auth"): - print("Give this address and HidServAuth to the sender:") + if mode_settings.get("general", "public"): + print("Give this address to the sender:") print(url) - print(app.auth_string) else: - print("Give this address to the sender:") + print("Give this address and private key to the sender:") print(url) + print(f"Private key: {app.auth_string}") else: - if mode_settings.get("general", "client_auth"): - print("Give this address and HidServAuth line to the recipient:") + if mode_settings.get("general", "public"): + print("Give this address to the recipient:") print(url) - print(app.auth_string) else: - print("Give this address to the recipient:") + print("Give this address and private key to the recipient:") print(url) + print(f"Private key: {app.auth_string}") print("") print("Press Ctrl+C to stop the server") diff --git a/cli/onionshare_cli/mode_settings.py b/cli/onionshare_cli/mode_settings.py index 47900997..47ff1c63 100644 --- a/cli/onionshare_cli/mode_settings.py +++ b/cli/onionshare_cli/mode_settings.py @@ -37,8 +37,8 @@ class ModeSettings: self.default_settings = { "onion": { "private_key": None, - "hidservauth_string": None, - "password": None, + "client_auth_priv_key": None, + "client_auth_pub_key": None, }, "persistent": {"mode": None, "enabled": False}, "general": { @@ -46,8 +46,6 @@ class ModeSettings: "public": False, "autostart_timer": False, "autostop_timer": False, - "legacy": False, - "client_auth": False, "service_id": None, }, "share": {"autostop_sharing": True, "filenames": []}, diff --git a/cli/onionshare_cli/onion.py b/cli/onionshare_cli/onion.py index f9c7177e..7f6faa17 100644 --- a/cli/onionshare_cli/onion.py +++ b/cli/onionshare_cli/onion.py @@ -21,8 +21,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. from stem.control import Controller from stem import ProtocolError, SocketClosed from stem.connection import MissingPassword, UnreadableCookieFile, AuthenticationFailure -from Crypto.PublicKey import RSA import base64 +import nacl.public import os import tempfile import subprocess @@ -170,6 +170,20 @@ class Onion(object): # Keep track of onions where it's important to gracefully close to prevent truncated downloads self.graceful_close_onions = [] + def key_str(self, key): + """ + Returns a base32 decoded string of a key. + """ + # bytes to base 32 + key_bytes = bytes(key) + key_b32 = base64.b32encode(key_bytes) + # strip trailing ==== + assert key_b32[-4:] == b'====' + key_b32 = key_b32[:-4] + # change from b'ASDF' to ASDF + s = key_b32.decode('utf-8') + return s + def connect( self, custom_settings=None, @@ -570,14 +584,15 @@ class Onion(object): callable(list_ephemeral_hidden_services) and self.tor_version >= "0.2.7.1" ) - # Do the versions of stem and tor that I'm using support stealth onion services? + # Do the versions of stem and tor that I'm using support v3 stealth onion services? try: res = self.c.create_ephemeral_hidden_service( {1: 1}, - basic_auth={"onionshare": None}, + basic_auth=None, await_publication=False, key_type="NEW", - key_content="RSA1024", + key_content="ED25519-V3", + client_auth_v3="E2GOT5LTUTP3OAMRCRXO4GSH6VKJEUOXZQUC336SRKAHTTT5OVSA", ) tmp_service_id = res.service_id self.c.remove_ephemeral_hidden_service(tmp_service_id) @@ -612,65 +627,58 @@ class Onion(object): "Your version of Tor is too old, ephemeral onion services are not supported" ) raise TorTooOldEphemeral() - if mode_settings.get("general", "client_auth") and not self.supports_stealth: - print( - "Your version of Tor is too old, stealth onion services are not supported" - ) - raise TorTooOldStealth() - - auth_cookie = None - if mode_settings.get("general", "client_auth"): - if mode_settings.get("onion", "hidservauth_string"): - auth_cookie = mode_settings.get("onion", "hidservauth_string").split()[ - 2 - ] - if auth_cookie: - basic_auth = {"onionshare": auth_cookie} - else: - # If we had neither a scheduled auth cookie or a persistent hidservauth string, - # set the cookie to 'None', which means Tor will create one for us - basic_auth = {"onionshare": None} - else: - # Not using client auth at all - basic_auth = None if mode_settings.get("onion", "private_key"): key_content = mode_settings.get("onion", "private_key") - if self.is_v2_key(key_content): - key_type = "RSA1024" - else: - # Assume it was a v3 key. Stem will throw an error if it's something illegible - key_type = "ED25519-V3" + key_type = "ED25519-V3" else: + key_content = "ED25519-V3" key_type = "NEW" - # Work out if we can support v3 onion services, which are preferred - if self.supports_v3_onions and not mode_settings.get("general", "legacy"): - key_content = "ED25519-V3" - else: - # fall back to v2 onion services - key_content = "RSA1024" - - # v3 onions don't yet support basic auth. Our ticket: - # https://github.com/onionshare/onionshare/issues/697 - if ( - key_type == "NEW" - and key_content == "ED25519-V3" - and not mode_settings.get("general", "legacy") - ): - basic_auth = None debug_message = f"key_type={key_type}" if key_type == "NEW": debug_message += f", key_content={key_content}" self.common.log("Onion", "start_onion_service", debug_message) + + if mode_settings.get("general", "public"): + client_auth_priv_key = None + client_auth_pub_key = None + else: + if not self.supports_stealth: + print( + "Your version of Tor is too old, stealth onion services are not supported" + ) + raise TorTooOldStealth() + else: + if key_type == "NEW" or not mode_settings.get("onion", "client_auth_priv_key"): + # Generate a new key pair for Client Auth on new onions, or if + # it's a persistent onion but for some reason we don't them + client_auth_priv_key_raw = nacl.public.PrivateKey.generate() + client_auth_priv_key = self.key_str(client_auth_priv_key_raw) + client_auth_pub_key = self.key_str(client_auth_priv_key_raw.public_key) + else: + # These should have been saved in settings from the previous run of a persistent onion + client_auth_priv_key = mode_settings.get("onion", "client_auth_priv_key") + client_auth_pub_key = mode_settings.get("onion", "client_auth_pub_key") + try: - res = self.c.create_ephemeral_hidden_service( - {80: port}, - await_publication=await_publication, - basic_auth=basic_auth, - key_type=key_type, - key_content=key_content, - ) + if not self.supports_stealth: + res = self.c.create_ephemeral_hidden_service( + {80: port}, + await_publication=await_publication, + basic_auth=None, + key_type=key_type, + key_content=key_content, + ) + else: + res = self.c.create_ephemeral_hidden_service( + {80: port}, + await_publication=await_publication, + basic_auth=None, + key_type=key_type, + key_content=key_content, + client_auth_v3=client_auth_pub_key, + ) except ProtocolError as e: print("Tor error: {}".format(e.args[0])) @@ -688,12 +696,20 @@ class Onion(object): # Save the private key and hidservauth string if not mode_settings.get("onion", "private_key"): mode_settings.set("onion", "private_key", res.private_key) - if mode_settings.get("general", "client_auth") and not mode_settings.get( - "onion", "hidservauth_string" - ): - auth_cookie = list(res.client_auth.values())[0] - self.auth_string = f"HidServAuth {onion_host} {auth_cookie}" - mode_settings.set("onion", "hidservauth_string", self.auth_string) + + # If using V3 onions and Client Auth, save both the private and public key + # because we need to send the public key to ADD_ONION (if we restart this + # same share at a later date), and the private key to the other user for + # their Tor Browser. + if not mode_settings.get("general", "public"): + mode_settings.set("onion", "client_auth_priv_key", client_auth_priv_key) + mode_settings.set("onion", "client_auth_pub_key", client_auth_pub_key) + # If we were pasting the client auth directly into the filesystem behind a Tor client, + # it would need to be in the format below. However, let's just set the private key + # by itself, as this can be pasted directly into Tor Browser, which is likely to + # be the most common use case. + # self.auth_string = f"{onion_host}:x25519:{client_auth_priv_key}" + self.auth_string = client_auth_priv_key return onion_host @@ -825,15 +841,3 @@ class Onion(object): return ("127.0.0.1", 9150) else: return (self.settings.get("socks_address"), self.settings.get("socks_port")) - - def is_v2_key(self, key): - """ - Helper function for determining if a key is RSA1024 (v2) or not. - """ - try: - # Import the key - key = RSA.importKey(base64.b64decode(key)) - # Is this a v2 Onion key? (1024 bits) If so, we should keep using it. - return key.n.bit_length() == 1024 - except Exception: - return False diff --git a/cli/onionshare_cli/onionshare.py b/cli/onionshare_cli/onionshare.py index bd94100f..c2711b89 100644 --- a/cli/onionshare_cli/onionshare.py +++ b/cli/onionshare_cli/onionshare.py @@ -74,13 +74,15 @@ class OnionShare(object): if self.local_only: self.onion_host = f"127.0.0.1:{self.port}" + if not mode_settings.get("general", "public"): + self.auth_string = "E2GOT5LTUTP3OAMRCRXO4GSH6VKJEUOXZQUC336SRKAHTTT5OVSA" return self.onion_host = self.onion.start_onion_service( mode, mode_settings, self.port, await_publication ) - if mode_settings.get("general", "client_auth"): + if not mode_settings.get("general", "public"): self.auth_string = self.onion.auth_string def stop_onion_service(self, mode_settings): diff --git a/cli/onionshare_cli/resources/templates/401.html b/cli/onionshare_cli/resources/templates/401.html deleted file mode 100644 index 5e43ca01..00000000 --- a/cli/onionshare_cli/resources/templates/401.html +++ /dev/null @@ -1,21 +0,0 @@ -<!DOCTYPE html> -<html> - -<head> - <title>OnionShare: 401 Unauthorized Access</title> - <meta charset="utf-8" /> - <meta name="viewport" content="width=device-width, initial-scale=1"> - <link href="{{ static_url_path }}/img/favicon.ico" rel="icon" type="image/x-icon" /> - <link rel="stylesheet" rel="subresource" type="text/css" href="{{ static_url_path }}/css/style.css" media="all"> -</head> - -<body> - <div class="info-wrapper"> - <div class="info"> - <p><img class="logo" src="{{ static_url_path }}/img/logo_large.png" title="OnionShare"></p> - <p class="info-header">401 Unauthorized Access</p> - </div> - </div> -</body> - -</html> diff --git a/cli/onionshare_cli/resources/templates/send.html b/cli/onionshare_cli/resources/templates/send.html index bd9bd631..5fc1ba1f 100644 --- a/cli/onionshare_cli/resources/templates/send.html +++ b/cli/onionshare_cli/resources/templates/send.html @@ -40,7 +40,7 @@ <div class="d-flex"> <div> <img width="30" height="30" title="" alt="" src="{{ static_url_path }}/img/web_folder.png" /> - <a href="{{ info.basename }}"> + <a href="{{ info.link }}"> <span>{{ info.basename }}</span> </a> </div> @@ -53,7 +53,7 @@ <div> <img width="30" height="30" title="" alt="" src="{{ static_url_path }}/img/web_file.png" /> {% if download_individual_files %} - <a href="{{ info.basename }}"> + <a href="{{ info.link }}"> <span>{{ info.basename }}</span> </a> {% else %} diff --git a/cli/onionshare_cli/resources/version.txt b/cli/onionshare_cli/resources/version.txt index 45674f16..7208c218 100644 --- a/cli/onionshare_cli/resources/version.txt +++ b/cli/onionshare_cli/resources/version.txt @@ -1 +1 @@ -2.3.3
\ No newline at end of file +2.4
\ No newline at end of file diff --git a/cli/onionshare_cli/settings.py b/cli/onionshare_cli/settings.py index 137b690e..4755d5b3 100644 --- a/cli/onionshare_cli/settings.py +++ b/cli/onionshare_cli/settings.py @@ -57,36 +57,36 @@ class Settings(object): self.available_locales = { "ar": "العربية", # Arabic "bn": "বাংলা", # Bengali - "ca": "Català", # Catalan - "zh_Hant": "正體中文 (繁體)", # Traditional Chinese + # "ca": "Català", # Catalan + # "zh_Hant": "正體中文 (繁體)", # Traditional Chinese "zh_Hans": "中文 (简体)", # Simplified Chinese - "hr": "Hrvatski", # Croatian - "da": "Dansk", # Danish - "nl": "Nederlands", # Dutch + # "hr": "Hrvatski", # Croatian + # "da": "Dansk", # Danish + # "nl": "Nederlands", # Dutch "en": "English", # English - # "fi": "Suomi", # Finnish - "fr": "Français", # French + "fi": "Suomi", # Finnish + # "fr": "Français", # French "gl": "Galego", # Galician "de": "Deutsch", # German - "el": "Ελληνικά", # Greek + # "el": "Ελληνικά", # Greek "is": "Íslenska", # Icelandic - "id": "Bahasa Indonesia", # Indonesian + # "id": "Bahasa Indonesia", # Indonesian # "ga": "Gaeilge", # Irish - "it": "Italiano", # Italian - "ja": "日本語", # Japanese - "ckb": "Soranî", # Kurdish (Central) + # "it": "Italiano", # Italian + # "ja": "日本語", # Japanese + # "ckb": "Soranî", # Kurdish (Central) "lt": "Lietuvių Kalba", # Lithuanian "nb_NO": "Norsk Bokmål", # Norwegian Bokmål # "fa": "فارسی", # Persian "pl": "Polski", # Polish "pt_BR": "Português (Brasil)", # Portuguese Brazil - "pt_PT": "Português (Portugal)", # Portuguese Portugal + # "pt_PT": "Português (Portugal)", # Portuguese Portugal # "ro": "Română", # Romanian "ru": "Русский", # Russian - "sr_Latn": "Srpska (latinica)", # Serbian (latin) - "sk": "Slovenčina", # Slovak - "es": "Español", # Spanish + # "sr_Latn": "Srpska (latinica)", # Serbian (latin) + # "sk": "Slovenčina", # Slovak "sv": "Svenska", # Swedish + "es": "Español", # Spanish # "te": "తెలుగు", # Telugu "tr": "Türkçe", # Turkish "uk": "Українська", # Ukrainian diff --git a/cli/onionshare_cli/web/chat_mode.py b/cli/onionshare_cli/web/chat_mode.py index f6dc2d1a..e92ce385 100644 --- a/cli/onionshare_cli/web/chat_mode.py +++ b/cli/onionshare_cli/web/chat_mode.py @@ -68,15 +68,12 @@ class ChatModeWeb: ) self.web.add_request(self.web.REQUEST_LOAD, request.path) - r = make_response( - render_template( + return render_template( "chat.html", static_url_path=self.web.static_url_path, username=session.get("name"), title=self.web.settings.get("general", "title"), - ) ) - return self.web.add_security_headers(r) @self.web.app.route("/update-session-username", methods=["POST"], provide_automatic_options=False) def update_session_username(): @@ -112,7 +109,7 @@ class ChatModeWeb: success=False, ) ) - return self.web.add_security_headers(r) + return r @self.web.socketio.on("joined", namespace="/chat") def joined(message): diff --git a/cli/onionshare_cli/web/receive_mode.py b/cli/onionshare_cli/web/receive_mode.py index 76abb0a8..6b106d37 100644 --- a/cli/onionshare_cli/web/receive_mode.py +++ b/cli/onionshare_cli/web/receive_mode.py @@ -86,16 +86,13 @@ class ReceiveModeWeb: ) self.web.add_request(self.web.REQUEST_LOAD, request.path) - r = make_response( - render_template( - "receive.html", - static_url_path=self.web.static_url_path, - disable_text=self.web.settings.get("receive", "disable_text"), - disable_files=self.web.settings.get("receive", "disable_files"), - title=self.web.settings.get("general", "title"), - ) + return render_template( + "receive.html", + static_url_path=self.web.static_url_path, + disable_text=self.web.settings.get("receive", "disable_text"), + disable_files=self.web.settings.get("receive", "disable_files"), + title=self.web.settings.get("general", "title") ) - return self.web.add_security_headers(r) @self.web.app.route("/upload", methods=["POST"], provide_automatic_options=False) def upload(ajax=False): @@ -222,12 +219,11 @@ class ReceiveModeWeb: ) else: # It was the last upload and the timer ran out - r = make_response( + return make_response( render_template("thankyou.html"), static_url_path=self.web.static_url_path, title=self.web.settings.get("general", "title"), ) - return self.web.add_security_headers(r) @self.web.app.route("/upload-ajax", methods=["POST"], provide_automatic_options=False) def upload_ajax_public(): diff --git a/cli/onionshare_cli/web/send_base_mode.py b/cli/onionshare_cli/web/send_base_mode.py index e448d2dd..27de598a 100644 --- a/cli/onionshare_cli/web/send_base_mode.py +++ b/cli/onionshare_cli/web/send_base_mode.py @@ -149,10 +149,9 @@ class SendBaseModeWeb: # If filesystem_path is None, this is the root directory listing files, dirs = self.build_directory_listing(path, filenames, filesystem_path) - r = self.directory_listing_template( + return self.directory_listing_template( path, files, dirs, breadcrumbs, breadcrumbs_leaf ) - return self.web.add_security_headers(r) def build_directory_listing(self, path, filenames, filesystem_path): files = [] @@ -286,7 +285,6 @@ class SendBaseModeWeb: "filename*": "UTF-8''%s" % url_quote(basename), } r.headers.set("Content-Disposition", "inline", **filename_dict) - r = self.web.add_security_headers(r) (content_type, _) = mimetypes.guess_type(basename, strict=False) if content_type is not None: r.headers.set("Content-Type", content_type) diff --git a/cli/onionshare_cli/web/share_mode.py b/cli/onionshare_cli/web/share_mode.py index 51ddd674..92a4c9af 100644 --- a/cli/onionshare_cli/web/share_mode.py +++ b/cli/onionshare_cli/web/share_mode.py @@ -25,7 +25,7 @@ import sys import tempfile import zipfile import mimetypes -from datetime import datetime +from datetime import datetime, timezone from flask import Response, request, render_template, make_response, abort from unidecode import unidecode from werkzeug.http import parse_date, http_date @@ -127,7 +127,7 @@ class ShareModeWeb(SendBaseModeWeb): self.download_etag = None self.gzip_etag = None - self.last_modified = datetime.utcnow() + self.last_modified = datetime.now(tz=timezone.utc) def define_routes(self): """ @@ -149,8 +149,7 @@ class ShareModeWeb(SendBaseModeWeb): and self.download_in_progress ) if deny_download: - r = make_response(render_template("denied.html")) - return self.web.add_security_headers(r) + return render_template("denied.html") # If download is allowed to continue, serve download page if self.should_use_gzip(): @@ -172,8 +171,7 @@ class ShareModeWeb(SendBaseModeWeb): and self.download_in_progress ) if deny_download: - r = make_response(render_template("denied.html")) - return self.web.add_security_headers(r) + return render_template("denied.html") # Prepare some variables to use inside generate() function below # which is outside of the request context @@ -232,7 +230,6 @@ class ShareModeWeb(SendBaseModeWeb): "filename*": "UTF-8''%s" % url_quote(basename), } r.headers.set("Content-Disposition", "attachment", **filename_dict) - r = self.web.add_security_headers(r) # guess content type (content_type, _) = mimetypes.guess_type(basename, strict=False) if content_type is not None: @@ -288,6 +285,8 @@ class ShareModeWeb(SendBaseModeWeb): if_unmod = request.headers.get("If-Unmodified-Since") if if_unmod: if_date = parse_date(if_unmod) + if if_date and not if_date.tzinfo: + if_date = if_date.replace(tzinfo=timezone.utc) # Compatible with Flask < 2.0.0 if if_date and if_date > last_modified: abort(412) elif range_header is None: @@ -426,10 +425,7 @@ class ShareModeWeb(SendBaseModeWeb): # Render directory listing filenames = [] for filename in os.listdir(filesystem_path): - if os.path.isdir(os.path.join(filesystem_path, filename)): - filenames.append(filename + "/") - else: - filenames.append(filename) + filenames.append(filename) filenames.sort() return self.directory_listing(filenames, path, filesystem_path) 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): """ diff --git a/cli/poetry.lock b/cli/poetry.lock index a9a030ad..c51e1d62 100644 --- a/cli/poetry.lock +++ b/cli/poetry.lock @@ -1,32 +1,33 @@ [[package]] -name = "atomicwrites" -version = "1.4.0" -description = "Atomic file writes." category = "dev" +description = "Atomic file writes." +marker = "sys_platform == \"win32\"" +name = "atomicwrites" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.4.0" [[package]] -name = "attrs" -version = "21.2.0" -description = "Classes Without Boilerplate" category = "dev" +description = "Classes Without Boilerplate" +name = "attrs" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "21.2.0" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] +dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] +tests = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] [[package]] -name = "bidict" -version = "0.21.2" -description = "The bidirectional mapping library for Python." category = "main" +description = "The bidirectional mapping library for Python." +name = "bidict" optional = false python-versions = ">=3.6" +version = "0.21.2" [package.extras] coverage = ["coverage (<6)", "pytest-cov (<3)"] @@ -36,56 +37,67 @@ precommit = ["pre-commit (<3)"] test = ["hypothesis (<6)", "py (<2)", "pytest (<7)", "pytest-benchmark (>=3.2.0,<4)", "sortedcollections (<2)", "sortedcontainers (<3)", "Sphinx (<4)", "sphinx-autodoc-typehints (<2)"] [[package]] +category = "main" +description = "Python package for providing Mozilla's CA Bundle." name = "certifi" +optional = false +python-versions = "*" version = "2021.5.30" -description = "Python package for providing Mozilla's CA Bundle." + +[[package]] category = "main" +description = "Foreign Function Interface for Python calling C code." +name = "cffi" optional = false python-versions = "*" +version = "1.14.6" + +[package.dependencies] +pycparser = "*" [[package]] -name = "chardet" -version = "4.0.0" -description = "Universal encoding detector for Python 2 and 3" category = "main" +description = "Universal encoding detector for Python 2 and 3" +name = "chardet" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "4.0.0" [[package]] -name = "click" -version = "7.1.2" -description = "Composable command line interface toolkit" category = "main" +description = "Composable command line interface toolkit" +name = "click" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "7.1.2" [[package]] -name = "colorama" -version = "0.4.4" -description = "Cross-platform colored terminal text." category = "main" +description = "Cross-platform colored terminal text." +name = "colorama" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.4.4" [[package]] -name = "dnspython" -version = "1.16.0" -description = "DNS toolkit" category = "main" +description = "DNS toolkit" +name = "dnspython" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.16.0" [package.extras] DNSSEC = ["pycryptodome", "ecdsa (>=0.13)"] IDNA = ["idna (>=2.1)"] [[package]] -name = "eventlet" -version = "0.31.0" -description = "Highly concurrent networking library" category = "main" +description = "Highly concurrent networking library" +name = "eventlet" optional = false python-versions = "*" +version = "0.31.0" [package.dependencies] dnspython = ">=1.15.0,<2.0.0" @@ -93,18 +105,18 @@ greenlet = ">=0.3" six = ">=1.10.0" [[package]] -name = "flask" -version = "1.1.4" -description = "A simple framework for building complex web applications." category = "main" +description = "A simple framework for building complex web applications." +name = "flask" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "1.1.4" [package.dependencies] -click = ">=5.1,<8.0" -itsdangerous = ">=0.24,<2.0" Jinja2 = ">=2.10.1,<3.0" Werkzeug = ">=0.15,<2.0" +click = ">=5.1,<8.0" +itsdangerous = ">=0.24,<2.0" [package.extras] dev = ["pytest", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] @@ -112,86 +124,79 @@ docs = ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx- dotenv = ["python-dotenv"] [[package]] -name = "flask-httpauth" -version = "4.4.0" -description = "Basic and Digest HTTP authentication for Flask routes" category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -Flask = "*" - -[[package]] -name = "flask-socketio" -version = "5.0.1" description = "Socket.IO integration for Flask applications" -category = "main" +name = "flask-socketio" optional = false python-versions = "*" +version = "5.0.1" [package.dependencies] Flask = ">=0.9" python-socketio = ">=5.0.2" [[package]] -name = "greenlet" -version = "1.1.0" -description = "Lightweight in-process concurrent programming" category = "main" +description = "Lightweight in-process concurrent programming" +name = "greenlet" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" +version = "1.1.0" [package.extras] docs = ["sphinx"] [[package]] -name = "idna" -version = "2.10" -description = "Internationalized Domain Names in Applications (IDNA)" category = "main" +description = "Internationalized Domain Names in Applications (IDNA)" +name = "idna" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.10" [[package]] -name = "importlib-metadata" -version = "4.4.0" -description = "Read metadata from Python packages" category = "dev" +description = "Read metadata from Python packages" +marker = "python_version < \"3.8\"" +name = "importlib-metadata" optional = false python-versions = ">=3.6" +version = "4.4.0" [package.dependencies] -typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" +[package.dependencies.typing-extensions] +python = "<3.8" +version = ">=3.6.4" + [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] [[package]] -name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" category = "dev" +description = "iniconfig: brain-dead simple config-ini parsing" +name = "iniconfig" optional = false python-versions = "*" +version = "1.1.1" [[package]] -name = "itsdangerous" -version = "1.1.0" -description = "Various helpers to pass data to untrusted environments and back." category = "main" +description = "Various helpers to pass data to untrusted environments and back." +name = "itsdangerous" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.1.0" [[package]] -name = "jinja2" -version = "2.11.3" -description = "A very fast and expressive template engine." category = "main" +description = "A very fast and expressive template engine." +name = "jinja2" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "2.11.3" [package.dependencies] MarkupSafe = ">=0.23" @@ -200,122 +205,143 @@ MarkupSafe = ">=0.23" i18n = ["Babel (>=0.8)"] [[package]] -name = "markupsafe" -version = "2.0.1" -description = "Safely add untrusted strings to HTML/XML markup." category = "main" +description = "Safely add untrusted strings to HTML/XML markup." +name = "markupsafe" optional = false python-versions = ">=3.6" +version = "2.0.1" [[package]] -name = "packaging" -version = "20.9" -description = "Core utilities for Python packages" category = "dev" +description = "Core utilities for Python packages" +name = "packaging" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "20.9" [package.dependencies] pyparsing = ">=2.0.2" [[package]] -name = "pluggy" -version = "0.13.1" -description = "plugin and hook calling mechanisms for python" category = "dev" +description = "plugin and hook calling mechanisms for python" +name = "pluggy" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.13.1" [package.dependencies] -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +[package.dependencies.importlib-metadata] +python = "<3.8" +version = ">=0.12" [package.extras] dev = ["pre-commit", "tox"] [[package]] -name = "psutil" -version = "5.8.0" -description = "Cross-platform lib for process and system monitoring in Python." category = "main" +description = "Cross-platform lib for process and system monitoring in Python." +name = "psutil" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "5.8.0" [package.extras] test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"] [[package]] +category = "dev" +description = "library with cross-python path, ini-parsing, io, code, log facilities" name = "py" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "1.10.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "dev" + +[[package]] +category = "main" +description = "C parser in Python" +name = "pycparser" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.20" [[package]] -name = "pycryptodome" -version = "3.10.1" -description = "Cryptographic library for Python" category = "main" +description = "Python binding to the Networking and Cryptography (NaCl) library" +name = "pynacl" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.4.0" + +[package.dependencies] +cffi = ">=1.4.1" +six = "*" + +[package.extras] +docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] +tests = ["pytest (>=3.2.1,<3.3.0 || >3.3.0)", "hypothesis (>=3.27.0)"] [[package]] -name = "pyparsing" -version = "2.4.7" -description = "Python parsing module" category = "dev" +description = "Python parsing module" +name = "pyparsing" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "2.4.7" [[package]] -name = "pysocks" -version = "1.7.1" -description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information." category = "main" +description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information." +name = "pysocks" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.7.1" [[package]] -name = "pytest" -version = "6.2.4" -description = "pytest: simple powerful testing with Python" category = "dev" +description = "pytest: simple powerful testing with Python" +name = "pytest" optional = false python-versions = ">=3.6" +version = "6.2.4" [package.dependencies] -atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +atomicwrites = ">=1.0" attrs = ">=19.2.0" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +colorama = "*" iniconfig = "*" packaging = "*" pluggy = ">=0.12,<1.0.0a1" py = ">=1.8.2" toml = "*" +[package.dependencies.importlib-metadata] +python = "<3.8" +version = ">=0.12" + [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] [[package]] -name = "python-engineio" -version = "4.2.0" -description = "Engine.IO server" category = "main" +description = "Engine.IO server" +name = "python-engineio" optional = false python-versions = "*" +version = "4.2.0" [package.extras] asyncio_client = ["aiohttp (>=3.4)"] client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"] [[package]] -name = "python-socketio" -version = "5.3.0" -description = "Socket.IO server" category = "main" +description = "Socket.IO server" +name = "python-socketio" optional = false python-versions = "*" +version = "5.3.0" [package.dependencies] bidict = ">=0.21.0" @@ -326,105 +352,114 @@ asyncio_client = ["aiohttp (>=3.4)", "websockets (>=7.0)"] client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"] [[package]] -name = "requests" -version = "2.25.1" -description = "Python HTTP for Humans." category = "main" +description = "Python HTTP for Humans." +name = "requests" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "2.25.1" [package.dependencies] certifi = ">=2017.4.17" chardet = ">=3.0.2,<5" idna = ">=2.5,<3" -PySocks = {version = ">=1.5.6,<1.5.7 || >1.5.7", optional = true, markers = "extra == \"socks\""} urllib3 = ">=1.21.1,<1.27" +[package.dependencies.PySocks] +optional = true +version = ">=1.5.6,<1.5.7 || >1.5.7" + [package.extras] security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] -socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] [[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" category = "main" +description = "Python 2 and 3 compatibility utilities" +name = "six" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +version = "1.16.0" [[package]] -name = "stem" -version = "1.8.0" -description = "Stem is a Python controller library that allows applications to interact with Tor (https://www.torproject.org/)." category = "main" +description = "" +name = "stem" optional = false python-versions = "*" +version = "1.8.1" + +[package.source] +reference = "de3d03a03c7ee57c74c80e9c63cb88072d833717" +type = "git" +url = "https://github.com/onionshare/stem.git" [[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" category = "dev" +description = "Python Library for Tom's Obvious, Minimal Language" +name = "toml" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "0.10.2" [[package]] -name = "typing-extensions" -version = "3.10.0.0" -description = "Backported and Experimental Type Hints for Python 3.5+" category = "dev" +description = "Backported and Experimental Type Hints for Python 3.5+" +marker = "python_version < \"3.8\"" +name = "typing-extensions" optional = false python-versions = "*" +version = "3.10.0.0" [[package]] -name = "unidecode" -version = "1.2.0" -description = "ASCII transliterations of Unicode text" category = "main" +description = "ASCII transliterations of Unicode text" +name = "unidecode" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.2.0" [[package]] -name = "urllib3" -version = "1.26.5" -description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" +description = "HTTP library with thread-safe connection pooling, file post, and more." +name = "urllib3" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +version = "1.26.5" [package.extras] brotli = ["brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] [[package]] -name = "werkzeug" -version = "1.0.1" -description = "The comprehensive WSGI web application library." category = "main" +description = "The comprehensive WSGI web application library." +name = "werkzeug" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "1.0.1" [package.extras] dev = ["pytest", "pytest-timeout", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinx-issues"] watchdog = ["watchdog"] [[package]] -name = "zipp" -version = "3.4.1" -description = "Backport of pathlib-compatible object wrapper for zip files" category = "dev" +description = "Backport of pathlib-compatible object wrapper for zip files" +marker = "python_version < \"3.8\"" +name = "zipp" optional = false python-versions = ">=3.6" +version = "3.4.1" [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] [metadata] -lock-version = "1.1" +content-hash = "181891640e59dac730905019444d42ef8e99da0c34c96fb8a616781661bae537" python-versions = "^3.6" -content-hash = "b33fc47db907e6db7cb254b5cac34b0d9558547418e8074280063159b291766a" [metadata.files] atomicwrites = [ @@ -443,6 +478,53 @@ certifi = [ {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"}, {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"}, ] +cffi = [ + {file = "cffi-1.14.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c"}, + {file = "cffi-1.14.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99"}, + {file = "cffi-1.14.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819"}, + {file = "cffi-1.14.6-cp27-cp27m-win32.whl", hash = "sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20"}, + {file = "cffi-1.14.6-cp27-cp27m-win_amd64.whl", hash = "sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224"}, + {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7"}, + {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33"}, + {file = "cffi-1.14.6-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534"}, + {file = "cffi-1.14.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a"}, + {file = "cffi-1.14.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5"}, + {file = "cffi-1.14.6-cp35-cp35m-win32.whl", hash = "sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca"}, + {file = "cffi-1.14.6-cp35-cp35m-win_amd64.whl", hash = "sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218"}, + {file = "cffi-1.14.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f"}, + {file = "cffi-1.14.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872"}, + {file = "cffi-1.14.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195"}, + {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d"}, + {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b"}, + {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb"}, + {file = "cffi-1.14.6-cp36-cp36m-win32.whl", hash = "sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a"}, + {file = "cffi-1.14.6-cp36-cp36m-win_amd64.whl", hash = "sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e"}, + {file = "cffi-1.14.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5"}, + {file = "cffi-1.14.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf"}, + {file = "cffi-1.14.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69"}, + {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56"}, + {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c"}, + {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762"}, + {file = "cffi-1.14.6-cp37-cp37m-win32.whl", hash = "sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771"}, + {file = "cffi-1.14.6-cp37-cp37m-win_amd64.whl", hash = "sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a"}, + {file = "cffi-1.14.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0"}, + {file = "cffi-1.14.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e"}, + {file = "cffi-1.14.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346"}, + {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc"}, + {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd"}, + {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc"}, + {file = "cffi-1.14.6-cp38-cp38-win32.whl", hash = "sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548"}, + {file = "cffi-1.14.6-cp38-cp38-win_amd64.whl", hash = "sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156"}, + {file = "cffi-1.14.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d"}, + {file = "cffi-1.14.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e"}, + {file = "cffi-1.14.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c"}, + {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202"}, + {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f"}, + {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87"}, + {file = "cffi-1.14.6-cp39-cp39-win32.whl", hash = "sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728"}, + {file = "cffi-1.14.6-cp39-cp39-win_amd64.whl", hash = "sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2"}, + {file = "cffi-1.14.6.tar.gz", hash = "sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd"}, +] chardet = [ {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, @@ -467,10 +549,6 @@ flask = [ {file = "Flask-1.1.4-py2.py3-none-any.whl", hash = "sha256:c34f04500f2cbbea882b1acb02002ad6fe6b7ffa64a6164577995657f50aed22"}, {file = "Flask-1.1.4.tar.gz", hash = "sha256:0fbeb6180d383a9186d0d6ed954e0042ad9f18e0e8de088b2b419d526927d196"}, ] -flask-httpauth = [ - {file = "Flask-HTTPAuth-4.4.0.tar.gz", hash = "sha256:bcaaa7a35a3cba0b2eafd4f113b3016bf70eb78087456d96484c3c18928b813a"}, - {file = "Flask_HTTPAuth-4.4.0-py2.py3-none-any.whl", hash = "sha256:d9131122cdc5709dda63790f6e9b3142d8101447d424b0b95ffd4ee279f49539"}, -] flask-socketio = [ {file = "Flask-SocketIO-5.0.1.tar.gz", hash = "sha256:5c4319f5214ada20807857dc8fdf3dc7d2afe8d6dd38f5c516c72e2be47d2227"}, {file = "Flask_SocketIO-5.0.1-py2.py3-none-any.whl", hash = "sha256:5d9a4438bafd806c5a3b832e74b69758781a8ee26fb6c9b1dbdda9b4fced432e"}, @@ -547,12 +625,22 @@ jinja2 = [ {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"}, ] markupsafe = [ + {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, + {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, @@ -561,14 +649,21 @@ markupsafe = [ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, @@ -578,6 +673,9 @@ markupsafe = [ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, @@ -624,37 +722,29 @@ py = [ {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, ] -pycryptodome = [ - {file = "pycryptodome-3.10.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1c5e1ca507de2ad93474be5cfe2bfa76b7cf039a1a32fc196f40935944871a06"}, - {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:6260e24d41149268122dd39d4ebd5941e9d107f49463f7e071fd397e29923b0c"}, - {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:3f840c49d38986f6e17dbc0673d37947c88bc9d2d9dba1c01b979b36f8447db1"}, - {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:2dea65df54349cdfa43d6b2e8edb83f5f8d6861e5cf7b1fbc3e34c5694c85e27"}, - {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:e61e363d9a5d7916f3a4ce984a929514c0df3daf3b1b2eb5e6edbb131ee771cf"}, - {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:2603c98ae04aac675fefcf71a6c87dc4bb74a75e9071ae3923bbc91a59f08d35"}, - {file = "pycryptodome-3.10.1-cp27-cp27m-win32.whl", hash = "sha256:38661348ecb71476037f1e1f553159b80d256c00f6c0b00502acac891f7116d9"}, - {file = "pycryptodome-3.10.1-cp27-cp27m-win_amd64.whl", hash = "sha256:1723ebee5561628ce96748501cdaa7afaa67329d753933296321f0be55358dce"}, - {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:77997519d8eb8a4adcd9a47b9cec18f9b323e296986528186c0e9a7a15d6a07e"}, - {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:99b2f3fc51d308286071d0953f92055504a6ffe829a832a9fc7a04318a7683dd"}, - {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:e0a4d5933a88a2c98bbe19c0c722f5483dc628d7a38338ac2cb64a7dbd34064b"}, - {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d3d6958d53ad307df5e8469cc44474a75393a434addf20ecd451f38a72fe29b8"}, - {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:a8eb8b6ea09ec1c2535bf39914377bc8abcab2c7d30fa9225eb4fe412024e427"}, - {file = "pycryptodome-3.10.1-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:31c1df17b3dc5f39600a4057d7db53ac372f492c955b9b75dd439f5d8b460129"}, - {file = "pycryptodome-3.10.1-cp35-abi3-manylinux1_i686.whl", hash = "sha256:a3105a0eb63eacf98c2ecb0eb4aa03f77f40fbac2bdde22020bb8a536b226bb8"}, - {file = "pycryptodome-3.10.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:a92d5c414e8ee1249e850789052608f582416e82422502dc0ac8c577808a9067"}, - {file = "pycryptodome-3.10.1-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:60386d1d4cfaad299803b45a5bc2089696eaf6cdd56f9fc17479a6f89595cfc8"}, - {file = "pycryptodome-3.10.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:501ab36aae360e31d0ec370cf5ce8ace6cb4112060d099b993bc02b36ac83fb6"}, - {file = "pycryptodome-3.10.1-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:fc7489a50323a0df02378bc2fff86eb69d94cc5639914346c736be981c6a02e7"}, - {file = "pycryptodome-3.10.1-cp35-abi3-win32.whl", hash = "sha256:9b6f711b25e01931f1c61ce0115245a23cdc8b80bf8539ac0363bdcf27d649b6"}, - {file = "pycryptodome-3.10.1-cp35-abi3-win_amd64.whl", hash = "sha256:7fd519b89585abf57bf47d90166903ec7b43af4fe23c92273ea09e6336af5c07"}, - {file = "pycryptodome-3.10.1-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:09c1555a3fa450e7eaca41ea11cd00afe7c91fef52353488e65663777d8524e0"}, - {file = "pycryptodome-3.10.1-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:758949ca62690b1540dfb24ad773c6da9cd0e425189e83e39c038bbd52b8e438"}, - {file = "pycryptodome-3.10.1-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:e3bf558c6aeb49afa9f0c06cee7fb5947ee5a1ff3bd794b653d39926b49077fa"}, - {file = "pycryptodome-3.10.1-pp27-pypy_73-win32.whl", hash = "sha256:f977cdf725b20f6b8229b0c87acb98c7717e742ef9f46b113985303ae12a99da"}, - {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6d2df5223b12437e644ce0a3be7809471ffa71de44ccd28b02180401982594a6"}, - {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:98213ac2b18dc1969a47bc65a79a8fca02a414249d0c8635abb081c7f38c91b6"}, - {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:12222a5edc9ca4a29de15fbd5339099c4c26c56e13c2ceddf0b920794f26165d"}, - {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-win32.whl", hash = "sha256:6bbf7fee7b7948b29d7e71fcacf48bac0c57fb41332007061a933f2d996f9713"}, - {file = "pycryptodome-3.10.1.tar.gz", hash = "sha256:3e2e3a06580c5f190df843cdb90ea28d61099cf4924334d5297a995de68e4673"}, +pycparser = [ + {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, + {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, +] +pynacl = [ + {file = "PyNaCl-1.4.0-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff"}, + {file = "PyNaCl-1.4.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:d452a6746f0a7e11121e64625109bc4468fc3100452817001dbe018bb8b08514"}, + {file = "PyNaCl-1.4.0-cp27-cp27m-win32.whl", hash = "sha256:2fe0fc5a2480361dcaf4e6e7cea00e078fcda07ba45f811b167e3f99e8cff574"}, + {file = "PyNaCl-1.4.0-cp27-cp27m-win_amd64.whl", hash = "sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80"}, + {file = "PyNaCl-1.4.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7757ae33dae81c300487591c68790dfb5145c7d03324000433d9a2c141f82af7"}, + {file = "PyNaCl-1.4.0-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:757250ddb3bff1eecd7e41e65f7f833a8405fede0194319f87899690624f2122"}, + {file = "PyNaCl-1.4.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:30f9b96db44e09b3304f9ea95079b1b7316b2b4f3744fe3aaecccd95d547063d"}, + {file = "PyNaCl-1.4.0-cp35-abi3-win32.whl", hash = "sha256:4e10569f8cbed81cb7526ae137049759d2a8d57726d52c1a000a3ce366779634"}, + {file = "PyNaCl-1.4.0-cp35-abi3-win_amd64.whl", hash = "sha256:c914f78da4953b33d4685e3cdc7ce63401247a21425c16a39760e282075ac4a6"}, + {file = "PyNaCl-1.4.0-cp35-cp35m-win32.whl", hash = "sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4"}, + {file = "PyNaCl-1.4.0-cp35-cp35m-win_amd64.whl", hash = "sha256:511d269ee845037b95c9781aa702f90ccc36036f95d0f31373a6a79bd8242e25"}, + {file = "PyNaCl-1.4.0-cp36-cp36m-win32.whl", hash = "sha256:11335f09060af52c97137d4ac54285bcb7df0cef29014a1a4efe64ac065434c4"}, + {file = "PyNaCl-1.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:cd401ccbc2a249a47a3a1724c2918fcd04be1f7b54eb2a5a71ff915db0ac51c6"}, + {file = "PyNaCl-1.4.0-cp37-cp37m-win32.whl", hash = "sha256:8122ba5f2a2169ca5da936b2e5a511740ffb73979381b4229d9188f6dcb22f1f"}, + {file = "PyNaCl-1.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:537a7ccbea22905a0ab36ea58577b39d1fa9b1884869d173b5cf111f006f689f"}, + {file = "PyNaCl-1.4.0-cp38-cp38-win32.whl", hash = "sha256:9c4a7ea4fb81536c1b1f5cc44d54a296f96ae78c1ebd2311bd0b60be45a48d96"}, + {file = "PyNaCl-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:7c6092102219f59ff29788860ccb021e80fffd953920c4a8653889c029b2d420"}, + {file = "PyNaCl-1.4.0.tar.gz", hash = "sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505"}, ] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, @@ -685,9 +775,7 @@ six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -stem = [ - {file = "stem-1.8.0.tar.gz", hash = "sha256:a0b48ea6224e95f22aa34c0bc3415f0eb4667ddeae3dfb5e32a6920c185568c2"}, -] +stem = [] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, diff --git a/cli/pyproject.toml b/cli/pyproject.toml index 51405d3d..9994240c 100644 --- a/cli/pyproject.toml +++ b/cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "onionshare_cli" -version = "2.3.3" +version = "2.4" description = "OnionShare lets you securely and anonymously send and receive files. It works by starting a web server, making it accessible as a Tor onion service, and generating an unguessable web address so others can download files from you, or upload files to you. It does _not_ require setting up a separate server or using a third party file-sharing service." authors = ["Micah Lee <micah@micahflee.com>"] license = "GPLv3+" @@ -19,18 +19,17 @@ classifiers = [ python = "^3.6" click = "*" flask = "1.1.4" -flask-httpauth = "*" flask-socketio = "5.0.1" psutil = "*" -pycryptodome = "*" pysocks = "*" requests = {extras = ["socks"], version = "*"} -stem = "*" unidecode = "*" urllib3 = "*" eventlet = "*" setuptools = "*" +pynacl = "^1.4.0" colorama = "*" +stem = {git = "https://github.com/onionshare/stem.git", rev = "1.8.1"} [tool.poetry.dev-dependencies] pytest = "*" diff --git a/cli/setup.py b/cli/setup.py index ce5e229f..407991d0 100644 --- a/cli/setup.py +++ b/cli/setup.py @@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. """ import setuptools -version = "2.3.3" +version = "2.4" setuptools.setup( name="onionshare-cli", diff --git a/cli/tests/test_cli_common.py b/cli/tests/test_cli_common.py index 3288e52b..9f113a84 100644 --- a/cli/tests/test_cli_common.py +++ b/cli/tests/test_cli_common.py @@ -169,7 +169,7 @@ class TestGetTorPaths: obfs4proxy_file_path, ) - @pytest.mark.skipif(sys.platform != "Linux", reason="requires Linux") + @pytest.mark.skipif(sys.platform != "linux", reason="requires Linux") def test_get_tor_paths_linux(self, platform_linux, common_obj): ( tor_path, diff --git a/cli/tests/test_cli_settings.py b/cli/tests/test_cli_settings.py index 4c012901..ed8d5bb9 100644 --- a/cli/tests/test_cli_settings.py +++ b/cli/tests/test_cli_settings.py @@ -123,7 +123,7 @@ class TestSettings: "~/Library/Application Support/OnionShare-testdata/onionshare.json" ) - @pytest.mark.skipif(sys.platform != "Linux", reason="requires Linux") + @pytest.mark.skipif(sys.platform != "linux", reason="requires Linux") def test_filename_linux(self, monkeypatch, platform_linux): obj = settings.Settings(common.Common()) assert obj.filename == os.path.expanduser( diff --git a/cli/tests/test_cli_web.py b/cli/tests/test_cli_web.py index f8c96f9c..71bfeeeb 100644 --- a/cli/tests/test_cli_web.py +++ b/cli/tests/test_cli_web.py @@ -48,7 +48,6 @@ def web_obj(temp_dir, common_obj, mode, num_files=0): common_obj.settings = Settings(common_obj) mode_settings = ModeSettings(common_obj) web = Web(common_obj, False, mode_settings, mode) - web.generate_password() web.running = True web.cleanup_filenames == [] @@ -75,23 +74,13 @@ class TestWeb: web = web_obj(temp_dir, common_obj, "share", 3) assert web.mode == "share" with web.app.test_client() as c: - # Load / without auth + # Load / res = c.get("/") res.get_data() - assert res.status_code == 401 - - # Load / with invalid auth - res = c.get("/", headers=self._make_auth_headers("invalid")) - res.get_data() - assert res.status_code == 401 - - # Load / with valid auth - res = c.get("/", headers=self._make_auth_headers(web.password)) - res.get_data() assert res.status_code == 200 # Download - res = c.get("/download", headers=self._make_auth_headers(web.password)) + res = c.get("/download") res.get_data() assert res.status_code == 200 assert ( @@ -107,7 +96,7 @@ class TestWeb: with web.app.test_client() as c: # Download the first time - res = c.get("/download", headers=self._make_auth_headers(web.password)) + res = c.get("/download") res.get_data() assert res.status_code == 200 assert ( @@ -127,7 +116,7 @@ class TestWeb: with web.app.test_client() as c: # Download the first time - res = c.get("/download", headers=self._make_auth_headers(web.password)) + res = c.get("/download") res.get_data() assert res.status_code == 200 assert ( @@ -141,18 +130,8 @@ class TestWeb: assert web.mode == "receive" with web.app.test_client() as c: - # Load / without auth - res = c.get("/") - res.get_data() - assert res.status_code == 401 - - # Load / with invalid auth - res = c.get("/", headers=self._make_auth_headers("invalid")) - res.get_data() - assert res.status_code == 401 - # Load / with valid auth - res = c.get("/", headers=self._make_auth_headers(web.password)) + res = c.get("/",) res.get_data() assert res.status_code == 200 @@ -171,7 +150,7 @@ class TestWeb: ) with web.app.test_client() as c: - res = c.get("/", headers=self._make_auth_headers(web.password)) + res = c.get("/") res.get_data() assert res.status_code == 200 @@ -180,7 +159,6 @@ class TestWeb: buffered=True, content_type="multipart/form-data", data={"file[]": (BytesIO(b"THIS IS A TEST FILE"), "new_york.jpg")}, - headers=self._make_auth_headers(web.password), ) res.get_data() assert res.status_code == 200 @@ -202,7 +180,6 @@ class TestWeb: buffered=True, content_type="multipart/form-data", data={"text": "you know just sending an anonymous message"}, - headers=self._make_auth_headers(web.password), ) content = res.get_data() assert res.status_code == 200 @@ -237,7 +214,6 @@ class TestWeb: "file[]": (BytesIO(b"THIS IS A TEST FILE"), "new_york.jpg"), "text": "you know just sending an anonymous message", }, - headers=self._make_auth_headers(web.password), ) content = res.get_data() assert res.status_code == 200 @@ -270,7 +246,6 @@ class TestWeb: buffered=True, content_type="multipart/form-data", data={"file[]": (BytesIO(b"THIS IS A TEST FILE"), "new_york.jpg")}, - headers=self._make_auth_headers(web.password), ) content = res.get_data() assert res.status_code == 200 @@ -303,7 +278,6 @@ class TestWeb: buffered=True, content_type="multipart/form-data", data={}, - headers=self._make_auth_headers(web.password), ) content = res.get_data() assert res.status_code == 200 @@ -326,26 +300,6 @@ class TestWeb: res.get_data() assert res.status_code == 200 - def test_public_mode_off(self, temp_dir, common_obj): - web = web_obj(temp_dir, common_obj, "receive") - web.settings.set("general", "public", False) - - with web.app.test_client() as c: - # Load / without auth - res = c.get("/") - res.get_data() - assert res.status_code == 401 - - # But static resources should work without auth - res = c.get(f"{web.static_url_path}/css/style.css") - res.get_data() - assert res.status_code == 200 - - # Load / with valid auth - res = c.get("/", headers=self._make_auth_headers(web.password)) - res.get_data() - assert res.status_code == 200 - def test_cleanup(self, common_obj, temp_dir_1024, temp_file_1024): web = web_obj(temp_dir_1024, common_obj, "share", 3) @@ -356,12 +310,6 @@ class TestWeb: assert os.path.exists(temp_dir_1024) is False assert web.cleanup_filenames == [] - def _make_auth_headers(self, password): - auth = base64.b64encode(b"onionshare:" + password.encode()).decode() - h = Headers() - h.add("Authorization", "Basic " + auth) - return h - class TestZipWriterDefault: @pytest.mark.parametrize( @@ -450,8 +398,7 @@ def live_server(web): proc.start() url = "http://127.0.0.1:{}".format(port) - auth = base64.b64encode(b"onionshare:" + web.password.encode()).decode() - req = Request(url, headers={"Authorization": "Basic {}".format(auth)}) + req = Request(url) attempts = 20 while True: @@ -509,7 +456,7 @@ class TestRangeRequests: url = "/download" with web.app.test_client() as client: - resp = client.get(url, headers=self._make_auth_headers(web.password)) + resp = client.get(url) assert resp.headers["ETag"].startswith('"sha256:') assert resp.headers["Accept-Ranges"] == "bytes" assert resp.headers.get("Last-Modified") is not None @@ -524,7 +471,7 @@ class TestRangeRequests: contents = f.read() with web.app.test_client() as client: - resp = client.get(url, headers=self._make_auth_headers(web.password)) + resp = client.get(url) assert resp.status_code == 200 assert resp.data == contents @@ -536,7 +483,7 @@ class TestRangeRequests: contents = f.read() with web.app.test_client() as client: - headers = self._make_auth_headers(web.password) + headers = Headers() headers.extend({"Range": "bytes=0-10"}) resp = client.get(url, headers=headers) assert resp.status_code == 206 @@ -572,7 +519,7 @@ class TestRangeRequests: contents = f.read() with web.app.test_client() as client: - headers = self._make_auth_headers(web.password) + headers = Headers() resp = client.get(url, headers=headers) assert resp.status_code == 200 @@ -587,7 +534,7 @@ class TestRangeRequests: url = "/download" with web.app.test_client() as client: - headers = self._make_auth_headers(web.password) + headers = Headers() resp = client.get(url, headers=headers) assert resp.status_code == 200 last_mod = resp.headers["Last-Modified"] @@ -602,7 +549,7 @@ class TestRangeRequests: url = "/download" with web.app.test_client() as client: - headers = self._make_auth_headers(web.password) + headers = Headers() resp = client.get(url, headers=headers) assert resp.status_code == 200 @@ -621,13 +568,8 @@ class TestRangeRequests: resp = client.get(url, headers=headers) assert resp.status_code == 206 - def _make_auth_headers(self, password): - auth = base64.b64encode(b"onionshare:" + password.encode()).decode() - h = Headers() - h.add("Authorization", "Basic " + auth) - return h - @pytest.mark.skipif(sys.platform != "Linux", reason="requires Linux") + @pytest.mark.skipif(sys.platform != "linux", reason="requires Linux") @check_unsupported("curl", ["--version"]) def test_curl(self, temp_dir, tmpdir, common_obj): web = web_obj(temp_dir, common_obj, "share", 3) @@ -638,12 +580,9 @@ class TestRangeRequests: with live_server(web) as url: # Debugging help from `man curl`, on error 33 # 33 HTTP range error. The range "command" didn't work. - auth_header = self._make_auth_headers(web.password) subprocess.check_call( [ "curl", - "-H", - str(auth_header).strip(), "--output", str(download), "--continue-at", @@ -652,7 +591,7 @@ class TestRangeRequests: ] ) - @pytest.mark.skipif(sys.platform != "Linux", reason="requires Linux") + @pytest.mark.skipif(sys.platform != "linux", reason="requires Linux") @check_unsupported("wget", ["--version"]) def test_wget(self, temp_dir, tmpdir, common_obj): web = web_obj(temp_dir, common_obj, "share", 3) @@ -663,12 +602,9 @@ class TestRangeRequests: download.write("x" * 10) with live_server(web) as url: - auth_header = self._make_auth_headers(web.password) subprocess.check_call( [ "wget", - "--header", - str(auth_header).strip(), "--continue", "-O", str(download), |