From 15de277a5be330fe6962e5271c537e3b5ef40de4 Mon Sep 17 00:00:00 2001 From: retoor Date: Fri, 31 Jan 2025 12:06:42 +0100 Subject: [PATCH] drive. --- DockerfileDrive | 16 +++ src/snek/static/media-upload.js | 167 ++++++++++++++++++++++++++++++++ src/snekssh/app.py | 71 ++++++++++++++ src/snekssh/app2.py | 68 +++++++++++++ src/snekssh/app3.py | 71 ++++++++++++++ src/snekssh/app4.py | 77 +++++++++++++++ src/snekssh/app5.py | 106 ++++++++++++++++++++ ssh_host_key | 27 ++++++ ssh_host_key.pub | 1 + 9 files changed, 604 insertions(+) create mode 100644 DockerfileDrive create mode 100644 src/snek/static/media-upload.js create mode 100644 src/snekssh/app.py create mode 100644 src/snekssh/app2.py create mode 100644 src/snekssh/app3.py create mode 100644 src/snekssh/app4.py create mode 100644 src/snekssh/app5.py create mode 100644 ssh_host_key create mode 100644 ssh_host_key.pub diff --git a/DockerfileDrive b/DockerfileDrive new file mode 100644 index 0000000..28f183a --- /dev/null +++ b/DockerfileDrive @@ -0,0 +1,16 @@ +FROM python:3.12.8-alpine3.21 +WORKDIR /code +RUN apk add --no-cache gcc musl-dev linux-headers git openssh + +#WKHTMLTOPDFNEEDS + +COPY setup.cfg setup.cfg +COPY pyproject.toml pyproject.toml +COPY src src +COpy ssh_host_key ssh_host_key +RUN pip install --upgrade pip +RUN pip install -e . +EXPOSE 2225 + +#CMD ["python","-m","snekssh.app"] +#CMD ["gunicorn", "-w", "10", "-k", "aiohttp.worker.GunicornWebWorker", "snek.gunicorn:app","--bind","0.0.0.0:8081"] diff --git a/src/snek/static/media-upload.js b/src/snek/static/media-upload.js new file mode 100644 index 0000000..e190aed --- /dev/null +++ b/src/snek/static/media-upload.js @@ -0,0 +1,167 @@ +class TileGridElement extends HTMLElement { + + constructor() { + super(); + this.attachShadow({mode: 'open'}); + this.gridId = this.getAttribute('grid'); + this.component = document.createElement('div'); + this.shadowRoot.appendChild(this.component) + } + + + connectedCallback() { + console.log('connected'); + this.styleElement = document.createElement('style'); + this.styleElement.innerText = ` + .grid { + padding: 10px; + display: flex; + flex-wrap: wrap; + gap: 10px; + justify-content: center; + } + .grid .tile { + margin: 10px; + width: 100px; + height: 100px; + background-color: #ccc; + object-fit: cover; + border-radius: 10px; + transition: transform 0.3s ease-in-out; + } + .grid .tile:hover { + transform: scale(1.1); + } + + `; + this.component.appendChild(this.styleElement); + this.container = document.createElement('div'); + this.container.classList.add('gallery'); + this.component.appendChild(this.container); + } + addImage(src) { + const item = document.createElement('img'); + item.src = src; + item.classList.add('tile'); + item.style.width = '100px'; + item.style.height = '100px'; + this.container.appendChild(item); + } + addImages(srcs) { + srcs.forEach(src => this.addImage(src)); + } + addElement(element) { + element.cclassList.add('tile'); + this.container.appendChild(element); + } + +} + +class UploadButton extends HTMLElement { + constructor() { + super(); + this.attachShadow({mode: 'open'}); + this.component = document.createElement('div'); + + this.shadowRoot.appendChild(this.component) + window.u = this + } + get gridSelector(){ + return this.getAttribute('grid'); + } + grid = null + + addImages(urls) { + this.grid.addImages(urls); + } + connectedCallback() + { + console.log('connected'); + this.styleElement = document.createElement('style'); + this.styleElement.innerHTML = ` + .upload-button { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 10px; + } + .upload-button input[type="file"] { + display: none; + } + .upload-button label { + display: block; + padding: 10px 20px; + background-color: #ccc; + border-radius: 5px; + cursor: pointer; + } + .upload-button label:hover { + background-color: #999; + } + `; + this.shadowRoot.appendChild(this.styleElement); + this.container = document.createElement('div'); + this.container.classList.add('upload-button'); + this.shadowRoot.appendChild(this.container); + const input = document.createElement('input'); + input.type = 'file'; + input.accept = 'image/*'; + input.multiple = true; + input.addEventListener('change', (e) => { + const files = e.target.files; + const urls = []; + for (let i = 0; i < files.length; i++) { + const file = files[i]; + const reader = new FileReader(); + reader.onload = (e) => { + urls.push(e.target.result); + if (urls.length === files.length) { + this.addImages(urls); + } + }; + reader.readAsDataURL(file); + } + }); + const label = document.createElement('label'); + label.textContent = 'Upload Images'; + label.appendChild(input); + this.container.appendChild(label); + } +} + +customElements.define('upload-button', UploadButton); + +customElements.define('tile-grid', TileGridElement); + +class MeniaUploadElement extends HTMLElement { + + constructor(){ + super() + this.attachShadow({mode:'open'}) + this.component = document.createElement("div") + alert('aaaa') + this.shadowRoot.appendChild(this.component) + } + connectedCallback() { + + this.container = document.createElement("div") + this.component.style.height = '100%' + this.component.style.backgroundColor ='blue'; + this.shadowRoot.appendChild(this.container) + + this.tileElement = document.createElement("tile-grid") + this.tileElement.style.backgroundColor = 'red' + this.tileElement.style.height = '100%' + this.component.appendChild(this.tileElement) + + this.uploadButton = document.createElement('upload-button') + this.component.appendChild(this.uploadButton) + + // const mediaUpload = document.createElement('media-upload') + //this.component.appendChild(mediaUpload) + } + +} + +customElements.define('menia-upload', MeniaUploadElement) \ No newline at end of file diff --git a/src/snekssh/app.py b/src/snekssh/app.py new file mode 100644 index 0000000..27e1b4b --- /dev/null +++ b/src/snekssh/app.py @@ -0,0 +1,71 @@ +import asyncio +import asyncssh +import os +import logging +asyncssh.set_debug_level(2) +logging.basicConfig(level=logging.DEBUG) +# Configuration for SFTP server +SFTP_ROOT = "." # Directory to serve +USERNAME = "test" +PASSWORD = "woeii" +HOST = "localhost" +PORT = 2225 + +class MySFTPServer(asyncssh.SFTPServer): + def __init__(self, chan): + super().__init__(chan) + self.root = os.path.abspath(SFTP_ROOT) + + async def stat(self, path): + """Handles 'stat' command from SFTP client""" + full_path = os.path.join(self.root, path.lstrip("/")) + return await super().stat(full_path) + + async def open(self, path, flags, attrs): + """Handles file open requests""" + full_path = os.path.join(self.root, path.lstrip("/")) + return await super().open(full_path, flags, attrs) + + async def listdir(self, path): + """Handles directory listing""" + full_path = os.path.join(self.root, path.lstrip("/")) + return await super().listdir(full_path) + +class MySSHServer(asyncssh.SSHServer): + """Custom SSH server to handle authentication""" + def connection_made(self, conn): + print(f"New connection from {conn.get_extra_info('peername')}") + + def connection_lost(self, exc): + print("Client disconnected") + + def begin_auth(self, username): + return True # No additional authentication steps + + def password_auth_supported(self): + return True # Support password authentication + + def validate_password(self, username, password): + print(username,password) + + return True + return username == USERNAME and password == PASSWORD + +async def start_sftp_server(): + os.makedirs(SFTP_ROOT, exist_ok=True) # Ensure the root directory exists + + await asyncssh.create_server( + lambda: MySSHServer(), + host=HOST, + port=PORT, + server_host_keys=["ssh_host_key"], + process_factory=MySFTPServer + ) + print(f"SFTP server running on {HOST}:{PORT}") + await asyncio.Future() # Keep running forever + +if __name__ == "__main__": + try: + asyncio.run(start_sftp_server()) + except (OSError, asyncssh.Error) as e: + print(f"Error starting SFTP server: {e}") diff --git a/src/snekssh/app2.py b/src/snekssh/app2.py new file mode 100644 index 0000000..1bfec21 --- /dev/null +++ b/src/snekssh/app2.py @@ -0,0 +1,68 @@ +import asyncio +import asyncssh +import os + +# SSH Server Configuration +HOST = "0.0.0.0" +PORT = 2225 +USERNAME = "user" +PASSWORD = "password" +SHELL = "/bin/sh" # Change to another shell if needed + +class CustomSSHServer(asyncssh.SSHServer): + def connection_made(self, conn): + print(f"New connection from {conn.get_extra_info('peername')}") + + def connection_lost(self, exc): + print("Client disconnected") + + def password_auth_supported(self): + return True + + def validate_password(self, username, password): + return username == USERNAME and password == PASSWORD + +async def custom_bash_process(process): + """Spawns a custom bash shell process""" + env = os.environ.copy() + env["TERM"] = "xterm-256color" + + # Start the Bash shell + bash_proc = await asyncio.create_subprocess_exec( + SHELL, "-i", stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, env=env + ) + + async def read_output(): + while True: + data = await bash_proc.stdout.read(1) + if not data: + break + process.stdout.write(data) + + async def read_input(): + while True: + data = await process.stdin.read(1) + if not data: + break + bash_proc.stdin.write(data) + + await asyncio.gather(read_output(), read_input()) + +async def start_ssh_server(): + """Starts the AsyncSSH server with Bash""" + await asyncssh.create_server( + lambda: CustomSSHServer(), + host=HOST, + port=PORT, + server_host_keys=["ssh_host_key"], + process_factory=custom_bash_process + ) + print(f"SSH server running on {HOST}:{PORT}") + await asyncio.Future() # Keep running + +if __name__ == "__main__": + try: + asyncio.run(start_ssh_server()) + except (OSError, asyncssh.Error) as e: + print(f"Error starting SSH server: {e}") + diff --git a/src/snekssh/app3.py b/src/snekssh/app3.py new file mode 100644 index 0000000..d50cc54 --- /dev/null +++ b/src/snekssh/app3.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3.7 +# +# Copyright (c) 2013-2024 by Ron Frederick and others. +# +# This program and the accompanying materials are made available under +# the terms of the Eclipse Public License v2.0 which accompanies this +# distribution and is available at: +# +# http://www.eclipse.org/legal/epl-2.0/ +# +# This program may also be made available under the following secondary +# licenses when the conditions for such availability set forth in the +# Eclipse Public License v2.0 are satisfied: +# +# GNU General Public License, Version 2.0, or any later versions of +# that license +# +# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later +# +# Contributors: +# Ron Frederick - initial implementation, API, and documentation + +# To run this program, the file ``ssh_host_key`` must exist with an SSH +# private key in it to use as a server host key. An SSH host certificate +# can optionally be provided in the file ``ssh_host_key-cert.pub``. +# +# The file ``ssh_user_ca`` must exist with a cert-authority entry of +# the certificate authority which can sign valid client certificates. + +import asyncio, asyncssh, sys + +async def handle_client(process: asyncssh.SSHServerProcess) -> None: + + + + + + width, height, pixwidth, pixheight = process.term_size + + process.stdout.write(f'Terminal type: {process.term_type}, ' + f'size: {width}x{height}') + if pixwidth and pixheight: + process.stdout.write(f' ({pixwidth}x{pixheight} pixels)') + process.stdout.write('\nTry resizing your window!\n') + + while not process.stdin.at_eof(): + try: + await process.stdin.read() + except asyncssh.TerminalSizeChanged as exc: + process.stdout.write(f'New window size: {exc.width}x{exc.height}') + if exc.pixwidth and exc.pixheight: + process.stdout.write(f' ({exc.pixwidth}' + f'x{exc.pixheight} pixels)') + process.stdout.write('\n') + + + + +async def start_server() -> None: + await asyncssh.listen('', 2230, server_host_keys=['ssh_host_key'], + #authorized_client_keys='ssh_user_ca', + process_factory=handle_client) + +loop = asyncio.new_event_loop() + +try: + loop.run_until_complete(start_server()) +except (OSError, asyncssh.Error) as exc: + sys.exit('Error starting server: ' + str(exc)) + +loop.run_forever() diff --git a/src/snekssh/app4.py b/src/snekssh/app4.py new file mode 100644 index 0000000..e54480f --- /dev/null +++ b/src/snekssh/app4.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3.7 +# +# Copyright (c) 2013-2024 by Ron Frederick and others. +# +# This program and the accompanying materials are made available under +# the terms of the Eclipse Public License v2.0 which accompanies this +# distribution and is available at: +# +# http://www.eclipse.org/legal/epl-2.0/ +# +# This program may also be made available under the following secondary +# licenses when the conditions for such availability set forth in the +# Eclipse Public License v2.0 are satisfied: +# +# GNU General Public License, Version 2.0, or any later versions of +# that license +# +# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later +# +# Contributors: +# Ron Frederick - initial implementation, API, and documentation + +# To run this program, the file ``ssh_host_key`` must exist with an SSH +# private key in it to use as a server host key. An SSH host certificate +# can optionally be provided in the file ``ssh_host_key-cert.pub``. + +import asyncio, asyncssh, bcrypt, sys +from typing import Optional + +passwords = {'guest': b'', # guest account with no password + 'user': bcrypt.hashpw(b'user', bcrypt.gensalt()), + } + +def handle_client(process: asyncssh.SSHServerProcess) -> None: + username = process.get_extra_info('username') + process.stdout.write(f'Welcome to my SSH server, {username}!\n') + #process.exit(0) + +class MySSHServer(asyncssh.SSHServer): + def connection_made(self, conn: asyncssh.SSHServerConnection) -> None: + peername = conn.get_extra_info('peername')[0] + print(f'SSH connection received from {peername}.') + + def connection_lost(self, exc: Optional[Exception]) -> None: + if exc: + print('SSH connection error: ' + str(exc), file=sys.stderr) + else: + print('SSH connection closed.') + + def begin_auth(self, username: str) -> bool: + # If the user's password is the empty string, no auth is required + return passwords.get(username) != b'' + + def password_auth_supported(self) -> bool: + return True + + def validate_password(self, username: str, password: str) -> bool: + if username not in passwords: + return False + pw = passwords[username] + if not password and not pw: + return True + return bcrypt.checkpw(password.encode('utf-8'), pw) + +async def start_server() -> None: + await asyncssh.create_server(MySSHServer, '', 2231, + server_host_keys=['ssh_host_key'], + process_factory=handle_client) + +loop = asyncio.new_event_loop() + +try: + loop.run_until_complete(start_server()) +except (OSError, asyncssh.Error) as exc: + sys.exit('Error starting server: ' + str(exc)) + +loop.run_forever() diff --git a/src/snekssh/app5.py b/src/snekssh/app5.py new file mode 100644 index 0000000..bbaf3d3 --- /dev/null +++ b/src/snekssh/app5.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3.7 +# +# Copyright (c) 2016-2024 by Ron Frederick and others. +# +# This program and the accompanying materials are made available under +# the terms of the Eclipse Public License v2.0 which accompanies this +# distribution and is available at: +# +# http://www.eclipse.org/legal/epl-2.0/ +# +# This program may also be made available under the following secondary +# licenses when the conditions for such availability set forth in the +# Eclipse Public License v2.0 are satisfied: +# +# GNU General Public License, Version 2.0, or any later versions of +# that license +# +# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later +# +# Contributors: +# Ron Frederick - initial implementation, API, and documentation + +# To run this program, the file ``ssh_host_key`` must exist with an SSH +# private key in it to use as a server host key. An SSH host certificate +# can optionally be provided in the file ``ssh_host_key-cert.pub``. +# +# The file ``ssh_user_ca`` must exist with a cert-authority entry of +# the certificate authority which can sign valid client certificates. + +import asyncio, asyncssh, sys +from typing import List, cast + +class ChatClient: + _clients: List['ChatClient'] = [] + + def __init__(self, process: asyncssh.SSHServerProcess): + self._process = process + + @classmethod + async def handle_client(cls, process: asyncssh.SSHServerProcess): + await cls(process).run() + + + + async def readline(self) -> str: + return cast(str, self._process.stdin.readline()) + + def write(self, msg: str) -> None: + self._process.stdout.write(msg) + + def broadcast(self, msg: str) -> None: + for client in self._clients: + if client != self: + client.write(msg) + + + def begin_auth(self, username: str) -> bool: + # If the user's password is the empty string, no auth is required + #return False + return True # passwords.get(username) != b'' + + def password_auth_supported(self) -> bool: + return True + + def validate_password(self, username: str, password: str) -> bool: + #if username not in passwords: + # return False + #pw = passwords[username] + #if not password and not pw: + # return True + return True + #return bcrypt.checkpw(password.encode('utf-8'), pw) + + + async def run(self) -> None: + self.write('Welcome to chat!\n\n') + + self.write('Enter your name: ') + name = (await self.readline()).rstrip('\n') + + self.write(f'\n{len(self._clients)} other users are connected.\n\n') + + self._clients.append(self) + self.broadcast(f'*** {name} has entered chat ***\n') + + try: + async for line in self._process.stdin: + self.broadcast(f'{name}: {line}') + except asyncssh.BreakReceived: + pass + + self.broadcast(f'*** {name} has left chat ***\n') + self._clients.remove(self) + +async def start_server() -> None: + await asyncssh.listen('', 2235, server_host_keys=['ssh_host_key'], + process_factory=ChatClient.handle_client) + +loop = asyncio.new_event_loop() + +try: + loop.run_until_complete(start_server()) +except (OSError, asyncssh.Error) as exc: + sys.exit('Error starting server: ' + str(exc)) + +loop.run_forever() diff --git a/ssh_host_key b/ssh_host_key new file mode 100644 index 0000000..b65e9f2 --- /dev/null +++ b/ssh_host_key @@ -0,0 +1,27 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn +NhAAAAAwEAAQAAAQEAqKVFRuoa+WMGl0Z6UAYu943G9pInhd8nnsqPinu6t+D6X9U4A1bF +u46JBfIdwJMHoGUzVnLdDL9OO38yvZXnDXmRSEQmQhYL981BwFSFPBRNPYrY5bF6qgM6IW +gH8RFL5tLA5RZoUpoyKzwIllXIyKHIcxwIf6be2G8KrheuXu9/Le0Tq3f6n3LhPEwhPD0m +7Nah3LZS4cU+G3TcET8jh6Nw/uE7wV11ojzjrn0Fa+2HCyUSGvp4Xt9gNZmlOrY4l+22DK +ZUf/Cv+0ebMpngaMjYfYKFWCprwElf/YxKn6NKCcJ2KaU9kUKACpitN8DzrwA6o6VROCQ1 +JmPq8b0PmwAAA8gZwhWRGcIVkQAAAAdzc2gtcnNhAAABAQCopUVG6hr5YwaXRnpQBi73jc +b2kieF3yeeyo+Ke7q34Ppf1TgDVsW7jokF8h3AkwegZTNWct0Mv047fzK9lecNeZFIRCZC +Fgv3zUHAVIU8FE09itjlsXqqAzohaAfxEUvm0sDlFmhSmjIrPAiWVcjIochzHAh/pt7Ybw +quF65e738t7ROrd/qfcuE8TCE8PSbs1qHctlLhxT4bdNwRPyOHo3D+4TvBXXWiPOOufQVr +7YcLJRIa+nhe32A1maU6tjiX7bYMplR/8K/7R5symeBoyNh9goVYKmvASV/9jEqfo0oJwn +YppT2RQoAKmK03wPOvADqjpVE4JDUmY+rxvQ+bAAAAAwEAAQAAAQAGgb66U+s2HUSY1TOA +H1NalWuZNRE1tuMP2yyBbfEWifI/FlPyu26McQOgz7NA+RGw3GOIF1oOCHRbrINOeBhesO +0SrVNCKeWZg9Lgn9VpxWkn61G5DKL5KLMdrNmsytUNExHPa121EJWU83XSKLDDHox3j7WI +PCEAl5aa5vdnjdf5LXmZKzVECx7pbEbQvvcC/8uTjK4nfBphVDGY43C8mo7hNeSIl6Rpcm +9FVm+/4tvn2aRKWc7PkyXsVH9Tc64iZmmQSP6YVxMMd2ZkMY//ZOnuy+/tenLzaeKRZrUP +Vr2W3p8PXahYjHjMoKasosiuyf79lxrTUXbGTVjI3eiBAAAAgQCXYkyC83how+QsnygxZi +1xuE4hTD0BhCWTJFtuuKAIb3uib3ciMkxxy5qbfW2AfHb3vngqim+rPAwoxW55YuLTs5zu +1yO5k1ieWgn/ubDWLr3j8+1yCrVSha33Hyd6/NaffIuLM4Oy72zKrAq73b5tWrLNvllxic +i/kZ5YkYbQrQAAAIEA3RZMiNtHMB0xev2gf6bPYxYvPoZvzd66p+P1+4EVTdCrYAdRZLWZ +UETfWZt6YZYwbRpwrZatOampyEUy6ApQH4ga75LBRQo0P3SXP441XZucWm6X4PRYyKY7VT +fhfAdgbrUBOPcOrEAdBT3W56PjPnY6apUXy07xSoZ7WuhLKMEAAACBAMNG9n+7o0rBtxZe +6DCtM8xYsCh122+NWiLRck95rxYzhv2xm0k3xLE2CdIZuM4+KJnG/5SwOxDY2cHdkXpF+6 +InwyWxvnV6TiCsLxYrsmMToHZP7U1mwdxWaV0xySxhaIamwnYJOqYm4uNaL8VTaNzkGzjs +quwPw1GCjmh9j1NbAAAADnJldG9vckByZXRvb3IyAQIDBA== +-----END OPENSSH PRIVATE KEY----- diff --git a/ssh_host_key.pub b/ssh_host_key.pub new file mode 100644 index 0000000..ca1693e --- /dev/null +++ b/ssh_host_key.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCopUVG6hr5YwaXRnpQBi73jcb2kieF3yeeyo+Ke7q34Ppf1TgDVsW7jokF8h3AkwegZTNWct0Mv047fzK9lecNeZFIRCZCFgv3zUHAVIU8FE09itjlsXqqAzohaAfxEUvm0sDlFmhSmjIrPAiWVcjIochzHAh/pt7YbwquF65e738t7ROrd/qfcuE8TCE8PSbs1qHctlLhxT4bdNwRPyOHo3D+4TvBXXWiPOOufQVr7YcLJRIa+nhe32A1maU6tjiX7bYMplR/8K/7R5symeBoyNh9goVYKmvASV/9jEqfo0oJwnYppT2RQoAKmK03wPOvADqjpVE4JDUmY+rxvQ+b retoor@retoor2