diff --git a/install/lib/class.FroxlorInstall.php b/install/lib/class.FroxlorInstall.php index 4cc136a5..86e50f78 100644 --- a/install/lib/class.FroxlorInstall.php +++ b/install/lib/class.FroxlorInstall.php @@ -995,7 +995,7 @@ class FroxlorInstall $content .= $this->_status_message('green', $this->_lng['requirements']['installed']); } - // check for bstring-extension + // check for mbstring-extension $content .= $this->_status_message('begin', $this->_lng['requirements']['phpmbstring']); if (! extension_loaded('mbstring')) { diff --git a/lib/classes/api/abstract.ApiCommand.php b/lib/classes/api/abstract.ApiCommand.php index 156f84a5..e3677f9a 100644 --- a/lib/classes/api/abstract.ApiCommand.php +++ b/lib/classes/api/abstract.ApiCommand.php @@ -414,6 +414,49 @@ abstract class ApiCommand return $json_response; } + /** + * returns an array of customers the current user can access + * + * @param string $customer_hide_option optional, when called as customer, some options might be hidden due to the panel.customer_hide_options ettings + * + * @throws Exception + * @return array + */ + protected function getAllowedCustomerIds($customer_hide_option = '') + { + $customer_ids = array(); + if ($this->isAdmin()) { + // if we're an admin, list all ftp-users of all the admins customers + // or optionally for one specific customer identified by id or loginname + $customerid = $this->getParam('customerid', true, 0); + $loginname = $this->getParam('loginname', true, ''); + + if (! empty($customerid) || ! empty($loginname)) { + $_result = $this->apiCall('Customers.get', array( + 'id' => $customerid, + 'loginname' => $loginname + )); + $custom_list_result = array( + $_result + ); + } else { + $_custom_list_result = $this->apiCall('Customers.listing'); + $custom_list_result = $_custom_list_result['list']; + } + foreach ($custom_list_result as $customer) { + $customer_ids[] = $customer['customerid']; + } + } else { + if (!empty($customer_hide_option) && Settings::IsInList('panel.customer_hide_options', $customer_hide_option)) { + throw new Exception("You cannot access this resource", 405); + } + $customer_ids = array( + $this->getUserDetail('customerid') + ); + } + return $customer_ids; + } + /** * increase/decrease a resource field for customers/admins * diff --git a/lib/classes/api/commands/class.Certificates.php b/lib/classes/api/commands/class.Certificates.php index fb17a709..c49869ec 100644 --- a/lib/classes/api/commands/class.Certificates.php +++ b/lib/classes/api/commands/class.Certificates.php @@ -50,18 +50,27 @@ class Certificates extends ApiCommand implements ResourceEntity 'id' => $domainid, 'domainname' => $domainname )); + $domainid = $domain['id']; + // parameters $ssl_cert_file = $this->getParam('ssl_cert_file'); $ssl_key_file = $this->getParam('ssl_key_file'); $ssl_ca_file = $this->getParam('ssl_ca_file', true, ''); $ssl_cert_chainfile = $this->getParam('ssl_cert_chainfile', true, ''); - $this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, true); - $idna_convert = new idna_convert_wrapper(); - $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added ssl-certificate for '" . $domain['domain'] . "'"); + + // validate whether the domain does not already have an entry $result = $this->apiCall('Certificates.get', array( - 'id' => $domain['id'] + 'id' => $domainid )); - return $this->response(200, "successfull", $result); + if (empty($result)) { + $this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, true); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added ssl-certificate for '" . $domain['domain'] . "'"); + $result = $this->apiCall('Certificates.get', array( + 'id' => $domain['id'] + )); + return $this->response(200, "successfull", $result); + } + throw new Exception("Domain '" . $domain['domain'] . "' already has a certificate. Did you mean to call update?", 406); } /** diff --git a/lib/classes/api/commands/class.Ftps.php b/lib/classes/api/commands/class.Ftps.php index 6411e3f2..6f3223be 100644 --- a/lib/classes/api/commands/class.Ftps.php +++ b/lib/classes/api/commands/class.Ftps.php @@ -448,43 +448,14 @@ class Ftps extends ApiCommand implements ResourceEntity */ public function listing() { - if ($this->isAdmin()) { - // if we're an admin, list all ftp-users of all the admins customers - // or optionally for one specific customer identified by id or loginname - $customerid = $this->getParam('customerid', true, 0); - $loginname = $this->getParam('loginname', true, ''); - - if (! empty($customerid) || ! empty($loginname)) { - $_result = $this->apiCall('Customers.get', array( - 'id' => $customerid, - 'loginname' => $loginname - )); - $custom_list_result = array( - $_result - ); - } else { - $_custom_list_result = $this->apiCall('Customers.listing'); - $custom_list_result = $_custom_list_result['list']; - } - $customer_ids = array(); - foreach ($custom_list_result as $customer) { - $customer_ids[] = $customer['customerid']; - } - } else { - if (Settings::IsInList('panel.customer_hide_options', 'ftp')) { - throw new Exception("You cannot access this resource", 405); - } - $customer_ids = array( - $this->getUserDetail('customerid') - ); - } + $customer_ids = $this->getAllowedCustomerIds('ftp'); $result = array(); $params['customerid'] = implode(", ", $customer_ids); $result_stmt = Database::prepare(" SELECT * FROM `" . TABLE_FTP_USERS . "` WHERE `customerid` IN (:customerid) "); - Database::pexecute($result_stmt, $params); + Database::pexecute($result_stmt, $params, true, true); while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) { $result[] = $row; } diff --git a/lib/classes/api/commands/class.Mysqls.php b/lib/classes/api/commands/class.Mysqls.php index 55929a80..dfb1cd55 100644 --- a/lib/classes/api/commands/class.Mysqls.php +++ b/lib/classes/api/commands/class.Mysqls.php @@ -425,36 +425,7 @@ class Mysqls extends ApiCommand implements ResourceEntity { $result = array(); $dbserver = $this->getParam('mysql_server', true, - 1); - if ($this->isAdmin()) { - // if we're an admin, list all databases of all the admins customers - // or optionally for one specific customer identified by id or loginname - $customerid = $this->getParam('customerid', true, 0); - $loginname = $this->getParam('loginname', true, ''); - - if (! empty($customer_id) || ! empty($loginname)) { - $customer = $this->apiCall('Customers.get', array( - 'id' => $customerid, - 'loginname' => $loginname - )); - $custom_list_result = array( - $customer - ); - } else { - $_custom_list_result = $this->apiCall('Customers.listing'); - $custom_list_result = $_custom_list_result['list']; - } - $customer_ids = array(); - foreach ($custom_list_result as $customer) { - $customer_ids[] = $customer['customerid']; - } - } else { - if (Settings::IsInList('panel.customer_hide_options', 'mysql')) { - throw new Exception("You cannot access this resource", 405); - } - $customer_ids = array( - $this->getUserDetail('customerid') - ); - } + $customer_ids = $this->getAllowedCustomerIds('mysql'); $result_stmt = Database::prepare(" SELECT * FROM `" . TABLE_PANEL_DATABASES . "` WHERE `customerid`= :customerid AND `dbserver` = :dbserver diff --git a/lib/classes/api/commands/class.SubDomains.php b/lib/classes/api/commands/class.SubDomains.php index 1e2e1cbf..ba257882 100644 --- a/lib/classes/api/commands/class.SubDomains.php +++ b/lib/classes/api/commands/class.SubDomains.php @@ -247,6 +247,7 @@ class SubDomains extends ApiCommand implements ResourceEntity $stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_DOMAINS . "` SET `customerid` = :customerid, + `adminid` = :adminid, `domain` = :domain, `documentroot` = :documentroot, `aliasdomain` = :aliasdomain, @@ -268,6 +269,7 @@ class SubDomains extends ApiCommand implements ResourceEntity "); $params = array( "customerid" => $customer['customerid'], + "adminid" => $customer['adminid'], "domain" => $completedomain, "documentroot" => $path, "aliasdomain" => $aliasdomain != 0 ? $aliasdomain : null, diff --git a/phpunit.xml b/phpunit.xml index 391c3a47..efedaf51 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -14,6 +14,7 @@ tests/IpsAndPorts tests/Domains tests/SubDomains + tests/Certificates tests/Ftps diff --git a/tests/Certificates/CertificatesTest.php b/tests/Certificates/CertificatesTest.php new file mode 100644 index 00000000..85a307a4 --- /dev/null +++ b/tests/Certificates/CertificatesTest.php @@ -0,0 +1,194 @@ +generateKey(); + $json_result = Certificates::getLocal($admin_userdata, array( + 'domainname' => 'test2.local', + 'ssl_cert_file' => $certdata['cert'], + 'ssl_key_file' => $certdata['key'] + ))->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(3, $result['domainid']); + } + + public function testResellerCertificatesAddAgain() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + + $certdata = $this->generateKey(); + $this->expectExceptionCode(406); + $this->expectExceptionMessage("Domain 'test2.local' already has a certificate. Did you mean to call update?"); + $json_result = Certificates::getLocal($reseller_userdata, array( + 'domainname' => 'test2.local', + 'ssl_cert_file' => $certdata['cert'], + 'ssl_key_file' => $certdata['key'] + ))->add(); + } + + public function testCustomerCertificatesAdd() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $certdata = $this->generateKey(); + $json_result = Certificates::getLocal($customer_userdata, array( + 'domainname' => 'mysub2.test2.local', + 'ssl_cert_file' => $certdata['cert'], + 'ssl_key_file' => $certdata['key'] + ))->add(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(5, $result['domainid']); + } + + public function testAdminCertificatesList() + { + global $admin_userdata; + + $json_result = Certificates::getLocal($admin_userdata)->listing(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(2, $result['count']); + } + + public function testResellerCertificatesList() + { + global $admin_userdata; + // get reseller + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + + $json_result = Certificates::getLocal($reseller_userdata)->listing(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(2, $result['count']); + } + + public function testCustomerCertificatesList() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $json_result = Certificates::getLocal($customer_userdata)->listing(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(2, $result['count']); + } + + public function testAdminCertificatesUpdate() + { + global $admin_userdata; + + $certdata = $this->generateKey(); + $json_result = Certificates::getLocal($admin_userdata, array( + 'domainname' => 'test2.local', + 'ssl_cert_file' => $certdata['cert'], + 'ssl_key_file' => $certdata['key'] + ))->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(3, $result['domainid']); + $this->assertEquals(str_replace("\n", "", $certdata['cert']), str_replace("\n", "", $result['ssl_cert_file'])); + } + + public function testCustomerCertificatesUpdate() + { + global $admin_userdata; + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $certdata = $this->generateKey(); + $json_result = Certificates::getLocal($customer_userdata, array( + 'domainname' => 'mysub2.test2.local', + 'ssl_cert_file' => $certdata['cert'], + 'ssl_key_file' => $certdata['key'] + ))->update(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(5, $result['domainid']); + $this->assertEquals(str_replace("\n", "", $certdata['cert']), str_replace("\n", "", $result['ssl_cert_file'])); + } + + /** + * @depends testAdminCertificatesUpdate + */ + public function testCustomerCertificatesDelete() + { + global $admin_userdata; + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + $json_result = Certificates::getLocal($customer_userdata, array( + 'id' => 1 + ))->delete(); + $result = json_decode($json_result, true)['data']; + $this->assertEquals(3, $result['domainid']); + } + + private function generateKey() + { + $dn = array( + "countryName" => "DE", + "stateOrProvinceName" => "Hessen", + "localityName" => "Frankfurt", + "organizationName" => "Froxlor", + "organizationalUnitName" => "Testing", + "commonName" => "test2.local", + "emailAddress" => "team@froxlor.org" + ); + + // generate key pair + $privkey = openssl_pkey_new(array( + "private_key_bits" => 2048, + "private_key_type" => OPENSSL_KEYTYPE_RSA + )); + + // generate csr + $csr = openssl_csr_new($dn, $privkey, array( + 'digest_alg' => 'sha256' + )); + + // generate self-signed certificate + $sscert = openssl_csr_sign($csr, null, $privkey, 365, array( + 'digest_alg' => 'sha256' + )); + + // export + openssl_csr_export($csr, $csrout); + openssl_x509_export($sscert, $certout); + openssl_pkey_export($privkey, $pkeyout, null); + + return array( + 'cert' => $certout, + 'key' => $pkeyout + ); + } +} diff --git a/tests/Domains/DomainsTest.php b/tests/Domains/DomainsTest.php index ed13d5ff..964b29e4 100644 --- a/tests/Domains/DomainsTest.php +++ b/tests/Domains/DomainsTest.php @@ -77,10 +77,10 @@ class DomainsTest extends TestCase public function testResellerDomainsAddWithCanEditPhpSettingsAllowedIp() { global $admin_userdata; - // first, allow reseller access to ip #3 + // first, allow reseller access to ip #4 Admins::getLocal($admin_userdata, array( 'loginname' => 'reseller', - 'ipaddress' => 3 + 'ipaddress' => 4 ))->update(); // get reseller $json_result = Admins::getLocal($admin_userdata, array( @@ -91,7 +91,7 @@ class DomainsTest extends TestCase $data = [ 'domain' => 'test2.local', 'customerid' => 1, - 'ipandport' => 3, + 'ipandport' => 4, 'isemaildomain' => 1, 'subcanemaildomain' => 2 ]; diff --git a/tests/IpsAndPorts/IpsAndPortsTest.php b/tests/IpsAndPorts/IpsAndPortsTest.php index 9b3ec579..94d32f27 100644 --- a/tests/IpsAndPorts/IpsAndPortsTest.php +++ b/tests/IpsAndPorts/IpsAndPortsTest.php @@ -13,7 +13,7 @@ class IpsAndPortsTest extends TestCase global $admin_userdata; $json_result = IpsAndPorts::getLocal($admin_userdata)->listing(); $result = json_decode($json_result, true)['data']; - $this->assertEquals(1, $result['count']); + $this->assertEquals(2, $result['count']); $this->assertEquals('82.149.225.46', $result['list'][0]['ip']); } @@ -40,7 +40,7 @@ class IpsAndPortsTest extends TestCase ]; $json_result = IpsAndPorts::getLocal($admin_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; - $this->assertEquals(2, $result['id']); + $this->assertEquals(3, $result['id']); $this->assertEquals(80, $result['port']); } @@ -66,7 +66,7 @@ class IpsAndPortsTest extends TestCase ]; $json_result = IpsAndPorts::getLocal($admin_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; - $this->assertEquals(3, $result['id']); + $this->assertEquals(4, $result['id']); $this->assertEquals('/var/www/html/', $result['docroot']); } @@ -84,10 +84,10 @@ class IpsAndPortsTest extends TestCase public function testResellerIpsAndPortsList() { global $admin_userdata; - // update reseller to allow ip access to ip id #2 + // update reseller to allow ip access to ip id #3 $json_result = Admins::getLocal($admin_userdata, array( 'loginname' => 'reseller', - 'ipaddress' => array(2) + 'ipaddress' => array(3) ))->update(); $reseller_userdata = json_decode($json_result, true)['data']; $reseller_userdata['adminsession'] = 1; @@ -109,7 +109,7 @@ class IpsAndPortsTest extends TestCase ))->get(); $reseller_userdata = json_decode($json_result, true)['data']; $reseller_userdata['adminsession'] = 1; - $json_result = IpsAndPorts::getLocal($reseller_userdata, array('id' => 2))->get(); + $json_result = IpsAndPorts::getLocal($reseller_userdata, array('id' => 3))->get(); $result = json_decode($json_result, true)['data']; $this->assertEquals('82.149.225.47', $result['ip']); } @@ -120,7 +120,7 @@ class IpsAndPortsTest extends TestCase public function testResellerIpsAndPortsGetRestrictedNotOwned() { global $admin_userdata; - // update reseller to allow ip access to ip id #2 + // get reseller $json_result = Admins::getLocal($admin_userdata, array( 'loginname' => 'reseller' ))->get(); @@ -134,7 +134,7 @@ class IpsAndPortsTest extends TestCase public function testResellerIpsAndPortsAdd() { global $admin_userdata; - // update reseller to allow ip access to ip id #2 + // get reseller $json_result = Admins::getLocal($admin_userdata, array( 'loginname' => 'reseller' ))->get(); @@ -230,7 +230,7 @@ class IpsAndPortsTest extends TestCase $reseller_userdata = json_decode($json_result, true)['data']; $reseller_userdata['adminsession'] = 1; $data = [ - 'id' => 2, + 'id' => 3, 'ip' => '82.149.225.46' ]; $this->expectExceptionMessage("This IP/Port combination already exists."); @@ -251,7 +251,7 @@ class IpsAndPortsTest extends TestCase { global $admin_userdata; $data = [ - 'id' => 2 + 'id' => 3 ]; $json_result = IpsAndPorts::getLocal($admin_userdata, $data)->delete(); $result = json_decode($json_result, true)['data']; diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 31001195..fcf14947 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -103,6 +103,20 @@ Database::query("INSERT INTO `" . TABLE_PANEL_IPSANDPORTS . "` SET $defaultip = Database::lastInsertId(); Settings::Set('system.defaultip', $defaultip, true); +// add ssl ip (system default) +Database::query("INSERT INTO `" . TABLE_PANEL_IPSANDPORTS . "` SET + `ip` = '82.149.225.56', + `port` = '443', + `listen_statement` = '0', + `namevirtualhost_statement` = '0', + `vhostcontainer` = '1', + `vhostcontainer_servername_statement` = '1', + `specialsettings` = '', + `ssl` = '1' +"); +$defaultip = Database::lastInsertId(); +Settings::Set('system.defaultsslip', $defaultip, true); + // get userdata of admin 'admin' $sel_stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_ADMINS . "` WHERE `adminid` = '1'"); $admin_userdata = Database::pexecute_first($sel_stmt);