Added input fields for secrets

This commit is contained in:
Jeroen De Meerleer 2021-04-13 14:44:58 +02:00
parent 6ec0c18016
commit eff3429f43
Signed by: JeroenED
GPG Key ID: 28CCCB8F62BFADD6
8 changed files with 112 additions and 27 deletions

View File

@ -4,8 +4,8 @@
namespace JeroenED\Framework;
use http\Exception\InvalidArgumentException;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Filesystem\Exception\InvalidArgumentException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGenerator;

View File

@ -1,3 +1,3 @@
.crontype-inputs {
.hidden {
display: none;
}

View File

@ -1,6 +1,7 @@
$(function() {
initDatePickers();
initCronType();
initSecretInputs();
});
function initDatePickers()
@ -14,7 +15,15 @@ function initCronType()
$('.crontype-item').on('click', function() {
let type = $(this).data('type');
$('.crontype').val(type);
$('.crontype-inputs').hide();
$('.crontype-' + type).show();
$('.crontype-inputs:not(.hidden)').addClass('hidden');
$('.crontype-' + type).removeClass('hidden');
})
}
function initSecretInputs()
{
$('.addsecret-btn').on('click', function() {
$('.secret-group:first-child').clone().appendTo('.secrets').removeClass('hidden');
$('.secrets-description').removeClass('hidden');
})
}

View File

@ -22,7 +22,8 @@ class JobController extends Controller
public function viewAction($id)
{
return new Response('Not implemented yet', Response::HTTP_TOO_EARLY);
$jobRepo = new Job($this->getDbCon());
$job = $jobRepo->getJob($id);
}
public function addAction()

View File

@ -52,6 +52,7 @@ class Job
case 'http':
$parsedUrl = parse_url($values['url']);
$data['url'] = $values['url'];
$data['basicauth-username'] = $values['basicauth-username'];
if(empty($parsedUrl['host'])) {
return ['success' => false, 'message' => 'Some data was invalid'];
}
@ -59,6 +60,12 @@ class Job
break;
}
if(!empty($values['secretval'])) {
foreach($values['secretval'] as $key => $name) {
if(!empty($name)) $data['secrets'][$values['secretid'][$key]] = base64_encode(Secret::encrypt($values['secretval'][$key]));
}
}
$data = json_encode($data);
$addJobSql = "INSERT INTO job(name, data, delay, nextrun, lastrun) VALUES (:name, :data, :delay, :nextrun, :lastrun)";
@ -67,4 +74,19 @@ class Job
return ['success' => true, 'message' => 'Cronjob succesfully added'];
}
public function getJob(int $id, bool $withSecrets = false) {
$jobSql = "SELECT * FROM job WHERE id = :id";
$jobStmt = $this->dbcon->prepare($jobSql);
$jobRslt = $jobStmt->execute([':id' => $id])->fetchAssociative();
$jobRslt['data'] = json_decode($jobRslt['data'], true);
if(!empty($jobRslt['data']['secrets'])) {
foreach ($jobRslt['data']['secrets'] as $key => &$value) {
$value = ($withSecrets) ? Secret::decrypt(base64_decode($value)) : '';
}
}
return $jobRslt;
}
}

33
src/Repository/Secret.php Normal file
View File

@ -0,0 +1,33 @@
<?php
namespace JeroenED\Webcron\Repository;
class Secret
{
static function encrypt($plaintext) {
$password = $_ENV['SECRET'];
$method = $_ENV['ENCRYPTION_METHOD'];
$key = hash($_ENV['HASHING_METHOD'], $password, true);
$iv = openssl_random_pseudo_bytes(16);
$ciphertext = openssl_encrypt($plaintext, $method, $key, OPENSSL_RAW_DATA, $iv);
$hash = hash_hmac('sha256', $ciphertext . $iv, $key, true);
return $iv . $hash . $ciphertext;
}
static function decrypt($ivHashCiphertext) {
$password = $_ENV['SECRET'];
$method = $_ENV['ENCRYPTION_METHOD'];
$iv = substr($ivHashCiphertext, 0, 16);
$hash = substr($ivHashCiphertext, 16, 32);
$ciphertext = substr($ivHashCiphertext, 48);
$key = hash($_ENV['HASHING_METHOD'], $password, true);
if (!hash_equals(hash_hmac($_ENV['HASHING_METHOD'], $ciphertext . $iv, $key, true), $hash)) return null;
return openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv);
}
}

View File

