summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDevaev Maxim <[email protected]>2018-12-15 04:29:40 +0300
committerDevaev Maxim <[email protected]>2018-12-15 04:29:40 +0300
commit3c33bd37190772a783369894e209bcfe0858177a (patch)
treee095f08f37371a3182f6ced0b280c4bcaa06983b
parent3445766a50eab16a96d969397a6fe0422f7cfcd2 (diff)
own auth
-rw-r--r--Makefile1
-rw-r--r--PKGBUILD1
-rw-r--r--configs/kvmd/htpasswd1
-rw-r--r--configs/kvmd/platforms/kvmd.v1-hdmi.yaml3
-rw-r--r--configs/kvmd/platforms/kvmd.v1-vga.yaml5
-rw-r--r--configs/nginx/nginx.conf65
-rw-r--r--extras/kvm/manifest.yaml2
-rw-r--r--kvmd/apps/kvmd/__init__.py8
-rw-r--r--kvmd/apps/kvmd/auth.py37
-rw-r--r--kvmd/apps/kvmd/server.py103
-rw-r--r--testenv/Dockerfile6
-rw-r--r--testenv/customizepkg.nginx1
-rw-r--r--testenv/kvmd.yaml3
-rw-r--r--testenv/requirements.txt1
-rw-r--r--testenv/tox.ini2
-rw-r--r--web/browserconfig.xml9
-rw-r--r--web/index.html26
-rw-r--r--web/kvm/index.html88
-rw-r--r--web/login/index.html47
-rw-r--r--web/mstile-150x150.pngbin3678 -> 0 bytes
-rw-r--r--web/share/android-chrome-192x192.png (renamed from web/android-chrome-192x192.png)bin6197 -> 6197 bytes
-rw-r--r--web/share/apple-touch-icon.png (renamed from web/apple-touch-icon.png)bin2376 -> 2376 bytes
-rw-r--r--web/share/css/index/index.css (renamed from web/css/index/index.css)0
-rw-r--r--web/share/css/kvm/about.css (renamed from web/css/kvm/about.css)0
-rw-r--r--web/share/css/kvm/hid.css (renamed from web/css/kvm/hid.css)0
-rw-r--r--web/share/css/kvm/keyboard.css (renamed from web/css/kvm/keyboard.css)0
-rw-r--r--web/share/css/kvm/msd.css (renamed from web/css/kvm/msd.css)0
-rw-r--r--web/share/css/kvm/stream.css (renamed from web/css/kvm/stream.css)0
-rw-r--r--web/share/css/leds.css (renamed from web/css/leds.css)0
-rw-r--r--web/share/css/login/login.css25
-rw-r--r--web/share/css/main.css (renamed from web/css/main.css)13
-rw-r--r--web/share/css/menu.css (renamed from web/css/menu.css)1
-rw-r--r--web/share/css/modals.css (renamed from web/css/modals.css)1
-rw-r--r--web/share/css/progress.css (renamed from web/css/progress.css)0
-rw-r--r--web/share/css/sliders.css (renamed from web/css/sliders.css)0
-rw-r--r--web/share/css/switches.css (renamed from web/css/switches.css)0
-rw-r--r--web/share/css/vars.css (renamed from web/css/vars.css)0
-rw-r--r--web/share/css/windows.css (renamed from web/css/windows.css)0
-rw-r--r--web/share/favicon-16x16.png (renamed from web/favicon-16x16.png)bin638 -> 638 bytes
-rw-r--r--web/share/favicon-32x32.png (renamed from web/favicon-32x32.png)bin937 -> 937 bytes
-rw-r--r--web/share/js/bb.js (renamed from web/js/bb.js)0
-rw-r--r--web/share/js/index/main.js (renamed from web/js/index/main.js)0
-rw-r--r--web/share/js/kvm/atx.js (renamed from web/js/kvm/atx.js)0
-rw-r--r--web/share/js/kvm/hid.js (renamed from web/js/kvm/hid.js)0
-rw-r--r--web/share/js/kvm/keyboard.js (renamed from web/js/kvm/keyboard.js)0
-rw-r--r--web/share/js/kvm/main.js (renamed from web/js/kvm/main.js)0
-rw-r--r--web/share/js/kvm/mouse.js (renamed from web/js/kvm/mouse.js)0
-rw-r--r--web/share/js/kvm/msd.js (renamed from web/js/kvm/msd.js)0
-rw-r--r--web/share/js/kvm/session.js (renamed from web/js/kvm/session.js)28
-rw-r--r--web/share/js/kvm/stream.js (renamed from web/js/kvm/stream.js)0
-rw-r--r--web/share/js/login/main.js36
-rw-r--r--web/share/js/tools.js (renamed from web/js/tools.js)9
-rw-r--r--web/share/js/wm.js (renamed from web/js/wm.js)0
-rw-r--r--web/share/png/blank-stream.png (renamed from web/png/blank-stream.png)bin346999 -> 346999 bytes
-rw-r--r--web/share/safari-pinned-tab.svg (renamed from web/safari-pinned-tab.svg)0
-rw-r--r--web/share/site.webmanifest (renamed from web/site.webmanifest)2
-rw-r--r--web/share/svg/atx-hdd-led.svg (renamed from web/svg/atx-hdd-led.svg)0
-rw-r--r--web/share/svg/atx-power-led.svg (renamed from web/svg/atx-power-led.svg)0
-rw-r--r--web/share/svg/fan-led.svg (renamed from web/svg/fan-led.svg)0
-rw-r--r--web/share/svg/favicon.svg (renamed from web/svg/favicon.svg)0
-rw-r--r--web/share/svg/gear-led.svg (renamed from web/svg/gear-led.svg)0
-rw-r--r--web/share/svg/hid-keyboard-led.svg (renamed from web/svg/hid-keyboard-led.svg)0
-rw-r--r--web/share/svg/hid-mouse-led.svg (renamed from web/svg/hid-mouse-led.svg)0
-rw-r--r--web/share/svg/info.svg (renamed from web/svg/info.svg)0
-rw-r--r--web/share/svg/kvm.svg (renamed from web/svg/kvm.svg)0
-rw-r--r--web/share/svg/link-led.svg (renamed from web/svg/link-led.svg)0
-rw-r--r--web/share/svg/logo.svg (renamed from web/svg/logo.svg)0
-rw-r--r--web/share/svg/msd-led.svg (renamed from web/svg/msd-led.svg)0
-rw-r--r--web/share/svg/select-arrow-inactive.svg (renamed from web/svg/select-arrow-inactive.svg)0
-rw-r--r--web/share/svg/select-arrow-intensive.svg (renamed from web/svg/select-arrow-intensive.svg)0
-rw-r--r--web/share/svg/select-arrow-normal.svg (renamed from web/svg/select-arrow-normal.svg)0
-rw-r--r--web/share/svg/stream-led.svg (renamed from web/svg/stream-led.svg)0
-rw-r--r--web/share/svg/stream-mouse-cursor.svg (renamed from web/svg/stream-mouse-cursor.svg)0
-rw-r--r--web/share/svg/warning.svg (renamed from web/svg/warning.svg)0
74 files changed, 388 insertions, 136 deletions
diff --git a/Makefile b/Makefile
index 1fc418d9..21187949 100644
--- a/Makefile
+++ b/Makefile
@@ -6,6 +6,7 @@ TESTENV_CMD ?= /bin/bash -c " \
(socat PTY,link=$(TESTENV_HID) PTY,link=/dev/ttyS11 &) \
&& cp -r /usr/share/kvmd/configs.default/nginx/* /etc/nginx \
&& cp /usr/share/kvmd/configs.default/kvmd/*.yaml /etc/kvmd \
+ && cp /usr/share/kvmd/configs.default/kvmd/htpasswd /etc/kvmd \
&& cp /testenv/kvmd.yaml /etc/kvmd \
&& nginx -c /etc/nginx/nginx.conf \
&& ln -s $(TESTENV_VIDEO) /dev/kvmd-video \
diff --git a/PKGBUILD b/PKGBUILD
index 3e66c52c..dd7de7ef 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -14,6 +14,7 @@ depends=(
python-yaml
python-aiohttp
python-aiofiles
+ python-passlib
python-pyudev
python-raspberry-gpio
python-pyserial
diff --git a/configs/kvmd/htpasswd b/configs/kvmd/htpasswd
new file mode 100644
index 00000000..e62e44d0
--- /dev/null
+++ b/configs/kvmd/htpasswd
@@ -0,0 +1 @@
+admin:$apr1$INC0KeyU$YdLQ9qosXzNVlhxQPUf7A/
diff --git a/configs/kvmd/platforms/kvmd.v1-hdmi.yaml b/configs/kvmd/platforms/kvmd.v1-hdmi.yaml
index 8a1f9c8b..1991e4c5 100644
--- a/configs/kvmd/platforms/kvmd.v1-hdmi.yaml
+++ b/configs/kvmd/platforms/kvmd.v1-hdmi.yaml
@@ -7,6 +7,9 @@ kvmd:
port: 8081
heartbeat: 3.0
+ auth:
+ htpasswd: /etc/kvmd/htpasswd
+
info:
meta: /etc/kvmd/meta.yaml
extras: /usr/share/kvmd/extras
diff --git a/configs/kvmd/platforms/kvmd.v1-vga.yaml b/configs/kvmd/platforms/kvmd.v1-vga.yaml
index 05c1234d..5688f306 100644
--- a/configs/kvmd/platforms/kvmd.v1-vga.yaml
+++ b/configs/kvmd/platforms/kvmd.v1-vga.yaml
@@ -1,5 +1,5 @@
# Don't touch this file otherwise your device may stop working.
-# You can find a workable configuration in /usr/share/kvmd/configs.default/kvmd.
+# You can find a working configuration in /usr/share/kvmd/configs.default/kvmd.
kvmd:
server:
@@ -7,6 +7,9 @@ kvmd:
port: 8081
heartbeat: 3.0
+ auth:
+ htpasswd: /etc/kvmd/htpasswd
+
info:
meta: /etc/kvmd/meta.yaml
extras: /usr/share/kvmd/extras
diff --git a/configs/nginx/nginx.conf b/configs/nginx/nginx.conf
index 5a368833..49776d9d 100644
--- a/configs/nginx/nginx.conf
+++ b/configs/nginx/nginx.conf
@@ -1,5 +1,3 @@
-load_module /usr/lib/nginx/modules/ngx_http_lua_module.so;
-
user http;
worker_processes 4;
@@ -28,6 +26,7 @@ http {
tcp_nodelay on;
tcp_nopush on;
keepalive_timeout 10;
+ client_max_body_size 4k;
client_body_temp_path /tmp/nginx.client_body_temp;
fastcgi_temp_path /tmp/nginx.fastcgi_temp;
@@ -45,11 +44,6 @@ http {
include /usr/share/kvmd/extras/*/nginx.http-ctx.conf;
-#PROD lua_shared_dict WS_TOKENS 10m;
-#PROD init_by_lua_block {
-#PROD WS_TOKEN_EXPIRES = 10;
-#PROD }
-
#PROD server {
#PROD listen 80;
#PROD server_name localhost;
@@ -67,34 +61,47 @@ http {
#PROD add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
-#PROD auth_basic "Restricted Area";
-#PROD auth_basic_user_file /etc/nginx/htpasswd;
+ auth_request /auth;
+
+ location = /auth {
+ internal;
+ proxy_pass http://kvmd/auth/check;
+ proxy_pass_request_body off;
+ proxy_set_header Content-Length "";
+ auth_request off;
+ }
location / {
root /usr/share/kvmd/web;
+ error_page 401 = @login;
+ error_page 403 = @login;
+ }
+
+ location @login {
+ return 302 /login;
+ }
+
+ location /login {
+ root /usr/share/kvmd/web;
+ auth_request off;
+ }
+
+ location /share {
+ root /usr/share/kvmd/web;
+ auth_request off;
+ }
+
+ location = /favicon.ico {
+ alias /usr/share/kvmd/web/favicon.ico;
+ auth_request off;
}
- location /ws_auth {
- # Workaround for Safari: https://bugs.webkit.org/show_bug.cgi?id=80362
-#PROD access_by_lua_block {
-#PROD local token = ngx.encode_base64(ngx.sha1_bin(ngx.var.http_Authorization));
-#PROD ngx.shared.WS_TOKENS:set(token, token, WS_TOKEN_EXPIRES);
-#PROD ngx.header["Set-Cookie"] = "WS_ACCESS_TOKEN=" .. token .. "; Path=/; Expires=" .. ngx.cookie_time(ngx.time() + WS_TOKEN_EXPIRES);
-#PROD }
- content_by_lua_block {
- ngx.say("ok");
- }
+ location = /robots.txt {
+ alias /usr/share/kvmd/web/robots.txt;
+ auth_request off;
}
location /kvmd/ws {
-#PROD auth_basic off;
-#PROD access_by_lua_block {
-#PROD local token = ngx.var.cookie_WS_ACCESS_TOKEN;
-#PROD local value, _ = ngx.shared.WS_TOKENS:get(token);
-#PROD if value == nil then
-#PROD ngx.exec("/ws_auth");
-#PROD end
-#PROD }
rewrite ^/kvmd/ws$ /ws break;
rewrite ^/kvmd/ws\?(.*)$ /ws?$1 break;
proxy_pass http://kvmd;
@@ -104,6 +111,7 @@ http {
proxy_connect_timeout 7d;
proxy_send_timeout 7d;
proxy_read_timeout 7d;
+ auth_request off;
}
location /kvmd/msd/write {
@@ -115,6 +123,7 @@ http {
limit_rate_after 50k;
client_max_body_size 0;
proxy_request_buffering off;
+ auth_request off;
}
location /kvmd/log {
@@ -126,6 +135,7 @@ http {
postpone_output 0;
proxy_buffering off;
proxy_ignore_headers X-Accel-Buffering;
+ auth_request off;
}
location /kvmd {
@@ -133,6 +143,7 @@ http {
rewrite ^/kvmd/(.*)$ /$1 break;
proxy_pass http://kvmd;
include /etc/nginx/proxy-params.conf;
+ auth_request off;
}
location /streamer {
diff --git a/extras/kvm/manifest.yaml b/extras/kvm/manifest.yaml
index d99cd7bf..609cb495 100644
--- a/extras/kvm/manifest.yaml
+++ b/extras/kvm/manifest.yaml
@@ -1,6 +1,6 @@
name: KVM
description: Open KVM session in a web browser
-icon: svg/kvm.svg
+icon: share/svg/kvm.svg
path: kvm
keyboard_cap: true
daemon: kvmd
diff --git a/kvmd/apps/kvmd/__init__.py b/kvmd/apps/kvmd/__init__.py
index 7662fae0..480a1017 100644
--- a/kvmd/apps/kvmd/__init__.py
+++ b/kvmd/apps/kvmd/__init__.py
@@ -5,8 +5,9 @@ from ...logging import get_logger
from ... import gpio
-from .logreader import LogReader
+from .auth import AuthManager
from .info import InfoManager
+from .logreader import LogReader
from .hid import Hid
from .atx import Atx
from .msd import MassStorageDevice
@@ -20,6 +21,10 @@ def main() -> None:
with gpio.bcm():
loop = asyncio.get_event_loop()
+ auth_manager = AuthManager(
+ htpasswd_path=str(config["auth"]["htpasswd"]),
+ )
+
info_manager = InfoManager(
meta_path=str(config["info"]["meta"]),
extras_path=str(config["info"]["extras"]),
@@ -80,6 +85,7 @@ def main() -> None:
)
Server(
+ auth_manager=auth_manager,
info_manager=info_manager,
log_reader=log_reader,
diff --git a/kvmd/apps/kvmd/auth.py b/kvmd/apps/kvmd/auth.py
new file mode 100644
index 00000000..f319b5cc
--- /dev/null
+++ b/kvmd/apps/kvmd/auth.py
@@ -0,0 +1,37 @@
+import secrets
+
+from typing import Dict
+from typing import Optional
+
+import passlib.apache
+
+from ...logging import get_logger
+
+
+# =====
+class AuthManager:
+ def __init__(self, htpasswd_path: str) -> None:
+ self.__htpasswd_path = htpasswd_path
+ self.__tokens: Dict[str, str] = {} # {token: user}
+
+ def login(self, user: str, passwd: str) -> Optional[str]:
+ htpasswd = passlib.apache.HtpasswdFile(self.__htpasswd_path)
+ if htpasswd.check_password(user, passwd):
+ for (token, token_user) in self.__tokens.items():
+ if user == token_user:
+ return token
+ token = secrets.token_hex(32)
+ self.__tokens[token] = user
+ get_logger().info("Logged in user %r", user)
+ return token
+ else:
+ get_logger().error("Access denied for user %r", user)
+ return None
+
+ def logout(self, token: str) -> None:
+ user = self.__tokens.pop(token, "")
+ if user:
+ get_logger().info("Logged out user %r", user)
+
+ def check(self, token: str) -> bool:
+ return (token in self.__tokens)
diff --git a/kvmd/apps/kvmd/server.py b/kvmd/apps/kvmd/server.py
index 2fae9994..3981f59b 100644
--- a/kvmd/apps/kvmd/server.py
+++ b/kvmd/apps/kvmd/server.py
@@ -1,4 +1,5 @@
import os
+import re
import signal
import socket
import asyncio
@@ -23,6 +24,7 @@ from ...aioregion import RegionIsBusyError
from ... import __version__
+from .auth import AuthManager
from .info import InfoManager
from .logreader import LogReader
from .hid import Hid
@@ -33,8 +35,29 @@ from .streamer import Streamer
# =====
-def _json(result: Optional[Dict]=None, status: int=200) -> aiohttp.web.Response:
- return aiohttp.web.Response(
+class HttpError(Exception):
+ pass
+
+
+class BadRequestError(HttpError):
+ pass
+
+
+class UnauthorizedError(HttpError):
+ pass
+
+
+class ForbiddenError(HttpError):
+ pass
+
+
+def _json(
+ result: Optional[Dict]=None,
+ status: int=200,
+ set_cookies: Optional[Dict[str, str]]=None,
+) -> aiohttp.web.Response:
+
+ response = aiohttp.web.Response(
text=json.dumps({
"ok": (status == 200),
"result": (result or {}),
@@ -42,37 +65,53 @@ def _json(result: Optional[Dict]=None, status: int=200) -> aiohttp.web.Response:
status=status,
content_type="application/json",
)
+ if set_cookies:
+ for (key, value) in set_cookies.items():
+ response.set_cookie(key, value)
+ return response
def _json_exception(err: Exception, status: int) -> aiohttp.web.Response:
name = type(err).__name__
msg = str(err)
- get_logger().error("API error: %s: %s", name, msg)
+ if not isinstance(err, (UnauthorizedError, ForbiddenError)):
+ get_logger().error("API error: %s: %s", name, msg)
return _json({
"error": name,
"error_msg": msg,
}, status=status)
-class BadRequestError(Exception):
- pass
-
-
_ATTR_EXPOSED = "exposed"
_ATTR_EXPOSED_METHOD = "exposed_method"
_ATTR_EXPOSED_PATH = "exposed_path"
_ATTR_SYSTEM_TASK = "system_task"
+_COOKIE_AUTH_TOKEN = "auth_token"
-def _exposed(http_method: str, path: str) -> Callable:
+
+def _exposed(http_method: str, path: str, auth_required: bool=True) -> Callable:
def make_wrapper(method: Callable) -> Callable:
async def wrap(self: "Server", request: aiohttp.web.Request) -> aiohttp.web.Response:
try:
+ if auth_required:
+ token = request.cookies.get(_COOKIE_AUTH_TOKEN, "")
+ if token:
+ if not self._auth_manager.check(_valid_token(token)):
+ raise ForbiddenError("Forbidden")
+ else:
+ raise UnauthorizedError("Unauthorized")
+
return (await method(self, request))
+
except RegionIsBusyError as err:
return _json_exception(err, 409)
except (BadRequestError, MsdOperationError) as err:
return _json_exception(err, 400)
+ except UnauthorizedError as err:
+ return _json_exception(err, 401)
+ except ForbiddenError as err:
+ return _json_exception(err, 403)
setattr(wrap, _ATTR_EXPOSED, True)
setattr(wrap, _ATTR_EXPOSED_METHOD, http_method)
@@ -95,6 +134,29 @@ def _system_task(method: Callable) -> Callable:
return wrap
+def _valid_user(user: Optional[str]) -> str:
+ if isinstance(user, str):
+ stripped = user.strip()
+ if re.match(r"^[a-z_][a-z0-9_-]*$", stripped):
+ return stripped
+ raise BadRequestError("Invalid user characters %r" % (user))
+
+
+def _valid_passwd(passwd: Optional[str]) -> str:
+ if isinstance(passwd, str):
+ if re.match(r"[\x20-\x7e]*$", passwd):
+ return passwd
+ raise BadRequestError("Invalid password characters")
+
+
+def _valid_token(token: Optional[str]) -> str:
+ if isinstance(token, str):
+ token = token.strip().lower()
+ if re.match(r"^[0-9a-f]{64}$", token):
+ return token
+ raise BadRequestError("Invalid auth token characters")
+
+
def _valid_bool(name: str, flag: Optional[str]) -> bool:
flag = str(flag).strip().lower()
if flag in ["1", "true", "yes"]:
@@ -127,6 +189,7 @@ class _Events(Enum):
class Server: # pylint: disable=too-many-instance-attributes
def __init__( # pylint: disable=too-many-arguments
self,
+ auth_manager: AuthManager,
info_manager: InfoManager,
log_reader: LogReader,
@@ -142,6 +205,7 @@ class Server: # pylint: disable=too-many-instance-attributes
loop: asyncio.AbstractEventLoop,
) -> None:
+ self._auth_manager = auth_manager
self.__info_manager = info_manager
self.__log_reader = log_reader
@@ -210,6 +274,29 @@ class Server: # pylint: disable=too-many-instance-attributes
"extras": await self.__info_manager.get_extras(),
}
+ # ===== AUTH
+
+ @_exposed("POST", "/auth/login", auth_required=False)
+ async def __auth_login_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response:
+ credentials = await request.post()
+ token = self._auth_manager.login(
+ user=_valid_user(credentials.get("user", "")),
+ passwd=_valid_passwd(credentials.get("passwd", "")),
+ )
+ if token:
+ return _json({}, set_cookies={_COOKIE_AUTH_TOKEN: token})
+ raise ForbiddenError("Forbidden")
+
+ @_exposed("POST", "/auth/logout")
+ async def __auth_logout_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response:
+ token = _valid_token(request.cookies.get(_COOKIE_AUTH_TOKEN, ""))
+ self._auth_manager.logout(token)
+ return _json({})
+
+ @_exposed("GET", "/auth/check")
+ async def __auth_check_handler(self, _: aiohttp.web.Request) -> aiohttp.web.Response:
+ return _json({})
+
# ===== SYSTEM
@_exposed("GET", "/info")
diff --git a/testenv/Dockerfile b/testenv/Dockerfile
index 78226b17..4fe453e8 100644
--- a/testenv/Dockerfile
+++ b/testenv/Dockerfile
@@ -31,12 +31,7 @@ RUN useradd -r -d / packer \
&& cd - \
&& rm -rf /tmp/packer-color
-COPY testenv/customizepkg.nginx /etc/customizepkg.d/nginx-mainline-mod-ndk
-COPY testenv/customizepkg.nginx /etc/customizepkg.d/nginx-mainline-mod-lua
-
RUN pacman -Syy \
- && user-packer -S --noconfirm \
- customizepkg \
&& mkdir /.npm \
&& chmod 777 /.npm \
&& user-packer -S --noconfirm \
@@ -50,7 +45,6 @@ RUN pacman -Syy \
htmlhint \
eslint \
&& rm -rf /.npm \
- && env MAKEPKGOPTS="--skipchecksums --skippgpcheck" user-packer -S --noconfirm nginx-mainline-mod-lua \
&& pacman -Sc --noconfirm
COPY testenv/requirements.txt requirements.txt
diff --git a/testenv/customizepkg.nginx b/testenv/customizepkg.nginx
deleted file mode 100644
index 5e7db5a4..00000000
--- a/testenv/customizepkg.nginx
+++ /dev/null
@@ -1 +0,0 @@
-replace#global#_nginxver=.*#_nginxver=`pacman -Q nginx-mainline | grep -Po "\\d+\\.\\d+\\.\\d+"`
diff --git a/testenv/kvmd.yaml b/testenv/kvmd.yaml
index 8233cd58..da37e9d8 100644
--- a/testenv/kvmd.yaml
+++ b/testenv/kvmd.yaml
@@ -4,6 +4,9 @@ kvmd:
port: 8081
heartbeat: 3.0
+ auth:
+ htpasswd: /etc/kvmd/htpasswd
+
info:
meta: /etc/kvmd/meta.yaml
extras: /usr/share/kvmd/extras
diff --git a/testenv/requirements.txt b/testenv/requirements.txt
index 4f95d90a..a968fcb1 100644
--- a/testenv/requirements.txt
+++ b/testenv/requirements.txt
@@ -1,6 +1,7 @@
git+git://github.com/willbuckner/rpi-gpio-development-mock@master#egg=rpi
aiohttp
aiofiles
+passlib
pyudev
pyyaml
pyserial
diff --git a/testenv/tox.ini b/testenv/tox.ini
index 47ce1636..b10700a2 100644
--- a/testenv/tox.ini
+++ b/testenv/tox.ini
@@ -33,7 +33,7 @@ deps =
[testenv:eslint]
whitelist_externals = eslint
-commands = eslint --config=testenv/eslintrc.yaml --color --ext .js web/js
+commands = eslint --config=testenv/eslintrc.yaml --color --ext .js web/share/js
[testenv:htmlhint]
whitelist_externals = htmlhint
diff --git a/web/browserconfig.xml b/web/browserconfig.xml
deleted file mode 100644
index fcb73892..00000000
--- a/web/browserconfig.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<browserconfig>
- <msapplication>
- <tile>
- <square150x150logo src="/mstile-150x150.png"/>
- <TileColor>#2b5797</TileColor>
- </tile>
- </msapplication>
-</browserconfig>
diff --git a/web/index.html b/web/index.html
index b628b615..e1113454 100644
--- a/web/index.html
+++ b/web/index.html
@@ -4,22 +4,22 @@
<meta charset="utf-8" />
<title>Pi-KVM Index</title>
- <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
- <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
- <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
- <link rel="manifest" href="/site.webmanifest">
- <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
+ <link rel="apple-touch-icon" sizes="180x180" href="/share/apple-touch-icon.png">
+ <link rel="icon" type="image/png" sizes="32x32" href="/share/favicon-32x32.png">
+ <link rel="icon" type="image/png" sizes="16x16" href="/share/favicon-16x16.png">
+ <link rel="manifest" href="/share/site.webmanifest">
+ <link rel="mask-icon" href="/share/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#2b5797">
<meta name="theme-color" content="#ffffff">
- <link rel="stylesheet" href="css/vars.css">
- <link rel="stylesheet" href="css/main.css">
- <link rel="stylesheet" href="css/modals.css">
- <link rel="stylesheet" href="css/index/index.css">
+ <link rel="stylesheet" href="share/css/vars.css">
+ <link rel="stylesheet" href="share/css/main.css">
+ <link rel="stylesheet" href="share/css/modals.css">
+ <link rel="stylesheet" href="share/css/index/index.css">
- <script src="js/bb.js"></script>
- <script src="js/tools.js"></script>
- <script src="js/index/main.js"></script>
+ <script src="share/js/bb.js"></script>
+ <script src="share/js/tools.js"></script>
+ <script src="share/js/index/main.js"></script>
<script>window.onload = main;</script>
</head>
@@ -30,7 +30,7 @@
<table>
<tr>
<td valign="top" class="logo">
- <img class="svg-gray" src="svg/logo.svg" alt="Open Source Hardware" height="40" />
+ <img class="svg-gray" src="share/svg/logo.svg" alt="Open Source Hardware" height="40" />
</td>
<td valign="top">
<table>
diff --git a/web/kvm/index.html b/web/kvm/index.html
index 8cc66513..4472112a 100644
--- a/web/kvm/index.html
+++ b/web/kvm/index.html
@@ -4,40 +4,40 @@
<meta charset="utf-8" />
<title>Pi-KVM Session</title>
- <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
- <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
- <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
- <link rel="manifest" href="/site.webmanifest">
- <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
+ <link rel="apple-touch-icon" sizes="180x180" href="/share/apple-touch-icon.png">
+ <link rel="icon" type="image/png" sizes="32x32" href="/share/favicon-32x32.png">
+ <link rel="icon" type="image/png" sizes="16x16" href="/share/favicon-16x16.png">
+ <link rel="manifest" href="/share/site.webmanifest">
+ <link rel="mask-icon" href="/share/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#2b5797">
<meta name="theme-color" content="#ffffff">
- <link rel="stylesheet" href="../css/vars.css">
- <link rel="stylesheet" href="../css/main.css">
- <link rel="stylesheet" href="../css/menu.css">
- <link rel="stylesheet" href="../css/windows.css">
- <link rel="stylesheet" href="../css/modals.css">
- <link rel="stylesheet" href="../css/leds.css">
- <link rel="stylesheet" href="../css/sliders.css">
- <link rel="stylesheet" href="../css/switches.css">
- <link rel="stylesheet" href="../css/progress.css">
- <link rel="stylesheet" href="../css/kvm/stream.css">
- <link rel="stylesheet" href="../css/kvm/hid.css">
- <link rel="stylesheet" href="../css/kvm/msd.css">
- <link rel="stylesheet" href="../css/kvm/keyboard.css">
- <link rel="stylesheet" href="../css/kvm/about.css">
+ <link rel="stylesheet" href="../share/css/vars.css">
+ <link rel="stylesheet" href="../share/css/main.css">
+ <link rel="stylesheet" href="../share/css/menu.css">
+ <link rel="stylesheet" href="../share/css/windows.css">
+ <link rel="stylesheet" href="../share/css/modals.css">
+ <link rel="stylesheet" href="../share/css/leds.css">
+ <link rel="stylesheet" href="../share/css/sliders.css">
+ <link rel="stylesheet" href="../share/css/switches.css">
+ <link rel="stylesheet" href="../share/css/progress.css">
+ <link rel="stylesheet" href="../share/css/kvm/stream.css">
+ <link rel="stylesheet" href="../share/css/kvm/hid.css">
+ <link rel="stylesheet" href="../share/css/kvm/msd.css">
+ <link rel="stylesheet" href="../share/css/kvm/keyboard.css">
+ <link rel="stylesheet" href="../share/css/kvm/about.css">
- <script src="../js/bb.js"></script>
- <script src="../js/tools.js"></script>
- <script src="../js/wm.js"></script>
- <script src="../js/kvm/stream.js"></script>
- <script src="../js/kvm/atx.js"></script>
- <script src="../js/kvm/keyboard.js"></script>
- <script src="../js/kvm/mouse.js"></script>
- <script src="../js/kvm/hid.js"></script>
- <script src="../js/kvm/msd.js"></script>
- <script src="../js/kvm/session.js"></script>
- <script src="../js/kvm/main.js"></script>
+ <script src="../share/js/bb.js"></script>
+ <script src="../share/js/tools.js"></script>
+ <script src="../share/js/wm.js"></script>
+ <script src="../share/js/kvm/stream.js"></script>
+ <script src="../share/js/kvm/atx.js"></script>
+ <script src="../share/js/kvm/keyboard.js"></script>
+ <script src="../share/js/kvm/mouse.js"></script>
+ <script src="../share/js/kvm/hid.js"></script>
+ <script src="../share/js/kvm/msd.js"></script>
+ <script src="../share/js/kvm/session.js"></script>
+ <script src="../share/js/kvm/main.js"></script>
<script>window.onload = main;</script>
</head>
@@ -47,16 +47,16 @@
<li class="menu-left-items">
<a id="menu-logo" href="/">
&#8617;&nbsp;&nbsp;
- <img class="svg-gray" src="../svg/logo.svg" alt="&pi;-kvm" />
+ <img class="svg-gray" src="../share/svg/logo.svg" alt="&pi;-kvm" />
</a>
</li>
<li class="menu-right-items">
<a class="menu-item" href="#">
- <img data-dont-hide-menu id="link-led" class="led-gray" src="../svg/link-led.svg" />
- <img data-dont-hide-menu id="stream-led" class="led-gray" src="../svg/stream-led.svg" />
- <img data-dont-hide-menu id="hid-keyboard-led" class="led-gray" src="../svg/hid-keyboard-led.svg" />
- <img data-dont-hide-menu id="hid-mouse-led" class="led-gray" src="../svg/hid-mouse-led.svg" />
+ <img data-dont-hide-menu id="link-led" class="led-gray" src="../share/svg/link-led.svg" />
+ <img data-dont-hide-menu id="stream-led" class="led-gray" src="../share/svg/stream-led.svg" />
+ <img data-dont-hide-menu id="hid-keyboard-led" class="led-gray" src="../share/svg/hid-keyboard-led.svg" />
+ <img data-dont-hide-menu id="hid-mouse-led" class="led-gray" src="../share/svg/hid-mouse-led.svg" />
System &#8628;
</a>
<div data-dont-hide-menu class="menu-item-content">
@@ -118,8 +118,8 @@
<li class="menu-right-items">
<a class="menu-item" href="#">
- <img data-dont-hide-menu id="atx-power-led" class="led-gray" src="../svg/atx-power-led.svg" />
- <img data-dont-hide-menu id="atx-hdd-led" class="led-gray" src="../svg/atx-hdd-led.svg" />
+ <img data-dont-hide-menu id="atx-power-led" class="led-gray" src="../share/svg/atx-power-led.svg" />
+ <img data-dont-hide-menu id="atx-hdd-led" class="led-gray" src="../share/svg/atx-hdd-led.svg" />
ATX &#8628;
</a>
<div class="menu-item-content menu-item-content-buttons">
@@ -132,7 +132,7 @@
<li class="menu-right-items">
<a class="menu-item" href="#">
- <img data-dont-hide-menu id="msd-led" class="led-gray" src="../svg/msd-led.svg" />
+ <img data-dont-hide-menu id="msd-led" class="led-gray" src="../share/svg/msd-led.svg" />
Mass Storage &#8628;
</a>
<div data-dont-hide-menu id="msd-menu" class="menu-item-content">
@@ -140,7 +140,7 @@
<div class="menu-item-content-text">
<table>
<tr>
- <td><img src="../svg/warning.svg" /></td>
+ <td><img src="../share/svg/warning.svg" /></td>
<td><b>Mass Storage Device is not operational</b></td>
</tr>
</table>
@@ -152,7 +152,7 @@
<div class="menu-item-content-text">
<table>
<tr>
- <td><img src="../svg/warning.svg" /></td>
+ <td><img src="../share/svg/warning.svg" /></td>
<td><b>Current image is broken!</b><br><sub>Perhaps uploading was interrupted</sub></td>
</tr>
</table>
@@ -164,7 +164,7 @@
<div class="menu-item-content-text">
<table>
<tr>
- <td><img src="../svg/info.svg" /></td>
+ <td><img src="../share/svg/info.svg" /></td>
<td><b>Another user uploads an image</b></td>
</tr>
</table>
@@ -233,7 +233,7 @@
<li class="menu-right-items">
<a class="menu-item" href="#">
- <img data-dont-hide-menu id="hid-pak-led" class="led-gray" src="../svg/gear-led.svg" />
+ <img data-dont-hide-menu id="hid-pak-led" class="led-gray" src="../share/svg/gear-led.svg" />
Shortcuts &#8628;
</a>
<div data-dont-hide-menu class="menu-item-content">
@@ -282,7 +282,7 @@
</div>
<div id="stream-info"></div>
<div id="stream-box" class="stream-box-inactive">
- <img id="stream-image" class="stream-image-inactive" src="../png/blank-stream.png" />
+ <img id="stream-image" class="stream-image-inactive" src="../share/png/blank-stream.png" />
</div>
<div id="stream-mouse-buttons">
<button data-mouse-button="left" class="row50">Left Click</button>
@@ -534,7 +534,7 @@
<table>
<tr>
<td valign="top" class="logo">
- <img class="svg-gray" src="../svg/logo.svg" alt="Open Source Hardware" height="40" />
+ <img class="svg-gray" src="../share/svg/logo.svg" alt="Open Source Hardware" height="40" />
</td>
<td valign="top">
<table>
diff --git a/web/login/index.html b/web/login/index.html
new file mode 100644
index 00000000..10ca2e2b
--- /dev/null
+++ b/web/login/index.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8" />
+ <title>Pi-KVM Login</title>
+
+ <link rel="apple-touch-icon" sizes="180x180" href="/share/apple-touch-icon.png">
+ <link rel="icon" type="image/png" sizes="32x32" href="/share/favicon-32x32.png">
+ <link rel="icon" type="image/png" sizes="16x16" href="/share/favicon-16x16.png">
+ <link rel="manifest" href="/share/site.webmanifest">
+ <link rel="mask-icon" href="/share/safari-pinned-tab.svg" color="#5bbad5">
+ <meta name="msapplication-TileColor" content="#2b5797">
+ <meta name="theme-color" content="#ffffff">
+
+ <link rel="stylesheet" href="../share/css/vars.css">
+ <link rel="stylesheet" href="../share/css/main.css">
+ <link rel="stylesheet" href="../share/css/modals.css">
+ <link rel="stylesheet" href="../share/css/login/login.css">
+
+ <script src="../share/js/bb.js"></script>
+ <script src="../share/js/tools.js"></script>
+ <script src="../share/js/login/main.js"></script>
+
+ <script>window.onload = main;</script>
+</head>
+
+<body>
+ <div id="login-box">
+ <div id="login">
+ <table>
+ <tr>
+ <td>Username:</td>
+ <td><input type="text" id="user-input"></td>
+ </tr>
+ <tr>
+ <td>Password:</td>
+ <td><input type="password" id="passwd-input"></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td><button id="login-button">Login</button></td>
+ </tr>
+ </table>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/web/mstile-150x150.png b/web/mstile-150x150.png
deleted file mode 100644
index 353ef691..00000000
--- a/web/mstile-150x150.png
+++ /dev/null
Binary files differ
diff --git a/web/android-chrome-192x192.png b/web/share/android-chrome-192x192.png
index 2473df5c..2473df5c 100644
--- a/web/android-chrome-192x192.png
+++ b/web/share/android-chrome-192x192.png
Binary files differ
diff --git a/web/apple-touch-icon.png b/web/share/apple-touch-icon.png
index 207dc65e..207dc65e 100644
--- a/web/apple-touch-icon.png
+++ b/web/share/apple-touch-icon.png
Binary files differ
diff --git a/web/css/index/index.css b/web/share/css/index/index.css
index 72ab5c24..72ab5c24 100644
--- a/web/css/index/index.css
+++ b/web/share/css/index/index.css
diff --git a/web/css/kvm/about.css b/web/share/css/kvm/about.css
index e6e2360f..e6e2360f 100644
--- a/web/css/kvm/about.css
+++ b/web/share/css/kvm/about.css
diff --git a/web/css/kvm/hid.css b/web/share/css/kvm/hid.css
index 6663a85c..6663a85c 100644
--- a/web/css/kvm/hid.css
+++ b/web/share/css/kvm/hid.css
diff --git a/web/css/kvm/keyboard.css b/web/share/css/kvm/keyboard.css
index 2ff622f3..2ff622f3 100644
--- a/web/css/kvm/keyboard.css
+++ b/web/share/css/kvm/keyboard.css
diff --git a/web/css/kvm/msd.css b/web/share/css/kvm/msd.css
index 4a1c5d28..4a1c5d28 100644
--- a/web/css/kvm/msd.css
+++ b/web/share/css/kvm/msd.css
diff --git a/web/css/kvm/stream.css b/web/share/css/kvm/stream.css
index c99c3c9c..c99c3c9c 100644
--- a/web/css/kvm/stream.css
+++ b/web/share/css/kvm/stream.css
diff --git a/web/css/leds.css b/web/share/css/leds.css
index fe23a434..fe23a434 100644
--- a/web/css/leds.css
+++ b/web/share/css/leds.css
diff --git a/web/share/css/login/login.css b/web/share/css/login/login.css
new file mode 100644
index 00000000..1fae42d6
--- /dev/null
+++ b/web/share/css/login/login.css
@@ -0,0 +1,25 @@
+div#login-box {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ text-align: center;
+ min-height: 100vh;
+}
+
+div#login {
+ text-align: left;
+ outline: none;
+ word-wrap: break-word;
+ max-width: 400px;
+ border: var(--border-window-thin);
+ border-radius: 8px;
+ box-sizing: border-box;
+ box-shadow: var(--shadow-big);
+ background-color: var(--cs-window-default-bg);
+ padding: 15px;
+}
+
+input[type="text"]#user-input, input[type="password"]#passwd-input {
+ text-align: center;
+}
diff --git a/web/css/main.css b/web/share/css/main.css
index 28361905..f5f0db75 100644
--- a/web/css/main.css
+++ b/web/share/css/main.css
@@ -62,6 +62,7 @@ img.svg-gray {
button, select {
box-shadow: none;
border: none;
+ border-radius: 4px;
color: var(--cs-control-default-fg);
background-color: var(--cs-control-default-bg);
display: block;
@@ -116,8 +117,18 @@ select:active {
background-image: url("../svg/select-arrow-intensive.svg") !important;
}
+input[type=text], input[type=password] {
+ overflow-x: auto;
+ font-family: monospace;
+ border: thin;
+ border-radius: 4px;
+ color: var(--cs-code-default-fg);
+ background-color: var(--cs-code-default-bg);
+ padding: 2px;
+}
+
@media only screen and (min-width: 768px) and (max-width: 1024px) and (orientation: portrait) {
- button, select {
+ button, select, input[type=text], input[type=password] {
height: 45px !important;
}
}
diff --git a/web/css/menu.css b/web/share/css/menu.css
index 5ececb32..151b82b1 100644
--- a/web/css/menu.css
+++ b/web/share/css/menu.css
@@ -98,6 +98,7 @@ ul#menu li div.menu-item-content-text {
}
ul#menu li div.menu-item-content button, select {
+ border-radius: 0;
text-align: left;
padding: 0 16px;
}
diff --git a/web/css/modals.css b/web/share/css/modals.css
index f557bd08..ed9837c7 100644
--- a/web/css/modals.css
+++ b/web/share/css/modals.css
@@ -47,6 +47,7 @@ div.modal div.modal-window div.modal-buttons {
}
div.modal div.modal-window div.modal-buttons button {
+ border-radius: 0;
height: 40px;
}
@media only screen and (min-width: 768px) and (max-width: 1024px) and (orientation: portrait) {
diff --git a/web/css/progress.css b/web/share/css/progress.css
index 1085e5cf..1085e5cf 100644
--- a/web/css/progress.css
+++ b/web/share/css/progress.css
diff --git a/web/css/sliders.css b/web/share/css/sliders.css
index fe0be262..fe0be262 100644
--- a/web/css/sliders.css
+++ b/web/share/css/sliders.css
diff --git a/web/css/switches.css b/web/share/css/switches.css
index 65e187f9..65e187f9 100644
--- a/web/css/switches.css
+++ b/web/share/css/switches.css
diff --git a/web/css/vars.css b/web/share/css/vars.css
index 57e71c0f..57e71c0f 100644
--- a/web/css/vars.css
+++ b/web/share/css/vars.css
diff --git a/web/css/windows.css b/web/share/css/windows.css
index 32628e55..32628e55 100644
--- a/web/css/windows.css
+++ b/web/share/css/windows.css
diff --git a/web/favicon-16x16.png b/web/share/favicon-16x16.png
index adc66e3e..adc66e3e 100644
--- a/web/favicon-16x16.png
+++ b/web/share/favicon-16x16.png
Binary files differ
diff --git a/web/favicon-32x32.png b/web/share/favicon-32x32.png
index 5fec1005..5fec1005 100644
--- a/web/favicon-32x32.png
+++ b/web/share/favicon-32x32.png
Binary files differ
diff --git a/web/js/bb.js b/web/share/js/bb.js
index 565a3ab4..565a3ab4 100644
--- a/web/js/bb.js
+++ b/web/share/js/bb.js
diff --git a/web/js/index/main.js b/web/share/js/index/main.js
index 8f5c74a6..8f5c74a6 100644
--- a/web/js/index/main.js
+++ b/web/share/js/index/main.js
diff --git a/web/js/kvm/atx.js b/web/share/js/kvm/atx.js
index ed5f045e..ed5f045e 100644
--- a/web/js/kvm/atx.js
+++ b/web/share/js/kvm/atx.js
diff --git a/web/js/kvm/hid.js b/web/share/js/kvm/hid.js
index 49d659ab..49d659ab 100644
--- a/web/js/kvm/hid.js
+++ b/web/share/js/kvm/hid.js
diff --git a/web/js/kvm/keyboard.js b/web/share/js/kvm/keyboard.js
index 3c821406..3c821406 100644
--- a/web/js/kvm/keyboard.js
+++ b/web/share/js/kvm/keyboard.js
diff --git a/web/js/kvm/main.js b/web/share/js/kvm/main.js
index 5c6d775a..5c6d775a 100644
--- a/web/js/kvm/main.js
+++ b/web/share/js/kvm/main.js
diff --git a/web/js/kvm/mouse.js b/web/share/js/kvm/mouse.js
index 58e46d15..58e46d15 100644
--- a/web/js/kvm/mouse.js
+++ b/web/share/js/kvm/mouse.js
diff --git a/web/js/kvm/msd.js b/web/share/js/kvm/msd.js
index c23de99d..c23de99d 100644
--- a/web/js/kvm/msd.js
+++ b/web/share/js/kvm/msd.js
diff --git a/web/js/kvm/session.js b/web/share/js/kvm/session.js
index e3e74160..5ba43511 100644
--- a/web/js/kvm/session.js
+++ b/web/share/js/kvm/session.js
@@ -14,8 +14,7 @@ function Session() {
var __streamer = new Streamer();
var __init__ = function() {
- $("link-led").title = "Not connected yet...";
- __startPoller();
+ __startSession();
};
/********************************************************************************/
@@ -44,24 +43,15 @@ function Session() {
$("about-version-streamer").innerHTML = `${state.version.streamer} (${state.streamer})`;
};
- var __startPoller = function() {
+ var __startSession = function() {
$("link-led").className = "led-yellow";
$("link-led").title = "Connecting...";
-
- var http = tools.makeRequest("GET", "/ws_auth", function() {
- if (http.readyState === 4) {
- if (http.status === 200) {
- var proto = (location.protocol === "https:" ? "wss" : "ws");
- __ws = new WebSocket(`${proto}://${location.host}/kvmd/ws`);
- __ws.onopen = __wsOpenHandler;
- __ws.onmessage = __wsMessageHandler;
- __ws.onerror = __wsErrorHandler;
- __ws.onclose = __wsCloseHandler;
- } else {
- __wsCloseHandler(null);
- }
- }
- });
+ var proto = (location.protocol === "https:" ? "wss" : "ws");
+ __ws = new WebSocket(`${proto}://${location.host}/kvmd/ws`);
+ __ws.onopen = __wsOpenHandler;
+ __ws.onmessage = __wsMessageHandler;
+ __ws.onerror = __wsErrorHandler;
+ __ws.onclose = __wsCloseHandler;
};
var __wsOpenHandler = function(event) {
@@ -118,7 +108,7 @@ function Session() {
setTimeout(function() {
$("link-led").className = "led-yellow";
- setTimeout(__startPoller, 500);
+ setTimeout(__startSession, 500);
}, 500);
};
diff --git a/web/js/kvm/stream.js b/web/share/js/kvm/stream.js
index 01ded5bc..01ded5bc 100644
--- a/web/js/kvm/stream.js
+++ b/web/share/js/kvm/stream.js
diff --git a/web/share/js/login/main.js b/web/share/js/login/main.js
new file mode 100644
index 00000000..dc45d03f
--- /dev/null
+++ b/web/share/js/login/main.js
@@ -0,0 +1,36 @@
+function main() {
+ if (checkBrowser()) {
+ tools.setOnClick($("login-button"), __login);
+ document.onkeyup = function(event) {
+ if (event.code == "Enter") {
+ event.preventDefault();
+ __login();
+ }
+ };
+ $("user-input").focus();
+ }
+}
+
+function __login() {
+ var user = $("user-input").value;
+ var passwd = $("passwd-input").value;
+ var body = `user=${encodeURIComponent(user)}&passwd=${encodeURIComponent(passwd)}`;
+ var http = tools.makeRequest("POST", "/kvmd/auth/login", function() {
+ if (http.readyState === 4) {
+ if (http.status === 200) {
+ document.location.href = "/";
+ }
+ __setDisabled(false);
+ $("passwd-input").focus();
+ $("passwd-input").select();
+ }
+ }, body, "application/x-www-form-urlencoded");
+ http.send();
+ __setDisabled(true);
+}
+
+function __setDisabled(disabled) {
+ $("user-input").disabled = disabled;
+ $("passwd-input").disabled = disabled;
+ $("login-button").disabled = disabled;
+}
diff --git a/web/js/tools.js b/web/share/js/tools.js
index 81970ef8..80b074ee 100644
--- a/web/js/tools.js
+++ b/web/share/js/tools.js
@@ -1,12 +1,15 @@
var tools = new function() {
var __debug = (new URL(window.location.href)).searchParams.get("debug");
- this.makeRequest = function(method, url, callback, timeout=null) {
+ this.makeRequest = function(method, url, callback, body=null, content_type=null) {
var http = new XMLHttpRequest();
http.open(method, url, true);
+ if (content_type) {
+ http.setRequestHeader("Content-Type", content_type);
+ }
http.onreadystatechange = callback;
- http.timeout = (timeout ? timeout : 5000);
- http.send();
+ http.timeout = 5000;
+ http.send(body);
return http;
};
diff --git a/web/js/wm.js b/web/share/js/wm.js
index c292ece7..c292ece7 100644
--- a/web/js/wm.js
+++ b/web/share/js/wm.js
diff --git a/web/png/blank-stream.png b/web/share/png/blank-stream.png
index f1e11b14..f1e11b14 100644
--- a/web/png/blank-stream.png
+++ b/web/share/png/blank-stream.png
Binary files differ
diff --git a/web/safari-pinned-tab.svg b/web/share/safari-pinned-tab.svg
index 86314c0a..86314c0a 100644
--- a/web/safari-pinned-tab.svg
+++ b/web/share/safari-pinned-tab.svg
diff --git a/web/site.webmanifest b/web/share/site.webmanifest
index 409a6c2e..849f6a89 100644
--- a/web/site.webmanifest
+++ b/web/share/site.webmanifest
@@ -3,7 +3,7 @@
"short_name": "",
"icons": [
{
- "src": "/android-chrome-192x192.png",
+ "src": "/share/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
diff --git a/web/svg/atx-hdd-led.svg b/web/share/svg/atx-hdd-led.svg
index 75987570..75987570 100644
--- a/web/svg/atx-hdd-led.svg
+++ b/web/share/svg/atx-hdd-led.svg
diff --git a/web/svg/atx-power-led.svg b/web/share/svg/atx-power-led.svg
index b82af7bc..b82af7bc 100644
--- a/web/svg/atx-power-led.svg
+++ b/web/share/svg/atx-power-led.svg
diff --git a/web/svg/fan-led.svg b/web/share/svg/fan-led.svg
index 8cbdd76d..8cbdd76d 100644
--- a/web/svg/fan-led.svg
+++ b/web/share/svg/fan-led.svg
diff --git a/web/svg/favicon.svg b/web/share/svg/favicon.svg
index a3fde08f..a3fde08f 100644
--- a/web/svg/favicon.svg
+++ b/web/share/svg/favicon.svg
diff --git a/web/svg/gear-led.svg b/web/share/svg/gear-led.svg
index db9aff21..db9aff21 100644
--- a/web/svg/gear-led.svg
+++ b/web/share/svg/gear-led.svg
diff --git a/web/svg/hid-keyboard-led.svg b/web/share/svg/hid-keyboard-led.svg
index dc17c615..dc17c615 100644
--- a/web/svg/hid-keyboard-led.svg
+++ b/web/share/svg/hid-keyboard-led.svg
diff --git a/web/svg/hid-mouse-led.svg b/web/share/svg/hid-mouse-led.svg
index b922bdde..b922bdde 100644
--- a/web/svg/hid-mouse-led.svg
+++ b/web/share/svg/hid-mouse-led.svg
diff --git a/web/svg/info.svg b/web/share/svg/info.svg
index b28139d5..b28139d5 100644
--- a/web/svg/info.svg
+++ b/web/share/svg/info.svg
diff --git a/web/svg/kvm.svg b/web/share/svg/kvm.svg
index 7a0dcf71..7a0dcf71 100644
--- a/web/svg/kvm.svg
+++ b/web/share/svg/kvm.svg
diff --git a/web/svg/link-led.svg b/web/share/svg/link-led.svg
index 8cc10e13..8cc10e13 100644
--- a/web/svg/link-led.svg
+++ b/web/share/svg/link-led.svg
diff --git a/web/svg/logo.svg b/web/share/svg/logo.svg
index eec39fd3..eec39fd3 100644
--- a/web/svg/logo.svg
+++ b/web/share/svg/logo.svg
diff --git a/web/svg/msd-led.svg b/web/share/svg/msd-led.svg
index a8da7092..a8da7092 100644
--- a/web/svg/msd-led.svg
+++ b/web/share/svg/msd-led.svg
diff --git a/web/svg/select-arrow-inactive.svg b/web/share/svg/select-arrow-inactive.svg
index ba68f05c..ba68f05c 100644
--- a/web/svg/select-arrow-inactive.svg
+++ b/web/share/svg/select-arrow-inactive.svg
diff --git a/web/svg/select-arrow-intensive.svg b/web/share/svg/select-arrow-intensive.svg
index 3223f099..3223f099 100644
--- a/web/svg/select-arrow-intensive.svg
+++ b/web/share/svg/select-arrow-intensive.svg
diff --git a/web/svg/select-arrow-normal.svg b/web/share/svg/select-arrow-normal.svg
index 174663ce..174663ce 100644
--- a/web/svg/select-arrow-normal.svg
+++ b/web/share/svg/select-arrow-normal.svg
diff --git a/web/svg/stream-led.svg b/web/share/svg/stream-led.svg
index 8dfbfbe3..8dfbfbe3 100644
--- a/web/svg/stream-led.svg
+++ b/web/share/svg/stream-led.svg
diff --git a/web/svg/stream-mouse-cursor.svg b/web/share/svg/stream-mouse-cursor.svg
index ff852ef6..ff852ef6 100644
--- a/web/svg/stream-mouse-cursor.svg
+++ b/web/share/svg/stream-mouse-cursor.svg
diff --git a/web/svg/warning.svg b/web/share/svg/warning.svg
index 3137b24d..3137b24d 100644
--- a/web/svg/warning.svg
+++ b/web/share/svg/warning.svg