forked from zer.ooo/web
commit
2e2f4e9f6f
File diff suppressed because one or more lines are too long
@ -0,0 +1,5 @@
|
|||||||
|
#create-csr {
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 15px;
|
||||||
|
float: right
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
.menu-list {
|
||||||
|
width: 200px;
|
||||||
|
float: left;
|
||||||
|
list-style: none;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 20px 0 0;
|
||||||
|
background: #fff;
|
||||||
|
color: black;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-bottom: 3px solid #d2d5e7;
|
||||||
|
border-right: 3px solid #d2d5e7;
|
||||||
|
position: absolute;
|
||||||
|
left: -220px;
|
||||||
|
|
||||||
|
@include break-small {
|
||||||
|
width: 100%;
|
||||||
|
float: none;
|
||||||
|
position: inherit;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-list > li {
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: black;
|
||||||
|
padding: 10px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-list > li.selected {
|
||||||
|
background: hsl(12, 52%, 77%);
|
||||||
|
}
|
@ -1,66 +0,0 @@
|
|||||||
$(function () {
|
|
||||||
var error = $('<div class="alert alert-danger" role="alert"></div>');
|
|
||||||
|
|
||||||
$('.magic-csr').click(function () {
|
|
||||||
var name = $('#name').val();
|
|
||||||
|
|
||||||
if (name.length === 0) {
|
|
||||||
$('h2').after(error.text("Name can't be empty"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!/^[A-Za-z0-9\-]+$/.test(name)) {
|
|
||||||
$('h2').after(error.text("Only alphanumeric and - allowed in name"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var user = $('#user-input').val();
|
|
||||||
|
|
||||||
var keys = forge.pki.rsa.generateKeyPair(1024);
|
|
||||||
var csr = forge.pki.createCertificationRequest();
|
|
||||||
var commonName = name + '.' + user;
|
|
||||||
|
|
||||||
csr.publicKey = keys.publicKey;
|
|
||||||
csr.setSubject([{
|
|
||||||
name: 'commonName',
|
|
||||||
value: commonName
|
|
||||||
}]);
|
|
||||||
csr.sign(keys.privateKey);
|
|
||||||
var csrPem = forge.pki.certificationRequestToPem(csr);
|
|
||||||
|
|
||||||
var newCertParams = {
|
|
||||||
csr: csrPem,
|
|
||||||
name: name
|
|
||||||
};
|
|
||||||
|
|
||||||
var keyPem = "";
|
|
||||||
|
|
||||||
if ($('#wantsPassword').prop('checked')) {
|
|
||||||
keyPem = forge.pki.encryptRsaPrivateKey(keys.privateKey, $('#password').val());
|
|
||||||
newCertParams.key = keyPem;
|
|
||||||
} else {
|
|
||||||
keyPem = forge.pki.privateKeyToPem(keys.privateKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
$.post('/panel/certificates/new', newCertParams, function (data) {
|
|
||||||
if (data.success) {
|
|
||||||
var zip = new JSZip();
|
|
||||||
zip.file(commonName + '.key', keyPem);
|
|
||||||
|
|
||||||
for(var file in data.zip) {
|
|
||||||
zip.file(file, data.zip[file]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var content = zip.generate({type:"blob"});
|
|
||||||
saveAs(content, commonName + '-vpn.zip');
|
|
||||||
location.href = '/panel';
|
|
||||||
} else {
|
|
||||||
$('h2').after(error.text(data.error));
|
|
||||||
}
|
|
||||||
}, 'json');
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#wantsPassword").change(function () {
|
|
||||||
$('#password, #saveOnline').prop('disabled', !this.checked);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,13 +0,0 @@
|
|||||||
// This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.
|
|
||||||
require('../../js/transition.js')
|
|
||||||
require('../../js/alert.js')
|
|
||||||
require('../../js/button.js')
|
|
||||||
require('../../js/carousel.js')
|
|
||||||
require('../../js/collapse.js')
|
|
||||||
require('../../js/dropdown.js')
|
|
||||||
require('../../js/modal.js')
|
|
||||||
require('../../js/tooltip.js')
|
|
||||||
require('../../js/popover.js')
|
|
||||||
require('../../js/scrollspy.js')
|
|
||||||
require('../../js/tab.js')
|
|
||||||
require('../../js/affix.js')
|
|
@ -0,0 +1,32 @@
|
|||||||
|
$(function () {
|
||||||
|
var errorDiv = $('<div>').addClass('error-message');
|
||||||
|
|
||||||
|
function error(msg) {
|
||||||
|
$('.title').after(errorDiv.text(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.actions .certificate-delete').click(function () {
|
||||||
|
$(this).parents('.actions').hide().parents('li').first().find('.revoke-q').show();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.revoke-q .no').click(function () {
|
||||||
|
$(this).parents('.revoke-q').hide().parents('li').first().find('.actions').show();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.revoke-q .yes').click(function () {
|
||||||
|
var _this=this;
|
||||||
|
|
||||||
|
$.post('/panel/certificates/revoke', {
|
||||||
|
name: $(_this).data('name')
|
||||||
|
}, function (data) {
|
||||||
|
if (data.success) {
|
||||||
|
$(_this)
|
||||||
|
.parents('li')
|
||||||
|
.first()
|
||||||
|
.remove();
|
||||||
|
} else {
|
||||||
|
error(data.error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,73 @@
|
|||||||
|
$(function () {
|
||||||
|
var errorDiv = $('<div>').addClass('error-message');
|
||||||
|
|
||||||
|
function error(msg) {
|
||||||
|
$('h1').after(errorDiv.text(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#has-password').change(function () {
|
||||||
|
$('#password, #save-online').prop('disabled', !this.checked);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#create-csr').click(function () {
|
||||||
|
var name = $('#name').val();
|
||||||
|
|
||||||
|
if (name.length === 0) {
|
||||||
|
error("Name can't be empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^[A-Za-z0-9\-]+$/.test(name)) {
|
||||||
|
error("Only alphanumeric and - allowed in name");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = $('#user-input').val();
|
||||||
|
|
||||||
|
var keys = forge.pki.rsa.generateKeyPair(1024);
|
||||||
|
var csr = forge.pki.createCertificationRequest();
|
||||||
|
var commonName = name + '.' + user;
|
||||||
|
|
||||||
|
csr.publicKey = keys.publicKey;
|
||||||
|
csr.setSubject([{
|
||||||
|
name: 'commonName',
|
||||||
|
value: commonName
|
||||||
|
}]);
|
||||||
|
csr.sign(keys.privateKey);
|
||||||
|
var csrPem = forge.pki.certificationRequestToPem(csr);
|
||||||
|
|
||||||
|
var newCertParams = {
|
||||||
|
csr: csrPem,
|
||||||
|
name: name
|
||||||
|
};
|
||||||
|
|
||||||
|
var keyPem = "";
|
||||||
|
|
||||||
|
if ($('#has-password').prop('checked')) {
|
||||||
|
keyPem = forge.pki.encryptRsaPrivateKey(keys.privateKey, $('#password').val());
|
||||||
|
if ($('#save-online').prop('checked')) {
|
||||||
|
newCertParams.key = keyPem;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
keyPem = forge.pki.privateKeyToPem(keys.privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
$.post('/panel/certificates/new', newCertParams, function (data) {
|
||||||
|
if (data.success) {
|
||||||
|
var zip = new JSZip();
|
||||||
|
zip.file(commonName + '.key', keyPem);
|
||||||
|
|
||||||
|
for(var file in data.zip) {
|
||||||
|
if (!data.zip.hasOwnProperty(file)) continue;
|
||||||
|
zip.file(file, data.zip[file]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = zip.generate({type:"blob"});
|
||||||
|
saveAs(content, commonName + '-vpn.zip');
|
||||||
|
location.href = '/panel';
|
||||||
|
} else {
|
||||||
|
error(data.error);
|
||||||
|
}
|
||||||
|
}, 'json');
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Eater\Glim\Handler\Panel;
|
||||||
|
|
||||||
|
|
||||||
|
use Eater\Glim\Handler\Panel;
|
||||||
|
|
||||||
|
class Certificates extends Panel
|
||||||
|
{
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
return $this->render('panel/certificates.html.twig');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Created by PhpStorm.
|
||||||
|
* User: eater
|
||||||
|
* Date: 4/5/16
|
||||||
|
* Time: 2:03 AM
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Eater\Glim\Handler\Panel\Certificates;
|
||||||
|
|
||||||
|
|
||||||
|
use Eater\Glim\Handler\Session;
|
||||||
|
use Eater\Glim\Model\CertificateQuery;
|
||||||
|
|
||||||
|
class DownloadKey extends Session
|
||||||
|
{
|
||||||
|
protected $shouldHaveUser = true;
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$name = $this->attr('name');
|
||||||
|
|
||||||
|
$cert = CertificateQuery::create()
|
||||||
|
->findOneByUserAndName($this->getUser(), $name);
|
||||||
|
|
||||||
|
if ($cert === null || empty($cert->getPrivateKey())) {
|
||||||
|
return $this->getResponse()->withStatus(404)->write("Couldn't find your Certificate with the name '{$name}'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->getResponse()
|
||||||
|
->withHeader('Content-Type', 'plain/text')
|
||||||
|
->withHeader('Content-Disposition', 'attachment; filename="' . $name . '.' . $this->getUser()->getUsername() .'.key"')
|
||||||
|
->write($cert->getPrivateKey());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Eater\Glim\Handler\Panel\Profile;
|
||||||
|
|
||||||
|
|
||||||
|
use Eater\Glim\Handler\Panel;
|
||||||
|
|
||||||
|
class Show extends Panel
|
||||||
|
{
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
return $this->render('panel/profile.html.twig');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Eater\Glim\Handler\Panel;
|
||||||
|
|
||||||
|
use Aura\Session\Segment;
|
||||||
|
use Eater\Glim\Handler\Panel;
|
||||||
|
use Eater\Glim\Model\ServerQuery;
|
||||||
|
|
||||||
|
class Servers extends Panel
|
||||||
|
{
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
/** @var Segment $segment */
|
||||||
|
$segment = $this->get('session')->getSegment('main');
|
||||||
|
$this->get('twig-vars')->def('message', $segment->getFlash('message'));
|
||||||
|
|
||||||
|
$superuser = $this->getUser()->getSuperuser();
|
||||||
|
$vars = [
|
||||||
|
'superuser' => $superuser,
|
||||||
|
'servers' => $this->fetchServers()
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($superuser) {
|
||||||
|
$vars['registered_servers'] = $this->fetchServers('registered');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('panel/servers.html.twig', $vars);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fetchServers($status = "signed")
|
||||||
|
{
|
||||||
|
return ServerQuery::create()->filterByStatus($status)->find();
|
||||||
|
}
|
||||||
|
}
|
@ -1,191 +1,31 @@
|
|||||||
{% extends "base_bootstrap.html.twig" %}
|
{% extends "base.html.twig" %}
|
||||||
|
|
||||||
{% block head %}
|
{% block head %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
|
|
||||||
<script src="/js/panel.js"></script>
|
<script src="/js/jquery.min.js"></script>
|
||||||
|
<script src="/js/pages/certificates.js"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
<div class="login-page">
|
||||||
<h2 id="certificates">Certificates</h2>
|
<div class="login-page-logo">Zer.ooo</div>
|
||||||
</div>
|
<div class="intro-page-welcome-text">Panel</div>
|
||||||
<div class="row">
|
<div> </div>
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Serial</th>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Expires on</th>
|
|
||||||
<th>
|
|
||||||
<a href="/panel/certificates/new" class="btn btn-default pull-right">Create new certificate</a>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for certificate in user.getCertificates() if certificate.getRevoked() == 0 %}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{{ certificate.getSerial() }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ certificate.getName() }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ certificate.getExpiresOn().format('Y-m-d H:i:s') }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="pull-right">
|
|
||||||
<button data-name="{{ certificate.getName() }}" class="revoke btn btn-warning">Revoke
|
|
||||||
</button>
|
|
||||||
<a target="_blank" href="/panel/certificates/download/{{ certificate.getName() }}"
|
|
||||||
class="download btn btn-default">Download certificate</a>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% else %}
|
|
||||||
<tr>
|
|
||||||
<td colspan="4">
|
|
||||||
You don't seem to have any certificates yet, <a href="/panel/certificates/new">want to
|
|
||||||
create one?</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<h2>Servers</h2>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Hostname</th>
|
|
||||||
<th>Fingerprint</th>
|
|
||||||
<th>Location</th>
|
|
||||||
<th>Speed</th>
|
|
||||||
<th>Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for server in servers %}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{{ server.getFqdn() }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ server.getFingerprint() }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ server.getLocation() }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ server.getSpeed() }} MB/s
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="pull-right">
|
|
||||||
<div class="btn-group">
|
|
||||||
<a href="/panel/server/{{ server.getFingerprint() }}/config" class="btn btn-default">Download config</a>
|
|
||||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
||||||
<span class="caret"></span>
|
|
||||||
<span class="sr-only">Toggle Dropdown</span>
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
{% for certificate in user.getCertificates() if certificate.getRevoked() == 0 %}
|
|
||||||
<li>
|
|
||||||
<a href="/panel/server/{{ server.getFingerprint() }}/config/{{ certificate.getName() }}">Download config for "{{ certificate.getName() }}"</a>
|
|
||||||
</li>
|
|
||||||
{% else %}
|
|
||||||
<li>
|
|
||||||
<a>You dont have any certificates</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{% if user.isSuperUser() %}
|
|
||||||
<a href="/panel/server/{{ server.getFingerprint() }}" class="btn btn-default">Edit</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% else %}
|
|
||||||
<tr>
|
|
||||||
<td colspan="5">
|
|
||||||
There don't seem to be any servers yet :(
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% if user.getSuperuser() %}
|
|
||||||
<div class="row">
|
|
||||||
<h2>Registered servers</h2>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Hostname</th>
|
|
||||||
<th>Fingerprint</th>
|
|
||||||
<th>Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for server in registeredServers %}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{{ server.getFqdn() }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ server.getFingerprint() }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="pull-right">
|
|
||||||
<button data-fingerprint="{{ server.getFingerprint() }}" class="remove btn btn-warning">Remove</button>
|
|
||||||
<a href="/panel/server/{{ server.getFingerprint() }}" class="btn btn-default">Edit</a>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% else %}
|
|
||||||
<tr>
|
|
||||||
<td colspan="5">
|
|
||||||
There don't seem to be any servers yet :(
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal fade in revoke-modal" tabindex="-1" role="dialog">
|
|
||||||
<div class="modal-dialog">
|
<div class="panel-page">
|
||||||
<div class="modal-content">
|
<ul class="menu-list">
|
||||||
<div class="modal-header">
|
{% for menuitem in menu %}
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
|
<li{% if menuitem.path == currentpath %} class="selected"{% endif %}>
|
||||||
aria-hidden="true">×</span></button>
|
<a href="{{ menuitem.path }}">{{ menuitem.name }}</a>
|
||||||
<h4 class="modal-title">Are you sure?</h4>
|
</li>
|
||||||
</div>
|
{% endfor %}
|
||||||
<div class="modal-body">
|
</ul>
|
||||||
<div id="revoke-put-error-after-me" class="alert alert-danger modal-top-alert">
|
<div class="panel-content">
|
||||||
<h4>This may not be what you want!</h4>
|
{% block panel_contents %}
|
||||||
<p>Revoking the certificate means that you <b>can't use</b> the VPN anymore with that
|
{% endblock %}
|
||||||
certificate</p>
|
|
||||||
</div>
|
|
||||||
<p>If you're really sure that you want to do this please enter your <b>password</b></p>
|
|
||||||
<div>
|
|
||||||
<input type="password" class="revoke-password form-control">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
|
||||||
<button type="button" class="btn btn-warning revoke-confirm">Revoke</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
{% extends 'panel.html.twig' %}
|
||||||
|
|
||||||
|
{% block panel_contents %}
|
||||||
|
{% if user.getActiveCertificates()|length < user.getMaxKeys() %}
|
||||||
|
<a href="/panel/certificates/new" class="certificate-create">create</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="title">
|
||||||
|
<h1>Certificates</h1>
|
||||||
|
<div class="undertone">You used {{ user.getActiveCertificates()|length }} of your {{ user.getMaxKeys() }} certificates</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="certificate-list">
|
||||||
|
{% for certificate in user.getActiveCertificates() %}
|
||||||
|
<li>
|
||||||
|
<div class="text">
|
||||||
|
<div class="name">{{ certificate.getName() }}.{{ user.getUsername() }}</div>
|
||||||
|
<div class="expiry">expires {{ certificate.getExpiresOn().format('j F Y') }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
{% if not certificate.getPrivateKey() is empty %}
|
||||||
|
<span class="certificate-download-key">
|
||||||
|
<a target="_blank" href="/panel/certificates/download-key/{{ certificate.getName() }}">download key</a>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
<span class="certificate-download">
|
||||||
|
<a target="_blank" href="/panel/certificates/download/{{ certificate.getName() }}">download</a>
|
||||||
|
</span>
|
||||||
|
<span class="certificate-delete">revoke</span>
|
||||||
|
</div>
|
||||||
|
<div class="revoke-q">
|
||||||
|
<span class="q">Are you sure you want to revoke this certificate?</span>
|
||||||
|
<span data-name="{{ certificate.getName() }}" class="yes">
|
||||||
|
<a>yes</a>
|
||||||
|
</span>
|
||||||
|
<span class="no">
|
||||||
|
<a>no</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<li>You don't have any certificates :( why not <a href="/panel/certificates/new">create</a> one?</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endblock %}
|
@ -1,57 +1,38 @@
|
|||||||
{% extends "base_bootstrap.html.twig" %}
|
{% extends "panel.html.twig" %}
|
||||||
|
|
||||||
{% block head %}
|
{% block head %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
|
|
||||||
|
<script src="/js/jquery.min.js"></script>
|
||||||
<script src="/js/forge.min.js"></script>
|
<script src="/js/forge.min.js"></script>
|
||||||
<script src="/js/jszip.min.js"></script>
|
<script src="/js/jszip.min.js"></script>
|
||||||
<script src="/js/FileSaver.min.js"></script>
|
<script src="/js/FileSaver.min.js"></script>
|
||||||
<script src="/js/new_certificate.js"></script>
|
<script src="/js/pages/certificates/new.js"></script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block panel_contents %}
|
||||||
<div class="container">
|
<h1>Create certificate</h1>
|
||||||
|
<form action="/panel/certificates/new" method="post">
|
||||||
|
<input type="hidden" id="user-input" value="{{ user.getUsername() }}">
|
||||||
|
<div class="row">
|
||||||
|
<label for="name">Name</label>
|
||||||
|
<input type="text" id="name" name="name">
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label for="has-password">Add password?</label>
|
||||||
|
<input type="checkbox" id="has-password" name="has-password">
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input type="password" disabled id="password" name="password">
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label for="save-online">Save private key online?</label>
|
||||||
|
<input type="checkbox" disabled name="save-online" id="save-online">
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-md-offset-3">
|
<button id="create-csr" type="button">Create</button>
|
||||||
<div class="row">
|
|
||||||
<h2>New Certificate for "<span class="user">{{ user.getUsername() }}</span>"</h2>
|
|
||||||
</div>
|
|
||||||
<div class="form-horizontal">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-md-4" for="name">Name</label>
|
|
||||||
<div class="col-md-8">
|
|
||||||
<input name="name" id="name" type="text" class="form-control">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-md-4">Private Key password</label>
|
|
||||||
<div class="col-md-8">
|
|
||||||
<div class="input-group">
|
|
||||||
<div class="input-group-addon">
|
|
||||||
<input id="wantsPassword" type="checkbox" />
|
|
||||||
</div>
|
|
||||||
<input type="password" disabled class="form-control" id="password" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-md-4">Save private key online</label>
|
|
||||||
<div class="col-md-8">
|
|
||||||
<div class="checkbox">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" id="saveOnline" disabled /> This may be a security risk.
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<button type="button" class="magic-csr btn btn-primary pull-right">Create certificate</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<input type="hidden" value="{{ user.getUsername() }}" id="user-input">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -1,55 +1,40 @@
|
|||||||
{% extends "base_bootstrap.html.twig" %}
|
{% extends "panel.html.twig" %}
|
||||||
|
|
||||||
{% block head %}
|
{% block head %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
|
|
||||||
<script src="/js/clipboard.min.js"></script>
|
<script src="/js/clipboard.min.js"></script>
|
||||||
<script src="/js/invites.js"></script>
|
<script src="/js/pages/invites.js"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block panel_contents %}
|
||||||
<div class="container">
|
{% if user.getActiveCertificates()|length < user.getMaxKeys() %}
|
||||||
<div class="row">
|
<form class="invite-create" action="/panel/invites/create" method="post">
|
||||||
<h2 id="certificates">Invites <small>You used {{ used_invites }} from your {{ max_invites == -1 ? 'infinite' : max_invites }} invites</small></h2>
|
<button type="submit">create</button>
|
||||||
</div>
|
</form>
|
||||||
{% if error %}
|
{% endif %}
|
||||||
<div class="row">
|
|
||||||
<div class="alert alert-warning" role="alert">
|
<div class="title">
|
||||||
{{ error }}
|
<h1>Invites</h1>
|
||||||
</div>
|
<div class="undertone">You used {{ used_invites }} from your {{ max_invites == -1 ? 'infinite' : max_invites }} invites</div>
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<div class="row">
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Invite</th>
|
|
||||||
<th>
|
|
||||||
{% if max_invites > used_invites or max_invites == -1 %}
|
|
||||||
<form action="/panel/invites/create" method="post">
|
|
||||||
<button class="btn btn-default pull-right" type="submit">Create new invite</button>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for invite in invites %}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<kbd>{{ invite.getInvite() }}</kbd>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<button class="btn btn-default copy pull-right" data-clipboard-text="{{ invite.getInvite() }}">Copy</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% else %}
|
|
||||||
<tr>
|
|
||||||
<td colspan="2">You don't have any invites :(</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ul class="invites">
|
||||||
|
{% for invite in invites %}
|
||||||
|
<li>
|
||||||
|
<div class="text">
|
||||||
|
<kbd>{{ invite.getInvite() }}</kbd>
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<span>
|
||||||
|
<a class="copy" data-clipboard-text="{{ invite.getInvite() }}">copy</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<li>
|
||||||
|
You don't have any invites
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
{% extends "panel.html.twig" %}
|
||||||
|
|
||||||
|
{% block panel_contents %}
|
||||||
|
<h1 class="title">{{ user.getUsername() }}</h1>
|
||||||
|
<div class="form">
|
||||||
|
<div class="row">
|
||||||
|
<label>Email address</label>
|
||||||
|
<kbd>{{ user.getPlainEmailAddress() }}</kbd>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label>Amount certificates</label>
|
||||||
|
<kbd>{{ user.getMaxKeys() }}</kbd>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -0,0 +1,56 @@
|
|||||||
|
{% extends "panel.html.twig" %}
|
||||||
|
|
||||||
|
{% block head %}
|
||||||
|
{{ parent() }}
|
||||||
|
|
||||||
|
<script src="/js/clipboard.min.js"></script>
|
||||||
|
<script src="/js/pages/invites.js"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block panel_contents %}
|
||||||
|
<h1>Servers</h1>
|
||||||
|
{% if message %}
|
||||||
|
<div class="error-message">{{ message }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<ul class="servers">
|
||||||
|
{% for server in servers %}
|
||||||
|
<li>
|
||||||
|
<div class="text">
|
||||||
|
{{ server.getName() }}
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<span><a href="/panel/servers/{{ server.getFingerprint() }}">Details</a></span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<li>
|
||||||
|
There are no servers :(
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% if superuser %}
|
||||||
|
<h1>Registered servers</h1>
|
||||||
|
<ul class="servers">
|
||||||
|
{% for server in registered_servers %}
|
||||||
|
<li>
|
||||||
|
<div class="text">
|
||||||
|
{{ server.getFingerprint() }}
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<span class="delete">
|
||||||
|
<form action="/panel/server/remove" method="post">
|
||||||
|
<input type="hidden" value="{{ server.getFingerprint() }}" name="fingerprint">
|
||||||
|
<button type="submit">remove</button>
|
||||||
|
</form>
|
||||||
|
</span>
|
||||||
|
<span class="edit" >
|
||||||
|
<a href="/panel/server/{{ server.getFingerprint() }}">edit</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<li>No servers registered</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
@ -1,96 +1,65 @@
|
|||||||
{% extends "base_bootstrap.html.twig" %}
|
{% extends "panel.html.twig" %}
|
||||||
|
|
||||||
{% block head %}
|
{% block head %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
|
|
||||||
<script src="/js/edit_server.js"></script>
|
<script src="/js/pages/servers/edit.js"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block panel_contents %}
|
||||||
<div class="container">
|
<h1 class="title">Editing server '{{ server.getFqdn() }}'</h1>
|
||||||
<div data-fingerprint="{{ server.getFingerprint() }}" class="row server-form">
|
<div class="server-form" data-fingerprint="{{ server.getFingerprint() }}">
|
||||||
<div class="col-md-6 col-md-offset-3">
|
<div class="row">
|
||||||
<div class="row">
|
<label for="fqdn">Hostname</label>
|
||||||
<h2>Editing server '{{ server.getFqdn() }}'</h2>
|
<input name="fqdn" id="fqdn" type="text" value="{{ server.getFqdn() }}">
|
||||||
</div>
|
|
||||||
<div class="form-horizontal">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-md-4" for="fqdn">Hostname</label>
|
|
||||||
<div class="col-md-8">
|
|
||||||
<input name="fqdn" id="fqdn" type="text" class="form-control" value="{{ server.getFqdn() }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-md-4" for="external-ip">External IP</label>
|
|
||||||
<div class="col-md-8">
|
|
||||||
<input name="external-ip" id="external-ip" type="text" min="-1" class="form-control" value="{{ server.getExternalIp() }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<h3>Details</h3>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-md-4" for="location">Location</label>
|
|
||||||
<div class="col-md-8">
|
|
||||||
<input name="location" id="location" type="text" class="form-control" value="{{ server.getLocation() }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-md-4" for="speed">Speed</label>
|
|
||||||
<div class="col-md-8">
|
|
||||||
<input name="speed" id="speed" type="number" min="-1" class="form-control" value="{{ server.getSpeed() }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<h3>Config</h3>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-md-4" for="internal-ip">Internal IP</label>
|
|
||||||
<div class="col-md-8">
|
|
||||||
<input name="internal-ip" id="internal-ip" type="text" min="-1" class="form-control" value="{{ server.getInternalIp() ?: '10.24.0.0' }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-md-4" for="speed">Netmask</label>
|
|
||||||
<div class="col-md-8">
|
|
||||||
<input name="netmask" id="netmask" type="number" min="8" max="24" class="form-control" value="{{ server.getNetmask() ?: 16 }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-md-4" for="port">Port</label>
|
|
||||||
<div class="col-md-8">
|
|
||||||
<input name="port" id="port" type="number" class="form-control" value="{{ server.getPort() ?: 1194 }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-md-4" for="protocol">Protocol</label>
|
|
||||||
<div class="col-md-8">
|
|
||||||
<select name="protocol" id="protocol" class="form-control">
|
|
||||||
<option value="udp" {{ server.getProtocol() == 'udp' ? 'selected' }}>UDP</option>
|
|
||||||
<option value="tcp" {{ server.getProtocol() == 'tcp' ? 'selected' }}>TCP</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-md-4" for="first-dns">First DNS</label>
|
|
||||||
<div class="col-md-8">
|
|
||||||
<input name="first-dns" id="first-dns" type="text" class="form-control" value="{{ server.getFirstDns() ?: '8.8.8.8' }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-md-4" for="second-dns">second DNS</label>
|
|
||||||
<div class="col-md-8">
|
|
||||||
<input name="second-dns" id="first-dns" type="text" class="form-control" value="{{ server.getSecondDns() ?: '8.8.4.4' }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<div class="pull-right">
|
|
||||||
<button type="button" class="btn save">Save</button>
|
|
||||||
{% if server.getStatus() == 'registered' %}
|
|
||||||
<button type="button" class="btn btn-primary save-and-sign">Sign and save</button>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label for="external-ip">External IP</label>
|
||||||
|
<input name="external-ip" id="external-ip" type="text" value="{{ server.getExternalIp() }}">
|
||||||
|
</div>
|
||||||
|
<h2>Details</h2>
|
||||||
|
<div class="row">
|
||||||
|
<label for="location">Location</label>
|
||||||
|
<input name="location" id="location" type="text" value="{{ server.getLocation() }}">
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label for="speed">Speed</label>
|
||||||
|
<input name="speed" id="speed" type="number" min="-1" value="{{ server.getSpeed() }}">
|
||||||
|
</div>
|
||||||
|
<h2>Config</h2>
|
||||||
|
<div class="row">
|
||||||
|
<label for="internal-ip">Internal IP</label>
|
||||||
|
<input name="internal-ip" id="internal-ip" type="text" value="{{ server.getInternalIp() ?: '10.24.0.0' }}">
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label for="netmask">Netmask</label>
|
||||||
|
<input name="netmask" id="netmask" type="number" min="8" max="24" value="{{ server.getNetmask() ?: 16 }}">
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label for="port">Port</label>
|
||||||
|
<input name="port" id="port" type="number" value="{{ server.getPort() ?: 1194 }}">
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label for="protocol">Protocol</label>
|
||||||
|
<select name="protocol" id="protocol">
|
||||||
|
<option value="udp" {{ server.getProtocol() == 'udp' ? 'selected' }}>UDP</option>
|
||||||
|
<option value="tcp" {{ server.getProtocol() == 'tcp' ? 'selected' }}>TCP</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label for="second-dns">second DNS</label>
|
||||||
|
<input name="second-dns" id="first-dns" type="text" value="{{ server.getSecondDns() ?: '8.8.4.4' }}" />
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<label class="control-label col-md-4" for="second-dns">second DNS</label>
|
||||||
|
<input name="second-dns" id="first-dns" type="text" class="form-control" value="{{ server.getSecondDns() ?: '8.8.4.4' }}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="save" type="button">Save</button>
|
||||||
|
{% if server.getStatus() == 'registered' %}
|
||||||
|
<button class="save-and-sign" type="button">Sign and save</button>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
Loading…
Reference in New Issue