@ -40,34 +40,24 @@ class User
return false;
}
public function createAutologinToken($password): string {
$method = $_ENV['ENCRYPTION_METHOD'];
$key = hash($_ENV['HASHING_METHOD'], $_ENV['SECRET'], true);
$iv = openssl_random_pseudo_bytes(16);
public function createAutologinToken($password): string
{
$time = time();
$ciphertext = openssl_encrypt($password . substr($time, -7), $method, $key, OPENSSL_RAW_DATA, $iv);
$hash = hash_hmac($_ENV['HASHING_METHOD'], $ciphertext . $iv, $key, true);
return base64_encode(json_encode(['time' => $time, 'password' => base64_encode($iv . $hash . $ciphertext)]));
$password = substr($time, -7) . $password;
$encrypted = Secret::encrypt($password);
return base64_encode(json_encode(['time' => $time, 'password' => base64_encode($encrypted)]));
}
public function getPassFromAutologinToken($token) {
$extracted = json_decode(base64_decode($token), true);
$method = $_ENV['ENCRYPTION_METHOD'];
$encrypted = base64_decode($extracted['password']);
$iv = substr($encrypted, 0, 16);
$hash = substr($encrypted, 16, 32);
$ciphertext = substr($encrypted, 48);
$key = hash($_ENV['HASHING_METHOD'], $_ENV['SECRET'], true);
if (!hash_equals(hash_hmac($_ENV['HASHING_METHOD'], $ciphertext . $iv, $key, true), $hash)) return null;
$decryption = openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv);
$decrypted = Secret::decrypt($encrypted);
return (
(($extracted['time'] + $_ENV['COOKIE_LIFETIME']) > time()) &&
substr($extracted['time'], -7) == substr($decryption, -7)
substr($extracted['time'], -7) == substr($decrypted, -7)
)
? substr($decryption, 0, -7) : null;
? substr($decrypted, 0, -7) : null;
}
}

View File

@ -4,6 +4,7 @@
<h2>Add a cronjob</h2>
<form method="post" class="form-horizontal" action="{{ path('job_add') }}">
<h3>General info</h3>
<div class="mb-3">
<label for="name">Name</label>
<input type="text" name="name" class="form-control" id="name" placeholder="System update">
@ -14,16 +15,18 @@
</div>
<div class="mb-3">
<label for="nextrun">Next run</label>
<input type="text" id="nextrunselector" class="form-control datetimepicker-input" data-target="#nextrunselector" data-toggle="datetimepicker" name="nextrun">
<input type="text" autocomplete="off" id="nextrunselector" class="form-control datetimepicker-input" data-target="#nextrunselector" data-toggle="datetimepicker" name="nextrun">
</div>
<div class="mb-3">
<label for="lastrun">Last run</label>
<input type="text" id="lastrunselector" class="form-control datetimepicker-input" data-target="#lastrunselector" data-toggle="datetimepicker" name="lastrun">
<input type="text" autocomplete="off" id="lastrunselector" class="form-control datetimepicker-input" data-target="#lastrunselector" data-toggle="datetimepicker" name="lastrun">
</div>
<h3>Job details</h3>
<div class="mb-3">
<div class="dropdown">
<button class="btn btn-outline-primary dropdown-toggle" type="button" id="crontypeButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Cronjob type
Job type
</button>
<div class="dropdown-menu" aria-labelledby="crontypeButton">
<a class="dropdown-item crontype-item" href="#" data-type="local">Local</a>
@ -35,11 +38,38 @@
</div>
</div>
<div class="crontype-http crontype-inputs">
<div class="crontype-http crontype-inputs hidden">
<div class="mb-3">
<label for="url">Url</label>
<input type="text" name="url" class="form-control" id="url" placeholder="https://scripts.example.com/">
</div>
<div class="mb-3">
<label for="basicauth-username">Username for Basic-Auth</label>
<input type="text" name="basicauth-username" class="form-control" id="basicauth-username" placeholder="www-data">
</div>
<div class="mb-3">
<label for="basicauth-password">Password for Basic-Auth</label>
<input type="hidden" name="secretid[]" value="basicauth-password">
<input type="password" name="secretval[]" class="form-control" placeholder="correct horse battery staple">
<small id="basicauth-password-help" class="form-text text-muted">This field is being saved as a secret</small>
</div>
</div>
<h3>Secrets</h3>
<div class="secrets mb-3">
<div class="input-group secret-group hidden">
<input type="text" name="secretid[]" class="form-control" placeholder="name">
<input type="password" name="secretval[]" class="form-control" placeholder="value">
</div>
</div>
<div class="secrets-description mb-3 hidden">
<p>
You can add secrets by using {secret-name} in job details
</p>
</div>
<div class="mb-3">
<a href="#" class="btn btn-outline-primary addsecret-btn">Add secret</a>
</div>
<input type="hidden" name="type" class="crontype" value=""><button type="submit" class="btn btn-outline-primary">Submit</button>
</form>