diff options
author | Jonathan Weth <git@jonathanweth.de> | 2021-06-23 12:33:28 +0200 |
---|---|---|
committer | Jonathan Weth <git@jonathanweth.de> | 2021-06-23 12:33:28 +0200 |
commit | ed1d15aa02b3c7c1350a5204861d1f8678550fbb (patch) | |
tree | 51d4aa7cdee4f90b5f2c3a4188cb80519dd2342d /session.py | |
parent | 74e02d1953c1ee03a4e7dfc73e80318a24ba56a7 (diff) | |
download | RWA.Support.SessionService-ed1d15aa02b3c7c1350a5204861d1f8678550fbb.tar.gz RWA.Support.SessionService-ed1d15aa02b3c7c1350a5204861d1f8678550fbb.tar.bz2 RWA.Support.SessionService-ed1d15aa02b3c7c1350a5204861d1f8678550fbb.zip |
Restructure project directory and provide script for session service
Diffstat (limited to 'session.py')
-rw-r--r-- | session.py | 301 |
1 files changed, 0 insertions, 301 deletions
diff --git a/session.py b/session.py deleted file mode 100644 index f977669..0000000 --- a/session.py +++ /dev/null @@ -1,301 +0,0 @@ -# This file is part of Remote Support Desktop -# https://gitlab.das-netzwerkteam.de/RemoteWebApp/rwa.support.sessionservice -# Copyright 2020, 2021 Jonathan Weth <dev@jonathanweth.de> -# Copyright 2020 Daniel Teichmann <daniel.teichmann@das-netzwerkteam.de> -# Copyright 2020 Mike Gabriel <mike.gabriel@das-netzwerkteam.de> -# SPDX-License-Identifier: GPL-2.0-or-later -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the -# Free Software Foundation, Inc., -# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <https://www.gnu.org/licenses/>. - -import os -import random -import secrets -import signal -import string -import subprocess -from typing import Dict, Union, Any - -import port_for -import psutil -import requests - -from config import settings -from log import logging -from vnc import run_vnc, save_password - -API_SERVER = settings.get("api_url", "http://127.0.0.1") -BASE_URL = API_SERVER + "/app/rwasupport/api/" -REGISTER_URL = BASE_URL + "register/" -STOP_URL = BASE_URL + "stop/" -STATUS_URL = BASE_URL + "status/" -MARK_JOB_AS_DONE_URL = BASE_URL + "jobs/mark_as_done/" - -logging.info(f"Load API config: {API_SERVER}") - - -def random_digits(length: int): - return "".join(random.choice(string.digits) for _ in range(length)) - - -def get_desktop_dir(): - """Get desktop directory from xdg vars.""" - return ( - subprocess.check_output(["xdg-user-dir", "DESKTOP"]) - .decode() - .strip() - .replace("\n", "") - ) - - -class Session: - #: Session is running - STATUS_RUNNING = "running" - - #: Remote has joined the session - STATUS_JOINED = "active" - - def __init__(self, trigger_port: int, mockup_session: bool = False): - self.trigger_token = secrets.token_urlsafe(20) - self.trigger_port = trigger_port - self.done_jobs = [] - self.mockup_session = mockup_session - self.desktop_dir = get_desktop_dir() - self.desktop_dir = get_desktop_dir() - self._generate_password() - self._start_vnc() - self._register_session() - self.status_text = self.STATUS_RUNNING - - @property - def pid(self) -> int: - return self.vnc_pid - - @property - def port(self) -> int: - return self.ws_port - - @property - def _api_headers(self) -> Dict[str, str]: - return {"Authorization": f"Token {self.api_token}"} - - def _generate_password(self): - """Generate password for x11vnc and save it.""" - self.password = secrets.token_urlsafe(20) - - # Don't actually save a password if we just pretend to be a session. - if not self.mockup_session: - self.pw_filename = save_password(self.password) - - logging.info("The password for the session has been generated.") - - def _start_vnc(self): - """Start x11vnc server if not in mockup_session mode.""" - if not self.mockup_session: - process_info = run_vnc(self.pw_filename) - - logging.info("The VNC server has been started.") - - self.vnc_pid = process_info["vnc"]["pid"] - self.vnc_port = process_info["vnc"]["port"] - self.ws_pid = process_info["ws"]["pid"] - self.ws_port = process_info["ws"]["port"] - else: - self.ws_port = port_for.select_random() - self.vnc_port = port_for.select_random() - - self.ws_pid = int(random_digits(5)) - self.vnc_pid = int(random_digits(5)) - - logging.info("The lock file for mocking a VNC server has been created.") - - # Create a temporary file to indicate that this process is still 'Running' - filename = f"/tmp/rwa/{str(self.ws_port) + str(self.vnc_port) + str(self.ws_pid) + str(self.vnc_pid)}.lock" - new_file = open(filename, "w") - new_file.write("this session is running") - - def _register_session(self): - """Register session in RWA.Support.WebApp if not in mockup_session mode.""" - if not self.mockup_session: - try: - r = requests.post( - REGISTER_URL, - json={ - "port": self.ws_port, - "pid": self.vnc_pid, - "trigger_port": self.trigger_port, - }, - ) - except requests.exceptions.ConnectionError: - raise ConnectionError() - - logging.info( - f"The session has been registered in RWA.Support.WebApp with status code {r.status_code} and response {r.content.decode()}." - ) - - if r.status_code != 200: - raise ConnectionError() - - self.meta = r.json() - self.session_id = self.meta["session_id"] - self.web_url = self.meta["url"] - self.api_token = self.meta["token"] - else: - logging.info(f"The session has pretended that he had created a session.") - self.meta = {} - self.session_id = int(random_digits(10)) - self.web_url = "http://example.com:" + random_digits(5) + "/app/rwasupport/test/" - self.api_token = secrets.token_urlsafe(10) - self.pin = int(random_digits(4)) - - def trigger(self, data: dict, method: str = "trigger") -> Union[dict, bool]: - """Event triggered by Django.""" - if method == "trigger" and data.get("token", "") == self.trigger_token: - self.pull() - return True - elif method == "authenticate" and data.get("pin", "") == self.pin: - return { - "password": self.password, - "trigger_token": self.trigger_token, - } - - - return False - def pull(self): - """Update status: Get status from Django.""" - if not self.mockup_session: - try: - r = requests.get( - STATUS_URL, params={"id": self.session_id}, headers=self._api_headers - ) - - logging.info( - f"The session has received its status from RWA.Support.WebApp with status code {r.status_code} and response {r.content.decode()}." - ) - except requests.ConnectionError: - pass - - if r.status_code in (401, 402, 403, 404, 405): - # Session doesn't exist anymore, so stop it local - self.stop(triggered=True) - else: - self.status_text = r.json()["status"] - self.jobs = r.json()["jobs"] - self._do_jobs() - - def _do_jobs(self): - """Go through all jobs and execute undone ones.""" - for job in self.jobs: - if not job["done"] or job["job_id"] in self.done_jobs: - job_type = job["job_type"] - if job_type == "file": - self._do_file_job(job) - - def _do_file_job(self, job): - """Download a file from server to the user's desktop.""" - logging.info( - f"The session has received a file job and is downloading it now ({job}):" - ) - subprocess.Popen(["wget", job["file"], "-P", self.desktop_dir]) - self._mark_job_as_done(job) - - def _mark_job_as_done(self, job): - """Mark a job as done (in this service and on the server).""" - self.done_jobs.append(job["job_id"]) - try: - r = requests.post( - MARK_JOB_AS_DONE_URL, - params={"id": job["job_id"]}, - headers=self._api_headers, - ) - logging.info( - f"The session has marked the job {job} as done in RWA.Support.WebApp with status code {r.status_code} and response {r.content.decode()}." - ) - except requests.ConnectionError: - pass - - def push(self): - """Update status: Push status to Django.""" - pass - - def stop(self, triggered: bool = False): - """Stop session and clean up.""" - if self.mockup_session: - logging.info("Mock session has been stopped by deleting its lock file.") - filename = f"/tmp/rwa/{str(self.ws_port) + str(self.vnc_port) + str(self.ws_pid) + str(self.vnc_pid)}.lock" - if os.path.isfile(filename): - os.remove(filename) - - # Delete self - del self - return - - # Kill websockify - if self.ws_pid in psutil.pids(): - os.kill(self.ws_pid, signal.SIGINT) - logging.info("The websockify server has been terminated.") - - # Kill VNC - if self.vnc_pid in psutil.pids(): - os.kill(self.vnc_pid, signal.SIGINT) - logging.info("The VNC server has been terminated.") - - # Delete PW file - if os.path.exists(self.pw_filename): - os.remove(self.pw_filename) - logging.info("The VNC server password file has been removed.") - - self.push() - - if not triggered: - try: - r = requests.post( - STOP_URL, params={"id": self.session_id}, headers=self._api_headers - ) - logging.info( - f"The stop action has been registered in RWA.Support.WebApp with status code {r.status_code} and response {r.content.decode()}." - ) - except requests.ConnectionError: - pass - - self.status_text = "stopped" - - # Delete self - del self - - @property - def vnc_process_running(self) -> bool: - """Check if the VNC process is still running.""" - if self.mockup_session: - filename = f"/tmp/rwa/{str(self.ws_port) + str(self.vnc_port) + str(self.ws_pid) + str(self.vnc_pid)}.lock" - return os.path.isfile(filename) - - if self.vnc_pid in psutil.pids(): - p = psutil.Process(self.vnc_pid) - if p.status() == "zombie": - return False - return True - return False - - @property - def client_meta(self) -> Dict[str, Union[str, int]]: - return {"id": self.pid, "session_id": self.session_id, "url": self.web_url, "pin": self.pin} - - @property - def status(self) -> Dict[str, Union[str, int]]: - return {"id": self.pid, "status": self.status_text} |