Skip to content

Commit

Permalink
Add Keyset check to admin UI
Browse files Browse the repository at this point in the history
  • Loading branch information
csev committed May 17, 2024
1 parent c9fc7b7 commit e31c8aa
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 19 deletions.
5 changes: 5 additions & 0 deletions admin/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@
</a>
</li>
<hr/>
<li>
<a href="#" title="Manage OAuth2 Keyset"
onclick="showModalIframeUrl(this.title, 'iframe-dialog', 'iframe-frame', 'keyset', _TSUGI.spinnerUrl); return false;" >
Check Keyset
</a></li>
<li>
<a href="#" title="Check Cache"
onclick="showModalIframeUrl(this.title, 'iframe-dialog', 'iframe-frame', 'cache', _TSUGI.spinnerUrl); return false;" >
Expand Down
104 changes: 104 additions & 0 deletions admin/keyset.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php
if ( ! defined('COOKIE_SESSION') ) define('COOKIE_SESSION', true);
require_once("../config.php");
session_start();
require_once("gate.php");
require_once("admin_util.php");
if ( $REDIRECTED === true || ! isset($_SESSION["admin"]) ) return;

use \Tsugi\Util\U;
use \Tsugi\UI\Output;
use \Tsugi\Core\Keyset;

if (U::isKeyNotEmpty($_POST, "maintain") ) {
Keyset::maintain();
}

$sign_kid = null;
$sign_privkey = null;
$success = false;

if (U::isKeyNotEmpty($_POST, "getkey") ) {
$success = Keyset::getSigning($sign_privkey, $sign_kid);
}

$keyset_url = $CFG->wwwroot . "/lti/keyset.php";

$apc_check = U::appCacheGet('keyset_last_check', 0);
$now = time();
$delta = -1;
if ( $apc_check > 0 ) $delta = abs($now-$apc_check);
$privkey = U::appCacheGet('keyset_privkey', null);
$kid = U::appCacheGet('keyset_kid', null);

?>
<html>
<head>
</head>
<body>
<h1>Keyset Detail</h1>
<p>Keyset URL:
<a href="<?= $keyset_url ?>" target="_blank"><?= $keyset_url ?></a>
</p>
<form method="POST">
<input type="submit" name="maintain" value="Maintain / Check for Key Rotation">
<input type="submit" name="getkey" value="Retrieve Signing Key">
</form>
<?php

if ( is_string($success) ) {
echo("<p>Problem retrieving key: ".$success."</p>\n");
} else if ( $success == true ) {
echo("<p>Current Signing KID: ".$sign_kid."\n");
echo("<p>Current Signing Private Key:: ".substr($sign_privkey,0, 80)." ...\n");
echo("<hr/>\n");
}

if ( U::apcuAvailable() ) {
echo("<p>Cache is available</p>\n");
echo("<p>Last apc check: ".$delta." seconds ago.\n");
if ( $privkey != null && $kid != null ) {
echo("<p>Cached KID: ".$kid."\n");
echo("<p>Cached Private Key:: ".substr($privkey,0, 80)." ...\n");
} else {
echo("<p>There is no key in the cache. Cache is emptry until a key is retrieved. Keys in the cache expire after 10 minutes.</p>\n");
}
} else {
echo("<p>Cache is not available</p>\n");
}

$stmt = $PDOX->queryDie(
"SELECT * FROM {$CFG->dbprefix}lti_keyset
WHERE deleted = 0 AND pubkey IS NOT NULL AND privkey IS NOT NULL
ORDER BY created_at DESC LIMIT 10"
);
if ( $stmt->success ) {
$rows = $stmt->fetchAll(\PDO::FETCH_ASSOC);
} else {
$rows = array();
}

if ( count($rows) > 0 ) {
echo("<p>Key pairs in the database:</p>\n");
echo("<table border=\"3\">\n");
?>
<thead><tr><td>Created_at</td><td>Public Key</td><td>Private Key</td></tr></thead>
<?php
foreach($rows as $row) {
$row['privkey'] = str_replace("\n", " ", substr($row['privkey'], 0, 40)) . " ...";
$row['pubkey'] = str_replace("\n", " ", substr($row['pubkey'], 0, 40)) . " ...";
echo("<tr>\n");
echo("<td>".$row['created_at']."</td>\n");
echo("<td>".$row['pubkey']."</td>\n");
echo("<td>".$row['privkey']."</td>\n");
echo("</tr>\n");
}
echo("</table>\n");
} else {
echo("<p>No key pairs in the database</p>\n");
}

?>
</body>
</html>

2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"react/dns" : ">=1.12.0",
"react/socket" : ">=1.15.0",

