Added PUT /records/{recordId}/credentials/{credentialId}
This commit is contained in:
parent
22334174da
commit
02f8e48f11
4 changed files with 199 additions and 0 deletions
|
@ -129,4 +129,46 @@ class Credentials
|
||||||
return $res->withJson(['error' => 'No matching credential found.'], 404);
|
return $res->withJson(['error' => 'No matching credential found.'], 404);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function put(Request $req, Response $res, array $args)
|
||||||
|
{
|
||||||
|
$body = $req->getParsedBody();
|
||||||
|
|
||||||
|
if ((array_key_exists('type', $body) && $body['type'] === 'key' && !array_key_exists('key', $body))
|
||||||
|
|| (array_key_exists('type', $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']);
|
||||||
|
$credentialId = intval($args['credentialId']);
|
||||||
|
|
||||||
|
$ac = new \Operations\AccessControl($this->c);
|
||||||
|
if (!$ac->canAccessRecord($userId, $recordId)) {
|
||||||
|
$this->logger->info('User tries to update 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;
|
||||||
|
$description = array_key_exists('description', $body) ? $body['description'] : null;
|
||||||
|
$type = array_key_exists('type', $body) ? $body['type'] : null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$credentials->updateCredential($recordId, $credentialId, $description, $type, $key, $password);
|
||||||
|
return $res->withStatus(204);
|
||||||
|
} catch (\Exceptions\SemanticException $e) {
|
||||||
|
$this->logger->debug('User tries to update credential with wrong type.');
|
||||||
|
return $res->withJson(['error' => 'The type is invalid.'], 400);
|
||||||
|
} catch (\Exceptions\InvalidKeyException $e) {
|
||||||
|
$this->logger->debug('User tries to update invalid credential key.');
|
||||||
|
return $res->withJson(['error' => 'The provided key is invalid.'], 400);
|
||||||
|
} catch (\Exceptions\NotFoundException $e) {
|
||||||
|
$this->logger->debug('User tries to update not existent credential.');
|
||||||
|
return $res->withJson(['error' => 'The provided credential does not exist.'], 404);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
namespace Operations;
|
namespace Operations;
|
||||||
|
|
||||||
|
use function Monolog\Handler\error_log;
|
||||||
|
|
||||||
require '../vendor/autoload.php';
|
require '../vendor/autoload.php';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -181,4 +183,60 @@ class Credentials
|
||||||
|
|
||||||
return $record;
|
return $record;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new credential
|
||||||
|
*
|
||||||
|
* @param $record Record for which this credential should be valid
|
||||||
|
* @param $credential Credential to update
|
||||||
|
* @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 updateCredential(int $record, int $credential, ? 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);
|
||||||
|
} elseif ($type === null) {
|
||||||
|
$secret = null;
|
||||||
|
} else {
|
||||||
|
throw new \Exceptions\SemanticException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->beginTransaction();
|
||||||
|
|
||||||
|
$query = $this->db->prepare('SELECT id,record,description,type,security FROM remote WHERE id=:id AND record=:record');
|
||||||
|
$query->bindValue(':id', $credential, \PDO::PARAM_INT);
|
||||||
|
$query->bindValue(':record', $record, \PDO::PARAM_INT);
|
||||||
|
$query->execute();
|
||||||
|
|
||||||
|
$record = $query->fetch();
|
||||||
|
|
||||||
|
if ($record === false) {
|
||||||
|
$this->db->rollBack();
|
||||||
|
throw new \Exceptions\NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$description = $description !== null ? $description : $record['description'];
|
||||||
|
$type = $type !== null ? $type : $record['type'];
|
||||||
|
$secret = $secret !== null ? $secret : $record['security'];
|
||||||
|
|
||||||
|
$query = $this->db->prepare('UPDATE remote SET description=:description,type=:type,security=:security');
|
||||||
|
$query->bindValue(':description', $description);
|
||||||
|
$query->bindValue(':type', $type);
|
||||||
|
$query->bindValue(':security', $secret);
|
||||||
|
$query->execute();
|
||||||
|
|
||||||
|
$this->db->commit();
|
||||||
|
|
||||||
|
return $record;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ $app->group('/v1', function () {
|
||||||
$this->post('/records/{recordId}/credentials', '\Controllers\Credentials:postNew');
|
$this->post('/records/{recordId}/credentials', '\Controllers\Credentials:postNew');
|
||||||
$this->delete('/records/{recordId}/credentials/{credentialId}', '\Controllers\Credentials:delete');
|
$this->delete('/records/{recordId}/credentials/{credentialId}', '\Controllers\Credentials:delete');
|
||||||
$this->get('/records/{recordId}/credentials/{credentialId}', '\Controllers\Credentials:getSingle');
|
$this->get('/records/{recordId}/credentials/{credentialId}', '\Controllers\Credentials:getSingle');
|
||||||
|
$this->put('/records/{recordId}/credentials/{credentialId}', '\Controllers\Credentials:put');
|
||||||
})->add('\Middlewares\Authentication');
|
})->add('\Middlewares\Authentication');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -124,6 +124,104 @@ test.run(async function () {
|
||||||
type: 'password',
|
type: 'password',
|
||||||
}, 'Added password does not match.');
|
}, 'Added password does not match.');
|
||||||
|
|
||||||
|
//Update credential
|
||||||
|
var res = await req({
|
||||||
|
url: '/records/1/credentials/4',
|
||||||
|
method: 'put',
|
||||||
|
data: {
|
||||||
|
type: 'key',
|
||||||
|
key: '-----BEGIN PUBLIC KEY-----\nMDwwDQYJKoZIhvcNAQEBBQADKwAwKAIhAMTyWha8C93l2NAPMkLPZ2WnbkqWXOnH\no3RenmVJHn1tAgMBAAE=\n-----END PUBLIC KEY-----'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(res.status, 204, 'Updating record should succeed.');
|
||||||
|
|
||||||
|
var res = await req({
|
||||||
|
url: '/records/1/credentials/4',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(res.status, 200, 'Updated credential should be found.');
|
||||||
|
assert.equal(res.data, {
|
||||||
|
id: 4,
|
||||||
|
description: 'Test Key',
|
||||||
|
type: 'key',
|
||||||
|
key: '-----BEGIN PUBLIC KEY-----\nMDwwDQYJKoZIhvcNAQEBBQADKwAwKAIhAMTyWha8C93l2NAPMkLPZ2WnbkqWXOnH\no3RenmVJHn1tAgMBAAE=\n-----END PUBLIC KEY-----'
|
||||||
|
}, 'Updated key does not match.');
|
||||||
|
|
||||||
|
// Change type to password
|
||||||
|
var res = await req({
|
||||||
|
url: '/records/1/credentials/4',
|
||||||
|
method: 'put',
|
||||||
|
data: {
|
||||||
|
description: 'Foo Bar',
|
||||||
|
type: 'password',
|
||||||
|
password: 'foo'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(res.status, 204, 'Updating record should succeed.');
|
||||||
|
|
||||||
|
var res = await req({
|
||||||
|
url: '/records/1/credentials/4',
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(res.status, 200, 'Updated credential should be found.');
|
||||||
|
assert.equal(res.data, {
|
||||||
|
id: 4,
|
||||||
|
description: 'Foo Bar',
|
||||||
|
type: 'password'
|
||||||
|
}, 'Added key does not match.');
|
||||||
|
|
||||||
|
//Test update fails
|
||||||
|
var res = await req({
|
||||||
|
url: '/records/1/credentials/4',
|
||||||
|
method: 'put',
|
||||||
|
data: {
|
||||||
|
type: 'foo'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assert.equal(res.status, 400, 'Invalid type should trigger error.');
|
||||||
|
|
||||||
|
var res = await req({
|
||||||
|
url: '/records/1/credentials/4',
|
||||||
|
method: 'put',
|
||||||
|
data: {
|
||||||
|
type: 'key',
|
||||||
|
key: 'foo'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assert.equal(res.status, 400, 'Invalid key should trigger error.');
|
||||||
|
|
||||||
|
var res = await req({
|
||||||
|
url: '/records/1/credentials/4',
|
||||||
|
method: 'put',
|
||||||
|
data: {
|
||||||
|
type: 'key'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assert.equal(res.status, 422, 'Missing key should trigger error.');
|
||||||
|
|
||||||
|
var res = await req({
|
||||||
|
url: '/records/1/credentials/4',
|
||||||
|
method: 'put',
|
||||||
|
data: {
|
||||||
|
type: 'password'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assert.equal(res.status, 422, 'Missing password should trigger error.');
|
||||||
|
|
||||||
|
var res = await req({
|
||||||
|
url: '/records/1/credentials/100',
|
||||||
|
method: 'put',
|
||||||
|
data: {
|
||||||
|
description: 'foo'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assert.equal(res.status, 404, 'Invalid credential should trigger error.');
|
||||||
|
|
||||||
|
|
||||||
//Delete entry
|
//Delete entry
|
||||||
var res = await req({
|
var res = await req({
|
||||||
url: '/records/1/credentials/4',
|
url: '/records/1/credentials/4',
|
||||||
|
|
Loading…
Reference in a new issue