From b9a6e5d7f95874be712393baa35f7210d2deed60 Mon Sep 17 00:00:00 2001 From: Lukas Metzger Date: Sat, 31 Mar 2018 14:59:45 +0200 Subject: [PATCH] Added POST /records/{recordId}/credentials --- backend/src/controllers/Credentials.php | 38 +++++ .../src/exceptions/InvalidKeyException.php | 9 ++ backend/src/operations/Credentials.php | 48 ++++++ backend/src/public/index.php | 1 + backend/test/tests/credentials-crud.js | 137 ++++++++++++++++++ 5 files changed, 233 insertions(+) create mode 100644 backend/src/exceptions/InvalidKeyException.php create mode 100644 backend/test/tests/credentials-crud.js diff --git a/backend/src/controllers/Credentials.php b/backend/src/controllers/Credentials.php index 14374b1..74bb014 100644 --- a/backend/src/controllers/Credentials.php +++ b/backend/src/controllers/Credentials.php @@ -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); + } + } } diff --git a/backend/src/exceptions/InvalidKeyException.php b/backend/src/exceptions/InvalidKeyException.php new file mode 100644 index 0000000..4c437d0 --- /dev/null +++ b/backend/src/exceptions/InvalidKeyException.php @@ -0,0 +1,9 @@ +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; + } } diff --git a/backend/src/public/index.php b/backend/src/public/index.php index dbe1faa..1c79f4a 100644 --- a/backend/src/public/index.php +++ b/backend/src/public/index.php @@ -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'); }); diff --git a/backend/test/tests/credentials-crud.js b/backend/test/tests/credentials-crud.js new file mode 100644 index 0000000..b29a0cd --- /dev/null +++ b/backend/test/tests/credentials-crud.js @@ -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.'); + }); +}); \ No newline at end of file