Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HTTP Basic Auth (password to access) #11

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ python meshchat.py --help

```
usage: meshchat.py [-h] [--host [HOST]] [--port [PORT]] [--headless] [--identity-file IDENTITY_FILE] [--identity-base64 IDENTITY_BASE64] [--generate-identity-file GENERATE_IDENTITY_FILE] [--generate-identity-base64]
[--reticulum-config-dir RETICULUM_CONFIG_DIR] [--storage-dir STORAGE_DIR]
[--reticulum-config-dir RETICULUM_CONFIG_DIR] [--storage-dir STORAGE_DIR] [--password PASSWORD]

ReticulumMeshChat

Expand All @@ -188,6 +188,8 @@ options:
Path to a Reticulum config directory for the RNS stack to use (e.g: ~/.reticulum)
--storage-dir STORAGE_DIR
Path to a directory for storing databases and config files (default: ./storage)
--password PASSWORD
Use HTTP Basic Auth to password protect access to the application
```

## Running on Android
Expand Down
25 changes: 22 additions & 3 deletions meshchat.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def get_file_path(filename):

class ReticulumMeshChat:

def __init__(self, identity: RNS.Identity, storage_dir, reticulum_config_dir):
def __init__(self, identity: RNS.Identity, storage_dir, reticulum_config_dir, password):

# when providing a custom storage_dir, files will be saved as
# <storage_dir>/identities/<identity_hex>/
Expand Down Expand Up @@ -125,6 +125,8 @@ def __init__(self, identity: RNS.Identity, storage_dir, reticulum_config_dir):
self.audio_call_manager = AudioCallManager(identity=self.identity)
self.audio_call_manager.register_incoming_call_callback(self.on_incoming_audio_call)

self.password = password

# start background thread for auto announce loop
thread = threading.Thread(target=asyncio.run, args=(self.announce_loop(),))
thread.daemon = True
Expand Down Expand Up @@ -1120,6 +1122,22 @@ async def index(request):
"message": "ok",
})

@web.middleware
async def password_protect(request, handler):
if self.password is not None:
correct_password = False
if "Authorization" in request.headers:
# header value in format of "Basic <base64>"
auth_header = request.headers["Authorization"]
provided_password = ":".join(base64.b64decode(auth_header.split("Basic ")[1]).decode("utf-8").split(":")[1:])
if provided_password == self.password and auth_header.startswith("Basic"):
correct_password = True
if not correct_password:
return web.Response(status=401, headers={
"WWW-Authenticate": "Basic realm=\"Access the application\", charset=\"UTF-8\""
})
return await handler(request);

# called when web app has started
async def on_startup(app):

Expand All @@ -1131,7 +1149,7 @@ async def on_startup(app):
print("failed to launch web browser")

# create and run web app
app = web.Application(client_max_size=1024 * 1024 * 50) # allow uploading files up to 50mb
app = web.Application(client_max_size=1024 * 1024 * 50, middlewares=[password_protect]) # allow uploading files up to 50mb
app.add_routes(routes)
app.add_routes([web.static('/', get_file_path("public/"))]) # serve anything in public folder
app.on_shutdown.append(self.shutdown) # need to force close websockets and stop reticulum now
Expand Down Expand Up @@ -2169,6 +2187,7 @@ def main():
parser.add_argument("--reticulum-config-dir", type=str, help="Path to a Reticulum config directory for the RNS stack to use (e.g: ~/.reticulum)")
parser.add_argument("--storage-dir", type=str, help="Path to a directory for storing databases and config files (default: ./storage)")
parser.add_argument("--test-exception-message", type=str, help="Throws an exception. Used for testing the electron error dialog")
parser.add_argument("--password", type=str, help="Use HTTP Basic Auth to password protect access to the application")
parser.add_argument('args', nargs=argparse.REMAINDER) # allow unknown command line args
args = parser.parse_args()

Expand Down Expand Up @@ -2229,7 +2248,7 @@ def main():
print("Reticulum Identity <{}> has been loaded from file {}.".format(identity.hash.hex(), default_identity_file))

# init app
reticulum_meshchat = ReticulumMeshChat(identity, args.storage_dir, args.reticulum_config_dir)
reticulum_meshchat = ReticulumMeshChat(identity, args.storage_dir, args.reticulum_config_dir, args.password)
reticulum_meshchat.run(args.host, args.port, launch_browser=args.headless is False)


Expand Down