mirror of
https://github.com/lucaspalomodevelop/core.git
synced 2026-03-15 17:14:46 +00:00
System: Trust: Authorities - work in progress for https://github.com/opnsense/core/issues/7248
* add boilerplate code (more or less the same as Certificates)
This commit is contained in:
parent
46354f486c
commit
7cb95beef7
@ -0,0 +1,196 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2024 Deciso B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
namespace OPNsense\Trust\Api;
|
||||
|
||||
use OPNsense\Base\ApiMutableModelControllerBase;
|
||||
use OPNsense\Base\UserException;
|
||||
use OPNsense\Core\Config;
|
||||
use OPNsense\Trust\Store as CertStore;
|
||||
|
||||
/**
|
||||
* Class CaController
|
||||
* @package OPNsense\Trust\Api
|
||||
*/
|
||||
class CaController extends ApiMutableModelControllerBase
|
||||
{
|
||||
protected static $internalModelName = 'ca';
|
||||
protected static $internalModelClass = 'OPNsense\Trust\Ca';
|
||||
|
||||
protected function setBaseHook($node)
|
||||
{
|
||||
if (empty((string)$node->refid)) {
|
||||
$node->refid = uniqid();
|
||||
}
|
||||
$error = false;
|
||||
if (!empty((string)$node->prv_payload)) {
|
||||
/** private key manually offered */
|
||||
$node->prv = base64_encode((string)$node->prv_payload);
|
||||
}
|
||||
switch ((string)$node->action) {
|
||||
case 'internal':
|
||||
break;
|
||||
case 'existing':
|
||||
if (CertStore::parseX509((string)$node->crt_payload) === false) {
|
||||
$error = gettext('Invalid X509 certificate provided');
|
||||
} else {
|
||||
$node->crt = base64_encode((string)$node->crt_payload);
|
||||
if (
|
||||
!empty(trim((string)$node->prv_payload)) &&
|
||||
openssl_pkey_get_private((string)$node->prv_payload) === false
|
||||
) {
|
||||
$error = gettext('Invalid private key provided');
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'import':
|
||||
break;
|
||||
case 'ocsp':
|
||||
break;
|
||||
}
|
||||
if ($error !== false) {
|
||||
throw new UserException($error, "Certificate error");
|
||||
}
|
||||
}
|
||||
|
||||
public function searchAction()
|
||||
{
|
||||
$carefs = $this->request->get('carefs');
|
||||
$filter_funct = function ($record) use ($carefs) {
|
||||
return empty($carefs) || array_intersect(explode(',', $record->caref), $carefs);
|
||||
};
|
||||
return $this->searchBase(
|
||||
'ca',
|
||||
['refid', 'descr', 'caref', 'name', 'valid_from', 'valid_to'],
|
||||
null,
|
||||
$filter_funct
|
||||
);
|
||||
}
|
||||
|
||||
public function getAction($uuid = null)
|
||||
{
|
||||
return $this->getBase('ca', 'ca', $uuid);
|
||||
}
|
||||
|
||||
public function addAction()
|
||||
{
|
||||
return $this->addBase('ca', 'ca');
|
||||
}
|
||||
|
||||
public function setAction($uuid = null)
|
||||
{
|
||||
return $this->setBase('ca', 'ca', $uuid);
|
||||
}
|
||||
|
||||
public function delAction($uuid)
|
||||
{
|
||||
if ($this->request->isPost() && !empty($uuid)) {
|
||||
$node = $this->getModel()->getNodeByReference('ca.' . $uuid);
|
||||
if ($node !== null) {
|
||||
$this->checkAndThrowValueInUse((string)$node->refid, false, false, ['ca']);
|
||||
}
|
||||
return $this->delBase('ca', $uuid);
|
||||
}
|
||||
return ['status' => 'failed'];
|
||||
}
|
||||
|
||||
public function toggleAction($uuid, $enabled = null)
|
||||
{
|
||||
return $this->toggleBase('ca', $uuid, $enabled);
|
||||
}
|
||||
|
||||
public function caInfoAction($caref)
|
||||
{
|
||||
if ($this->request->isGet()) {
|
||||
$ca = CertStore::getCACertificate($caref);
|
||||
if ($ca) {
|
||||
$payload = CertStore::parseX509($ca['cert']);
|
||||
if ($payload) {
|
||||
return $payload;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
public function rawDumpAction($uuid)
|
||||
{
|
||||
$payload = $this->getBase('ca', 'ca', $uuid);
|
||||
if (!empty($payload['ca'])) {
|
||||
if (!empty($payload['ca']['crt_payload'])) {
|
||||
return CertStore::dumpX509($payload['ca']['crt_payload']);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
public function caListAction()
|
||||
{
|
||||
$result = [];
|
||||
if ($this->request->isGet()) {
|
||||
$result['rows'] = [];
|
||||
if (isset(Config::getInstance()->object()->ca)) {
|
||||
foreach (Config::getInstance()->object()->ca as $cert) {
|
||||
if (isset($cert->refid)) {
|
||||
$result['rows'][] = [
|
||||
'caref' => (string)$cert->refid,
|
||||
'descr' => (string)$cert->descr
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
$result['count'] = count($result['rows']);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* generate file download content
|
||||
* @param string $uuid certificate reference
|
||||
* @param string $type one of crt/prv/pkcs12,
|
||||
* $_POST['password'] my contain an optional password for the pkcs12 format
|
||||
* @return array
|
||||
*/
|
||||
public function generateFileAction($uuid = null, $type = 'crt')
|
||||
{
|
||||
$result = ['status' => 'failed'];
|
||||
if ($this->request->isPost() && !empty($uuid)) {
|
||||
$node = $this->getModel()->getNodeByReference('ca.' . $uuid);
|
||||
if ($node === null || empty((string)$node->crt_payload)) {
|
||||
$result['error'] = gettext('Misssing certificate');
|
||||
} elseif ($type == 'crt') {
|
||||
$result['status'] = 'ok';
|
||||
$result['payload'] = (string)$node->crt_payload;
|
||||
} elseif ($type == 'prv') {
|
||||
$result['status'] = 'ok';
|
||||
$result['payload'] = (string)$node->prv_payload;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2024 Deciso B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
namespace OPNsense\Trust;
|
||||
|
||||
class CaController extends \OPNsense\Base\IndexController
|
||||
{
|
||||
protected function templateJSIncludes()
|
||||
{
|
||||
$result = parent::templateJSIncludes();
|
||||
$result[] = '/ui/js/moment-with-locales.min.js';
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function indexAction()
|
||||
{
|
||||
$this->view->formDialogEditCert = $this->getForm("dialogCa");
|
||||
$this->view->pick('OPNsense/Trust/ca');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,123 @@
|
||||
<form>
|
||||
<field>
|
||||
<id>ca.action</id>
|
||||
<label>Method</label>
|
||||
<type>dropdown</type>
|
||||
</field>
|
||||
<field>
|
||||
<type>header</type>
|
||||
<label>Key</label>
|
||||
<style>action action_internal action_internal</style>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.cert_type</id>
|
||||
<label>Type</label>
|
||||
<type>dropdown</type>
|
||||
<style>selectpicker action action_internal </style>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.private_key_location</id>
|
||||
<label>Private key location</label>
|
||||
<style>selectpicker action action_internal</style>
|
||||
<type>dropdown</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.key_type</id>
|
||||
<label>Key type</label>
|
||||
<type>dropdown</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.digest</id>
|
||||
<label>Digest Algorithm</label>
|
||||
<type>dropdown</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.caref</id>
|
||||
<label>Issuer</label>
|
||||
<style>selectpicker action action_internal </style>
|
||||
<type>dropdown</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.lifetime</id>
|
||||
<label>Lifetime (days)</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<type>header</type>
|
||||
<label>General</label>
|
||||
<style>action action_internal</style>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.descr</id>
|
||||
<label>Description</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.country</id>
|
||||
<label>Country Code</label>
|
||||
<type>dropdown</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.state</id>
|
||||
<label>State or Province</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.city</id>
|
||||
<label>City</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.organization</id>
|
||||
<label>Organization</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.organizationalunit</id>
|
||||
<label>Organizational Unit</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.email</id>
|
||||
<label>Email Address</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.commonname</id>
|
||||
<label>Common Name</label>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.ocsp_uri</id>
|
||||
<label>OCSP uri</label>
|
||||
<type>text</type>
|
||||
<style>action action_internal </style>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.altnames_email</id>
|
||||
<label>Email addresses</label>
|
||||
<type>textbox</type>
|
||||
</field>
|
||||
<field>
|
||||
<type>header</type>
|
||||
<label>Output (PEM format)</label>
|
||||
<collapse>true</collapse>
|
||||
<style>pem_section</style>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.crt_payload</id>
|
||||
<label>Certificate data</label>
|
||||
<type>textbox</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.prv_payload</id>
|
||||
<label>Private key data</label>
|
||||
<type>textbox</type>
|
||||
</field>
|
||||
<field>
|
||||
<id>ca.csr_payload</id>
|
||||
<label>Certificate signing request</label>
|
||||
<type>textbox</type>
|
||||
<style>selectpicker action action_import_csr</style>
|
||||
</field>
|
||||
</form>
|
||||
42
src/opnsense/mvc/app/models/OPNsense/Trust/Ca.php
Normal file
42
src/opnsense/mvc/app/models/OPNsense/Trust/Ca.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (C) 2024 Deciso B.V.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OPNsense\Trust;
|
||||
|
||||
use OPNsense\Base\Messages\Message;
|
||||
use OPNsense\Base\BaseModel;
|
||||
|
||||
/**
|
||||
* Class Ca
|
||||
* @package OPNsense\Trust
|
||||
*/
|
||||
class Ca extends BaseModel
|
||||
{
|
||||
}
|
||||
87
src/opnsense/mvc/app/models/OPNsense/Trust/Ca.xml
Normal file
87
src/opnsense/mvc/app/models/OPNsense/Trust/Ca.xml
Normal file
@ -0,0 +1,87 @@
|
||||
<model>
|
||||
<mount>/ca+</mount>
|
||||
<version>1.0.0</version>
|
||||
<items>
|
||||
<ca type=".\CAsField">
|
||||
<refid type="TextField"/>
|
||||
<descr type="DescriptionField"/>
|
||||
<crt type="TextField"/>
|
||||
<prv type="TextField"/>
|
||||
<serial type="IntegerField"/>
|
||||
<caref type="CertificateField">
|
||||
<type>ca</type>
|
||||
<BlankDesc>self-signed</BlankDesc>
|
||||
<ValidationMessage>Please select a valid certificate from the list</ValidationMessage>
|
||||
</caref>
|
||||
<action type="OptionField" volatile="true">
|
||||
<Default>internal</Default>
|
||||
<Required>Y</Required>
|
||||
<OptionValues>
|
||||
<existing>Import an existing Certificate Authority</existing>
|
||||
<internal>Create an internal Certificate Authority</internal>
|
||||
<ocsp>Create an OCSP signing certificate</ocsp>
|
||||
</OptionValues>
|
||||
</action>
|
||||
<key_type type="OptionField" volatile="true">
|
||||
<Required>Y</Required>
|
||||
<Default>2048</Default>
|
||||
<OptionValues>
|
||||
<RSA-512 value='512'>RSA-512</RSA-512>
|
||||
<RSA-1024 value='1024'>RSA-1024</RSA-1024>
|
||||
<RSA-2048 value='2048'>RSA-2048</RSA-2048>
|
||||
<RSA-3072 value='3072'>RSA-3072</RSA-3072>
|
||||
<RSA-4096 value='4096'>RSA-4096</RSA-4096>
|
||||
<RSA-8192 value='8192'>RSA-8192</RSA-8192>
|
||||
<prime256v1>Elliptic Curve prime256v1</prime256v1>
|
||||
<secp384r1>Elliptic Curve secp384r1</secp384r1>
|
||||
<secp521r1>Elliptic Curve secp521r1</secp521r1>
|
||||
</OptionValues>
|
||||
</key_type>
|
||||
<digest type="OptionField" volatile="true">
|
||||
<Required>Y</Required>
|
||||
<Default>sha256</Default>
|
||||
<OptionValues>
|
||||
<sha1>SHA1</sha1>
|
||||
<sha224>SHA224</sha224>
|
||||
<sha256>SHA256</sha256>
|
||||
<sha384>SHA384</sha384>
|
||||
<sha512>SHA512</sha512>
|
||||
</OptionValues>
|
||||
</digest>
|
||||
<lifetime type="IntegerField" volatile="true">
|
||||
<Required>Y</Required>
|
||||
<Default>397</Default>
|
||||
</lifetime>
|
||||
<city type="TextField" volatile="true">
|
||||
<Mask>/^[^\x00-\x08\x0b\x0c\x0e-\x1f\n]*$/</Mask>
|
||||
</city>
|
||||
<state type="TextField" volatile="true">
|
||||
<Mask>/^[^\x00-\x08\x0b\x0c\x0e-\x1f\n]*$/</Mask>
|
||||
</state>
|
||||
<organization type="TextField" volatile="true">
|
||||
<Mask>/^[^\x00-\x08\x0b\x0c\x0e-\x1f\n]*$/</Mask>
|
||||
</organization>
|
||||
<organizationalunit type="TextField" volatile="true">
|
||||
<Mask>/^[^\x00-\x08\x0b\x0c\x0e-\x1f\n]*$/</Mask>
|
||||
</organizationalunit>
|
||||
<country type="CountryField" volatile="true">
|
||||
<Mask>/^[^\x00-\x08\x0b\x0c\x0e-\x1f\n]*$/</Mask>
|
||||
<Default>NL</Default>
|
||||
<Required>Y</Required>
|
||||
</country>
|
||||
<email type="TextField" volatile="true">
|
||||
<Mask>/^[^\x00-\x08\x0b\x0c\x0e-\x1f\n]*$/</Mask>
|
||||
</email>
|
||||
<commonname type="TextField" volatile="true">
|
||||
<Mask>/^[^\x00-\x08\x0b\x0c\x0e-\x1f\n]*$/</Mask>
|
||||
</commonname>
|
||||
<ocsp_uri type="UrlField" volatile="true"/>
|
||||
<crt_payload type="TextField" volatile="true"/>
|
||||
<prv_payload type="TextField" volatile="true"/>
|
||||
|
||||
<name type="TextField" volatile="true"/>
|
||||
<valid_from type="TextField" volatile="true"/>
|
||||
<valid_to type="TextField" volatile="true"/>
|
||||
</ca>
|
||||
</items>
|
||||
</model>
|
||||
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2024 Deciso B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
namespace OPNsense\Trust\FieldTypes;
|
||||
|
||||
use OPNsense\Core\Config;
|
||||
use OPNsense\Base\FieldTypes\ArrayField;
|
||||
use OPNsense\Base\FieldTypes\ContainerField;
|
||||
use OPNsense\Base\FieldTypes\TextField;
|
||||
|
||||
/**
|
||||
* Class CaContainerField
|
||||
* @package OPNsense\Trust\FieldTypes
|
||||
*/
|
||||
class CaContainerField extends ContainerField
|
||||
{
|
||||
/**
|
||||
* @return array dn
|
||||
*/
|
||||
public function dn()
|
||||
{
|
||||
$dn = [];
|
||||
foreach (
|
||||
[
|
||||
'country' => 'countryName',
|
||||
'state' => 'stateOrProvinceName',
|
||||
'city' => 'localityName',
|
||||
'organization' => 'organizationName',
|
||||
'organizationalunit' => 'organizationalUnitName',
|
||||
'email' => 'emailAddress',
|
||||
'commonname' => 'commonName',
|
||||
] as $source => $target
|
||||
) {
|
||||
if (!empty((string)$this->$source)) {
|
||||
$dn[$target] = (string)$this->$source;
|
||||
}
|
||||
}
|
||||
|
||||
return $dn;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class CAsField
|
||||
* @package OPNsense\Trust\FieldTypes
|
||||
*/
|
||||
class CAsField extends ArrayField
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function newContainerField($ref, $tagname)
|
||||
{
|
||||
$container_node = new CaContainerField($ref, $tagname);
|
||||
$pmodel = $this->getParentModel();
|
||||
$container_node->setParentModel($pmodel);
|
||||
return $container_node;
|
||||
}
|
||||
|
||||
protected function actionPostLoadingEvent()
|
||||
{
|
||||
foreach ($this->internalChildnodes as $node) {
|
||||
$node->crt_payload = !empty((string)$node->crt) ? (string)base64_decode($node->crt) : '';
|
||||
$payload = false;
|
||||
if (!empty((string)$node->crt_payload)) {
|
||||
$payload = \OPNsense\Trust\Store::parseX509($node->crt_payload);
|
||||
}
|
||||
if ($payload !== false) {
|
||||
$countries = [];
|
||||
foreach ($payload as $key => $value) {
|
||||
if (isset($node->$key)) {
|
||||
/* prevent injection of invalid countries which trip migrations */
|
||||
if ($key == 'country') {
|
||||
if (empty($countries)) {
|
||||
$countries = array_keys($node->$key->getNodeData());
|
||||
}
|
||||
if (in_array($value, $countries)) {
|
||||
$node->$key = $value;
|
||||
}
|
||||
} else {
|
||||
$node->$key = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$node->prv_payload = !empty((string)$node->prv) ? (string)base64_decode($node->prv) : '';
|
||||
|
||||
if (!empty((string)$node->crt_payload)) {
|
||||
$node->action = 'existing';
|
||||
}
|
||||
}
|
||||
return parent::actionPostLoadingEvent();
|
||||
}
|
||||
}
|
||||
@ -119,7 +119,17 @@ class CertificatesField extends ArrayField
|
||||
if ($payload !== false) {
|
||||
foreach ($payload as $key => $value) {
|
||||
if (isset($node->$key)) {
|
||||
$node->$key = $value;
|
||||
/* prevent injection of invalid countries which trip migrations */
|
||||
if ($key == 'country') {
|
||||
if (empty($countries)) {
|
||||
$countries = array_keys($node->$key->getNodeData());
|
||||
}
|
||||
if (in_array($value, $countries)) {
|
||||
$node->$key = $value;
|
||||
}
|
||||
} else {
|
||||
$node->$key = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
228
src/opnsense/mvc/app/views/OPNsense/Trust/ca.volt
Normal file
228
src/opnsense/mvc/app/views/OPNsense/Trust/ca.volt
Normal file
@ -0,0 +1,228 @@
|
||||
{#
|
||||
# Copyright (c) 2024 Deciso B.V.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#}
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function download_content(payload, filename, file_type) {
|
||||
let a_tag = $('<a></a>').attr('href','data:application/json;charset=utf8,' + encodeURIComponent(payload))
|
||||
.attr('download', filename).appendTo('body');
|
||||
|
||||
a_tag.ready(function() {
|
||||
if ( window.navigator.msSaveOrOpenBlob && window.Blob ) {
|
||||
var blob = new Blob( [ payload ], { type: file_type } );
|
||||
navigator.msSaveOrOpenBlob( blob, 'aliases.json' );
|
||||
} else {
|
||||
a_tag.get(0).click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$( document ).ready(function () {
|
||||
let grid_cert = $("#grid-cert").UIBootgrid({
|
||||
search:'/api/trust/ca/search/',
|
||||
get:'/api/trust/ca/get/',
|
||||
add:'/api/trust/ca/add/',
|
||||
set:'/api/trust/ca/set/',
|
||||
del:'/api/trust/ca/del/',
|
||||
commands: {
|
||||
raw_dump: {
|
||||
method: function(event){
|
||||
let uuid = $(this).data("row-id") !== undefined ? $(this).data("row-id") : '';
|
||||
ajaxGet('/api/trust/ca/raw_dump/' + uuid, {}, function(data, status){
|
||||
if (data.stdout) {
|
||||
BootstrapDialog.show({
|
||||
title: "{{ lang._('Certificate info') }}",
|
||||
type:BootstrapDialog.TYPE_INFO,
|
||||
message: $("<div/>").text(data.stdout).html(),
|
||||
cssClass: 'monospace-dialog',
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
classname: 'fa fa-fw fa-info-circle',
|
||||
title: "{{ lang._('show certificate info') }}",
|
||||
sequence: 10
|
||||
},
|
||||
download: {
|
||||
method: function(event){
|
||||
let uuid = $(this).data("row-id") !== undefined ? $(this).data("row-id") : '';
|
||||
let $container = $("<div style='height:150px;'/>");
|
||||
let $type = $("<select id='download_type'/>");
|
||||
let $password = $("<input id='download_password' type='password'/>");
|
||||
$type.append($("<option value='crt'/>").text('Certificate'));
|
||||
$type.append($("<option value='prv'/>").text('Private key'));
|
||||
$container.append(
|
||||
$("<div class='form-group'/>").append(
|
||||
$("<label for='download_type'>{{ lang._('File type') }}</label>"),
|
||||
$type
|
||||
)
|
||||
);
|
||||
BootstrapDialog.show({
|
||||
title: "{{ lang._('Certificate download') }}",
|
||||
type:BootstrapDialog.TYPE_INFO,
|
||||
message: $container,
|
||||
buttons: [{
|
||||
label: "{{ lang._('Download') }}",
|
||||
action: function(dialogItself){
|
||||
let params = {};
|
||||
if ($password.val()) {
|
||||
params['password'] = $password.val();
|
||||
}
|
||||
ajaxCall(
|
||||
'/api/trust/ca/generate_file/'+uuid+'/'+$type.val(),
|
||||
params,
|
||||
function(data, status) {
|
||||
download_content(data.payload, $type.val() + '.pem', 'application/octet-stream');
|
||||
}
|
||||
)
|
||||
dialogItself.close();
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
},
|
||||
classname: 'fa fa-fw fa-cloud-download',
|
||||
title: "{{ lang._('Download') }}",
|
||||
sequence: 10
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Autofill certificate fields when choosing a different CA
|
||||
*/
|
||||
$("#ca\\.caref").change(function(event){
|
||||
if (event.originalEvent !== undefined) {
|
||||
// not called on form open, only when the user chooses a new ca
|
||||
ajaxGet('/api/trust/ca/ca_info/' + $(this).val(), {}, function(data, status){
|
||||
if (data.name !== undefined) {
|
||||
[
|
||||
'city', 'state', 'country', 'name', 'email', 'organization', 'ocsp_uri'
|
||||
].forEach(function(field){
|
||||
if (data[field]) {
|
||||
$("#ca\\." + field).val(data[field]);
|
||||
}
|
||||
});
|
||||
}
|
||||
$("#ca\\.country").selectpicker('refresh');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$("#ca\\.action").change(function(event){
|
||||
if (event.originalEvent === undefined) {
|
||||
// lock valid options based on server offered action
|
||||
let visible_options = [$(this).val()];
|
||||
if ($(this).val() == 'internal') {
|
||||
visible_options.push('existing');
|
||||
visible_options.push('ocsp');
|
||||
}
|
||||
$("#ca\\.action option").each(function(){
|
||||
if (visible_options.includes($(this).val())) {
|
||||
$(this).attr('disabled', null);
|
||||
} else {
|
||||
$(this).attr('disabled', 'disabled');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let this_action = $(this).val();
|
||||
$(".action").each(function(){
|
||||
let target = null;
|
||||
if ($(this)[0].tagName == 'DIV') {
|
||||
target = $(this)
|
||||
} else {
|
||||
target = $(this).closest("tr");
|
||||
}
|
||||
target.hide();
|
||||
if ($(this).hasClass('action_' + this_action)) {
|
||||
target.show();
|
||||
}
|
||||
});
|
||||
/* expand/collapse PEM section */
|
||||
if (['import', 'import_csr'].includes($(this).val())) {
|
||||
if ($(".pem_section > table > tbody > tr:eq(0) > td:eq(0)").is(':hidden')) {
|
||||
$(".pem_section > table > thead").click();
|
||||
}
|
||||
} else {
|
||||
if (!$(".pem_section > table > tbody > tr:eq(0) > td:eq(0)").is(':hidden')) {
|
||||
$(".pem_section > table > thead").click();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.monospace-dialog {
|
||||
font-family: monospace;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.monospace-dialog > .modal-dialog {
|
||||
width:70% !important;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
max-height: calc(100vh - 210px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
|
||||
<li class="active"><a data-toggle="tab" href="#cert">{{ lang._('Certificates') }}</a></li>
|
||||
</ul>
|
||||
<div class="tab-content content-box">
|
||||
<div id="cert" class="tab-pane fade in active">
|
||||
<table id="grid-cert" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="DialogCert">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
|
||||
<th data-column-id="descr" data-width="15em" data-type="string">{{ lang._('Description') }}</th>
|
||||
<th data-column-id="caref" data-width="15em" data-type="string">{{ lang._('Issuer') }}</th>
|
||||
<th data-column-id="name" data-type="string">{{ lang._('Name') }}</th>
|
||||
<th data-column-id="valid_from" data-width="10em" data-type="datetime">{{ lang._('Valid from') }}</th>
|
||||
<th data-column-id="valid_to" data-width="10em" data-type="datetime">{{ lang._('Valid to') }}</th>
|
||||
<th data-column-id="commands" data-width="11em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<button data-action="add" type="button" class="btn btn-xs btn-primary"><span class="fa fa-fw fa-plus"></span></button>
|
||||
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-fw fa-trash-o"></span></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{ partial("layout_partials/base_dialog",['fields':formDialogEditCert,'id':'DialogCert','label':lang._('Edit Certificate')])}}
|
||||
Loading…
x
Reference in New Issue
Block a user