Skip to content

Commit

Permalink
Adds User-Interface to handle API-Keys
Browse files Browse the repository at this point in the history
This commit adds a way to see and delete API-keys that are registered
for the current user.

This adds the functionality provided in the legacy-app via
https://legacy.joind.in/user/apikey
  • Loading branch information
heiglandreas committed Dec 22, 2016
1 parent a47f5ba commit b8bc6f5
Show file tree
Hide file tree
Showing 11 changed files with 365 additions and 2 deletions.
76 changes: 76 additions & 0 deletions app/src/Apikey/ApikeyApi.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php
namespace Apikey;

use Application\BaseApi;

class ApikeyApi extends BaseApi
{
/**
* Get all tokens associated with the current user
*
* @return array
*/
public function getCollection($queryParams)
{
$token_uri = $this->baseApiUrl . '/v2.1/token';

$tokens = (array) json_decode(
$this->apiGet($token_uri, $queryParams)
);
$meta = array_pop($tokens);

$collectionData = array();
foreach ($tokens['tokens'] as $item) {
$talk = new ApiKeyEntity($item);

$collectionData['tokens'][] = $talk;
}

$collectionData['pagination'] = $meta;

return $collectionData;
}

/**
* Get a specified API-key associated with the current user
*
* @return ApikeyEntity
*/
public function getById($id, $queryParams = ['verbose' => 'yes'])
{
$tokens_uri = $this->baseApiUrl . '/v2.1/token/' . urlencode($id);

$tokens = (array) json_decode(
$this->apiGet($tokens_uri, $queryParams)
);


if (! isset($tokens['tokens'][0])) {
throw new \UnexpectedValueException('No tokens available');
}

return new ApikeyEntity($tokens['tokens'][0]);
}

/**
* @param $tokenUri
*
* @throws \Exception
* @return bool
*/
public function deleteClient($tokenUri)
{
list ($status, $result, $headers) = $this->apiDelete($tokenUri);

if ($status != 204) {
$decoded = json_decode($result);
if (is_array($decoded)) {
$result = current($decoded);
}
throw new \Exception($result);
}

return true;

}
}
100 changes: 100 additions & 0 deletions app/src/Apikey/ApikeyController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php
namespace Apikey;

use Application\BaseController;
use Exception;
use Slim\Slim;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormFactoryInterface;

class ApikeyController extends BaseController
{
protected function defineRoutes(Slim $app)
{
$app->get('/apikey', array($this, 'index'))->name('apikey-show');
$app->get('/apikey/:apikey/delete', array($this, 'deleteApiKey'))->via('GET', 'POST')->name('apikey-delete');
}

public function index()
{
if (! isset($_SESSION['user'])) {
$thisUrl = $this->application->urlFor('apikey-show');
$this->application->redirect(
$this->application->urlFor('not-allowed') . '?redirect=' . $thisUrl
);
}

$tokenApi = $this->getApikeyApi();
$tokens = $tokenApi->getCollection([]);

$this->render('Apikey/index.html.twig', ['keys' => $tokens['tokens']]);
}

public function deleteApiKey($apikey)
{
if (!isset($_SESSION['user'])) {
$thisUrl = $this->application->urlFor('apikey-delete', ['apikey' => $apikey]);
$this->application->redirect(
$this->application->urlFor('not-allowed') . '?redirect=' . $thisUrl
);
}

$apikeyApi = $this->getApikeyApi();
try {
$apikey = $apikeyApi->getById($apikey);
} catch (Exception $e) {
$this->application->notFound();
return;
}

// default values
$data = [];
$data['apikey_id'] = $apikey->getId();

$factory = $this->application->formFactory;
$form = $factory->create(new ApikeyDeleteFormType(), $data);

$request = $this->application->request();

if ($request->isPost()) {
$form->submit($request->post($form->getName()));

if ($form->isValid()) {
try {
$apikeyApi->deleteClient($apikey->getApiUri());

$this->application->flash('message', sprintf(
'The API-Key %s has been permanently removed',
$apikey->getId()
));
$this->application->redirect(
$this->application->urlFor('apikey-show')
);
return;
} catch (\RuntimeException $e) {
$form->adderror(
new FormError('An error occurred while removing this API-Key: ' . $e->getmessage())
);
}
}
}

$this->render(
'Apikey/delete.html.twig',
[
'apikey' => $apikey,
'form' => $form->createView(),
'backUri' => $this->application->urlFor('apikey-show'),
]
);
}

/**
* @return ClientApi
*/
private function getApikeyApi()
{
return new ApikeyApi($this->cfg, $this->accessToken);
}
}
44 changes: 44 additions & 0 deletions app/src/Apikey/ApikeyDeleteFormType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace Apikey;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Event\EventEntity;

