public
nobgit
read
NobMail
Based on mailcow: dockerized
Languages
Repository composition by tracked source files.
PHP
49%
JavaScript
35%
HTML
9%
CSS
4%
Shell
2%
Python
1%
Lua
0%
Perl
0%
Ruby
0%
SCSS
0%
Create file
Wiki Documentation
Clone
https://nobgit.com/orgs/nobgit/nobmail.git
ssh://[email protected]:2222/orgs/nobgit/nobmail.git
Commit
[Web] Allow CIDR as allowed API networks; other minor fixes
aef15f00
data/web/admin.php | 101 +++++-----
data/web/inc/functions.fail2ban.inc.php | 31 +--
data/web/inc/functions.inc.php | 338 ++++++++++++++++----------------
data/web/inc/prerequisites.inc.php | 3 +-
data/web/inc/sessions.inc.php | 2 +-
data/web/js/site/admin.js | 1 +
data/web/lang/lang.de.json | 3 +-
data/web/lang/lang.en.json | 3 +-
8 files changed, 239 insertions(+), 243 deletions(-)
Diff
diff --git a/data/web/admin.php b/data/web/admin.php
index 8c5c606f..96e4f189 100644
--- a/data/web/admin.php
+++ b/data/web/admin.php
@@ -113,77 +113,71 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
</form>
</div>
- <legend style="margin-top:20px">
- <span style="font-size:12px" class="arrow rotate glyphicon glyphicon-wrench"></span> API
+ <legend style="cursor:pointer;" data-target="#admin_api" class="arrow-toggle" unselectable="on" data-toggle="collapse">
+ <span style="font-size:12px" class="arrow rotate glyphicon glyphicon-menu-down"></span> API
</legend>
+ <div id="admin_api" class="collapse">
<?php
$api_ro = admin_api('ro', 'get');
$api_rw = admin_api('rw', 'get');
?>
- <div class="panel-group" id="accordion">
- <div class="panel panel-default">
- <div class="panel-heading">
- <h4 class="panel-title">
- <a data-toggle="collapse" data-parent="#accordion" href="#api-ro">
- ⇇ Read-Only Access</a>
- </h4>
- </div>
- <div id="api-ro" class="panel-collapse collapse">
- <div class="panel-body">
- <form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post">
- <div class="form-group">
- <label class="control-label col-sm-3" for="allow_from_ro"><?=$lang['admin']['api_allow_from'];?>:</label>
- <div class="col-sm-9">
- <textarea class="form-control" rows="2" name="allow_from" id="allow_from_ro" <?=($api_ro['skip_ip_check'] == 1) ? 'disabled' : null;?> required><?=htmlspecialchars($api_ro['allow_from']);?></textarea>
+ <div class="col-lg-6">
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <h4 class="panel-title">⇇ Read-Only Access</h4>
+ </div>
+ <div class="panel-body">
+ <form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post">
+ <div class="form-group">
+ <label class="control-label col-sm-3" for="allow_from_ro"><?=$lang['admin']['api_allow_from'];?>:</label>
+ <div class="col-sm-9">
+ <textarea class="form-control textarea-code" rows="7" name="allow_from" id="allow_from_ro" <?=($api_ro['skip_ip_check'] == 1) ? 'disabled' : null;?> required><?=htmlspecialchars($api_ro['allow_from']);?></textarea>
+ </div>
</div>
- </div>
- <div class="form-group">
- <div class="col-sm-offset-3 col-sm-9">
- <label>
- <input type="checkbox" name="skip_ip_check" id="skip_ip_check_ro" <?=($api_ro['skip_ip_check'] == 1) ? 'checked' : null;?>> <?=$lang['admin']['api_skip_ip_check'];?>
- </label>
+ <div class="form-group">
+ <div class="col-sm-offset-3 col-sm-9">
+ <label>
+ <input type="checkbox" name="skip_ip_check" id="skip_ip_check_ro" <?=($api_ro['skip_ip_check'] == 1) ? 'checked' : null;?>> <?=$lang['admin']['api_skip_ip_check'];?>
+ </label>
+ </div>
</div>
- </div>
- <div class="form-group">
- <label class="control-label col-sm-3"><?=$lang['admin']['api_key'];?>:</label>
- <div class="col-sm-9">
- <pre><?=(empty(htmlspecialchars($api_ro['api_key']))) ? '-' : htmlspecialchars($api_ro['api_key']);?></pre>
+ <div class="form-group">
+ <label class="control-label col-sm-3"><?=$lang['admin']['api_key'];?>:</label>
+ <div class="col-sm-9">
+ <pre><?=(empty(htmlspecialchars($api_ro['api_key']))) ? '-' : htmlspecialchars($api_ro['api_key']);?></pre>
+ </div>
</div>
- </div>
- <div class="form-group">
- <div class="col-sm-offset-3 col-sm-9">
- <label>
- <input type="checkbox" name="active" <?=($api_ro['active'] == 1) ? 'checked' : null;?>> <?=$lang['admin']['activate_api'];?>
- </label>
+ <div class="form-group">
+ <div class="col-sm-offset-3 col-sm-9">
+ <label>
+ <input type="checkbox" name="active" <?=($api_ro['active'] == 1) ? 'checked' : null;?>> <?=$lang['admin']['activate_api'];?>
+ </label>
+ </div>
</div>
- </div>
- <div class="form-group">
- <div class="col-sm-offset-3 col-sm-9">
- <p class="help-block"><?=$lang['admin']['api_info'];?></p>
- <div class="btn-group">
- <button class="btn btn-sm btn-default" name="admin_api[ro]" type="submit" href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
- <button class="btn btn-sm btn-primary" name="admin_api_regen_key[ro]" type="submit" href="#"><?=$lang['admin']['regen_api_key'];?></button>
+ <div class="form-group">
+ <div class="col-sm-offset-3 col-sm-9">
+ <p class="help-block"><?=$lang['admin']['api_info'];?></p>
+ <div class="btn-group">
+ <button class="btn btn-sm btn-success" name="admin_api[ro]" type="submit" href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
+ <button class="btn btn-sm btn-default admin-ays-dialog" name="admin_api_regen_key[ro]" type="submit" href="#"><?=$lang['admin']['regen_api_key'];?></button>
+ </div>
</div>
</div>
- </div>
- </form>
- </div>
+ </form>
+ </div>
</div>
</div>
+ <div class="col-lg-6">
<div class="panel panel-default">
<div class="panel-heading">
- <h4 class="panel-title">
- <a data-toggle="collapse" data-parent="#accordion" href="#api-rw">
- ⇄ Read-Write Access</a>
- </h4>
+ <h4 class="panel-title">⇄ Read-Write Access</h4>
</div>
- <div id="api-rw" class="panel-collapse collapse">
<div class="panel-body">
<form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post">
<div class="form-group">
<label class="control-label col-sm-3" for="allow_from_rw"><?=$lang['admin']['api_allow_from'];?>:</label>
<div class="col-sm-9">
- <textarea class="form-control" rows="2" name="allow_from" id="allow_from_rw" <?=($api_rw['skip_ip_check'] == 1) ? 'disabled' : null;?> required><?=htmlspecialchars($api_rw['allow_from']);?></textarea>
+ <textarea class="form-control textarea-code" rows="7" name="allow_from" id="allow_from_rw" <?=($api_rw['skip_ip_check'] == 1) ? 'disabled' : null;?> required><?=htmlspecialchars($api_rw['allow_from']);?></textarea>
</div>
</div>
<div class="form-group">
@@ -210,17 +204,16 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
<div class="col-sm-offset-3 col-sm-9">
<p class="help-block"><?=$lang['admin']['api_info'];?></p>
<div class="btn-group">
- <button class="btn btn-sm btn-default" name="admin_api[rw]" type="submit" href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
- <button class="btn btn-sm btn-primary" name="admin_api_regen_key[rw]" type="submit" href="#"><?=$lang['admin']['regen_api_key'];?></button>
+ <button class="btn btn-sm btn-success" name="admin_api[rw]" type="submit" href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
+ <button class="btn btn-sm btn-default admin-ays-dialog" name="admin_api_regen_key[rw]" type="submit" href="#"><?=$lang['admin']['regen_api_key'];?></button>
</div>
</div>
</div>
</form>
</div>
- </div>
+ </div>
</div>
</div>
-
</div>
</div>
diff --git a/data/web/inc/functions.fail2ban.inc.php b/data/web/inc/functions.fail2ban.inc.php
index 6ec23d34..d607cc2a 100644
--- a/data/web/inc/functions.fail2ban.inc.php
+++ b/data/web/inc/functions.fail2ban.inc.php
@@ -1,19 +1,4 @@
<?php
-function valid_network($network) {
- $cidr = explode('/', $network);
- if (filter_var($cidr[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && (!isset($cidr[1]) || ($cidr[1] >= 0 && $cidr[1] <= 32))) {
- return true;
- }
- elseif (filter_var($cidr[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && (!isset($cidr[1]) || ($cidr[1] >= 0 && $cidr[1] <= 128))) {
- return true;
- }
- return false;
-}
-
-function valid_hostname($hostname) {
- return filter_var($hostname, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME);
-}
-
function fail2ban($_action, $_data = null) {
global $redis;
global $lang;
@@ -196,6 +181,14 @@ function fail2ban($_action, $_data = null) {
if (valid_network($wl_item) || valid_hostname($wl_item)) {
$redis->hSet('F2B_WHITELIST', $wl_item, 1);
}
+ else {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('network_host_invalid', $wl_item)
+ );
+ continue;
+ }
}
}
}
@@ -206,6 +199,14 @@ function fail2ban($_action, $_data = null) {
if (valid_network($bl_item) || valid_hostname($bl_item)) {
$redis->hSet('F2B_BLACKLIST', $bl_item, 1);
}
+ else {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('network_host_invalid', $bl_item)
+ );
+ continue;
+ }
}
}
}
diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php
index 9f74f89f..6cc052fb 100644
--- a/data/web/inc/functions.inc.php
+++ b/data/web/inc/functions.inc.php
@@ -10,6 +10,76 @@ function isset_has_content($var) {
return false;
}
}
+// Validates ips and cidrs
+function valid_network($network) {
+ if (filter_var($network, FILTER_VALIDATE_IP)) {
+ return true;
+ }
+ $parts = explode('/', $network);
+ if (count($parts) != 2) {
+ return false;
+ }
+ $ip = $parts[0];
+ $netmask = $parts[1];
+ if (!preg_match("/^\d+$/", $netmask)){
+ return false;
+ }
+ $netmask = intval($parts[1]);
+ if ($netmask < 0) {
+ return false;
+ }
+ if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
+ return $netmask <= 32;
+ }
+ if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
+ return $netmask <= 128;
+ }
+ return false;
+}
+function valid_hostname($hostname) {
+ return filter_var($hostname, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME);
+}
+// Thanks to https://stackoverflow.com/a/49373789
+// Validates exact ip matches and ip-in-cidr, ipv4 and ipv6
+function ip_acl($ip, $networks) {
+ foreach($networks as $network) {
+ if (filter_var($network, FILTER_VALIDATE_IP)) {
+ if ($ip == $network) {
+ return true;
+ }
+ else {
+ continue;
+ }
+ }
+ $ipb = inet_pton($ip);
+ $iplen = strlen($ipb);
+ if (strlen($ipb) < 4) {
+ continue;
+ }
+ $ar = explode('/', $network);
+ $ip1 = $ar[0];
+ $ip1b = inet_pton($ip1);
+ $ip1len = strlen($ip1b);
+ if ($ip1len != $iplen) {
+ continue;
+ }
+ if (count($ar)>1) {
+ $bits=(int)($ar[1]);
+ }
+ else {
+ $bits = $iplen * 8;
+ }
+ for ($c=0; $bits>0; $c++) {
+ $bytemask = ($bits < 8) ? 0xff ^ ((1 << (8-$bits))-1) : 0xff;
+ if (((ord($ipb[$c]) ^ ord($ip1b[$c])) & $bytemask) != 0) {
+ continue 2;
+ }
+ $bits-=8;
+ }
+ return true;
+ }
+ return false;
+}
function hash_password($password) {
$salt_str = bin2hex(openssl_random_pseudo_bytes(8));
return "{SSHA256}".base64_encode(hash('sha256', $password . $salt_str, true) . $salt_str);
@@ -1160,178 +1230,106 @@ function admin_api($access, $action, $data = null) {
);
return false;
}
- switch ($access) {
- case "rw":
- switch ($action) {
- case "edit":
- $active = (isset($data['active'])) ? 1 : 0;
- $skip_ip_check = (isset($data['skip_ip_check'])) ? 1 : 0;
- $allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $data['allow_from']));
- foreach ($allow_from as $key => $val) {
- if (empty($val)) {
- continue;
- }
- if (!filter_var($val, FILTER_VALIDATE_IP)) {
- $_SESSION['return'][] = array(
- 'type' => 'warning',
- 'log' => array(__FUNCTION__, $data),
- 'msg' => array('ip_invalid', htmlspecialchars($allow_from[$key]))
- );
- unset($allow_from[$key]);
- continue;
- }
- }
- $allow_from = implode(',', array_unique(array_filter($allow_from)));
- if (empty($allow_from) && $skip_ip_check == 0) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $data),
- 'msg' => 'ip_list_empty'
- );
- return false;
- }
- $api_key = implode('-', array(
- strtoupper(bin2hex(random_bytes(3))),
- strtoupper(bin2hex(random_bytes(3))),
- strtoupper(bin2hex(random_bytes(3))),
- strtoupper(bin2hex(random_bytes(3))),
- strtoupper(bin2hex(random_bytes(3)))
- ));
- $stmt = $pdo->query("SELECT `api_key` FROM `api` WHERE `access` = 'rw'");
- $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
- if (empty($num_results)) {
- $stmt = $pdo->prepare("INSERT INTO `api` (`api_key`, `skip_ip_check`, `active`, `allow_from`, `access`)
- VALUES (:api_key, :skip_ip_check, :active, :allow_from, 'rw');");
- $stmt->execute(array(
- ':api_key' => $api_key,
- ':skip_ip_check' => $skip_ip_check,
- ':active' => $active,
- ':allow_from' => $allow_from
- ));
- }
- else {
- if ($skip_ip_check == 0) {
- $stmt = $pdo->prepare("UPDATE `api` SET `skip_ip_check` = :skip_ip_check, `active` = :active, `allow_from` = :allow_from WHERE `access` = 'rw';");
- $stmt->execute(array(
- ':active' => $active,
- ':skip_ip_check' => $skip_ip_check,
- ':allow_from' => $allow_from
- ));
- }
- else {
- $stmt = $pdo->prepare("UPDATE `api` SET `skip_ip_check` = :skip_ip_check, `active` = :active WHERE `access` = 'rw';");
- $stmt->execute(array(
- ':active' => $active,
- ':skip_ip_check' => $skip_ip_check
- ));
- }
- }
- break;
- case "regen_key":
- $api_key = implode('-', array(
- strtoupper(bin2hex(random_bytes(3))),
- strtoupper(bin2hex(random_bytes(3))),
- strtoupper(bin2hex(random_bytes(3))),
- strtoupper(bin2hex(random_bytes(3))),
- strtoupper(bin2hex(random_bytes(3)))
- ));
- $stmt = $pdo->prepare("UPDATE `api` SET `api_key` = :api_key WHERE `access` = 'rw'");
- $stmt->execute(array(
- ':api_key' => $api_key
- ));
- break;
- case "get":
- $stmt = $pdo->query("SELECT * FROM `api` WHERE `access` = 'rw'");
- $apidata = $stmt->fetch(PDO::FETCH_ASSOC);
- return $apidata;
- break;
+ if ($access !== "ro" && $access !== "rw") {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__),
+ 'msg' => 'invalid access type'
+ );
+ return false;
+ }
+ if ($action == "edit") {
+ $active = (!empty($data['active'])) ? 1 : 0;
+ $skip_ip_check = (isset($data['skip_ip_check'])) ? 1 : 0;
+ $allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $data['allow_from']));
+ foreach ($allow_from as $key => $val) {
+ if (empty($val)) {
+ unset($allow_from[$key]);
+ continue;
}
- case "ro":
- switch ($action) {
- case "edit":
- $active = (isset($data['active'])) ? 1 : 0;
- $skip_ip_check = (isset($data['skip_ip_check'])) ? 1 : 0;
- $allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $data['allow_from']));
- foreach ($allow_from as $key => $val) {
- if (empty($val)) {
- continue;
- }
- if (!filter_var($val, FILTER_VALIDATE_IP)) {
- $_SESSION['return'][] = array(
- 'type' => 'warning',
- 'log' => array(__FUNCTION__, $data),
- 'msg' => array('ip_invalid', htmlspecialchars($allow_from[$key]))
- );
- unset($allow_from[$key]);
- continue;
- }
- }
- $allow_from = implode(',', array_unique(array_filter($allow_from)));
- if (empty($allow_from) && $skip_ip_check == 0) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $data),
- 'msg' => 'ip_list_empty'
- );
- return false;
- }
- $api_key = implode('-', array(
- strtoupper(bin2hex(random_bytes(3))),
- strtoupper(bin2hex(random_bytes(3))),
- strtoupper(bin2hex(random_bytes(3))),
- strtoupper(bin2hex(random_bytes(3))),
- strtoupper(bin2hex(random_bytes(3)))
- ));
- $stmt = $pdo->query("SELECT `api_key` FROM `api` WHERE `access` = 'ro'");
- $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
- if (empty($num_results)) {
- $stmt = $pdo->prepare("INSERT INTO `api` (`api_key`, `skip_ip_check`, `active`, `allow_from`, `access`)
- VALUES (:api_key, :skip_ip_check, :active, :allow_from, 'ro');");
- $stmt->execute(array(
- ':api_key' => $api_key,
- ':skip_ip_check' => $skip_ip_check,
- ':active' => $active,
- ':allow_from' => $allow_from
- ));
- }
- else {
- if ($skip_ip_check == 0) {
- $stmt = $pdo->prepare("UPDATE `api` SET `skip_ip_check` = :skip_ip_check, `active` = :active, `allow_from` = :allow_from WHERE `access` = 'ro';");
- $stmt->execute(array(
- ':active' => $active,
- ':skip_ip_check' => $skip_ip_check,
- ':allow_from' => $allow_from
- ));
- }
- else {
- $stmt = $pdo->prepare("UPDATE `api` SET `skip_ip_check` = :skip_ip_check, `active` = :active WHERE `access` = 'ro';");
- $stmt->execute(array(
- ':active' => $active,
- ':skip_ip_check' => $skip_ip_check
- ));
- }
- }
- break;
- case "regen_key":
- $api_key = implode('-', array(
- strtoupper(bin2hex(random_bytes(3))),
- strtoupper(bin2hex(random_bytes(3))),
- strtoupper(bin2hex(random_bytes(3))),
- strtoupper(bin2hex(random_bytes(3))),
- strtoupper(bin2hex(random_bytes(3)))
- ));
- $stmt = $pdo->prepare("UPDATE `api` SET `api_key` = :api_key WHERE `access` = 'ro'");
- $stmt->execute(array(
- ':api_key' => $api_key
- ));
- break;
- case "get":
- $stmt = $pdo->query("SELECT * FROM `api` WHERE `access` = 'ro'");
- $apidata = $stmt->fetch(PDO::FETCH_ASSOC);
- return $apidata;
- break;
+ if (valid_network($val) !== true) {
+ $_SESSION['return'][] = array(
+ 'type' => 'warning',
+ 'log' => array(__FUNCTION__, $data),
+ 'msg' => array('ip_invalid', htmlspecialchars($allow_from[$key]))
+ );
+ unset($allow_from[$key]);
+ continue;
}
- break;
+ }
+ $allow_from = implode(',', array_unique(array_filter($allow_from)));
+ if (empty($allow_from) && $skip_ip_check == 0) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $data),
+ 'msg' => 'ip_list_empty'
+ );
+ return false;
+ }
+ $api_key = implode('-', array(
+ strtoupper(bin2hex(random_bytes(3))),
+ strtoupper(bin2hex(random_bytes(3))),
+ strtoupper(bin2hex(random_bytes(3))),
+ strtoupper(bin2hex(random_bytes(3))),
+ strtoupper(bin2hex(random_bytes(3)))
+ ));
+ $stmt = $pdo->query("SELECT `api_key` FROM `api` WHERE `access` = '" . $access . "'");
+ $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+ if (empty($num_results)) {
+ $stmt = $pdo->prepare("INSERT INTO `api` (`api_key`, `skip_ip_check`, `active`, `allow_from`, `access`)
+ VALUES (:api_key, :skip_ip_check, :active, :allow_from, :access);");
+ $stmt->execute(array(
+ ':api_key' => $api_key,
+ ':skip_ip_check' => $skip_ip_check,
+ ':active' => $active,
+ ':allow_from' => $allow_from,
+ ':access' => $access
+ ));
+ }
+ else {
+ if ($skip_ip_check == 0) {
+ $stmt = $pdo->prepare("UPDATE `api` SET `skip_ip_check` = :skip_ip_check,
+ `active` = :active,
+ `allow_from` = :allow_from
+ WHERE `access` = :access;");
+ $stmt->execute(array(
+ ':active' => $active,
+ ':skip_ip_check' => $skip_ip_check,
+ ':allow_from' => $allow_from,
+ ':access' => $access
+ ));
+ }
+ else {
+ $stmt = $pdo->prepare("UPDATE `api` SET `skip_ip_check` = :skip_ip_check,
+ `active` = :active
+ WHERE `access` = :access;");
+ $stmt->execute(array(
+ ':active' => $active,
+ ':skip_ip_check' => $skip_ip_check,
+ ':access' => $access
+ ));
+ }
+ }
+ }
+ elseif ($action == "regen_key") {
+ $api_key = implode('-', array(
+ strtoupper(bin2hex(random_bytes(3))),
+ strtoupper(bin2hex(random_bytes(3))),
+ strtoupper(bin2hex(random_bytes(3))),
+ strtoupper(bin2hex(random_bytes(3))),
+ strtoupper(bin2hex(random_bytes(3)))
+ ));
+ $stmt = $pdo->prepare("UPDATE `api` SET `api_key` = :api_key WHERE `access` = :access");
+ $stmt->execute(array(
+ ':api_key' => $api_key,
+ ':access' => $access
+ ));
+ }
+ elseif ($action == "get") {
+ $stmt = $pdo->query("SELECT * FROM `api` WHERE `access` = '" . $access . "'");
+ $apidata = $stmt->fetch(PDO::FETCH_ASSOC);
+ $apidata['allow_from'] = str_replace(',', PHP_EOL, $apidata['allow_from']);
+ return $apidata;
}
$_SESSION['return'][] = array(
'type' => 'success',
diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php
index 0601cf8e..b4d5b622 100644
--- a/data/web/inc/prerequisites.inc.php
+++ b/data/web/inc/prerequisites.inc.php
@@ -179,6 +179,8 @@ function get_remote_ip($anonymize = null) {
}
}
+// Load core functions first
+require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/sessions.inc.php';
// IMAP lib
@@ -215,7 +217,6 @@ if(file_exists($langFile)) {
$lang = array_merge_real($lang, json_decode(file_get_contents($langFile), true));
}
-require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.acl.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.app_passwd.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.mailbox.inc.php';
diff --git a/data/web/inc/sessions.inc.php b/data/web/inc/sessions.inc.php
index d543458d..5c7ec710 100644
--- a/data/web/inc/sessions.inc.php
+++ b/data/web/inc/sessions.inc.php
@@ -53,7 +53,7 @@ if (!empty($_SERVER['HTTP_X_API_KEY'])) {
$skip_ip_check = ($api_return['skip_ip_check'] == 1);
$remote = get_remote_ip(false);
$allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $api_return['allow_from']));
- if (in_array($remote, $allow_from) || $skip_ip_check === true) {
+ if ($skip_ip_check === true || ip_acl($remote, $allow_from)) {
$_SESSION['mailcow_cc_username'] = 'API';
$_SESSION['mailcow_cc_role'] = 'admin';
$_SESSION['mailcow_cc_api'] = true;
diff --git a/data/web/js/site/admin.js b/data/web/js/site/admin.js
index 43726b1d..c30dfcea 100644
--- a/data/web/js/site/admin.js
+++ b/data/web/js/site/admin.js
@@ -21,6 +21,7 @@ jQuery(function($){
$("#mass_exclude").change(function(){ $("#mass_include").selectpicker('deselectAll'); });
$("#mass_include").change(function(){ $("#mass_exclude").selectpicker('deselectAll'); });
$("#mass_disarm").click(function() { $("#mass_send").attr("disabled", !this.checked); });
+ $(".admin-ays-dialog").click(function() { return confirm(lang.ays); });
$(".validate_rspamd_regex").click(function( event ) {
event.preventDefault();
var regex_map_id = $(this).data('regex-map');
diff --git a/data/web/lang/lang.de.json b/data/web/lang/lang.de.json
index 2b859399..925c3b4c 100644
--- a/data/web/lang/lang.de.json
+++ b/data/web/lang/lang.de.json
@@ -122,7 +122,7 @@
"admin_details": "Administrator bearbeiten",
"admin_domains": "Domain-Zuweisungen",
"advanced_settings": "Erweiterte Einstellungen",
- "api_allow_from": "IP-Adressen für Zugriff",
+ "api_allow_from": "IP-Adressen oder Netzwerke (CIDR Notation) für Zugriff auf API",
"api_info": "Das API befindet sich noch in Entwicklung, die Dokumentation kann unter <a href=\"/api\">/api</a> abgerufen werden.",
"api_key": "API-Key",
"api_skip_ip_check": "IP-Check für API nicht ausführen",
@@ -131,6 +131,7 @@
"apps_name": "\"mailcow Apps\" Name",
"arrival_time": "Ankunftszeit (Serverzeit)",
"authed_user": "Auth. Benutzer",
+ "ays": "Soll der Vorgang wirklich ausgeführt werden?",
"ban_list_info": "Übersicht ausgesperrter Netzwerke: <b>Netzwerk (verbleibende Banzeit) - [Aktionen]</b>.<br />IPs, die zum Entsperren eingereiht werden, verlassen die Liste aktiver Bans nach wenigen Sekunden.<br />Rote Labels sind Indikatoren für aktive Blacklisteinträge.",
"change_logo": "Logo ändern",
"configuration": "Konfiguration",
diff --git a/data/web/lang/lang.en.json b/data/web/lang/lang.en.json
index 41223ad7..7399f8e5 100644
--- a/data/web/lang/lang.en.json
+++ b/data/web/lang/lang.en.json
@@ -122,7 +122,7 @@
"admin_details": "Edit administrator details",
"admin_domains": "Domain assignments",
"advanced_settings": "Advanced settings",
- "api_allow_from": "Allow API access from these IPs (separated by comma or new line)",
+ "api_allow_from": "Allow API access from these IPs/CIDR network notations",
"api_info": "The API is a work in progress. The documentation can be found at <a href=\"/api\">/api</a>",
"api_key": "API key",
"api_skip_ip_check": "Skip IP check for API",
@@ -131,6 +131,7 @@
"apps_name": "\"mailcow Apps\" name",
"arrival_time": "Arrival time (server time)",
"authed_user": "Auth. user",
+ "ays": "Are you sure you want to proceed?",
"ban_list_info": "See a list of banned IPs below: <b>network (remaining ban time) - [actions]</b>.<br />IPs queued to be unbanned will be removed from the active ban list within a few seconds.<br />Red labels indicate active permanent bans by blacklisting.",
"change_logo": "Change logo",
"configuration": "Configuration",