Skip to content

Commit 7ee822e

Browse files
committed
initial commit
1 parent 902d7e5 commit 7ee822e

File tree

8 files changed

+171
-1
lines changed

8 files changed

+171
-1
lines changed

CHANGELOG.md

Whitespace-only changes.

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
BSD 3-Clause License
22

3-
Copyright (c) 2020, FZJ-JSC
3+
Copyright (c) 2020, Forschungszentrum Juelich GmbH, Juelich Supercomputing Centre
44
All rights reserved.
55

66
Redistribution and use in source and binary forms, with or without

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
graft jupyter_xprahtml5/share

environment.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
channels:
2+
- conda-forge
3+
dependencies:
4+
- xpra>=4.0.4
5+
- jupyter-server-proxy>=1.4
6+
- pip
7+
- pip:
8+
- .

jupyter_xprahtml5/__init__.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import os
2+
import logging
3+
4+
logger = logging.getLogger(__name__)
5+
logger.setLevel('INFO')
6+
7+
HERE = os.path.dirname(os.path.abspath(__file__))
8+
9+
def _xprahtml5_mappath(path):
10+
from getpass import getuser
11+
12+
uri_parms = '?' + '&'.join([
13+
'username=' + getuser(),
14+
# 'password=' + _xprahtml5_passwd,
15+
# 'encryption=AES',
16+
# 'key=' + _xprahtml5_aeskey,
17+
# 'sharing=true',
18+
])
19+
20+
if path in ('/', '/index.html', ):
21+
path = '/index.html' + uri_parms
22+
logger.info('Xpra URI: ' + path)
23+
24+
return path
25+
26+
27+
def setup_xprahtml5():
28+
""" Setup commands and and return a dictionary compatible
29+
with jupyter-server-proxy.
30+
"""
31+
from pathlib import Path
32+
from tempfile import gettempdir, mkstemp
33+
from random import choice
34+
from string import ascii_letters, digits
35+
from getpass import getuser
36+
37+
global _xprahtml5_passwd, _xprahtml5_aeskey
38+
39+
# password generator
40+
def _get_random_alphanumeric_string(length):
41+
letters_and_digits = ascii_letters + digits
42+
return (''.join((choice(letters_and_digits) for i in range(length))))
43+
44+
# ensure a known secure sockets directory exists, as /run/user/$UID might not be available
45+
socket_path = os.path.join(gettempdir(), 'xpra_sockets_' + str(os.getuid()))
46+
Path(socket_path).mkdir(mode=0o700, parents=True, exist_ok=True)
47+
logger.info('Created secure socket directory for Xpra: ' + socket_path)
48+
49+
# generate file with random one-time-password
50+
_xprahtml5_passwd = _get_random_alphanumeric_string(16)
51+
try:
52+
fd_passwd, fpath_passwd = mkstemp()
53+
logger.info('Created secure password file for Xpra: ' + fpath_passwd)
54+
55+
with open(fd_passwd, 'w') as f:
56+
f.write(_xprahtml5_passwd)
57+
58+
except:
59+
logger.error("Passwd generation in temp file FAILED")
60+
raise FileNotFoundError("Passwd generation in temp file FAILED")
61+
62+
# generate file with random encryption key
63+
_xprahtml5_aeskey = _get_random_alphanumeric_string(16)
64+
try:
65+
fd_aeskey, fpath_aeskey = mkstemp()
66+
logger.info('Created secure encryption key file for Xpra: ' + fpath_aeskey)
67+
68+
with open(fd_aeskey, 'w') as f:
69+
f.write(_xprahtml5_aeskey)
70+
except:
71+
logger.error("Encryption key generation in temp file FAILED")
72+
raise FileNotFoundError("Encryption key generation in temp file FAILED")
73+
74+
# create command
75+
cmd = [ os.path.join(HERE, 'share/launch_xpra.sh'),
76+
'start',
77+
'--html=on',
78+
'--bind-tcp=0.0.0.0:{port}',
79+
# '--socket-dir="' + socket_path + '/"', fixme: socket_dir not recognized
80+
'--server-idle-timeout=86400', # stop server after 24h with no client connection
81+
'--start=xterm',
82+
# '--start-child=xterm', '--exit-with-children',
83+
# '--tcp-auth=file:filename=' + fpath_passwd,
84+
# '--tcp-encryption=AES',
85+
# '--tcp-encryption-keyfile=' + fpath_aeskey,
86+
'--clipboard-direction=both',
87+
'--no-bell',
88+
'--no-speaker',
89+
'--no-printing',
90+
'--no-microphone',
91+
'--no-notifications',
92+
'--dpi=96',
93+
# '--sharing'
94+
]
95+
logger.info('Xpra command: ' + ' '.join(cmd))
96+
97+
return {
98+
'environment': { # as '--socket-dir' does not work as expected, we set this
99+
'XDG_RUNTIME_DIR': socket_path,
100+
},
101+
'command': cmd,
102+
'mappath': _xprahtml5_mappath,
103+
'absolute_url': False,
104+
'timeout': 30,
105+
'new_browser_tab': True,
106+
'launcher_entry': {
107+
'enabled': True,
108+
'icon_path': os.path.join(HERE, 'share/xpra-logo.svg'),
109+
'title': 'Xpra Desktop',
110+
},
111+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
3+
# you might need to modify your environment
4+
# before you can start 'xpra'
5+
# Do it here.
6+
7+
# example
8+
# module purge > /dev/null
9+
# module use $OTHERSTAGES > /dev/null
10+
# module load Stages/Devel-2020 > /dev/null
11+
# module load GCCcore/.9.3.0 > /dev/null
12+
# module load xpra/4.0.4-Python-3.8.5 > /dev/null
13+
14+
exec xpra $@

jupyter_xprahtml5/share/xpra-logo.svg

Lines changed: 1 addition & 0 deletions
Loading

setup.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from os import path
2+
from setuptools import setup, find_packages
3+
4+
HERE = path.abspath(path.dirname(__file__))
5+
with open(path.join(HERE, "README.md"), "r", encoding="utf-8") as fh:
6+
long_description = fh.read()
7+
8+
setup(
9+
name="jupyter-xprahtml5-server",
10+
packages=find_packages(),
11+
version='0.2.0',
12+
13+
author_email="[email protected]",
14+
description="Xpra for JupyterLab",
15+
long_description=long_description,
16+
long_description_content_type="text/markdown",
17+
url="https://github.com/FZJ_JSC/jupyter-xprahtml5-server",
18+
19+
keywords=["jupyter", "xpra", "jupyterhub", "jupyter-server-proxy"],
20+
classifiers=[
21+
"Programming Language :: Python :: 3",
22+
"License :: OSI Approved :: BSD License",
23+
"Operating System :: OS Independent",
24+
"Framework :: Jupyter",
25+
],
26+
27+
entry_points={
28+
'jupyter_serverproxy_servers': [
29+
'xprahtml5 = jupyter_xprahtml5:setup_xprahtml5',
30+
]
31+
},
32+
install_requires=['jupyter-server-proxy>=1.4.0'],
33+
include_package_data=True,
34+
zip_safe=False
35+
)

0 commit comments

Comments
 (0)