diff --git a/classes/IdTokenBuilder.php b/classes/IdTokenBuilder.php index 456a678910fa15f8e8fc32f01faa5b7179f69745..0f9ea1719f69b34ccc9e03630e09ce24db52b186 100644 --- a/classes/IdTokenBuilder.php +++ b/classes/IdTokenBuilder.php @@ -22,7 +22,7 @@ class IdTokenBuilder { } private function createPayloadArray(AccessToken $accessToken, string $nonce = null) { - + $user = $this->locator->getUserDAO()->findUserById($accessToken->userId); $payloadArr = array( @@ -56,4 +56,27 @@ class IdTokenBuilder { return $payloadArr; } + /** + * @param int $lifespan in hours + * @param string $audit target service + */ + public function generateNewToken(int $lifespan, string $audit) { + $keyPair = $this->locator->getJWKSDAO()->getNewestKeyPair(); + + $user = $this->locator->getSession()->getUser(); + + $iat = time(); + $exp = $iat + $lifespan * 3600; + + $payload = array( + 'iss' => $this->locator->config->jwtIssuer, + 'sub' => strval($user->id), + 'iat' => $iat, + 'exp' => $exp, + 'aud' => $audit + ); + + return JWT::encode($payload, $keyPair->privateKey, $keyPair->alg, $keyPair->keyId); + } + } diff --git a/config-example.json b/config-example.json index db809602b8f8d1ca213b1fa3c1fc7dbc4c2e7249..7d8a68604885b8122eec2ff21755266ad9c239f6 100644 --- a/config-example.json +++ b/config-example.json @@ -47,5 +47,12 @@ "gms": { "id": "gms", "joinEndpoint": "http://localhost:8082/gms/ws/jwt/join" + }, + "tokenIssuer": { + "services": [{ + "id": "fileserver", + "label": "File Server" + }], + "lifespan": [1, 6, 12, 24] } } diff --git a/css/style.css b/css/style.css index 021218b894fe81a217d6e3e322c0b01bc07b31b1..732e5dc44724bb948980905510b9639dad321d7b 100644 --- a/css/style.css +++ b/css/style.css @@ -188,4 +188,8 @@ body { .service-logo { padding-right: 10px; max-height: 50px; +} + +#token-issuer-btn { + margin-top: 20px; } \ No newline at end of file diff --git a/include/front-controller.php b/include/front-controller.php index 88010c7f41d835f1d8dda52d1b7b24c2afe5b628..4df876eabd279b985dca7f5487f90e3936332578 100644 --- a/include/front-controller.php +++ b/include/front-controller.php @@ -349,4 +349,54 @@ Flight::route('GET /account', function () { } }); +Flight::route('GET /token-issuer', function () { + + session_start(); + + if (empty($_SESSION['csrf_token'])) { + $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); + } + $csrfToken = $_SESSION['csrf_token']; + + global $locator; + + $user = $locator->getSession()->getUser(); + $config = $locator->config->tokenIssuer; + + if ($user === null) { + Flight::redirect('/'); + } else { + $admin = $locator->getUserDAO()->isAdmin($user->id); + Flight::render('token-issuer.php', array('title' => 'RAP Token Issuer', + 'version' => $locator->getVersion(), 'session' => $locator->getSession(), + 'config' => $config, 'csrfToken' => $csrfToken, + 'contextRoot' => $locator->config->contextRoot)); + } +}); + +Flight::route('POST /token-issuer', function () { + + session_start(); + global $locator; + + if (empty($_POST['csrf_token']) || !(hash_equals($_SESSION['csrf_token'], $_POST['csrf_token']))) { + throw new \RAP\UnauthorizedException("Invalid CSRF token"); + } + if ($locator->getSession()->getUser() === null) { + throw new \RAP\UnauthorizedException("You must be registered to perform this action"); + } + + $postData = Flight::request()->data; + if (!isset($postData['lifespan']) || !isset($postData['audit'])) { + throw new \RAP\BadRequestException("Missing form parameter"); + } + + $tokenBuilder = $locator->getIdTokenBuilder(); + $token = $tokenBuilder->generateNewToken($postData['lifespan'], $postData['audit']); + + header('Content-Type: text/plain'); + header("Content-disposition: attachment; filename=\"token.txt\""); + echo $token; +}); + include 'admin.php'; diff --git a/js/index.js b/js/index.js index d6b1003f08078743aa885f47b88e42dcedc9a813..78e521aeeba5d0a83f871c3e23452273652fd5d5 100644 --- a/js/index.js +++ b/js/index.js @@ -28,6 +28,7 @@ function loadTooltips() { $('.primary-identity-icon').tooltip(); $('#join-btn').tooltip(); + $('#token-issuer-btn').tooltip(); } // When the document is loaded diff --git a/views/account-management.php b/views/account-management.php index 5cb798c9a1285bccc9cb2f7bb5a7f5ab968029fa..03e9e4c19a38cc97b8867c1d5005d9b92653c849 100644 --- a/views/account-management.php +++ b/views/account-management.php @@ -17,10 +17,21 @@ include 'include/header.php'; </div> </div> </div> - <div class="col-sm-2 text-center"> - <a class="btn btn-success" id="join-btn" href="<?php echo $contextRoot; ?>?action=join" title="Perform an additional login to join your identities" data-toggle="tooltip" data-placement="bottom"> - Join with another identity - </a> + <div class="col-sm-2"> + <div class="row"> + <div class="col-sm-12"> + <a class="btn btn-success" id="join-btn" href="<?php echo $contextRoot; ?>?action=join" title="Perform an additional login to join your identities" data-toggle="tooltip" data-placement="bottom"> + Join with another identity + </a> + </div> + </div> + <div class="row"> + <div class="col-sm-12"> + <a class="btn btn-default" id="token-issuer-btn" href="<?php echo $contextRoot; ?>/token-issuer" title="Generate tokens for CLI" data-toggle="tooltip" data-placement="bottom"> + Token issuer + </a> + </div> + </div> </div> <div class="col-sm-5"> <a href="logout" class="btn btn-primary pull-right">Logout</a> diff --git a/views/token-issuer.php b/views/token-issuer.php new file mode 100644 index 0000000000000000000000000000000000000000..12ce18ace7b4b42f10d9d7ada6e5c064351b1d58 --- /dev/null +++ b/views/token-issuer.php @@ -0,0 +1,58 @@ +<?php +include 'include/header.php'; +?> + +<div class="row"> + <div class="col-sm-6 col-sm-offset-3"> + <p>This panel can be used to generate tokens to be used from command line interfaces and desktop applications.</p> + <br/> + <div class="panel panel-default"> + <div class="panel-heading"> + <h3 class="panel-title">Token issuer</h3> + </div> + <div class="panel-body"> + <form class="form-horizontal" action="<?php echo $contextRoot . '/token-issuer'; ?>" method="post"> + <div class="form-group"> + <label for="service" class="col-sm-4 control-label">Service</label> + <div class="col-sm-8"> + <select class="form-control" id="service" name="audit"> + <?php + foreach ($config->services as $service) { + echo "<option value=\"$service->id\">$service->label</option>"; + } + ?> + </select> + </div> + </div> + <div class="form-group"> + <label for="lifespan" class="col-sm-4 control-label">Duration (hours)</label> + <div class="col-sm-8"> + <select class="form-control" id="lifespan" name="lifespan"> + <?php + foreach ($config->lifespans as $lifespan) { + echo "<option>$lifespan</option>"; + } + ?> + </select> + </div> + </div> + <div class="form-group"> + <div class="col-sm-8 col-sm-offset-4"> + <input type="submit" class="btn btn-primary" value="Download token" /> + </div> + </div> + <input type="hidden" value="<?php echo $csrfToken; ?>" name="csrf_token" /> + </form> + </div> + </div> + <br/> + <p class="text-center"> + <strong> + <a href="<?php echo $contextRoot . '/account'; ?>">Back to account manager</a> + </strong> + </p> + </div> +</div> + +<?php +include 'include/footer.php';