Added input fields for secrets
This commit is contained in:
parent
6ec0c18016
commit
eff3429f43
|
@ -4,8 +4,8 @@
|
||||||
namespace JeroenED\Framework;
|
namespace JeroenED\Framework;
|
||||||
|
|
||||||
|
|
||||||
use http\Exception\InvalidArgumentException;
|
|
||||||
use Symfony\Component\Config\FileLocator;
|
use Symfony\Component\Config\FileLocator;
|
||||||
|
use Symfony\Component\Filesystem\Exception\InvalidArgumentException;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Generator\UrlGenerator;
|
use Symfony\Component\Routing\Generator\UrlGenerator;
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
.crontype-inputs {
|
.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
$(function() {
|
$(function() {
|
||||||
initDatePickers();
|
initDatePickers();
|
||||||
initCronType();
|
initCronType();
|
||||||
|
initSecretInputs();
|
||||||
});
|
});
|
||||||
|
|
||||||
function initDatePickers()
|
function initDatePickers()
|
||||||
|
@ -14,7 +15,15 @@ function initCronType()
|
||||||
$('.crontype-item').on('click', function() {
|
$('.crontype-item').on('click', function() {
|
||||||
let type = $(this).data('type');
|
let type = $(this).data('type');
|
||||||
$('.crontype').val(type);
|
$('.crontype').val(type);
|
||||||
$('.crontype-inputs').hide();
|
$('.crontype-inputs:not(.hidden)').addClass('hidden');
|
||||||
$('.crontype-' + type).show();
|
$('.crontype-' + type).removeClass('hidden');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function initSecretInputs()
|
||||||
|
{
|
||||||
|
$('.addsecret-btn').on('click', function() {
|
||||||
|
$('.secret-group:first-child').clone().appendTo('.secrets').removeClass('hidden');
|
||||||
|
$('.secrets-description').removeClass('hidden');
|
||||||
})
|
})
|
||||||
}
|
}
|
|
@ -22,7 +22,8 @@ class JobController extends Controller
|
||||||
|
|
||||||
public function viewAction($id)
|
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()
|
public function addAction()
|
||||||
|
|
|
@ -52,6 +52,7 @@ class Job
|
||||||
case 'http':
|
case 'http':
|
||||||
$parsedUrl = parse_url($values['url']);
|
$parsedUrl = parse_url($values['url']);
|
||||||
$data['url'] = $values['url'];
|
$data['url'] = $values['url'];
|
||||||
|
$data['basicauth-username'] = $values['basicauth-username'];
|
||||||
if(empty($parsedUrl['host'])) {
|
if(empty($parsedUrl['host'])) {
|
||||||
return ['success' => false, 'message' => 'Some data was invalid'];
|
return ['success' => false, 'message' => 'Some data was invalid'];
|
||||||
}
|
}
|
||||||
|
@ -59,6 +60,12 @@ class Job
|
||||||
break;
|
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);
|
$data = json_encode($data);
|
||||||
$addJobSql = "INSERT INTO job(name, data, delay, nextrun, lastrun) VALUES (:name, :data, :delay, :nextrun, :lastrun)";
|
$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'];
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,34 +40,24 @@ class User
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createAutologinToken($password): string {
|
public function createAutologinToken($password): string
|
||||||
$method = $_ENV['ENCRYPTION_METHOD'];
|
{
|
||||||
$key = hash($_ENV['HASHING_METHOD'], $_ENV['SECRET'], true);
|
|
||||||
$iv = openssl_random_pseudo_bytes(16);
|
|
||||||
$time = time();
|
$time = time();
|
||||||
|
$password = substr($time, -7) . $password;
|
||||||
$ciphertext = openssl_encrypt($password . substr($time, -7), $method, $key, OPENSSL_RAW_DATA, $iv);
|
$encrypted = Secret::encrypt($password);
|
||||||
$hash = hash_hmac($_ENV['HASHING_METHOD'], $ciphertext . $iv, $key, true);
|
return base64_encode(json_encode(['time' => $time, 'password' => base64_encode($encrypted)]));
|
||||||
return base64_encode(json_encode(['time' => $time, 'password' => base64_encode($iv . $hash . $ciphertext)]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPassFromAutologinToken($token) {
|
public function getPassFromAutologinToken($token) {
|
||||||
$extracted = json_decode(base64_decode($token), true);
|
$extracted = json_decode(base64_decode($token), true);
|
||||||
$method = $_ENV['ENCRYPTION_METHOD'];
|
|
||||||
$encrypted = base64_decode($extracted['password']);
|
$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;
|
$decrypted = Secret::decrypt($encrypted);
|
||||||
|
|
||||||
$decryption = openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(($extracted['time'] + $_ENV['COOKIE_LIFETIME']) > time()) &&
|
(($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;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
<h2>Add a cronjob</h2>
|
<h2>Add a cronjob</h2>
|
||||||
<form method="post" class="form-horizontal" action="{{ path('job_add') }}">
|
<form method="post" class="form-horizontal" action="{{ path('job_add') }}">
|
||||||
|
|
||||||
|
<h3>General info</h3>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="name">Name</label>
|
<label for="name">Name</label>
|
||||||
<input type="text" name="name" class="form-control" id="name" placeholder="System update">
|
<input type="text" name="name" class="form-control" id="name" placeholder="System update">
|
||||||
|
@ -14,16 +15,18 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="nextrun">Next run</label>
|
<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>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="lastrun">Last run</label>
|
<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>
|
</div>
|
||||||
|
|
||||||
|
<h3>Job details</h3>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<div class="dropdown">
|
<div class="dropdown">
|
||||||
<button class="btn btn-outline-primary dropdown-toggle" type="button" id="crontypeButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<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>
|
</button>
|
||||||
<div class="dropdown-menu" aria-labelledby="crontypeButton">
|
<div class="dropdown-menu" aria-labelledby="crontypeButton">
|
||||||
<a class="dropdown-item crontype-item" href="#" data-type="local">Local</a>
|
<a class="dropdown-item crontype-item" href="#" data-type="local">Local</a>
|
||||||
|
@ -35,11 +38,38 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="crontype-http crontype-inputs">
|
<div class="crontype-http crontype-inputs hidden">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="url">Url</label>
|
<label for="url">Url</label>
|
||||||
<input type="text" name="url" class="form-control" id="url" placeholder="https://scripts.example.com/">
|
<input type="text" name="url" class="form-control" id="url" placeholder="https://scripts.example.com/">
|
||||||
</div>
|
</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>
|
</div>
|
||||||
<input type="hidden" name="type" class="crontype" value=""><button type="submit" class="btn btn-outline-primary">Submit</button>
|
<input type="hidden" name="type" class="crontype" value=""><button type="submit" class="btn btn-outline-primary">Submit</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
Loading…
Reference in New Issue