Added POST /records/{recordId}/credentials
This commit is contained in:
parent
2447d10dd2
commit
b9a6e5d7f9
5 changed files with 233 additions and 0 deletions
|
@ -43,4 +43,42 @@ class Credentials
|
|||
'results' => $results
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function postNew(Request $req, Response $res, array $args)
|
||||
{
|
||||
$body = $req->getParsedBody();
|
||||
|
||||
if (!array_key_exists('description', $body) ||
|
||||
!array_key_exists('type', $body) || ($body['type'] === 'key' &&
|
||||
!array_key_exists('key', $body)) || ($body['type'] === 'password' &&
|
||||
!array_key_exists('password', $body))) {
|
||||
$this->logger->debug('One of the required fields is missing');
|
||||
return $res->withJson(['error' => 'One of the required fields is missing'], 422);
|
||||
}
|
||||
|
||||
$userId = $req->getAttribute('userId');
|
||||
$recordId = intval($args['recordId']);
|
||||
|
||||
$ac = new \Operations\AccessControl($this->c);
|
||||
if (!$ac->canAccessRecord($userId, $recordId)) {
|
||||
$this->logger->info('User tries to add credential for record without permission.');
|
||||
return $res->withJson(['error' => 'You have no permissions for the given record.'], 403);
|
||||
}
|
||||
|
||||
$credentials = new \Operations\Credentials($this->c);
|
||||
|
||||
$key = array_key_exists('key', $body) ? $body['key'] : null;
|
||||
$password = array_key_exists('password', $body) ? $body['password'] : null;
|
||||
|
||||
try {
|
||||
$result = $credentials->addCredential($recordId, $body['description'], $body['type'], $key, $password);
|
||||
return $res->withJson($result, 201);
|
||||
} catch (\Exceptions\SemanticException $e) {
|
||||
$this->logger->debug('User tries to add credential with wrong type.');
|
||||
return $res->withJson(['error' => 'The type is invalid.'], 400);
|
||||
} catch (\Exceptions\InvalidKeyException $e) {
|
||||
$this->logger->debug('User tries to add invalid credential key.');
|
||||
return $res->withJson(['error' => 'The provided key is invalid.'], 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
9
backend/src/exceptions/InvalidKeyException.php
Normal file
9
backend/src/exceptions/InvalidKeyException.php
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Exceptions;
|
||||
|
||||
require '../vendor/autoload.php';
|
||||
|
||||
class InvalidKeyException extends \Exception
|
||||
{
|
||||
}
|
|
@ -65,4 +65,52 @@ class Credentials
|
|||
return $item;
|
||||
}, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new credential
|
||||
*
|
||||
* @param $record Record for which this credential should be valid
|
||||
* @param $description Description for this credential
|
||||
* @param $type Type of the credential, can bei key or password
|
||||
* @param $key Key if type is key, null otherwise
|
||||
* @param $password Password if type was password, null otherwise
|
||||
*
|
||||
* @return array The new credential entry.
|
||||
*/
|
||||
public function addCredential(int $record, string $description, string $type, ? string $key, ? string $password) : array
|
||||
{
|
||||
if ($type === 'key') {
|
||||
if (openssl_pkey_get_public($key) === false) {
|
||||
throw new \Exceptions\InvalidKeyException();
|
||||
}
|
||||
$secret = $key;
|
||||
} elseif ($type === 'password') {
|
||||
$secret = password_hash($password, PASSWORD_DEFAULT);
|
||||
} else {
|
||||
throw new \Exceptions\SemanticException();
|
||||
}
|
||||
|
||||
$this->db->beginTransaction();
|
||||
|
||||
$query = $this->db->prepare('INSERT INTO remote (record, description, type, security) VALUES (:record, :description, :type, :security)');
|
||||
$query->bindValue(':record', $record, \PDO::PARAM_INT);
|
||||
$query->bindValue(':description', $description, \PDO::PARAM_STR);
|
||||
$query->bindValue(':type', $type, \PDO::PARAM_STR);
|
||||
$query->bindValue(':security', $secret, \PDO::PARAM_STR);
|
||||
$query->execute();
|
||||
|
||||
$query = $this->db->prepare('SELECT id, description, type, security FROM remote ORDER BY id DESC LIMIT 1');
|
||||
$query->execute();
|
||||
$record = $query->fetch();
|
||||
|
||||
$record['id'] = intval($record['id']);
|
||||
if ($record['type'] === 'key') {
|
||||
$record['key'] = $record['security'];
|
||||
unset($record['security']);
|
||||
} else {
|
||||
unset($record['security']);
|
||||
}
|
||||
|
||||
return $record;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ $app->group('/v1', function () {
|
|||
$this->put('/records/{recordId}', '\Controllers\Records:put');
|
||||
|
||||
$this->get('/records/{recordId}/credentials', '\Controllers\Credentials:getList');
|
||||
$this->post('/records/{recordId}/credentials', '\Controllers\Credentials:postNew');
|
||||
})->add('\Middlewares\Authentication');
|
||||
});
|
||||
|
||||
|
|
137
backend/test/tests/credentials-crud.js
Normal file
137
backend/test/tests/credentials-crud.js
Normal file
|
@ -0,0 +1,137 @@
|
|||
const test = require('../testlib');
|
||||
|
||||
test.run(async function () {
|
||||
await test('admin', async function (assert, req) {
|
||||
//Test missing field
|
||||
var res = await req({
|
||||
url: '/records/1/credentials',
|
||||
method: 'post',
|
||||
data: {
|
||||
description: 'Test'
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(res.status, 422);
|
||||
|
||||
//Test invalid type
|
||||
var res = await req({
|
||||
url: '/records/1/credentials',
|
||||
method: 'post',
|
||||
data: {
|
||||
description: 'Test',
|
||||
type: 'foo'
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(res.status, 400);
|
||||
|
||||
//Test missing key
|
||||
var res = await req({
|
||||
url: '/records/1/credentials',
|
||||
method: 'post',
|
||||
data: {
|
||||
description: 'Test',
|
||||
type: 'key'
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(res.status, 422);
|
||||
|
||||
//Test missing password
|
||||
var res = await req({
|
||||
url: '/records/1/credentials',
|
||||
method: 'post',
|
||||
data: {
|
||||
description: 'Test',
|
||||
type: 'password'
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(res.status, 422);
|
||||
|
||||
//Test invalid key
|
||||
var res = await req({
|
||||
url: '/records/1/credentials',
|
||||
method: 'post',
|
||||
data: {
|
||||
description: 'Test',
|
||||
type: 'key',
|
||||
key: 'foo'
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(res.status, 400);
|
||||
|
||||
//Add key (key is intensionally very short but valid)
|
||||
var res = await req({
|
||||
url: '/records/1/credentials',
|
||||
method: 'post',
|
||||
data: {
|
||||
description: 'Test Key',
|
||||
type: 'key',
|
||||
key: '-----BEGIN PUBLIC KEY-----\nMDwwDQYJKoZIhvcNAQEBBQADKwAwKAIhAMOLSxmtlYxSkEKep11gjq200PTKVUaA\nyalonAKxw3XnAgMBAAE=\n-----END PUBLIC KEY-----'
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(res.status, 201, 'Adding key should succeed.');
|
||||
assert.equal(res.data, {
|
||||
id: 4,
|
||||
description: 'Test Key',
|
||||
type: 'key',
|
||||
key: '-----BEGIN PUBLIC KEY-----\nMDwwDQYJKoZIhvcNAQEBBQADKwAwKAIhAMOLSxmtlYxSkEKep11gjq200PTKVUaA\nyalonAKxw3XnAgMBAAE=\n-----END PUBLIC KEY-----'
|
||||
}, 'Adding credential data fail.');
|
||||
|
||||
//Add password
|
||||
var res = await req({
|
||||
url: '/records/1/credentials',
|
||||
method: 'post',
|
||||
data: {
|
||||
description: 'Test Password',
|
||||
type: 'password',
|
||||
password: 'foo'
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(res.status, 201, 'Adding password should succeed.');
|
||||
assert.equal(res.data, {
|
||||
id: 5,
|
||||
description: 'Test Password',
|
||||
type: 'password',
|
||||
}, 'Adding credential data fail.');
|
||||
|
||||
|
||||
});
|
||||
|
||||
await test('user', async function (assert, req) {
|
||||
//Add password with missing permissions
|
||||
var res = await req({
|
||||
url: '/records/4/credentials',
|
||||
method: 'post',
|
||||
data: {
|
||||
description: 'Test Password',
|
||||
type: 'password',
|
||||
password: 'foo'
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(res.status, 403, 'Adding password should fail for missing permissions.');
|
||||
|
||||
//Add password with missing permissions
|
||||
var res = await req({
|
||||
url: '/records/1/credentials',
|
||||
method: 'post',
|
||||
data: {
|
||||
description: 'Test Password',
|
||||
type: 'password',
|
||||
password: 'foo'
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(res.status, 201, 'Adding password should succeed for user.');
|
||||
assert.equal(res.data, {
|
||||
id: 6,
|
||||
description: 'Test Password',
|
||||
type: 'password',
|
||||
}, 'Adding credential data fail.');
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue