aboutsummaryrefslogtreecommitdiff
path: root/session.py
diff options
context:
space:
mode:
authorJonathan Weth <git@jonathanweth.de>2021-06-23 12:33:28 +0200
committerJonathan Weth <git@jonathanweth.de>2021-06-23 12:33:28 +0200
commited1d15aa02b3c7c1350a5204861d1f8678550fbb (patch)
tree51d4aa7cdee4f90b5f2c3a4188cb80519dd2342d /session.py
parent74e02d1953c1ee03a4e7dfc73e80318a24ba56a7 (diff)
downloadRWA.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.py301
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}