Restrict API keys to public-visibility tickets by default (#70)
api/tickets_api.php (both the single-ticket read and the list/triage path) bypassed ticket visibility entirely for any 'read'-scope key, regardless of who it was issued to or what it was for — any key got blanket read access to Confidential and Internal ticket titles, descriptions, and comments, with no way to scope a key more narrowly. Added see_all_visibility to api_keys (migration 006), defaulting to false for both new and existing keys — the prior blanket-access behavior is what's being restricted here, so unlike scope's own un-migrated-database fallback (which defaults toward preserving old behavior), a missing/null value here defaults to the new, restrictive one. An admin can opt a specific key in via a new checkbox in the API Key Management UI when it genuinely needs the full queue. tickets_api.php now builds a synthetic "no special access" user and runs it through TicketModel's existing per-user visibility plumbing (getVisibilityFilter/canUserAccessTicket) instead of a separate SQL path, so this stays in lockstep with however visibility rules evolve for real users. That synthetic user_id is -1, not 0: testing surfaced that canUserAccessTicket()'s confidential-ticket check does a PHP-level (int) cast, and (int)null === 0, so an unassigned confidential ticket's NULL assigned_to would otherwise false-positive-match a user_id of 0. Verified against real MariaDB with public/confidential/internal test tickets: a public-only-scoped key's list only returns the public ticket, and canUserAccessTicket() correctly returns false for both the confidential ticket (unassigned, then reassigned to a real user — both cases) and the internal one; a see_all_visibility key sees all three, unchanged from the prior behavior. Also verified createKey()/ validateKey()'s default-false and explicit-true paths round-trip correctly through the real DB. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0117oBw2jN4kALYeS8HPq4zV
This commit is contained in:
@@ -97,12 +97,15 @@ The following features are intentionally **not planned** for this system:
|
|||||||
- **Admin UI**: Generate and manage API keys at `/admin/api-keys` (paginated)
|
- **Admin UI**: Generate and manage API keys at `/admin/api-keys` (paginated)
|
||||||
- **Bearer Token Auth**: Use API keys with `Authorization: Bearer YOUR_KEY` header
|
- **Bearer Token Auth**: Use API keys with `Authorization: Bearer YOUR_KEY` header
|
||||||
- **Key Scopes**: `read` (GET only) or `read_write` (create/comment/close). A `read` key cannot mutate anything, including creating tickets. Existing keys default to `read_write`.
|
- **Key Scopes**: `read` (GET only) or `read_write` (create/comment/close). A `read` key cannot mutate anything, including creating tickets. Existing keys default to `read_write`.
|
||||||
|
- **Visibility Scope**: keys default to **public-visibility tickets only** — Confidential and Internal tickets are excluded from `/api/tickets_api.php`, same as they'd be for a regular user with no special access. Check **See all visibility** when generating a key only if that specific integration genuinely needs the full queue; this is a deliberate opt-in, not something a key gets by having `read_write` scope or any other setting.
|
||||||
- **Expiration**: Optional expiration dates for keys
|
- **Expiration**: Optional expiration dates for keys
|
||||||
- **Revocation**: Revoke compromised keys instantly
|
- **Revocation**: Revoke compromised keys instantly
|
||||||
|
|
||||||
### Bearer API (automation / triage)
|
### Bearer API (automation / triage)
|
||||||
All Bearer-authenticated, rate-limited, and (like `create_ticket_api.php`) exempt from Authelia at the reverse proxy — the API key is the only credential. Comments/closes made via the API are attributed to the **key's name** (linked to the key's owner).
|
All Bearer-authenticated, rate-limited, and (like `create_ticket_api.php`) exempt from Authelia at the reverse proxy — the API key is the only credential. Comments/closes made via the API are attributed to the **key's name** (linked to the key's owner).
|
||||||
|
|
||||||
|
`/api/tickets_api.php` filters by ticket visibility according to the key's **Visibility Scope** setting above (public-only by default); every other Bearer endpoint below operates on a single ticket_id supplied by the caller and does not filter a list, so this setting doesn't apply to them.
|
||||||
|
|
||||||
| Endpoint | Method | Scope | Purpose |
|
| Endpoint | Method | Scope | Purpose |
|
||||||
|----------|--------|-------|---------|
|
|----------|--------|-------|---------|
|
||||||
| `/create_ticket_api.php` | POST | read_write | Create a ticket (hwmonDaemon, external tools) |
|
| `/create_ticket_api.php` | POST | read_write | Create a ticket (hwmonDaemon, external tools) |
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ try {
|
|||||||
$keyName = trim($input['key_name'] ?? '');
|
$keyName = trim($input['key_name'] ?? '');
|
||||||
$expiresInDays = $input['expires_in_days'] ?? null;
|
$expiresInDays = $input['expires_in_days'] ?? null;
|
||||||
$scope = $input['scope'] ?? 'read_write';
|
$scope = $input['scope'] ?? 'read_write';
|
||||||
|
$seeAllVisibility = !empty($input['see_all_visibility']);
|
||||||
|
|
||||||
if (empty($keyName)) {
|
if (empty($keyName)) {
|
||||||
http_response_code(400);
|
http_response_code(400);
|
||||||
@@ -100,7 +101,7 @@ try {
|
|||||||
|
|
||||||
// Generate API key
|
// Generate API key
|
||||||
$apiKeyModel = new ApiKeyModel($conn);
|
$apiKeyModel = new ApiKeyModel($conn);
|
||||||
$result = $apiKeyModel->createKey($keyName, $_SESSION['user']['user_id'], $expiresInDays, $scope);
|
$result = $apiKeyModel->createKey($keyName, $_SESSION['user']['user_id'], $expiresInDays, $scope, $seeAllVisibility);
|
||||||
|
|
||||||
if (!$result['success']) {
|
if (!$result['success']) {
|
||||||
throw new Exception($result['error'] ?? "Failed to generate API key");
|
throw new Exception($result['error'] ?? "Failed to generate API key");
|
||||||
@@ -113,7 +114,7 @@ try {
|
|||||||
'create',
|
'create',
|
||||||
'api_key',
|
'api_key',
|
||||||
$result['key_id'],
|
$result['key_id'],
|
||||||
['key_name' => $keyName, 'expires_in_days' => $expiresInDays, 'scope' => $scope]
|
['key_name' => $keyName, 'expires_in_days' => $expiresInDays, 'scope' => $scope, 'see_all_visibility' => $seeAllVisibility]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Clear output buffer
|
// Clear output buffer
|
||||||
@@ -127,6 +128,7 @@ try {
|
|||||||
'key_prefix' => $result['key_prefix'],
|
'key_prefix' => $result['key_prefix'],
|
||||||
'key_id' => $result['key_id'],
|
'key_id' => $result['key_id'],
|
||||||
'scope' => $result['scope'],
|
'scope' => $result['scope'],
|
||||||
|
'see_all_visibility' => $result['see_all_visibility'],
|
||||||
'expires_at' => $result['expires_at']
|
'expires_at' => $result['expires_at']
|
||||||
]);
|
]);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
|||||||
+30
-5
@@ -4,8 +4,10 @@
|
|||||||
* tickets_api.php — Bearer-key read endpoint (list/triage + read-one).
|
* tickets_api.php — Bearer-key read endpoint (list/triage + read-one).
|
||||||
*
|
*
|
||||||
* GET only. Requires 'read' scope (a 'read_write' key also satisfies it).
|
* GET only. Requires 'read' scope (a 'read_write' key also satisfies it).
|
||||||
* Acts as a trusted automation/server credential: reads return the full queue
|
* By default, a key only sees public-visibility tickets — Confidential and
|
||||||
* (no per-user visibility filtering).
|
* Internal tickets are excluded, the same as they would be for a regular user
|
||||||
|
* with no special access. An admin can mark a specific key see_all_visibility
|
||||||
|
* (API Key Management) when it genuinely needs the full queue.
|
||||||
*
|
*
|
||||||
* GET ?ticket_id=NNN -> {success, ticket, comments}
|
* GET ?ticket_id=NNN -> {success, ticket, comments}
|
||||||
* GET ?status=&priority=&host= -> {success, tickets, page, total, pages}
|
* GET ?status=&priority=&host= -> {success, tickets, page, total, pages}
|
||||||
@@ -48,6 +50,20 @@ try {
|
|||||||
// Reads only need the 'read' scope.
|
// Reads only need the 'read' scope.
|
||||||
$apiKeyAuth->requireScope('read');
|
$apiKeyAuth->requireScope('read');
|
||||||
|
|
||||||
|
// Keys are public-ticket-only by default (#70) — a key must be explicitly
|
||||||
|
// marked see_all_visibility to bypass Confidential/Internal restrictions.
|
||||||
|
// Reuse TicketModel's existing per-user visibility plumbing with a synthetic
|
||||||
|
// "no special access" user rather than a separate SQL path, so this stays in
|
||||||
|
// lockstep with however visibility rules evolve for real users. user_id is
|
||||||
|
// -1, not 0: canUserAccessTicket() does a PHP-level (int) cast for the
|
||||||
|
// confidential-ticket check, and (int)null === 0, so an unassigned
|
||||||
|
// confidential ticket's assigned_to would otherwise false-positive-match a
|
||||||
|
// synthetic user_id of 0. No real user_id is ever <= 0, so -1 can't collide.
|
||||||
|
$keyContext = $apiKeyAuth->getKeyContext();
|
||||||
|
$visibilityUser = !empty($keyContext['see_all_visibility'])
|
||||||
|
? null
|
||||||
|
: ['user_id' => -1, 'is_admin' => false, 'groups' => ''];
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||||
http_response_code(405);
|
http_response_code(405);
|
||||||
echo json_encode(['success' => false, 'error' => 'Method not allowed. Use GET.']);
|
echo json_encode(['success' => false, 'error' => 'Method not allowed. Use GET.']);
|
||||||
@@ -67,6 +83,15 @@ if (isset($_GET['ticket_id']) && trim((string)$_GET['ticket_id']) !== '') {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A public-only key gets a plain 404 for a non-public ticket — same as a
|
||||||
|
// regular user hitting a ticket they can't see — rather than a 403 that
|
||||||
|
// would confirm the ticket exists.
|
||||||
|
if ($visibilityUser !== null && !$ticketModel->canUserAccessTicket($ticket, $visibilityUser)) {
|
||||||
|
http_response_code(404);
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Ticket not found']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
// Flat list of comments (newest first) — same fetch the ticket view uses.
|
// Flat list of comments (newest first) — same fetch the ticket view uses.
|
||||||
$commentModel = new CommentModel($conn);
|
$commentModel = new CommentModel($conn);
|
||||||
$comments = $commentModel->getCommentsByTicketId($ticketId, false);
|
$comments = $commentModel->getCommentsByTicketId($ticketId, false);
|
||||||
@@ -114,8 +139,8 @@ if (isset($_GET['host']) && trim((string)$_GET['host']) !== '') {
|
|||||||
$search = trim((string)$_GET['host']);
|
$search = trim((string)$_GET['host']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// user = null => getAllTickets skips visibility filtering and returns the full
|
// $visibilityUser is null (skip filtering, full queue) only for a key marked
|
||||||
// queue (this is a trusted server credential, not an end user).
|
// see_all_visibility; otherwise it restricts to public tickets (see above).
|
||||||
$result = $ticketModel->getAllTickets(
|
$result = $ticketModel->getAllTickets(
|
||||||
$page,
|
$page,
|
||||||
$limit,
|
$limit,
|
||||||
@@ -126,7 +151,7 @@ $result = $ticketModel->getAllTickets(
|
|||||||
null,
|
null,
|
||||||
$search,
|
$search,
|
||||||
$filters,
|
$filters,
|
||||||
null
|
$visibilityUser
|
||||||
);
|
);
|
||||||
|
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ class ApiKeyAuth
|
|||||||
{
|
{
|
||||||
$this->keyContext = [
|
$this->keyContext = [
|
||||||
'scope' => $keyData['scope'] ?? 'read_write',
|
'scope' => $keyData['scope'] ?? 'read_write',
|
||||||
|
'see_all_visibility' => !empty($keyData['see_all_visibility']),
|
||||||
'key_name' => $keyData['key_name'] ?? null,
|
'key_name' => $keyData['key_name'] ?? null,
|
||||||
'created_by' => $keyData['created_by'] ?? null,
|
'created_by' => $keyData['created_by'] ?? null,
|
||||||
'api_key_id' => $keyData['api_key_id'] ?? null,
|
'api_key_id' => $keyData['api_key_id'] ?? null,
|
||||||
@@ -46,7 +47,7 @@ class ApiKeyAuth
|
|||||||
/**
|
/**
|
||||||
* Get the context of the authenticated API key.
|
* Get the context of the authenticated API key.
|
||||||
*
|
*
|
||||||
* @return array|null ['scope', 'key_name', 'created_by', 'api_key_id'] or null
|
* @return array|null ['scope', 'see_all_visibility', 'key_name', 'created_by', 'api_key_id'] or null
|
||||||
*/
|
*/
|
||||||
public function getKeyContext(): ?array
|
public function getKeyContext(): ?array
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
-- Restrict Bearer API keys to public-visibility tickets by default (#70).
|
||||||
|
--
|
||||||
|
-- Previously any 'read'-scope key bypassed ticket visibility entirely —
|
||||||
|
-- Confidential and Internal tickets were readable by any key, regardless
|
||||||
|
-- of who it was issued to. see_all_visibility is an explicit opt-in an
|
||||||
|
-- admin sets per-key when a key genuinely needs to see non-public tickets;
|
||||||
|
-- it defaults to 0 (public-only) for both new and existing keys, since the
|
||||||
|
-- prior blanket-access behavior is the thing being restricted.
|
||||||
|
--
|
||||||
|
-- Safe to re-run.
|
||||||
|
|
||||||
|
ALTER TABLE `api_keys`
|
||||||
|
ADD COLUMN IF NOT EXISTS `see_all_visibility` tinyint(1) NOT NULL DEFAULT 0 AFTER `scope`;
|
||||||
+15
-4
@@ -19,9 +19,11 @@ class ApiKeyModel
|
|||||||
* @param int $createdBy User ID who created the key
|
* @param int $createdBy User ID who created the key
|
||||||
* @param int|null $expiresInDays Number of days until expiration (null for no expiration)
|
* @param int|null $expiresInDays Number of days until expiration (null for no expiration)
|
||||||
* @param string $scope Access scope: 'read' or 'read_write' (default 'read_write')
|
* @param string $scope Access scope: 'read' or 'read_write' (default 'read_write')
|
||||||
|
* @param bool $seeAllVisibility If true, the key bypasses ticket visibility (Confidential/
|
||||||
|
* Internal included); defaults to false (public tickets only)
|
||||||
* @return array Array with 'success', 'api_key' (plaintext), 'key_prefix', 'scope', 'error'
|
* @return array Array with 'success', 'api_key' (plaintext), 'key_prefix', 'scope', 'error'
|
||||||
*/
|
*/
|
||||||
public function createKey($keyName, $createdBy, $expiresInDays = null, $scope = 'read_write')
|
public function createKey($keyName, $createdBy, $expiresInDays = null, $scope = 'read_write', $seeAllVisibility = false)
|
||||||
{
|
{
|
||||||
// Validate the requested scope — only the two known values are allowed
|
// Validate the requested scope — only the two known values are allowed
|
||||||
if (!in_array($scope, ['read', 'read_write'], true)) {
|
if (!in_array($scope, ['read', 'read_write'], true)) {
|
||||||
@@ -47,11 +49,12 @@ class ApiKeyModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Insert API key into database
|
// Insert API key into database
|
||||||
|
$seeAllVisibilityInt = $seeAllVisibility ? 1 : 0;
|
||||||
$stmt = $this->conn->prepare(
|
$stmt = $this->conn->prepare(
|
||||||
"INSERT INTO api_keys (key_name, key_hash, key_prefix, scope, created_by, expires_at) "
|
"INSERT INTO api_keys (key_name, key_hash, key_prefix, scope, see_all_visibility, created_by, expires_at) "
|
||||||
. "VALUES (?, ?, ?, ?, ?, ?)"
|
. "VALUES (?, ?, ?, ?, ?, ?, ?)"
|
||||||
);
|
);
|
||||||
$stmt->bind_param("ssssis", $keyName, $keyHash, $keyPrefix, $scope, $createdBy, $expiresAt);
|
$stmt->bind_param("ssssiis", $keyName, $keyHash, $keyPrefix, $scope, $seeAllVisibilityInt, $createdBy, $expiresAt);
|
||||||
|
|
||||||
if ($stmt->execute()) {
|
if ($stmt->execute()) {
|
||||||
$keyId = $this->conn->insert_id;
|
$keyId = $this->conn->insert_id;
|
||||||
@@ -63,6 +66,7 @@ class ApiKeyModel
|
|||||||
'key_prefix' => $keyPrefix,
|
'key_prefix' => $keyPrefix,
|
||||||
'key_id' => $keyId,
|
'key_id' => $keyId,
|
||||||
'scope' => $scope,
|
'scope' => $scope,
|
||||||
|
'see_all_visibility' => $seeAllVisibility,
|
||||||
'expires_at' => $expiresAt
|
'expires_at' => $expiresAt
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
@@ -114,6 +118,13 @@ class ApiKeyModel
|
|||||||
$keyData['scope'] = 'read_write';
|
$keyData['scope'] = 'read_write';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unlike scope's backward-compatible fallback above, an un-migrated or
|
||||||
|
// null see_all_visibility defaults to the RESTRICTIVE value (public
|
||||||
|
// tickets only) — this column exists specifically to lock down a
|
||||||
|
// previously-unrestricted default, so a missing value must not fall
|
||||||
|
// back to the permissive behavior it's replacing.
|
||||||
|
$keyData['see_all_visibility'] = !empty($keyData['see_all_visibility']);
|
||||||
|
|
||||||
// Check expiration
|
// Check expiration
|
||||||
if ($keyData['expires_at'] !== null) {
|
if ($keyData['expires_at'] !== null) {
|
||||||
$expiresAt = strtotime($keyData['expires_at']);
|
$expiresAt = strtotime($keyData['expires_at']);
|
||||||
|
|||||||
@@ -45,10 +45,18 @@ include __DIR__ . '/../../views/layout_header.php';
|
|||||||
<option value="read">read</option>
|
<option value="read">read</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="lt-form-group" style="flex:1;margin:0">
|
||||||
|
<label class="lt-label" style="display:flex;align-items:center;gap:0.4rem;cursor:pointer">
|
||||||
|
<input type="checkbox" id="keySeeAllVisibility">
|
||||||
|
See all visibility
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<button type="submit" class="lt-btn lt-btn-primary" style="margin-bottom:0">GENERATE KEY</button>
|
<button type="submit" class="lt-btn lt-btn-primary" style="margin-bottom:0">GENERATE KEY</button>
|
||||||
</form>
|
</form>
|
||||||
<p class="lt-text-xs lt-text-muted" style="margin-top:0.5rem">
|
<p class="lt-text-xs lt-text-muted" style="margin-top:0.5rem">
|
||||||
Scope: <strong>read</strong> = GET only; <strong>read_write</strong> = create/comment/close.
|
Scope: <strong>read</strong> = GET only; <strong>read_write</strong> = create/comment/close.
|
||||||
|
By default a key only sees <strong>public</strong>-visibility tickets — check
|
||||||
|
<strong>See all visibility</strong> only if this key genuinely needs Confidential/Internal tickets too.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- New key display (hidden by default) -->
|
<!-- New key display (hidden by default) -->
|
||||||
@@ -74,6 +82,7 @@ include __DIR__ . '/../../views/layout_header.php';
|
|||||||
<th scope="col">Name</th>
|
<th scope="col">Name</th>
|
||||||
<th scope="col">Key Prefix</th>
|
<th scope="col">Key Prefix</th>
|
||||||
<th scope="col">Scope</th>
|
<th scope="col">Scope</th>
|
||||||
|
<th scope="col">Visibility</th>
|
||||||
<th scope="col">Created By</th>
|
<th scope="col">Created By</th>
|
||||||
<th scope="col">Created</th>
|
<th scope="col">Created</th>
|
||||||
<th scope="col">Expires</th>
|
<th scope="col">Expires</th>
|
||||||
@@ -86,7 +95,7 @@ include __DIR__ . '/../../views/layout_header.php';
|
|||||||
<?php
|
<?php
|
||||||
$apiKeysList = $apiKeys['keys'] ?? [];
|
$apiKeysList = $apiKeys['keys'] ?? [];
|
||||||
if (empty($apiKeysList)) : ?>
|
if (empty($apiKeysList)) : ?>
|
||||||
<tr><td colspan="9" class="lt-empty">No API keys found. Generate one above.</td></tr>
|
<tr><td colspan="10" class="lt-empty">No API keys found. Generate one above.</td></tr>
|
||||||
<?php else :
|
<?php else :
|
||||||
foreach ($apiKeysList as $key) : ?>
|
foreach ($apiKeysList as $key) : ?>
|
||||||
<?php
|
<?php
|
||||||
@@ -103,6 +112,13 @@ include __DIR__ . '/../../views/layout_header.php';
|
|||||||
<span class="lt-status lt-status-open"><?= htmlspecialchars($scope) ?></span>
|
<span class="lt-status lt-status-open"><?= htmlspecialchars($scope) ?></span>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</td>
|
</td>
|
||||||
|
<td data-label="Visibility">
|
||||||
|
<?php if (!empty($key['see_all_visibility'])) : ?>
|
||||||
|
<span class="lt-status lt-status-open" title="Bypasses ticket visibility — sees Confidential/Internal tickets too">all</span>
|
||||||
|
<?php else : ?>
|
||||||
|
<span class="lt-status lt-status-closed" title="Only sees public-visibility tickets">public only</span>
|
||||||
|
<?php endif ?>
|
||||||
|
</td>
|
||||||
<td data-label="Created By" class="lt-text-xs"><?= htmlspecialchars($key['display_name'] ?? $key['username'] ?? 'Unknown') ?></td>
|
<td data-label="Created By" class="lt-text-xs"><?= htmlspecialchars($key['display_name'] ?? $key['username'] ?? 'Unknown') ?></td>
|
||||||
<td data-label="Created" class="lt-text-xs lt-text-muted"><?= date('Y-m-d H:i', strtotime($key['created_at'])) ?></td>
|
<td data-label="Created" class="lt-text-xs lt-text-muted"><?= date('Y-m-d H:i', strtotime($key['created_at'])) ?></td>
|
||||||
<td data-label="Expires" class="lt-text-xs <?= $expired ? 'lt-text-danger' : 'lt-text-cyan' ?>">
|
<td data-label="Expires" class="lt-text-xs <?= $expired ? 'lt-text-danger' : 'lt-text-cyan' ?>">
|
||||||
@@ -239,11 +255,17 @@ document.addEventListener('click', function (e) {
|
|||||||
|
|
||||||
document.getElementById('generateKeyForm').addEventListener('submit', function (e) {
|
document.getElementById('generateKeyForm').addEventListener('submit', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var keyName = document.getElementById('keyName').value.trim();
|
var keyName = document.getElementById('keyName').value.trim();
|
||||||
var expiresIn = document.getElementById('expiresIn').value;
|
var expiresIn = document.getElementById('expiresIn').value;
|
||||||
var keyScope = document.getElementById('keyScope').value;
|
var keyScope = document.getElementById('keyScope').value;
|
||||||
|
var seeAllVisibility = document.getElementById('keySeeAllVisibility').checked;
|
||||||
if (!keyName) { lt.toast.error('Please enter a key name'); return; }
|
if (!keyName) { lt.toast.error('Please enter a key name'); return; }
|
||||||
lt.api.post('/api/generate_api_key.php', { key_name: keyName, expires_in_days: expiresIn || null, scope: keyScope })
|
lt.api.post('/api/generate_api_key.php', {
|
||||||
|
key_name: keyName,
|
||||||
|
expires_in_days: expiresIn || null,
|
||||||
|
scope: keyScope,
|
||||||
|
see_all_visibility: seeAllVisibility
|
||||||
|
})
|
||||||
.then(function (data) {
|
.then(function (data) {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
document.getElementById('newKeyValue').value = data.api_key;
|
document.getElementById('newKeyValue').value = data.api_key;
|
||||||
|
|||||||
Reference in New Issue
Block a user