"tsugi/lib": "dev-master#da95ced92a3a2222895cc06b0aeb04dd02928142",
"tsugi/lib": "dev-master#ab146db66e268c3bea09df3ad23ba83f8dc40995",
"koseu/lib": "dev-master#b9a31b7875108196dbdf284e685b813d424f2def"
},
"config": {
Expand Down
10 changes: 5 additions & 5 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions vendor/composer/installed.json
Original file line number Diff line number Diff line change
Expand Up @@ -7708,12 +7708,12 @@
"source": {
"type": "git",
"url": "https://github.com/tsugiproject/tsugi-php.git",
"reference": "da95ced92a3a2222895cc06b0aeb04dd02928142"
"reference": "ab146db66e268c3bea09df3ad23ba83f8dc40995"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tsugiproject/tsugi-php/zipball/da95ced92a3a2222895cc06b0aeb04dd02928142",
"reference": "da95ced92a3a2222895cc06b0aeb04dd02928142",
"url": "https://api.github.com/repos/tsugiproject/tsugi-php/zipball/ab146db66e268c3bea09df3ad23ba83f8dc40995",
"reference": "ab146db66e268c3bea09df3ad23ba83f8dc40995",
"shasum": ""
},
"require": {
Expand All @@ -7727,7 +7727,7 @@
"phpunit/php-timer": "v5.0.3",
"phpunit/phpunit": "9.*"
},
"time": "2024-05-15T17:56:56+00:00",
"time": "2024-05-17T14:41:06+00:00",
"default-branch": true,
"type": "library",
"installation-source": "dist",
Expand Down
6 changes: 3 additions & 3 deletions vendor/composer/installed.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
'name' => '__root__',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => 'ae3b0e04f48ddd2647f8960e6866f8b09dadb9e3',
'reference' => 'c9fc7b74a8400eaf5ae2dbda80d7693b241862e3',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
Expand All @@ -13,7 +13,7 @@
'__root__' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => 'ae3b0e04f48ddd2647f8960e6866f8b09dadb9e3',
'reference' => 'c9fc7b74a8400eaf5ae2dbda80d7693b241862e3',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
Expand Down Expand Up @@ -1078,7 +1078,7 @@
'tsugi/lib' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => 'da95ced92a3a2222895cc06b0aeb04dd02928142',
'reference' => 'ab146db66e268c3bea09df3ad23ba83f8dc40995',
'type' => 'library',
'install_path' => __DIR__ . '/../tsugi/lib',
'aliases' => array(
Expand Down
20 changes: 14 additions & 6 deletions vendor/tsugi/lib/src/Core/Keyset.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ public static function maintain() {

$delta = abs($now-$apc_check);
if ( is_string($kid) && is_string($privkey) && $apc_check > 0 && $delta < self::$apc_expire ) {
if ( self::$verbose ) error_log("Keyset::maintain Last key rotation check seconds=".$delta);
return;
if ( self::$verbose ) $retval = "Keyset::maintain Last key rotation check seconds=".$delta;
return true;
}

U::appCacheSet('keyset_last_check', $now, self::$apc_ttl);
Expand Down Expand Up @@ -66,8 +66,9 @@ public static function maintain() {
$stmt = $PDOX->queryReturnError($sql, $values);

if ( ! $stmt->success ) {
error_log("Keyset::maintain Unable to insert new key into keyset\n");
return;
$retval = "Keyset::maintain Unable to insert new key into keyset\n";
if ( self::$verbose ) error_log($retval);
return $retval;
}

// Reload our key
Expand All @@ -85,17 +86,22 @@ public static function maintain() {
error_log("KeySet::maintain table cleanup rows=".$stmt->rowCount());
}

return true;

} else {
if ( self::$verbose ) error_log("Keyset::maintain No key rotation necessary days=".$days);
return true;
}

}

// Get the private key and kid - call by reference
public static function getSigning(&$privkey, &$kid) {
global $PDOX, $CFG;

// Make sure we have a key and it is recent
self::maintain();
$success = self::maintain();
if ( is_string($success) ) return $success;

$now = time();
$last_load = U::appCacheGet('keyset_last_load', 0);
Expand All @@ -106,7 +112,7 @@ public static function getSigning(&$privkey, &$kid) {
$delta = abs($now-$last_load);
if ( is_string($kid) && is_string($privkey) && $delta < self::$apc_expire ) {
if ( self::$verbose ) error_log("Keyset::getSigning cache hit seconds=".$delta);
return;
return true;
}

$sql = "SELECT * FROM {$CFG->dbprefix}lti_keyset ORDER BY created_at DESC LIMIT 1";
Expand All @@ -122,10 +128,12 @@ public static function getSigning(&$privkey, &$kid) {
U::appCacheSet('keyset_last_load', $now, self::$apc_ttl);
U::appCacheSet('keyset_privkey', $privkey, self::$apc_ttl);
U::appCacheSet('keyset_kid', $kid, self::$apc_ttl);
return true;
} else {
U::appCacheDelete('keyset_last_load');
U::appCacheDelete('keyset_privkey');
U::appCacheDelete('keyset_kid');
return "Keyset::getSigning could not load key";
}
}

Expand Down

0 comments on commit e31c8aa

Please sign in to comment.