/**
* Form used to render and validate the submission or editing of a 3rd party application.
*/
class ApikeyDeleteFormType extends AbstractType
{

/**
* Returns the name of this form type.
*
* @return string
*/
public function getName()
{
return 'apikeydelete';
}

/**
* Adds fields with their types and validation constraints to this definition.
*
* @param FormBuilderInterface $builder
* @param array $options
*
* @return void
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'apikey_id',
'hidden',
[]
)
;
}
}
35 changes: 35 additions & 0 deletions app/src/Apikey/ApikeyEntity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
namespace Apikey;

use Application\BaseEntity;
use DateTime;
use DateInterval;

class ApikeyEntity extends BaseEntity
{
public function getId()
{
return substr($this->data->token_uri, strrpos($this->data->token_uri, '/') + 1);
}

public function getApplicationName()
{
return $this->data->application;
}

public function getLastUsedDateTime()
{
return new \DateTimeImmutable($this->data->last_used_date);
}

public function getCreationDateTime()
{
return new \DateTimeImmutable($this->data->created_date);
}


public function getApiUri()
{
return $this->data->token_uri;
}
}
29 changes: 29 additions & 0 deletions app/templates/Apikey/_common/deleteform.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{% block form %}
{% if user %}
{{ form_start(form, {'attr' : {'id' : 'apikey'} }) }}
{{ form_errors(form) }}
<div class="row">
<div class="col-sm-12 bg_warning">
<p>
You are about to permanently remove the API-key for
<strong>{{ apikey.getApplicationName }}</strong>.
</p><p>
You will need to log in with {{ apikey.getApplicationName }} to use the application again!
</p><p>
Are you sure you want to do delete this key?
</p>
</div>
</div>
<div class="row">
<div class="col-sm-12">
{{ form_row(form.apikey_id) }}
<input type="submit" class="btn btn-danger pull-right" value="Permanently delete the key" />
<a href="{{ backUri }}" class="btn btn-default pull-right">Cancel</a>
</div>
</div>
{{ form_end(form) }}
{% else %}
<h1>Login required</h1>
<p>In order to delete a key, please log in.</p>
{% endif %}
{% endblock %}
25 changes: 25 additions & 0 deletions app/templates/Apikey/delete.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{% extends '/layout.html.twig' %}

{% block title %}Delete API-key - Joind.in{% endblock %}

{% form_theme form _self %}
{% use '/Apikey/_common/deleteform.html.twig' %}

{% block body %}

<ol class="breadcrumb">
<li><a href="/apikey">API-keys</a></li>
<li class="active">Delete API-key</li>
</ol>

<div class="page-header">
<div class="row event">
<div class="col-xs-12 col-sm-12 title">
<h1>
Delete API-key for "{{ apikey.getApplicationName }}"
</h1>
</div>
</div>
</div>
{{ block('form') }}
{% endblock %}
46 changes: 46 additions & 0 deletions app/templates/Apikey/index.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{% extends '/layout.html.twig' %}

{% block title %}ApiKeys - Joind.in{% endblock %}

{% block body %}

<ol class="breadcrumb">
<li class="active">API-keys</li>
</ol>

<section id="apps">
<h2>My API-keys</h2>
<table class="table">
<thead>
<th>Application</th>
<th>last used</th>
<th>&nbsp;</th>
</thead>
{% if keys %}
<tbody>
{% for key in keys %}
<tr>
<td>
{{ key.getApplicationName }}
</td><td>
{{ key.getLastUsedDateTime | date('d.m.Y H:i:s') }}
</td><td>
<a class="btn btn-danger btn-xs" href="{{ urlFor('apikey-delete', {'apikey': key.getId}) }}">
Delete Api-Key
</a>
</td>
</tr>
{% endfor %}
</tbody>
{% endif %}
</table>
</section>

{% endblock %}


{% block topAside %}
{% endblock %}

{% block extraAside %}
{% endblock %}
7 changes: 6 additions & 1 deletion app/templates/User/profile.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@
{% if thisUser.canEdit %}
<p class="more">
<a href="{{ urlFor('user-profile-edit', {'username': thisUser.getUsername}) }}">
Edit</a>
Edit
</a>
&mdash;
<a href="{{ urlFor('apikey-show', {'username': thisUser.getUsername}) }}">
API-Keys
</a>
</p>
{% endif %}
</section>
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"Search": ["app/src/", "tests/"],
"Talk": ["app/src/", "tests/"],
"User": ["app/src/", "tests/"],
"Client": ["app/src/", "tests/"]
"Client": ["app/src/", "tests/"],
"Apikey": ["app/src/", "tests/"]
}
},
"config": {
Expand Down
1 change: 1 addition & 0 deletions vendor/composer/autoload_namespaces.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@
'Event' => array($baseDir . '/app/src', $baseDir . '/tests'),
'Client' => array($baseDir . '/app/src', $baseDir . '/tests'),
'Application' => array($baseDir . '/app/src', $baseDir . '/tests'),
'Apikey' => array($baseDir . '/app/src', $baseDir . '/tests'),
);
Loading

0 comments on commit b8bc6f5

Please sign in to comment.