Initial commit

This commit is contained in:
2024-09-24 13:27:33 -05:00
parent 03badb316d
commit 98f9582dad
10 changed files with 2124 additions and 8 deletions

54
src/Database.php Normal file
View File

@@ -0,0 +1,54 @@
<?php
namespace Hpz937\Phpvault;
use SQLite3;
class Database {
private $db;
public function __construct() {
$this->db = new SQLite3(__DIR__ . '/../database/db.sqlite');
if (!$this->db) {
echo 'Unable to connect to database';
exit;
}
}
public function query($query) {
return $this->db->query($query);
}
public function createTables() {
$queries = [
'CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL
)',
'CREATE TABLE IF NOT EXISTS vault (
id INTEGER PRIMARY KEY,
username TEXT NOT NULL,
vaultname TEXT NOT NULL,
encrypted_data TEXT NOT NULL
)',
'CREATE INDEX IF NOT EXISTS username_idx ON users (username)',
'CREATE UNIQUE INDEX IF NOT EXISTS vault_unique_idx ON vault (username, vaultname);',
];
foreach ($queries as $query) {
if (!$this->db->exec($query)) {
echo 'Error creating tables: ' . $this->db->lastErrorMsg();
exit;
}
}
}
public function getDb() {
return $this->db;
}
public function close() {
$this->db->close();
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Hpz937\Phpvault\Handler;
use Exception;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Hpz937\Phpvault\Database;
use stdClass;
class AuthHandler
{
private $secretKey;
private $database;
public function __construct($secretKey, Database $database)
{
$this->secretKey = $secretKey;
$this->database = $database->getDb();
}
public function generateToken($username, $password)
{
$query = "SELECT * FROM users WHERE username='$username'";
$result = $this->database->query($query);
if (!$result) {
return null;
}
$user = $result->fetchArray();
if (!$user) {
return null;
}
if (password_verify($password, $user['password'])) {
$payload = [
'username' => $username,
];
$issuedAt = time();
$expirationTime = $issuedAt + 3600; // jwt valid for 1 hour
$payload['iat'] = $issuedAt;
$payload['exp'] = $expirationTime;
return JWT::encode($payload, $this->secretKey, 'HS256');
} else {
return null;
}
}
public function addUser($username, $password)
{
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
$username = $this->database->escapeString($username);
$hashedPassword = $this->database->escapeString($hashedPassword);
$query = "INSERT INTO users (username, password) VALUES ('$username', '$hashedPassword')";
return $this->database->exec($query);
}
public function verifyToken($token)
{
try {
if (! preg_match('/Bearer\s(\S+)/', $token, $matches)) {
throw new Exception('Invalid token');
}
$decoded = JWT::decode($matches[1], new Key($this->secretKey, 'HS256'));
return $decoded->username;
} catch (Exception $e) {
return null;
}
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Hpz937\Phpvault\Middleware;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\Psr7\Response as SlimResponse;
use Hpz937\Phpvault\Handler\AuthHandler;
class AuthMiddleware
{
private $authHandler;
public function __construct(AuthHandler $authHandler)
{
$this->authHandler = $authHandler;
}
public function __invoke(Request $request, RequestHandler $handler): Response
{
$authHeader = $request->getHeaderLine('Authorization');
// Verify the token
$username = $this->authHandler->verifyToken($authHeader);
if ($username === null) {
// Token is invalid, return 401 Unauthorized
$response = new SlimResponse();
$response->getBody()->write(json_encode(['error' => 'Unauthorized']));
return $response->withStatus(401)->withHeader('Content-Type', 'application/json');
}
$request = $request->withAttribute('username', $username);
// Token is valid, proceed to the next middleware/route
return $handler->handle($request);
}
}

95
src/Vault.php Normal file
View File

@@ -0,0 +1,95 @@
<?php
namespace Hpz937\Phpvault;
use Hpz937\Encryption\DataEncryptor;
class Vault
{
private $database;
public function __construct(Database $database)
{
$this->database = $database->getDb();
}
public function storeSecret(string $userName, string $vaultName, string $key, string $secret)
{
$jsonSecret = json_decode($secret, true);
if (json_last_error() !== JSON_ERROR_NONE) {
return false;
}
$key = $jsonSecret['key'];
if (!$key) {
return false;
}
unset($jsonSecret['key']);
$dataEncryptor = new DataEncryptor($key);
$encryptedSecret = $dataEncryptor->encrypt(json_encode($jsonSecret));
// make sure vaultname does not exist first
$query = "SELECT * FROM vault WHERE username = '$userName' AND vaultname = '$vaultName'";
$result = $this->database->query($query);
$row = $result->fetchArray();
if ($row) {
throw new \Exception('Vault already exists, try PUT to update');
}
$query = "INSERT INTO vault (username, vaultname, encrypted_data) VALUES ('$userName', '$vaultName', '$encryptedSecret')";
if ($result = $this->database->exec($query)) {
return true;
}
}
public function getSecret(string $userName, string $key, string $vaultName)
{
$query = "SELECT encrypted_data FROM vault WHERE username = '$userName' AND vaultname = '$vaultName'";
$result = $this->database->query($query);
$row = $result->fetchArray();
if ($row) {
$dataEncryptor = new DataEncryptor($key);
return $dataEncryptor->decrypt($row['encrypted_data']);
}
return null;
}
public function updateSecret(string $userName, string $vaultName, string $key, string $secret)
{
$jsonSecret = json_decode($secret, true);
if (json_last_error() !== JSON_ERROR_NONE) {
return false;
}
$key = $jsonSecret['key'];
if (!$key) {
return false;
}
unset($jsonSecret['key']);
$dataEncryptor = new DataEncryptor($key);
$encryptedSecret = $dataEncryptor->encrypt(json_encode($jsonSecret));
$query = "SELECT * FROM vault WHERE username = '$userName' AND vaultname = '$vaultName'";
$result = $this->database->query($query);
$row = $result->fetchArray();
if ($row) {
$query = "UPDATE vault SET encrypted_data = '$encryptedSecret' WHERE username = '$userName' AND vaultname = '$vaultName'";
if ($this->database->exec($query)) {
return true;
}
} else {
throw new \Exception('Vault does not exist, try POST to create');
}
}
public function deleteSecret(string $userName, string $vaultName)
{
$query = "SELECT * FROM vault WHERE username = '$userName' AND vaultname = '$vaultName'";
$result = $this->database->query($query);
$row = $result->fetchArray();
if ($row) {
$query = "DELETE FROM vault WHERE username = '$userName' AND vaultname = '$vaultName'";
if ($this->database->exec($query)) {
return true;
}
} else {
throw new \Exception('Vault does not exist');
}
}
}