diff --git a/changelog/70_UNRELEASED_xxxx.xx.xx.md b/changelog/70_UNRELEASED_xxxx.xx.xx.md index 120703a6..c1c91547 100644 --- a/changelog/70_UNRELEASED_xxxx.xx.xx.md +++ b/changelog/70_UNRELEASED_xxxx.xx.xx.md @@ -115,3 +115,4 @@ - The following entities are now also available via the endpoint `/objects/{entity}` (only listing, no edit) - `quantity_unit_conversions_resolved` (returns all final/resolved conversion factors per product and any directly or indirectly related quantity units) - The endpoint `/batteries` now also returns the corresponding battery object (as field/property `battery`) +- API keys can now have a description (to e.g. track where the corresponding key is used) diff --git a/controllers/CalendarApiController.php b/controllers/CalendarApiController.php index d1d57806..e7419d21 100644 --- a/controllers/CalendarApiController.php +++ b/controllers/CalendarApiController.php @@ -2,6 +2,7 @@ namespace Grocy\Controllers; +use Grocy\Services\ApiKeyService; use Eluceo\iCal\Domain\Entity\Calendar; use Eluceo\iCal\Domain\Entity\Event; use Eluceo\iCal\Domain\Entity\TimeZone; @@ -94,7 +95,7 @@ class CalendarApiController extends BaseApiController try { return $this->ApiResponse($response, [ - 'url' => $this->AppContainer->get('UrlManager')->ConstructUrl('/api/calendar/ical?secret=' . $this->getApiKeyService()->GetOrCreateApiKey(\Grocy\Services\ApiKeyService::API_KEY_TYPE_SPECIAL_PURPOSE_CALENDAR_ICAL)) + 'url' => $this->AppContainer->get('UrlManager')->ConstructUrl('/api/calendar/ical?secret=' . $this->getApiKeyService()->GetOrCreateApiKey(ApiKeyService::API_KEY_TYPE_SPECIAL_PURPOSE_CALENDAR_ICAL)) ]); } catch (\Exception $ex) diff --git a/controllers/OpenApiController.php b/controllers/OpenApiController.php index d7b18f07..9602e30e 100644 --- a/controllers/OpenApiController.php +++ b/controllers/OpenApiController.php @@ -3,6 +3,7 @@ namespace Grocy\Controllers; use Grocy\Controllers\Users\User; +use Grocy\Services\ApiKeyService; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; @@ -10,22 +11,36 @@ class OpenApiController extends BaseApiController { public function ApiKeysList(Request $request, Response $response, array $args) { + $selectedKeyId = -1; + if (isset($request->getQueryParams()['key']) && filter_var($request->getQueryParams()['key'], FILTER_VALIDATE_INT)) + { + $selectedKeyId = $request->getQueryParams()['key']; + } + $apiKeys = $this->getDatabase()->api_keys(); if (!User::hasPermissions(User::PERMISSION_ADMIN)) { $apiKeys = $apiKeys->where('user_id', GROCY_USER_ID); } + return $this->renderPage($response, 'manageapikeys', [ 'apiKeys' => $apiKeys, - 'users' => $this->getDatabase()->users() + 'users' => $this->getDatabase()->users(), + 'selectedKeyId' => $selectedKeyId ]); } public function CreateNewApiKey(Request $request, Response $response, array $args) { - $newApiKey = $this->getApiKeyService()->CreateApiKey(); + $description = null; + if (isset($request->getQueryParams()['description'])) + { + $description = $request->getQueryParams()['description']; + } + + $newApiKey = $this->getApiKeyService()->CreateApiKey(ApiKeyService::API_KEY_TYPE_DEFAULT, $description); $newApiKeyId = $this->getApiKeyService()->GetApiKeyId($newApiKey); - return $response->withRedirect($this->AppContainer->get('UrlManager')->ConstructUrl("/manageapikeys?CreatedApiKeyId=$newApiKeyId")); + return $response->withRedirect($this->AppContainer->get('UrlManager')->ConstructUrl("/manageapikeys?key=$newApiKeyId")); } public function DocumentationSpec(Request $request, Response $response, array $args) diff --git a/migrations/0220.sql b/migrations/0220.sql new file mode 100644 index 00000000..52617b9c --- /dev/null +++ b/migrations/0220.sql @@ -0,0 +1,2 @@ +ALTER TABLE api_keys +ADD description TEXT; diff --git a/public/viewjs/manageapikeys.js b/public/viewjs/manageapikeys.js index 4323f0db..109b4527 100644 --- a/public/viewjs/manageapikeys.js +++ b/public/viewjs/manageapikeys.js @@ -1,5 +1,5 @@ var apiKeysTable = $('#apikeys-table').DataTable({ - 'order': [[4, 'desc']], + 'order': [[6, 'desc']], 'columnDefs': [ { 'orderable': false, 'targets': 0 }, { 'searchable': false, "targets": 0 } @@ -8,12 +8,6 @@ $('#apikeys-table tbody').removeClass("d-none"); apiKeysTable.columns.adjust().draw(); -var createdApiKeyId = GetUriParam('CreatedApiKeyId'); -if (createdApiKeyId !== undefined) -{ - animateCSS("#apiKeyRow_" + createdApiKeyId, "pulse"); -} - $("#search").on("keyup", Delay(function() { var value = $(this).val(); @@ -33,8 +27,9 @@ $("#clear-filter-button").on("click", function() $(document).on('click', '.apikey-delete-button', function(e) { - var objectName = $(e.currentTarget).attr('data-apikey-apikey'); - var objectId = $(e.currentTarget).attr('data-apikey-id'); + var button = $(e.currentTarget); + var objectName = button.attr('data-apikey-key'); + var objectId = button.attr('data-apikey-id'); bootbox.confirm({ message: __t('Are you sure to delete API key "%s"?', objectName), @@ -68,23 +63,36 @@ $(document).on('click', '.apikey-delete-button', function(e) }); }); -function QrCodeForApiKey(apiKeyType, apiKey) +$(".apikey-show-qr-button").on("click", function() { - var content = U('/api') + '|' + apiKey; - if (apiKeyType === 'special-purpose-calendar-ical') + var button = $(this); + var apiKey = button.data("apikey-key"); + var apiKeyType = button.data("apikey-type"); + var apiKeyDescription = button.data("apikey-description"); + + var content = U("/api") + "|" + apiKey; + if (apiKeyType === "special-purpose-calendar-ical") { - content = U('/api/calendar/ical?secret=' + apiKey); + content = U("/api/calendar/ical?secret=" + apiKey); } - return QrCodeImgHtml(content); -} - -$('.apikey-show-qr-button').on('click', function() -{ - var qrcodeHtml = QrCodeForApiKey($(this).data('apikey-type'), $(this).data('apikey-key')); bootbox.alert({ - title: __t('API key'), - message: "
" + qrcodeHtml + "
", + message: "