diff --git a/admin_index.php b/admin_index.php index 6efb31ff..8d7f0e48 100644 --- a/admin_index.php +++ b/admin_index.php @@ -393,6 +393,9 @@ if ($page == 'overview') { redirectTo($filename, array('s' => $s)); } } +elseif ($page == 'apikeys' && Settings::Get('api.enabled') == 1) { + require_once __DIR__ . '/api_keys.php'; +} elseif ($page == 'apihelp' && Settings::Get('api.enabled') == 1) { require_once __DIR__ . '/apihelp.php'; } diff --git a/api_keys.php b/api_keys.php new file mode 100644 index 00000000..d04891df --- /dev/null +++ b/api_keys.php @@ -0,0 +1,128 @@ + (2018-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package Panel + * @since 0.10.0 + * + */ + +// This file is being included in admin_index and customer_index +// and therefore does not need to require lib/init.php + +$log->logAction(USR_ACTION, LOG_NOTICE, "viewed api::api_keys"); + +// select all my (accessable) certificates +$keys_stmt_query = "SELECT ak.*, c.loginname, a.loginname as adminname + FROM `" . TABLE_API_KEYS . "` ak + LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` c ON `c`.`customerid` = `ak`.`customerid` + LEFT JOIN `" . TABLE_PANEL_ADMINS . "` a ON `a`.`adminid` = `ak`.`adminid` + WHERE "; + +$qry_params = array(); +if (AREA == 'admin' && $userinfo['customers_see_all'] == '0') { + // admin with only customer-specific permissions + $keys_stmt_query .= "ak.adminid = :adminid "; + $qry_params['adminid'] = $userinfo['adminid']; + $fields = array( + 'a.loginname' => $lng['login']['username'] + ); +} elseif (AREA == 'customer') { + // customer-area + $keys_stmt_query .= "ak.customerid = :cid "; + $qry_params['cid'] = $userinfo['customerid']; + $fields = array( + 'c.loginname' => $lng['login']['username'] + ); +} else { + // admin who can see all customers / reseller / admins + $keys_stmt_query .= "1 "; + $fields = array( + 'a.loginname' => $lng['login']['username'] + ); +} + +$paging = new paging($userinfo, TABLE_API_KEYS, $fields); +$keys_stmt_query .= $paging->getSqlWhere(true) . " " . $paging->getSqlOrderBy() . " " . $paging->getSqlLimit(); + +$keys_stmt = Database::prepare($keys_stmt_query); +Database::pexecute($keys_stmt, $qry_params); +$all_keys = $keys_stmt->fetchAll(PDO::FETCH_ASSOC); +$apikeys = ""; + +if (count($all_keys) == 0) { + $count = 0; + $message = $lng['apikeys']['no_api_keys']; + $sortcode = ""; + $searchcode = ""; + $pagingcode = ""; + eval("\$apikeys.=\"" . getTemplate("api_keys/keys_error", true) . "\";"); +} else { + $count = count($all_keys); + $paging->setEntries($count); + $sortcode = $paging->getHtmlSortCode($lng); + $arrowcode = $paging->getHtmlArrowCode($filename . '?page=' . $page . '&s=' . $s); + $searchcode = $paging->getHtmlSearchCode($lng); + $pagingcode = $paging->getHtmlPagingCode($filename . '?page=' . $page . '&s=' . $s); + + foreach ($all_keys as $idx => $key) { + if ($paging->checkDisplay($idx)) { + + // my own key + $isMyKey = false; + if ($key['adminid'] == $userinfo['adminid'] && (AREA == 'admin' || (AREA == 'customer' && $key['customerid'] == $userinfo['customerid']))) { + // this is mine + $isMyKey = true; + } + + $adminCustomerLink = ""; + if (AREA == 'admin') { + if ($isMyKey) { + $adminCustomerLink = $key['adminname']; + } else { + $adminCustomerLink = ' (' . (empty($key['customerid']) ? $key['adminname'] : $key['loginname']) . ')'; + } + } else { + // customer do not need links + $adminCustomerLink = $key['loginname']; + } + + // escape stuff + $row = htmlentities_array($key); + + // check whether the api key is not valid anymore + $isValid = true; + if ($row['valid_until'] >= 0) { + if ($row['valid_until'] < time()) { + $isValid = false; + } + // format + $row['valid_until'] = date('d.m.Y H:i', $row['valid_until']); + } else { + $row['valid_until'] = "∞"; + } + eval("\$apikeys.=\"" . getTemplate("api_keys/keys_key", true) . "\";"); + } else { + continue; + } + } +} +eval("echo \"" . getTemplate("api_keys/keys_list", true) . "\";"); diff --git a/customer_index.php b/customer_index.php index 85d02158..f1ba35fb 100644 --- a/customer_index.php +++ b/customer_index.php @@ -314,6 +314,9 @@ if ($page == 'overview') { redirectTo($filename, array('s' => $s)); } } +elseif ($page == 'apikeys' && Settings::Get('api.enabled') == 1) { + require_once __DIR__ . '/api_keys.php'; +} elseif ($page == 'apihelp' && Settings::Get('api.enabled') == 1) { require_once __DIR__ . '/apihelp.php'; } diff --git a/lib/classes/api/commands/class.Admins.php b/lib/classes/api/commands/class.Admins.php index 5b61b50c..e100f8ee 100644 --- a/lib/classes/api/commands/class.Admins.php +++ b/lib/classes/api/commands/class.Admins.php @@ -595,27 +595,31 @@ class Admins extends ApiCommand implements ResourceEntity standard_error('youcantdeleteyourself', '', true); } + // delete admin $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_PANEL_ADMINS . "` WHERE `adminid` = :adminid "); Database::pexecute($del_stmt, array( 'adminid' => $id ), true, true); - + + // delete the traffic-usage $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_PANEL_TRAFFIC_ADMINS . "` WHERE `adminid` = :adminid "); Database::pexecute($del_stmt, array( 'adminid' => $id ), true, true); - + + // delete the diskspace usage $del_stmt = Database::prepare(" DELETE FROM `" . TABLE_PANEL_DISKSPACE_ADMINS . "` WHERE `adminid` = :adminid "); Database::pexecute($del_stmt, array( 'adminid' => $id ), true, true); - + + // set admin-id of the old admin's customer to current admins $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_CUSTOMERS . "` SET `adminid` = :userid WHERE `adminid` = :adminid @@ -624,7 +628,8 @@ class Admins extends ApiCommand implements ResourceEntity 'userid' => $this->getUserDetail('adminid'), 'adminid' => $id ), true, true); - + + // set admin-id of the old admin's domains to current admins $upd_stmt = Database::prepare(" UPDATE `" . TABLE_PANEL_DOMAINS . "` SET `adminid` = :userid WHERE `adminid` = :adminid @@ -633,7 +638,26 @@ class Admins extends ApiCommand implements ResourceEntity 'userid' => $this->getUserDetail('adminid'), 'adminid' => $id ), true, true); - + + // delete old admin's api keys if exists (no customer keys) + $upd_stmt = Database::prepare(" + DELETE FROM `" . TABLE_API_KEYS . "` WHERE + `adminid` = :userid AND `customerid` = '0' + "); + Database::pexecute($upd_stmt, array( + 'adminid' => $id + ), true, true); + + // set admin-id of the old admin's api-keys to current admins + $upd_stmt = Database::prepare(" + UPDATE `" . TABLE_API_KEYS . "` SET + `adminid` = :userid WHERE `adminid` = :adminid + "); + Database::pexecute($upd_stmt, array( + 'userid' => $this->getUserDetail('adminid'), + 'adminid' => $id + ), true, true); + $this->logger()->logAction(ADM_ACTION, LOG_WARNING, "[API] deleted admin '" . $result['loginname'] . "'"); updateCounters(); return $this->response(200, "successfull", $result); diff --git a/lib/classes/api/commands/class.Customers.php b/lib/classes/api/commands/class.Customers.php index cf26d8aa..ed1ff384 100644 --- a/lib/classes/api/commands/class.Customers.php +++ b/lib/classes/api/commands/class.Customers.php @@ -857,9 +857,9 @@ class Customers extends ApiCommand implements ResourceEntity // activate/deactivate customer services if ($deactivated != $result['deactivated']) { - $yesno = (($deactivated) ? 'N' : 'Y'); - $pop3 = (($deactivated) ? '0' : (int) $result['pop3']); - $imap = (($deactivated) ? '0' : (int) $result['imap']); + $yesno = ($deactivated ? 'N' : 'Y'); + $pop3 = ($deactivated ? '0' : (int) $result['pop3']); + $imap = ($deactivated ? '0' : (int) $result['imap']); $upd_stmt = Database::prepare(" UPDATE `" . TABLE_MAIL_USERS . "` SET `postfix`= :yesno, `pop3` = :pop3, `imap` = :imap WHERE `customerid` = :customerid @@ -923,8 +923,16 @@ class Customers extends ApiCommand implements ResourceEntity // At last flush the new privileges $dbm->getManager()->flushPrivileges(); Database::needRoot(false); - - $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] deactivated user '" . $result['loginname'] . "'"); + + // reactivate/deactivate api-keys + $valid_until = $deactivated ? 0 : - 1; + $stmt = Database::prepare("UPDATE `" . TABLE_API_KEYS . "` SET `valid_until` = :vu WHERE `customerid` = :id"); + Database::pexecute($stmt, array( + 'id' => $id, + 'vu' => $valid_until + ), true, true); + + $this->logger()->logAction(ADM_ACTION, LOG_INFO, "[API] " . ($deactivated ? 'deactivated' : 'reactivated') . " user '" . $result['loginname'] . "'"); inserttask('1'); } @@ -1323,6 +1331,12 @@ class Customers extends ApiCommand implements ResourceEntity 'id' => $id ), true, true); + // remove api-keys + $stmt = Database::prepare("DELETE FROM `" . TABLE_API_KEYS . "` WHERE `customerid` = :id"); + Database::pexecute($stmt, array( + 'id' => $id + ), true, true); + // Delete all waiting "create user" -tasks for this user, #276 // Note: the WHERE selects part of a serialized array, but it should be safe this way $del_stmt = Database::prepare(" diff --git a/lib/navigation/00.froxlor.main.php b/lib/navigation/00.froxlor.main.php index 61c7fe6f..8775effd 100644 --- a/lib/navigation/00.froxlor.main.php +++ b/lib/navigation/00.froxlor.main.php @@ -38,6 +38,11 @@ return array( 'label' => $lng['menue']['main']['changetheme'], 'show_element' => (Settings::Get('panel.allow_theme_change_customer') == true) ), + array( + 'url' => 'customer_index.php?page=apikeys', + 'label' => $lng['menue']['main']['apikeys'], + 'show_element' => (Settings::Get('api.enabled') == true) + ), array( 'url' => 'customer_index.php?page=apihelp', 'label' => $lng['menue']['main']['apihelp'], @@ -184,6 +189,11 @@ return array( 'label' => $lng['menue']['main']['changetheme'], 'show_element' => (Settings::Get('panel.allow_theme_change_admin') == true) ), + array( + 'url' => 'admin_index.php?page=apikeys', + 'label' => $lng['menue']['main']['apikeys'], + 'show_element' => (Settings::Get('api.enabled') == true) + ), array( 'url' => 'admin_index.php?page=apihelp', 'label' => $lng['menue']['main']['apihelp'], diff --git a/lng/english.lng.php b/lng/english.lng.php index 264ba8c3..e0c4e59f 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2123,3 +2123,6 @@ $lng['admin']['notryfiles']['description'] = 'Say yes here if you want to specif // added in froxlor 0.10.0 $lng['panel']['none_value'] = 'None'; $lng['menue']['main']['apihelp'] = 'API help'; +$lng['menue']['main']['apikeys'] = 'API keys'; +$lng['apikeys']['no_api_keys'] = 'No API keys found'; +$lng['apikeys']['key_add'] = 'Add new key'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 06fac9f6..b6507e87 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1773,3 +1773,6 @@ $lng['admin']['notryfiles']['description'] = 'Wähle "Ja", wenn eine eigene try_ // added in froxlor 0.10.0 $lng['panel']['none_value'] = 'Keine'; $lng['menue']['main']['apihelp'] = 'API Hilfe'; +$lng['menue']['main']['apikeys'] = 'API Keys'; +$lng['apikeys']['no_api_keys'] = 'Keine API Keys gefunden'; +$lng['apikeys']['key_add'] = 'API Key hinzufügen'; diff --git a/templates/Sparkle/api_keys/keys_error.tpl b/templates/Sparkle/api_keys/keys_error.tpl new file mode 100644 index 00000000..e573c664 --- /dev/null +++ b/templates/Sparkle/api_keys/keys_error.tpl @@ -0,0 +1,3 @@ + + {$message} + diff --git a/templates/Sparkle/api_keys/keys_key.tpl b/templates/Sparkle/api_keys/keys_key.tpl new file mode 100644 index 00000000..12eb43a2 --- /dev/null +++ b/templates/Sparkle/api_keys/keys_key.tpl @@ -0,0 +1,27 @@ +class="primary-entry"> + + {$adminCustomerLink} + + + {$row['apikey']} + + + {$row['secret']} + + + {$row['allowed_from']} + + + + {$row['valid_until']} + + + + + {$lng['panel']['edit']} +   + + {$lng['panel']['delete']} + + + diff --git a/templates/Sparkle/api_keys/keys_list.tpl b/templates/Sparkle/api_keys/keys_list.tpl new file mode 100644 index 00000000..11671ad7 --- /dev/null +++ b/templates/Sparkle/api_keys/keys_list.tpl @@ -0,0 +1,68 @@ + $header +
+
+

+   + {$lng['menue']['main']['apikeys']} +

+
+ + +
+
{$lng['success']['success']}
+
+ $success_message +
+
+
+ +
+ +
+ + + +
+ {$searchcode} +
+ + + + + + + + + + + + + + + + + + + + + + + + + {$apikeys} + +
UserAPI-keysSecretAllowed fromValid until{$lng['panel']['options']}
{$pagingcode}
+
+ + + + +
+
+$footer diff --git a/templates/Sparkle/assets/css/main.css b/templates/Sparkle/assets/css/main.css index 5e61b727..85e0dfb0 100644 --- a/templates/Sparkle/assets/css/main.css +++ b/templates/Sparkle/assets/css/main.css @@ -1682,13 +1682,13 @@ fieldset.file { background-color: rgb(242, 222, 222); } -.domain-hostname { +.primary-entry { background-color: rgb(53, 106, 160); color: #ddd; font-weight: bold; } -table.hl tbody tr.domain-hostname:hover { +table.hl tbody tr.primary-entry:hover { background-color: rgb(64, 150, 238); } diff --git a/templates/Sparkle/header.tpl b/templates/Sparkle/header.tpl index d858f52c..ecc5f168 100644 --- a/templates/Sparkle/header.tpl +++ b/templates/Sparkle/header.tpl @@ -68,6 +68,7 @@
  • {$lng['panel']['theme']}
  • +
  • {$lng['menue']['main']['apikeys']}
  • {$lng['menue']['main']['apihelp']}