summaryrefslogtreecommitdiff
path: root/pages
diff options
context:
space:
mode:
authorRaindropsSys <contact@minteck.org>2023-05-13 19:25:44 +0200
committerRaindropsSys <contact@minteck.org>2023-05-13 19:25:44 +0200
commit21ed7d0e837d74c1ebd8ada4396f96ce42c14fb1 (patch)
tree8bdad11e806ad4ac6c68902eaf72913a4554e484 /pages
parentf80190dddaa72d9f8863b0b922e557668b6cba27 (diff)
downloadpluralconnect-21ed7d0e837d74c1ebd8ada4396f96ce42c14fb1.tar.gz
pluralconnect-21ed7d0e837d74c1ebd8ada4396f96ce42c14fb1.tar.bz2
pluralconnect-21ed7d0e837d74c1ebd8ada4396f96ce42c14fb1.zip
Updated 14 files and added 6 files (automated)
Diffstat (limited to 'pages')
-rw-r--r--pages/api.inc1
-rw-r--r--pages/api/browser.php4
-rw-r--r--pages/api/computer.php6
-rw-r--r--pages/api/disconnect.php26
-rw-r--r--pages/api/reauthenticate.php8
-rw-r--r--pages/api/rename.php17
-rw-r--r--pages/api/session.php24
-rw-r--r--pages/home.inc7
-rw-r--r--pages/logout.inc2
-rw-r--r--pages/pair.inc32
-rw-r--r--pages/sessions.inc182
11 files changed, 304 insertions, 5 deletions
diff --git a/pages/api.inc b/pages/api.inc
index f71ac10..5c13bac 100644
--- a/pages/api.inc
+++ b/pages/api.inc
@@ -12,6 +12,7 @@ if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/pages/api/" . $toplevel . ".php"))
require_once $_SERVER['DOCUMENT_ROOT'] . "/pages/api/" . $toplevel . ".php";
} else {
header("HTTP/1.1 500 Internal Server Error");
+ header("Content-Type: text/plain");
echo("Endpoint not found");
die();
} \ No newline at end of file
diff --git a/pages/api/browser.php b/pages/api/browser.php
new file mode 100644
index 0000000..657b2a7
--- /dev/null
+++ b/pages/api/browser.php
@@ -0,0 +1,4 @@
+<?php
+
+header("Content-Type: application/json");
+die(json_encode(get_browser(), JSON_PRETTY_PRINT)); \ No newline at end of file
diff --git a/pages/api/computer.php b/pages/api/computer.php
index a9b87ae..f5117f9 100644
--- a/pages/api/computer.php
+++ b/pages/api/computer.php
@@ -6,7 +6,11 @@ if (isset($_GET["chrome"])) {
}
require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/session.inc"; global $isLoggedIn; global $_PROFILE;
-if (!$isLoggedIn) header("Location: /-/login") and die();
+
+if (!$isLoggedIn || !isset($_PROFILE) || !isset($_PROFILE["login"])) {
+ header("Location: /-/login");
+ die();
+}
$request_raw = file_get_contents('php://input');
$json_object = json_decode($request_raw, true);
diff --git a/pages/api/disconnect.php b/pages/api/disconnect.php
new file mode 100644
index 0000000..13363e6
--- /dev/null
+++ b/pages/api/disconnect.php
@@ -0,0 +1,26 @@
+<?php
+
+require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/functions.inc";
+require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/session.inc"; global $isLoggedIn; global $isLowerLoggedIn; global $_PROFILE;
+if (!$isLoggedIn && !$isLowerLoggedIn) {
+ header("Location: /-/login");
+ die();
+}
+
+$list = array_filter([...scandir($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens"), ...scandir($_SERVER['DOCUMENT_ROOT'] . "/includes/lowertokens")], function ($token) use ($_PROFILE) {
+ $session = file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . $token) ? json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . $token), true) : json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/lowertokens/" . $token), true);
+
+ return $token !== "." && $token !== ".." && isset($session["last"]) && isset($session["profile"]) && $session["profile"]["id"] === $_PROFILE["id"];
+});
+
+foreach ($list as $token) {
+ $session = file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . $token) ? json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . $token), true) : json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/lowertokens/" . $token), true);
+
+ if (isset($_GET["id"]) && sha1($token) . md5($token) === $_GET["id"]) {
+ if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . $token)) {
+ unlink($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . $token);
+ } else {
+ unlink($_SERVER['DOCUMENT_ROOT'] . "/includes/lowertokens/" . $token);
+ }
+ }
+} \ No newline at end of file
diff --git a/pages/api/reauthenticate.php b/pages/api/reauthenticate.php
index 50657cc..e726e8e 100644
--- a/pages/api/reauthenticate.php
+++ b/pages/api/reauthenticate.php
@@ -13,7 +13,13 @@ if (!$isLoggedIn || $isLowerLoggedIn) {
$newToken = generateToken();
if (isset($_COOKIE['PEH2_SESSION_TOKEN'])) {
- file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . $newToken, file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . $_COOKIE['PEH2_SESSION_TOKEN']));
+ $old = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . $_COOKIE['PEH2_SESSION_TOKEN']), true);
+ $old["name"] = base64_decode($_GET["name"] ?? "LQo=");
+ $old["created"] = time();
+ $old["addresses"] = [];
+ $old["last"] = time();
+
+ file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . $newToken, json_encode($old));
}
die($newToken); \ No newline at end of file
diff --git a/pages/api/rename.php b/pages/api/rename.php
new file mode 100644
index 0000000..d450557
--- /dev/null
+++ b/pages/api/rename.php
@@ -0,0 +1,17 @@
+<?php
+
+require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/functions.inc";
+require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/session.inc"; global $isLoggedIn; global $isLowerLoggedIn;
+
+header("Content-Type: application/json");
+
+if (!$isLoggedIn || $isLowerLoggedIn) {
+ header("Location: /-/login");
+ die();
+}
+
+$data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . str_replace("/", "", $_COOKIE['PEH2_SESSION_TOKEN'])), true);
+
+$data["name"] = $_GET["name"] ?? $data["name"];
+
+file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . str_replace("/", "", $_COOKIE['PEH2_SESSION_TOKEN']), json_encode($data)); \ No newline at end of file
diff --git a/pages/api/session.php b/pages/api/session.php
new file mode 100644
index 0000000..f91288e
--- /dev/null
+++ b/pages/api/session.php
@@ -0,0 +1,24 @@
+<?php
+
+require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/functions.inc";
+require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/session.inc"; global $isLoggedIn; global $isLowerLoggedIn;
+
+header("Content-Type: application/json");
+
+if (!$isLoggedIn || $isLowerLoggedIn) {
+ die(json_encode([
+ "name" => null,
+ "created" => null,
+ "last_seen" => null,
+ "seen_at" => null
+ ], JSON_PRETTY_PRINT));
+}
+
+$data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . str_replace("/", "", $_COOKIE['PEH2_SESSION_TOKEN'])), true);
+
+die(json_encode([
+ "name" => $data["name"],
+ "created" => date('c', $data["created"]),
+ "last_seen" => date('c', $data["last"]),
+ "seen_at" => array_keys($data["addresses"])
+], JSON_PRETTY_PRINT)); \ No newline at end of file
diff --git a/pages/home.inc b/pages/home.inc
index 9686f7f..48c26da 100644
--- a/pages/home.inc
+++ b/pages/home.inc
@@ -77,6 +77,7 @@ function members() { global $isLoggedIn; global $isLowerLoggedIn; global $app; ?
</div>
<?php endif; ?>
+
<?php
$cache = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/cache/home.json"), true);
@@ -155,6 +156,12 @@ function members() { global $isLoggedIn; global $isLowerLoggedIn; global $app; ?
<b>Notice:</b> The administrators are currently trying a new optimisation technique based on a virtual file system (chvfs). Data loss, corruption or inconsistency may happen and should be reported on <a href="https://bugs.equestria.dev/issues/CH" target="_blank">bugs.equestria.dev</a>.
</div>-->
+ <?php if (isset($_COOKIE["PEH2_SESSION_TOKEN"]) && $_COOKIE["PEH2_SESSION_TOKEN"] !== "" && !$isLoggedIn && !$isLowerLoggedIn): ?>
+ <div class="alert alert-warning" style="margin-top:20px;">
+ <b>You were previously logged in to Cold Haze, </b>however you have been logged out due to inactivity, due to your device being removed, or due to switching to a new authentication system. Please log in again. <a href="https://bugs.equestria.dev/issue/CH-56/Better-session-security" target="_blank">Learn more.</a>
+ </div>
+ <?php endif; ?>
+
<?php
if ($isLowerLoggedIn || $isLoggedIn) {
diff --git a/pages/logout.inc b/pages/logout.inc
index 37d89ed..3d3f39b 100644
--- a/pages/logout.inc
+++ b/pages/logout.inc
@@ -6,6 +6,8 @@ if (isset($_COOKIE['PEH2_SESSION_TOKEN'])) {
} elseif (file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/lowertokens/" . str_replace(".", "", str_replace("/", "", $_COOKIE['PEH2_SESSION_TOKEN'])))) {
unlink($_SERVER['DOCUMENT_ROOT'] . "/includes/lowertokens/" . str_replace(".", "", str_replace("/", "", $_COOKIE['PEH2_SESSION_TOKEN'])));
}
+
+ header("Set-Cookie: PEH2_SESSION_TOKEN=; SameSite=None; Path=/; Secure; HttpOnly; Expires=0");
}
header("Location: /") and die(); \ No newline at end of file
diff --git a/pages/pair.inc b/pages/pair.inc
index 7f57420..fa301ad 100644
--- a/pages/pair.inc
+++ b/pages/pair.inc
@@ -21,9 +21,15 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/components/header.inc'; glob
<p>Pairing allows a connected device that cannot normally use Cold Haze to gather data. This device will get full access to your account as if they were acting on your behalf, so make sure you trust the device and/or application you are using.</p>
+ <?php $data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . str_replace("/", "", $_COOKIE['PEH2_SESSION_TOKEN'])), true); if (isset($data["profile"])): ?>
<p>Equestria.dev may not have verified the application you use, always check for a signature. Enter the pairing code displayed on your device below:</p>
<input autofocus type="text" placeholder="Pairing code" class="form-control" style="margin-bottom:15px;color:white;background:#111;border-color:#222;" id="code">
+ <?php else: ?>
+ <div class="alert alert-danger">
+ <b>Error:</b> You cannot use the pairing feature because your current session is using the old authentication system. Please log out and log in again to continue.
+ </div>
+ <?php endif; ?>
</div>
<div class="modal fade" id="confirm">
@@ -39,7 +45,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/components/header.inc'; glob
<p>You are about to pair the following device with your Cold Haze account:</p>
<blockquote style="display: grid; grid-template-columns: max-content 1fr; grid-gap: 10px;">
<div style="display: flex; align-items: center; justify-content: center;">
- <img src="/assets/logo/newlogo3.png" style="width: 32px; height: 32px;">
+ <img src="/assets/logo/newlogo3.png" id="device-icon" style="width: 32px; height: 32px;">
</div>
<div>
<b>Name:</b> <span id="device-name">-</span><br>
@@ -82,12 +88,30 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/components/header.inc'; glob
<script>
const modal = new bootstrap.Modal(document.getElementById("confirm"));
+ function getIconForName(name) {
+ if (name.startsWith("Luna Desktop ")) {
+ return "https://git.equestria.dev/equestria.dev/luna/raw/branch/mane/icons/logo.png";
+ }
+
+ if (name.startsWith("Cold Haze for Wear OS ")) {
+ return "https://static.equestria.horse/ch-wear.png";
+ }
+
+ return "/assets/logo/newlogo3.png";
+ }
+
(async () => {
const token = await (await fetch("/api/token")).text();
let ws = window.ws = new WebSocket("wss://ponies.equestria.horse/_PairingServices-WebSocket-EntryPoint/socket");
ws.onopen = (event) => {
console.log(event);
+
+ if (location.hash && location.hash.startsWith("#/")) {
+ document.getElementById("code").value = location.hash.substring(2);
+ document.getElementById("code").onkeydown();
+ location.hash = "";
+ }
}
ws.onclose = (event) => {
@@ -109,8 +133,9 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/components/header.inc'; glob
modal.hide();
document.getElementById("code").focus();
} else if (data.type === "device") {
- document.getElementById("device-name").innerText = document.getElementById("paired-name-1").innerText = document.getElementById("paired-name-2").innerText = data.identity.name;
+ document.getElementById("device-name").innerText = document.getElementById("paired-name-1").innerText = document.getElementById("paired-name-2").innerText = window.currentName = data.identity.name;
document.getElementById("device-address").innerText = data.identity.address;
+ document.getElementById("device-icon").src = getIconForName(data.identity.name ?? "");
document.getElementById("modal-button").classList.add("disabled");
modal.show();
@@ -134,6 +159,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/components/header.inc'; glob
}
window.currentCode = null;
+ window.currentName = "";
function pair(code) {
const ua = new UAParser(navigator.userAgent).getResult();
@@ -162,7 +188,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/components/header.inc'; glob
ws.send(JSON.stringify({
type: "confirm",
code: window.currentCode,
- token: (await (await fetch("/api/reauthenticate")).text()).trim()
+ token: (await (await fetch("/api/reauthenticate/?name=" + encodeURIComponent(btoa(window.currentName)))).text()).trim()
}));
}
diff --git a/pages/sessions.inc b/pages/sessions.inc
new file mode 100644
index 0000000..6b52a6d
--- /dev/null
+++ b/pages/sessions.inc
@@ -0,0 +1,182 @@
+<?php
+
+require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/init.inc"; global $title; global $isLoggedIn; global $isLowerLoggedIn; global $lang; global $pages;
+require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/components/header.inc'; global $_PROFILE;
+
+?>
+
+<br>
+<div class="container">
+ <h1>Sessions</h1>
+ <script>window.devices = {};</script>
+
+ <p>Here are all the currently open sessions for your account. Clicking on a session will delete it, meaning the device using this session will be logged out.</p>
+
+ <?php $data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . str_replace("/", "", $_COOKIE['PEH2_SESSION_TOKEN'])), true); if (isset($data["profile"])): ?>
+ <div class="list-group">
+ <?php
+
+ $list = array_filter([...scandir($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens"), ...scandir($_SERVER['DOCUMENT_ROOT'] . "/includes/lowertokens")], function ($token) {
+ $session = file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . $token) ? json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . $token), true) : json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/lowertokens/" . $token), true);
+
+ return $token !== "." && $token !== ".." && isset($session["last"]) && isset($session["profile"]);
+ });
+ usort($list, function ($token1, $token2) {
+ $session1 = file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . $token1) ? json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . $token1), true) : json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/lowertokens/" . $token1), true);
+ $session2 = file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . $token2) ? json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . $token2), true) : json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/lowertokens/" . $token2), true);
+
+ if (isset($session1["last"]) && isset($session2["last"])) {
+ return $session2["last"] - $session1["last"];
+ } else {
+ return INF;
+ }
+ });
+
+ foreach ($list as $token): $session = file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . $token) ? json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . $token), true) : json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/lowertokens/" . $token), true); if (isset($session["profile"]) && isset($session["name"]) && $session["profile"]["id"] === $_PROFILE["id"]): uasort($session["addresses"], function ($a, $b) {
+ return $b - $a;
+ }); ?>
+ <a class="list-group-item list-group-item-action" onclick="logOut(&quot;<?= sha1($token) . md5($token) ?>&quot;);">
+ <b><?= $session["name"] ?></b><?php if ($token === $_COOKIE["PEH2_SESSION_TOKEN"]): ?><span style="margin-left: 10px;" class="badge bg-primary">This device</span><?php endif; ?><script>window.devices["<?= sha1($token) . md5($token) ?>"]=JSON.parse(`<?= json_encode([
+ "name" => $session["name"],
+ "lastIP" => array_keys($session["addresses"])[count(array_keys($session["addresses"])) - 1],
+ "lastSeen" => timeAgo($session["last"]),
+ "currentDevice" => $token === $_COOKIE["PEH2_SESSION_TOKEN"]
+ ]) ?>`);</script><br>
+ Logged in <?= timeAgo($session["created"]) ?>, last activity <?= timeAgo($session["last"]) ?>
+ <blockquote class="session-bq">
+ <?php foreach ($session["addresses"] as $address => $last): ?>
+ <?= $address ?> ยท <?= timeAgo($last) ?><br>
+ <?php endforeach; ?>
+ </blockquote>
+ </a>
+ <?php endif; endforeach; ?>
+ </div>
+ <?php else: ?>
+ <div class="alert alert-danger">
+ <b>Error:</b> You cannot use the session manager because your current session is using the old authentication system. Please log out and log in again to continue.
+ </div>
+ <?php endif; ?>
+</div>
+
+<div class="modal fade" id="confirm">
+ <div class="modal-dialog">
+ <div class="modal-content">
+
+ <div class="modal-header">
+ <h4 class="modal-title">Log out this device?</h4>
+ <button type="button" class="btn-close" data-bs-toggle="modal"></button>
+ </div>
+
+ <div class="modal-body">
+ <p>You are about to log out the following device from your Cold Haze account:</p>
+ <blockquote id="device-bq">
+ <div>
+ <b>Name:</b> <span id="device-name">-</span><br>
+ <b>Last address:</b> <span id="device-address">-</span><br>
+ <b>Last activity:</b> <span id="device-activity">-</span>
+ </div>
+ </blockquote>
+ <p class="text-danger" id="device-current" style="display: none;">This is the device you are currently using, which means you will get logged out as soon as you click on confirm.</p>
+ <p id="device-normal"></p>
+ <span class="btn btn-success" id="modal-button" style="margin-right: 5px;" onclick="confirm();">Confirm</span><span class="btn btn-outline-secondary" data-bs-toggle="modal">Cancel</span>
+ </div>
+ </div>
+ </div>
+</div>
+
+<script>
+ window.currentSession = null;
+ window.currentDevice = null;
+ window.modal = new bootstrap.Modal(document.getElementById("confirm"));
+
+ async function confirm() {
+ await fetch("/api/disconnect?id=" + window.currentSession);
+
+ if (currentDevice.currentDevice) {
+ location.href = "/-/logout";
+ } else {
+ location.reload();
+ }
+ }
+
+ function logOut(id) {
+ window.currentSession = id;
+ window.currentDevice = devices[id];
+
+ if (currentDevice.currentDevice) {
+ document.getElementById("device-current").style.display = "";
+ document.getElementById("device-normal").style.display = "none";
+ } else {
+ document.getElementById("device-current").style.display = "none";
+ document.getElementById("device-normal").style.display = "";
+ }
+
+ document.getElementById("device-name").innerText = currentDevice.name;
+ document.getElementById("device-address").innerText = currentDevice.lastIP;
+ document.getElementById("device-activity").innerText = currentDevice.lastSeen;
+
+ modal.show();
+ }
+</script>
+
+<style>
+ .modal-header {
+ border-bottom: 1px solid #353738;
+ }
+
+ .modal-content {
+ border: 1px solid rgba(255, 255, 255, .2);
+ background-color: #111;
+ }
+
+ .btn-close {
+ filter: invert(1);
+ }
+
+ .list-group-item {
+ color: #fff;
+ background-color: #222;
+ border: 1px solid rgba(255, 255, 255, .125);
+ }
+
+ .list-group-item.disabled {
+ color: #fff;
+ background-color: #222;
+ border-color: rgba(255, 255, 255, .125);
+ opacity: .75;
+ }
+
+ .list-group-item:hover {
+ background-color: #252525;
+ color: #ddd;
+ }
+
+ .list-group-item:active, .list-group-item:focus {
+ background-color: #272727;
+ color: #bbb;
+ }
+
+ .member-link, .list-group-item-action {
+ cursor: pointer !important;
+ }
+
+ .alert-dismissible .btn-close {
+ filter: none !important;
+ }
+
+ .session-bq {
+ margin-bottom: 5px;
+ margin-top: 10px;
+ margin-left: 5px;
+ padding-left: 10px;
+ border-left: 3px solid rgba(255, 255, 255, .25);
+ }
+
+ #device-bq {
+ margin-left: 5px;
+ padding-left: 10px;
+ border-left: 3px solid rgba(255, 255, 255, .25);
+ }
+</style>
+
+<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/components/footer.inc'; ?>