API: Add API settings page
This commit is contained in:
parent
fe836e281e
commit
4de882ef85
|
@ -43,6 +43,8 @@ $route->addGroup(
|
||||||
$route->get('/certificates', 'SettingsController@certificate');
|
$route->get('/certificates', 'SettingsController@certificate');
|
||||||
$route->post('/certificates/ifsg', 'SettingsController@saveIfsgCertificate');
|
$route->post('/certificates/ifsg', 'SettingsController@saveIfsgCertificate');
|
||||||
$route->post('/certificates/driving', 'SettingsController@saveDrivingLicense');
|
$route->post('/certificates/driving', 'SettingsController@saveDrivingLicense');
|
||||||
|
$route->get('/api', 'SettingsController@api');
|
||||||
|
$route->post('/api', 'SettingsController@apiKeyReset');
|
||||||
$route->get('/oauth', 'SettingsController@oauth');
|
$route->get('/oauth', 'SettingsController@oauth');
|
||||||
$route->get('/sessions', 'SettingsController@sessions');
|
$route->get('/sessions', 'SettingsController@sessions');
|
||||||
$route->post('/sessions', 'SettingsController@sessionsDelete');
|
$route->post('/sessions', 'SettingsController@sessionsDelete');
|
||||||
|
|
|
@ -192,6 +192,9 @@ msgstr "Einstellungen gespeichert."
|
||||||
msgid "settings.sessions.delete_success"
|
msgid "settings.sessions.delete_success"
|
||||||
msgstr "Sitzung erfolgreich gelöscht."
|
msgstr "Sitzung erfolgreich gelöscht."
|
||||||
|
|
||||||
|
msgid "settings.api.key_reset_success"
|
||||||
|
msgstr "API Key erfolgreich zurückgesetzt."
|
||||||
|
|
||||||
msgid "faq.delete.success"
|
msgid "faq.delete.success"
|
||||||
msgstr "FAQ Eintrag erfolgreich gelöscht."
|
msgstr "FAQ Eintrag erfolgreich gelöscht."
|
||||||
|
|
||||||
|
|
|
@ -1743,6 +1743,35 @@ msgstr "Hier kannst Du Deine Sprache ändern."
|
||||||
msgid "settings.language.success"
|
msgid "settings.language.success"
|
||||||
msgstr "Sprache wurde erfolgreich geändert."
|
msgstr "Sprache wurde erfolgreich geändert."
|
||||||
|
|
||||||
|
msgid "settings.api"
|
||||||
|
msgstr "API"
|
||||||
|
|
||||||
|
msgid "settings.api.about"
|
||||||
|
msgstr ""
|
||||||
|
"Die API erlaubt es dir, über externe Programme, mit dem Engelsystem zu interagieren. "
|
||||||
|
"Sie ist noch nicht vollständig, wir arbeiten aber daran sie zu erweitern.\n"
|
||||||
|
"Der API Einstiegspunkt befindet sich unter `%s` und ist in der [OpenAPI Spezifikation](%s) beschrieben.\n"
|
||||||
|
"Teile deinen persönlichen API Key mit niemandem, er erlaubt es deine persönlichen Daten einzusehen "
|
||||||
|
"und Änderungen in deinem Namen durch zu führen!"
|
||||||
|
|
||||||
|
msgid "settings.api.shifts_json_show"
|
||||||
|
msgstr "JSON Schichten Export anzeigen"
|
||||||
|
|
||||||
|
msgid "settings.api.ical_show"
|
||||||
|
msgstr "iCal export anzeigen"
|
||||||
|
|
||||||
|
msgid "settings.api.news_show"
|
||||||
|
msgstr "News feeds anzeigen"
|
||||||
|
|
||||||
|
msgid "settings.api.key_show"
|
||||||
|
msgstr "API Key anzeigen"
|
||||||
|
|
||||||
|
msgid "settings.api.key_reset"
|
||||||
|
msgstr "API Key zurücksetzen"
|
||||||
|
|
||||||
|
msgid "settings.api.key_reset_confirm"
|
||||||
|
msgstr "Wenn du den API Key zurücksetzt, musst ihn in allen deinen Anwendungen aktualisieren."
|
||||||
|
|
||||||
msgid "settings.oauth"
|
msgid "settings.oauth"
|
||||||
msgstr "Single Sign-On"
|
msgstr "Single Sign-On"
|
||||||
|
|
||||||
|
|
|
@ -191,6 +191,9 @@ msgstr "Settings saved."
|
||||||
msgid "settings.sessions.delete_success"
|
msgid "settings.sessions.delete_success"
|
||||||
msgstr "Session deleted successfully."
|
msgstr "Session deleted successfully."
|
||||||
|
|
||||||
|
msgid "settings.api.key_reset_success"
|
||||||
|
msgstr "API key successfully reset."
|
||||||
|
|
||||||
msgid "faq.delete.success"
|
msgid "faq.delete.success"
|
||||||
msgstr "FAQ entry successfully deleted."
|
msgstr "FAQ entry successfully deleted."
|
||||||
|
|
||||||
|
|
|
@ -456,6 +456,35 @@ msgstr "Here you can change your language."
|
||||||
msgid "settings.language.success"
|
msgid "settings.language.success"
|
||||||
msgstr "Language was changed successfully."
|
msgstr "Language was changed successfully."
|
||||||
|
|
||||||
|
msgid "settings.api"
|
||||||
|
msgstr "API"
|
||||||
|
|
||||||
|
msgid "settings.api.about"
|
||||||
|
msgstr ""
|
||||||
|
"The API allows you to interact with the Engelsystem by using external programs. "
|
||||||
|
"It's not complete but we are working on extending it.\n"
|
||||||
|
"The API endpoint is located at `%s` and described in the [OpenAPI specification](%s).\n"
|
||||||
|
"Don't share your personal API key with anyone as it can be used to view your personal data "
|
||||||
|
"and do changes your behalf!"
|
||||||
|
|
||||||
|
msgid "settings.api.shifts_json_show"
|
||||||
|
msgstr "Show JSON shifts export"
|
||||||
|
|
||||||
|
msgid "settings.api.ical_show"
|
||||||
|
msgstr "Show iCal export"
|
||||||
|
|
||||||
|
msgid "settings.api.news_show"
|
||||||
|
msgstr "Show news feeds"
|
||||||
|
|
||||||
|
msgid "settings.api.key_show"
|
||||||
|
msgstr "Show API key"
|
||||||
|
|
||||||
|
msgid "settings.api.key_reset"
|
||||||
|
msgstr "Reset API key"
|
||||||
|
|
||||||
|
msgid "settings.api.key_reset_confirm"
|
||||||
|
msgstr "If you reset the API key you have to update it in all your applications."
|
||||||
|
|
||||||
msgid "settings.oauth"
|
msgid "settings.oauth"
|
||||||
msgstr "Single Sign-On"
|
msgstr "Single Sign-On"
|
||||||
|
|
||||||
|
|
|
@ -253,6 +253,7 @@ Renders a button.
|
||||||
Must be a Bootstrap icon class without prefix, such as "info" or "check".
|
Must be a Bootstrap icon class without prefix, such as "info" or "check".
|
||||||
@param {string} [opt.confirm_title] - Optional value for the confirmation title.
|
@param {string} [opt.confirm_title] - Optional value for the confirmation title.
|
||||||
@param {string} [opt.confirm_text] - Optional value for the confirmation text.
|
@param {string} [opt.confirm_text] - Optional value for the confirmation text.
|
||||||
|
@param {dictionary} [opt.attr] - Optional value for additional attributes like data fields.
|
||||||
#}
|
#}
|
||||||
{% macro button(label, opt) %}
|
{% macro button(label, opt) %}
|
||||||
{%- set icon_left = opt.icon_left is defined ? '<span class="bi bi-' ~ opt.icon_left ~ '"></span>' : '' %}
|
{%- set icon_left = opt.icon_left is defined ? '<span class="bi bi-' ~ opt.icon_left ~ '"></span>' : '' %}
|
||||||
|
@ -269,6 +270,7 @@ Renders a button.
|
||||||
{%- if opt.confirm_button_text is defined %}
|
{%- if opt.confirm_button_text is defined %}
|
||||||
data-confirm_button_text="{{ icon_left ~ ' ' ~ opt.confirm_button_text ~ ' ' ~ icon_right }}"
|
data-confirm_button_text="{{ icon_left ~ ' ' ~ opt.confirm_button_text ~ ' ' ~ icon_right }}"
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
|
{%- for key, value in opt.attr|default({}) %} {{ key }}="{{ value }}"{% endfor -%}
|
||||||
>
|
>
|
||||||
{{ icon_left|raw }}
|
{{ icon_left|raw }}
|
||||||
{{ label }}
|
{{ label }}
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
{% extends 'pages/settings/settings.twig' %}
|
||||||
|
{% import 'macros/form.twig' as f %}
|
||||||
|
|
||||||
|
{% block title %}{{ __('settings.api') }}{% endblock %}
|
||||||
|
|
||||||
|
{% block row_content %}
|
||||||
|
|
||||||
|
<div content="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
|
||||||
|
{{ f.button(
|
||||||
|
__('settings.api.key_show'),
|
||||||
|
{'size': 'sm', 'icon_left': 'key', 'attr': {
|
||||||
|
'data-bs-toggle': 'collapse', 'data-bs-target': '#key_hide',
|
||||||
|
'aria-expanded': 'true', 'aria-controls': 'key_hide'
|
||||||
|
}}
|
||||||
|
) }}
|
||||||
|
|
||||||
|
{% if has_permission_to('shifts_json_export') %}
|
||||||
|
{{ f.button(
|
||||||
|
__('settings.api.shifts_json_show'),
|
||||||
|
{'size': 'sm', 'icon_left': 'braces', 'attr': {
|
||||||
|
'data-bs-toggle': 'collapse', 'data-bs-target': '#shifts_json_hide',
|
||||||
|
'aria-expanded': 'true', 'aria-controls': 'shifts_json_hide'
|
||||||
|
}}
|
||||||
|
) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if has_permission_to('ical') %}
|
||||||
|
{{ f.button(
|
||||||
|
__('settings.api.ical_show'),
|
||||||
|
{'size': 'sm', 'icon_left': 'calendar-week', 'attr': {
|
||||||
|
'data-bs-toggle': 'collapse', 'data-bs-target': '#ical_hide',
|
||||||
|
'aria-expanded': 'true', 'aria-controls': 'ical_hide'
|
||||||
|
}}
|
||||||
|
) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if has_permission_to('atom') %}
|
||||||
|
{{ f.button(
|
||||||
|
__('settings.api.news_show'),
|
||||||
|
{'size': 'sm', 'icon_left': 'calendar-week', 'attr': {
|
||||||
|
'data-bs-toggle': 'collapse', 'data-bs-target': '#news_hide',
|
||||||
|
'aria-expanded': 'true', 'aria-controls': 'news_hide'
|
||||||
|
}}
|
||||||
|
) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="post" class="d-inline">
|
||||||
|
{{ csrf() }}
|
||||||
|
{{ f.submit(
|
||||||
|
__('settings.api.key_reset'),
|
||||||
|
{ 'size': 'sm', 'icon_left': 'arrow-repeat', 'confirm_text': __('settings.api.key_reset_confirm') }
|
||||||
|
) }}
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-12 pt-2" id="exports_hide">
|
||||||
|
|
||||||
|
<p id="key_hide" class="collapse" data-bs-parent="#exports_hide">
|
||||||
|
<code>{{ user.api_key }}</code>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{% if has_permission_to('shifts_json_export') %}
|
||||||
|
<p id="shifts_json_hide" class="collapse" data-bs-parent="#exports_hide">
|
||||||
|
<code>{{ url('/shifts-json-export', {'key': user.api_key}) }}</code>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if has_permission_to('ical') %}
|
||||||
|
<p id="ical_hide" class="collapse" data-bs-parent="#exports_hide">
|
||||||
|
<code>{{ url('/ical', {'key': user.api_key}) }}</code>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if has_permission_to('atom') %}
|
||||||
|
<p id="news_hide" class="collapse" data-bs-parent="#exports_hide">
|
||||||
|
<code>{{ url('/atom', {'key': user.api_key}) }}</code>
|
||||||
|
<code>{{ url('/atom', {'meetings': 1, 'key': user.api_key}) }}</code>
|
||||||
|
<code>{{ url('/rss', {'key': user.api_key}) }}</code>
|
||||||
|
<code>{{ url('/rss', {'meetings': 1, 'key': user.api_key}) }}</code>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
{{ __('settings.api.about', [url('/api/v0-beta'), url('/api/v0-beta/openapi')])|markdown|nl2br }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -22,6 +22,8 @@ class SettingsController extends BaseController
|
||||||
/** @var string[] */
|
/** @var string[] */
|
||||||
protected array $permissions = [
|
protected array $permissions = [
|
||||||
'user_settings',
|
'user_settings',
|
||||||
|
'api' => 'api',
|
||||||
|
'apiKeyReset' => 'api',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
@ -305,6 +307,24 @@ class SettingsController extends BaseController
|
||||||
return $this->redirect->to('/settings/certificates');
|
return $this->redirect->to('/settings/certificates');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function api(): Response
|
||||||
|
{
|
||||||
|
return $this->response->withView(
|
||||||
|
'pages/settings/api',
|
||||||
|
[
|
||||||
|
'settings_menu' => $this->settingsMenu(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function apiKeyReset(): Response
|
||||||
|
{
|
||||||
|
$this->auth->resetApiKey($this->auth->user());
|
||||||
|
|
||||||
|
$this->addNotification('settings.api.key_reset_success');
|
||||||
|
return $this->redirect->back();
|
||||||
|
}
|
||||||
|
|
||||||
public function oauth(): Response
|
public function oauth(): Response
|
||||||
{
|
{
|
||||||
$providers = $this->config->get('oauth');
|
$providers = $this->config->get('oauth');
|
||||||
|
@ -382,6 +402,10 @@ class SettingsController extends BaseController
|
||||||
$menu[url('/settings/oauth')] = ['title' => 'settings.oauth', 'hidden' => $this->checkOauthHidden()];
|
$menu[url('/settings/oauth')] = ['title' => 'settings.oauth', 'hidden' => $this->checkOauthHidden()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->auth->can('api')) {
|
||||||
|
$menu[url('/settings/api')] = ['title' => 'settings.api', 'icon' => 'braces'];
|
||||||
|
}
|
||||||
|
|
||||||
return $menu;
|
return $menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ use Engelsystem\Config\GoodieType;
|
||||||
use Engelsystem\Controllers\NotificationType;
|
use Engelsystem\Controllers\NotificationType;
|
||||||
use Engelsystem\Controllers\SettingsController;
|
use Engelsystem\Controllers\SettingsController;
|
||||||
use Engelsystem\Http\Exceptions\HttpNotFound;
|
use Engelsystem\Http\Exceptions\HttpNotFound;
|
||||||
|
use Engelsystem\Http\Redirector;
|
||||||
use Engelsystem\Http\Response;
|
use Engelsystem\Http\Response;
|
||||||
use Engelsystem\Models\AngelType;
|
use Engelsystem\Models\AngelType;
|
||||||
use Engelsystem\Models\Session as SessionModel;
|
use Engelsystem\Models\Session as SessionModel;
|
||||||
|
@ -886,6 +887,41 @@ class SettingsControllerTest extends ControllerTest
|
||||||
$this->controller->saveDrivingLicense($this->request);
|
$this->controller->saveDrivingLicense($this->request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\SettingsController::api
|
||||||
|
* @covers \Engelsystem\Controllers\SettingsController::settingsMenu
|
||||||
|
*/
|
||||||
|
public function testApi(): void
|
||||||
|
{
|
||||||
|
$this->setExpects($this->auth, 'user', null, $this->user, $this->atLeastOnce());
|
||||||
|
|
||||||
|
/** @var Response|MockObject $response */
|
||||||
|
$this->response->expects($this->once())
|
||||||
|
->method('withView')
|
||||||
|
->willReturnCallback(function ($view, $data) {
|
||||||
|
$this->assertEquals('pages/settings/api', $view);
|
||||||
|
$this->assertArrayHasKey('settings_menu', $data);
|
||||||
|
return $this->response;
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->controller = $this->app->make(SettingsController::class);
|
||||||
|
$this->controller->api();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\SettingsController::apiKeyReset
|
||||||
|
*/
|
||||||
|
public function testApiKeyReset(): void
|
||||||
|
{
|
||||||
|
$redirector = $this->createMock(Redirector::class);
|
||||||
|
$this->app->instance(Redirector::class, $redirector);
|
||||||
|
$this->setExpects($this->auth, 'user', null, $this->user, $this->atLeastOnce());
|
||||||
|
$this->setExpects($this->auth, 'resetApiKey', [$this->user], null, $this->atLeastOnce());
|
||||||
|
$this->setExpects($redirector, 'back', null, $this->response);
|
||||||
|
|
||||||
|
$this->controller = $this->app->make(SettingsController::class);
|
||||||
|
$this->controller->apiKeyReset();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers \Engelsystem\Controllers\SettingsController::settingsMenu
|
* @covers \Engelsystem\Controllers\SettingsController::settingsMenu
|
||||||
|
@ -982,6 +1018,26 @@ class SettingsControllerTest extends ControllerTest
|
||||||
$this->assertArrayNotHasKey('http://localhost/settings/certificates', $menu);
|
$this->assertArrayNotHasKey('http://localhost/settings/certificates', $menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\SettingsController::settingsMenu
|
||||||
|
*/
|
||||||
|
public function testSettingsMenuApi(): void
|
||||||
|
{
|
||||||
|
$this->setExpects($this->auth, 'can', ['api'], true, $this->atLeastOnce());
|
||||||
|
|
||||||
|
$menu = $this->controller->settingsMenu();
|
||||||
|
$this->assertArrayHasKey('http://localhost/settings/profile', $menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\SettingsController::settingsMenu
|
||||||
|
*/
|
||||||
|
public function testSettingsMenuApiNotAvailable(): void
|
||||||
|
{
|
||||||
|
$menu = $this->controller->settingsMenu();
|
||||||
|
$this->assertArrayNotHasKey('http://localhost/settings/api', $menu);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup environment
|
* Setup environment
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue