Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c010673705 | ||
|
|
b16ff4c613 | ||
|
|
e9c791c164 | ||
|
|
c7ec1300b7 | ||
|
|
064710324e | ||
|
|
e0356dcadf | ||
|
|
62a5992e3a | ||
|
|
6bb957fcb3 | ||
|
|
0c1f060759 | ||
|
|
aad46ec80a | ||
|
|
f5e9f0610d | ||
|
|
14a5250cd7 | ||
|
|
6e6edef57d | ||
|
|
d3810db965 | ||
|
|
187a012284 | ||
|
|
b3d2662fd2 | ||
|
|
1189aecae9 | ||
|
|
024d6d2d7a | ||
|
|
f2d5377347 | ||
|
|
af11bd8199 | ||
|
|
0e6a43a2c2 | ||
|
|
94eeaaaa51 | ||
|
|
4a246bec5f | ||
|
|
46b2b60718 | ||
|
|
9616d956cb | ||
|
|
df401b9add | ||
|
|
4b9e393971 |
@@ -1 +1 @@
|
||||
8.0.5
|
||||
8.4.0
|
||||
|
||||
@@ -20,7 +20,11 @@ fi;
|
||||
# read in the .env.deploy file and we must have
|
||||
# GITLAB_USER
|
||||
# GITLAB_TOKEN
|
||||
# GITLAB_URL
|
||||
# GITEA_USER
|
||||
# GITEA_DEPLOY_TOKEN
|
||||
# GITEA_URL_DL
|
||||
# GITEA_URL_PUSH
|
||||
if [ ! -f "${BASE_FOLDER}.env.deploy" ]; then
|
||||
echo "Deploy enviroment file .env.deploy is missing";
|
||||
exit;
|
||||
@@ -31,30 +35,34 @@ source .env.deploy;
|
||||
cd -;
|
||||
set +o allexport;
|
||||
|
||||
echo "[START]";
|
||||
# gitea
|
||||
if [ ! -z "${GITEA_USER}" ] && [ ! -z "${GITEA_TOKEN}" ]; then
|
||||
if [ ! -z "${GITEA_URL_DL}" ] && [ ! -z "${GITEA_URL_PUSH}" ] &&
|
||||
[ ! -z "${GITEA_USER}" ] && [ ! -z "${GITEA_TOKEN}" ]; then
|
||||
curl -LJO \
|
||||
--output-dir "${BASE_FOLDER}" \
|
||||
https://git.egplusww.jp/Composer/CoreLibs-Composer-All/archive/v${VERSION}.zip;
|
||||
${GITEA_URL_DL}/v${VERSION}.zip;
|
||||
curl --user ${GITEA_USER}:${GITEA_TOKEN} \
|
||||
--upload-file "${BASE_FOLDER}/CoreLibs-Composer-All-v${VERSION}.zip" \
|
||||
https://git.egplusww.jp/api/packages/Composer/composer?version=${VERSION};
|
||||
${GITEA_URL_PUSH}?version=${VERSION};
|
||||
echo "${VERSION}" > "${file_last_published}";
|
||||
else
|
||||
echo "Missing either GITEA_USER or GITEA_TOKEN environment variable";
|
||||
fi;
|
||||
|
||||
# gitlab
|
||||
if [ ! -z "${GITLAB_DEPLOY_TOKEN}" ]; then
|
||||
if [ ! -z "${GITLAB_URL}" ] && [ ! -z "${GITLAB_DEPLOY_TOKEN}" ]; then
|
||||
curl --data tag=v${VERSION} \
|
||||
--header "Deploy-Token: ${GITLAB_DEPLOY_TOKEN}" \
|
||||
"https://gitlab-na.factory.tools/api/v4/projects/950/packages/composer";
|
||||
"${GITLAB_URL}";
|
||||
curl --data branch=master \
|
||||
--header "Deploy-Token: ${GITLAB_DEPLOY_TOKEN}" \
|
||||
"https://gitlab-na.factory.tools/api/v4/projects/950/packages/composer";
|
||||
"${GITLAB_URL}";
|
||||
echo "${VERSION}" > "${file_last_published}";
|
||||
else
|
||||
echo "Missing GITLAB_DEPLOY_TOKEN environment variable";
|
||||
fi;
|
||||
echo "";
|
||||
echo "[DONE]";
|
||||
|
||||
# __END__
|
||||
|
||||
@@ -68,7 +68,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\ACL;
|
||||
|
||||
use CoreLibs\Check\Password;
|
||||
use CoreLibs\Security\Password;
|
||||
use CoreLibs\Convert\Json;
|
||||
|
||||
class Login
|
||||
{
|
||||
@@ -428,7 +429,7 @@ class Login
|
||||
|
||||
/**
|
||||
* Set options
|
||||
* Current allowed
|
||||
* Current allowed:
|
||||
* target <string>: site target
|
||||
* debug <bool>
|
||||
* auto_login <bool>: self start login process
|
||||
@@ -753,7 +754,10 @@ class Login
|
||||
// we have to get the themes in here too
|
||||
$q = "SELECT eu.edit_user_id, eu.username, eu.password, "
|
||||
. "eu.edit_group_id, "
|
||||
. "eg.name AS edit_group_name, admin, "
|
||||
. "eg.name AS edit_group_name, eu.admin, "
|
||||
// additinal acl lists
|
||||
. "eu.additional_acl AS user_additional_acl, "
|
||||
. "eg.additional_acl AS group_additional_acl, "
|
||||
// login error + locked
|
||||
. "eu.login_error_count, eu.login_error_date_last, "
|
||||
. "eu.login_error_date_first, eu.strict, eu.locked, "
|
||||
@@ -901,8 +905,10 @@ class Login
|
||||
$_SESSION['GROUP_NAME'] = $res['edit_group_name'];
|
||||
$_SESSION['USER_ACL_LEVEL'] = $res['user_level'];
|
||||
$_SESSION['USER_ACL_TYPE'] = $res['user_type'];
|
||||
$_SESSION['USER_ADDITIONAL_ACL'] = Json::jsonConvertToArray($res['user_additional_acl']);
|
||||
$_SESSION['GROUP_ACL_LEVEL'] = $res['group_level'];
|
||||
$_SESSION['GROUP_ACL_TYPE'] = $res['group_type'];
|
||||
$_SESSION['GROUP_ADDITIONAL_ACL'] = Json::jsonConvertToArray($res['group_additional_acl']);
|
||||
// deprecated TEMPLATE setting
|
||||
$_SESSION['TEMPLATE'] = $res['template'] ? $res['template'] : '';
|
||||
$_SESSION['HEADER_COLOR'] = !empty($res['second_header_color']) ?
|
||||
@@ -1021,7 +1027,8 @@ class Login
|
||||
$_SESSION['PAGES'] = $pages;
|
||||
$_SESSION['PAGES_ACL_LEVEL'] = $pages_acl;
|
||||
// load the edit_access user rights
|
||||
$q = "SELECT ea.edit_access_id, level, type, ea.name, ea.color, ea.uid, edit_default "
|
||||
$q = "SELECT ea.edit_access_id, level, type, ea.name, "
|
||||
. "ea.color, ea.uid, edit_default, ea.additional_acl "
|
||||
. "FROM edit_access_user eau, edit_access_right ear, edit_access ea "
|
||||
. "WHERE eau.edit_access_id = ea.edit_access_id "
|
||||
. "AND eau.edit_access_right_id = ear.edit_access_right_id "
|
||||
@@ -1048,6 +1055,7 @@ class Login
|
||||
'uid' => $res['uid'],
|
||||
'color' => $res['color'],
|
||||
'default' => $res['edit_default'],
|
||||
'additional_acl' => Json::jsonConvertToArray($res['additional_acl']),
|
||||
'data' => $ea_data
|
||||
];
|
||||
// set the default unit
|
||||
@@ -1122,6 +1130,11 @@ class Login
|
||||
// username (login), group name
|
||||
$this->acl['user_name'] = $_SESSION['USER_NAME'];
|
||||
$this->acl['group_name'] = $_SESSION['GROUP_NAME'];
|
||||
// set additional acl
|
||||
$this->acl['additional_acl'] = [
|
||||
'user' => $_SESSION['USER_ADDITIONAL_ACL'],
|
||||
'group' => $_SESSION['GROUP_ADDITIONAL_ACL'],
|
||||
];
|
||||
// we start with the default acl
|
||||
$this->acl['base'] = $this->default_acl_level;
|
||||
|
||||
@@ -1184,7 +1197,8 @@ class Login
|
||||
'uid' => $unit['uid'],
|
||||
'level' => $this->default_acl_list[$this->acl['unit'][$ea_id]]['name'] ?? -1,
|
||||
'default' => $unit['default'],
|
||||
'data' => $unit['data']
|
||||
'data' => $unit['data'],
|
||||
'additional_acl' => $unit['additional_acl']
|
||||
];
|
||||
// set default
|
||||
if (!empty($unit['default'])) {
|
||||
@@ -1594,7 +1608,7 @@ class Login
|
||||
// TODO: submit or JS to set target page as ajax call
|
||||
// NOTE: for the HTML block I ignore line lengths
|
||||
// phpcs:disable
|
||||
$this->login_template['password_change'] = <<<EOM
|
||||
$this->login_template['password_change'] = <<<HTML
|
||||
<div id="pw_change_div" class="hidden" style="position: absolute; top: 30px; left: 50px; width: 400px; height: 220px; background-color: white; border: 1px solid black; padding: 25px;">
|
||||
<table>
|
||||
<tr><td class="norm" align="center" colspan="2"><h3>{TITLE_PASSWORD_CHANGE}</h3></td></tr>
|
||||
@@ -1612,7 +1626,7 @@ class Login
|
||||
</table>
|
||||
</div>
|
||||
{PASSWORD_CHANGE_SHOW}
|
||||
EOM;
|
||||
HTML;
|
||||
// phpcs:enable
|
||||
}
|
||||
if ($this->password_forgot) {
|
||||
@@ -1636,7 +1650,7 @@ EOM;
|
||||
// now check templates
|
||||
// TODO: submit or JS to set target page as ajax call
|
||||
if (!$this->login_template['template']) {
|
||||
$this->login_template['template'] = <<<EOM
|
||||
$this->login_template['template'] = <<<HTML
|
||||
<!DOCTYPE html>
|
||||
<html lang="{LANGUAGE}">
|
||||
<head>
|
||||
@@ -1698,7 +1712,7 @@ h3 { font-size: 18px; }
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
EOM;
|
||||
HTML;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1164,7 +1164,7 @@ class Basic
|
||||
public function passwordSet(string $password): string
|
||||
{
|
||||
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Check\Password::passwordSet()', E_USER_DEPRECATED);
|
||||
return \CoreLibs\Check\Password::passwordSet($password);
|
||||
return \CoreLibs\Security\Password::passwordSet($password);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1177,7 +1177,7 @@ class Basic
|
||||
public function passwordVerify(string $password, string $hash): bool
|
||||
{
|
||||
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Check\Password::passwordVerify()', E_USER_DEPRECATED);
|
||||
return \CoreLibs\Check\Password::passwordVerify($password, $hash);
|
||||
return \CoreLibs\Security\Password::passwordVerify($password, $hash);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1189,7 +1189,7 @@ class Basic
|
||||
public function passwordRehashCheck(string $hash): bool
|
||||
{
|
||||
trigger_error('Method ' . __METHOD__ . ' is deprecated, use \CoreLibs\Check\Password::passwordRehashCheck()', E_USER_DEPRECATED);
|
||||
return \CoreLibs\Check\Password::passwordRehashCheck($hash);
|
||||
return \CoreLibs\Security\Password::passwordRehashCheck($hash);
|
||||
}
|
||||
|
||||
// *** BETTER PASSWORD OPTIONS END ***
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* NOTE: this is deprecated and all moved \CoreLibs\Security\Password
|
||||
*
|
||||
* core password set, check and rehash check wrapper functions
|
||||
*/
|
||||
|
||||
@@ -8,6 +10,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\Check;
|
||||
|
||||
use CoreLibs\Security\Password as PasswordNew;
|
||||
|
||||
class Password
|
||||
{
|
||||
/**
|
||||
@@ -15,13 +19,16 @@ class Password
|
||||
*
|
||||
* @param string $password password
|
||||
* @return string hashed password
|
||||
* @deprecated v9.0 Moved to \CoreLibs\Security\Password::passwordSet
|
||||
*/
|
||||
public static function passwordSet(string $password): string
|
||||
{
|
||||
// always use the PHP default for the password
|
||||
// password options ca be set in the password init,
|
||||
// but should be kept as default
|
||||
return password_hash($password, PASSWORD_DEFAULT);
|
||||
trigger_error(
|
||||
'Method ' . __METHOD__ . ' is deprecated, use '
|
||||
. '\CoreLibs\Security\Password::passwordSet',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
return PasswordNew::passwordSet($password);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,14 +37,16 @@ class Password
|
||||
* @param string $password password
|
||||
* @param string $hash password hash
|
||||
* @return bool true or false
|
||||
* @deprecated v9.0 Moved to \CoreLibs\Security\Password::passwordVerify
|
||||
*/
|
||||
public static function passwordVerify(string $password, string $hash): bool
|
||||
{
|
||||
if (password_verify($password, $hash)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
trigger_error(
|
||||
'Method ' . __METHOD__ . ' is deprecated, use '
|
||||
. '\CoreLibs\Security\Password::passwordVerify',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
return PasswordNew::passwordVerify($password, $hash);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,14 +54,16 @@ class Password
|
||||
*
|
||||
* @param string $hash password hash
|
||||
* @return bool true or false
|
||||
* @deprecated v9.0 Moved to \CoreLibs\Security\Password::passwordRehashCheck
|
||||
*/
|
||||
public static function passwordRehashCheck(string $hash): bool
|
||||
{
|
||||
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
trigger_error(
|
||||
'Method ' . __METHOD__ . ' is deprecated, use '
|
||||
. '\CoreLibs\Security\Password::passwordRehashCheck',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
return PasswordNew::passwordRehashCheck($hash);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -177,6 +177,65 @@ class ArrayHandler
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* search for one or many keys in array and return matching values
|
||||
* If flat is set to true, return flat array with found values only
|
||||
* If prefix is turned on each found group will be prefixed with the
|
||||
* search key
|
||||
*
|
||||
* @param array<mixed> $array array to search in
|
||||
* @param array<mixed> $needles keys to find in array
|
||||
* @param bool $flat [false] Turn on flat output
|
||||
* @param bool $prefix [false] Prefix found with needle key
|
||||
* @return array<mixed> Found values
|
||||
*/
|
||||
public static function arraySearchKey(
|
||||
array $array,
|
||||
array $needles,
|
||||
bool $flat = false,
|
||||
bool $prefix = false
|
||||
): array {
|
||||
$iterator = new \RecursiveArrayIterator($array);
|
||||
$recursive = new \RecursiveIteratorIterator(
|
||||
$iterator,
|
||||
\RecursiveIteratorIterator::SELF_FIRST
|
||||
);
|
||||
$hit_list = [];
|
||||
if ($prefix === true) {
|
||||
$hit_list = array_fill_keys($needles, []);
|
||||
}
|
||||
$key_path = [];
|
||||
$prev_depth = 0;
|
||||
foreach ($recursive as $key => $value) {
|
||||
if ($prev_depth > $recursive->getDepth()) {
|
||||
// remove all trailing to ne depth
|
||||
$diff = $prev_depth - $recursive->getDepth();
|
||||
array_splice($key_path, -$diff, $diff);
|
||||
}
|
||||
$prev_depth = $recursive->getDepth();
|
||||
if ($flat === false) {
|
||||
$key_path[$recursive->getDepth()] = $key;
|
||||
}
|
||||
if (in_array($key, $needles, true)) {
|
||||
ksort($key_path);
|
||||
if ($flat === true) {
|
||||
$hit = $value;
|
||||
} else {
|
||||
$hit = [
|
||||
'value' => $value,
|
||||
'path' => $key_path
|
||||
];
|
||||
}
|
||||
if ($prefix === true) {
|
||||
$hit_list[$key][] = $hit;
|
||||
} else {
|
||||
$hit_list[] = $hit;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $hit_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* correctly recursive merges as an array as array_merge_recursive
|
||||
* just glues things together
|
||||
|
||||
629
src/DB/IO.php
629
src/DB/IO.php
File diff suppressed because it is too large
Load Diff
@@ -42,6 +42,15 @@ interface SqlFunctions
|
||||
*/
|
||||
public function __dbSendQuery(string $query): bool;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $query
|
||||
* @param array<mixed> $params
|
||||
* @return bool
|
||||
*/
|
||||
public function __dbSendQueryParams(string $query, array $params): bool;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -74,6 +83,24 @@ interface SqlFunctions
|
||||
*/
|
||||
public function __dbExecute(string $name, array $data): \PgSql\Result|false;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $query
|
||||
* @return bool
|
||||
*/
|
||||
public function __dbSendPrepare(string $name, string $query): bool;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $name
|
||||
* @param array<mixed> $params
|
||||
* @return bool
|
||||
*/
|
||||
public function __dbSendExecute(string $name, array $params): bool;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -99,6 +126,15 @@ interface SqlFunctions
|
||||
*/
|
||||
public function __dbFieldName(\PgSql\Result|false $cursor, int $i): string|false;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param \PgSql\Result|false $cursor
|
||||
* @param int $i
|
||||
* @return string|false
|
||||
*/
|
||||
public function __dbFieldType(\PgSql\Result|false $cursor, int $i): string|false;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -173,10 +209,17 @@ interface SqlFunctions
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param \PgSql\Result|false $cursor
|
||||
* @return string
|
||||
* @return array{0:string,1:string}
|
||||
*/
|
||||
public function __dbPrintError(\PgSql\Result|false $cursor = false): string;
|
||||
public function __dbPrintLastError(): array;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param \PgSql\Result|false $cursor
|
||||
* @return array{0:string,1:string}
|
||||
*/
|
||||
public function __dbPrintError(\PgSql\Result|false $cursor = false): array;
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
|
||||
@@ -33,7 +33,11 @@
|
||||
* pg_affected_rows (*)
|
||||
* pg_fetch_array
|
||||
* pg_query
|
||||
* pg_query_params
|
||||
* pg_send_query
|
||||
* pg_send_query_params
|
||||
* pg_send_prepare
|
||||
* pg_send_execute
|
||||
* pg_get_result
|
||||
* pg_connection_busy
|
||||
* pg_close
|
||||
@@ -50,13 +54,14 @@ namespace CoreLibs\DB\SQL;
|
||||
// below no ignore is needed if we want to use PgSql interface checks with PHP 8.0
|
||||
// as main system. Currently all @var sets are written as object
|
||||
/** @#phan-file-suppress PhanUndeclaredTypeProperty,PhanUndeclaredTypeParameter,PhanUndeclaredTypeReturnType */
|
||||
/** @phan-file-suppress PhanTypeMismatchArgumentInternal, PhanTypeMismatchReturn */
|
||||
|
||||
class PgSQL implements Interface\SqlFunctions
|
||||
{
|
||||
/** @var string */
|
||||
private $last_error_query;
|
||||
/** @var \PgSql\Connection|false */
|
||||
private $dbh;
|
||||
private $dbh = false;
|
||||
|
||||
/**
|
||||
* queries last error query and returns true or false if error was set
|
||||
@@ -93,8 +98,7 @@ class PgSQL implements Interface\SqlFunctions
|
||||
}
|
||||
|
||||
/**
|
||||
* Proposed
|
||||
* wrapperf or pg_query_params for queries in the style of
|
||||
* wrapper for pg_query_params for queries in the style of
|
||||
* SELECT foo FROM bar WHERE foobar = $1
|
||||
*
|
||||
* @param string $query Query string with placeholders $1, ..
|
||||
@@ -132,6 +136,22 @@ class PgSQL implements Interface\SqlFunctions
|
||||
return $result ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* sends an async query to the server with params
|
||||
*
|
||||
* @param string $query Query string with placeholders $1, ..
|
||||
* @param array<mixed> $params Matching parameters for each placerhold
|
||||
* @return bool true/false Query sent successful status
|
||||
*/
|
||||
public function __dbSendQueryParams(string $query, array $params): bool
|
||||
{
|
||||
if (is_bool($this->dbh)) {
|
||||
return false;
|
||||
}
|
||||
$result = pg_send_query_params($this->dbh, $query, $params);
|
||||
return $result ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_get_result
|
||||
*
|
||||
@@ -208,6 +228,38 @@ class PgSQL implements Interface\SqlFunctions
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asnyc send for a prepared statement
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $query
|
||||
* @return bool
|
||||
*/
|
||||
public function __dbSendPrepare(string $name, string $query): bool
|
||||
{
|
||||
if (is_bool($this->dbh)) {
|
||||
return false;
|
||||
}
|
||||
$result = pg_send_prepare($this->dbh, $name, $query);
|
||||
return $result ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asnyc ssend for a prepared statement execution
|
||||
*
|
||||
* @param string $name
|
||||
* @param array<mixed> $params
|
||||
* @return bool
|
||||
*/
|
||||
public function __dbSendExecute(string $name, array $params): bool
|
||||
{
|
||||
if (is_bool($this->dbh)) {
|
||||
return false;
|
||||
}
|
||||
$result = pg_send_execute($this->dbh, $name, $params);
|
||||
return $result ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_num_rows
|
||||
*
|
||||
@@ -251,6 +303,21 @@ class PgSQL implements Interface\SqlFunctions
|
||||
return pg_field_name($cursor, $i);
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_field_name
|
||||
*
|
||||
* @param \PgSql\Result|false $cursor cursor
|
||||
* @param int $i field position
|
||||
* @return string|false field type name or false
|
||||
*/
|
||||
public function __dbFieldType(\PgSql\Result|false $cursor, int $i): string|false
|
||||
{
|
||||
if (is_bool($cursor)) {
|
||||
return false;
|
||||
}
|
||||
return pg_field_type($cursor, $i);
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for pg_fetch_array
|
||||
* if through/true false, use __dbResultType(true)
|
||||
@@ -465,18 +532,37 @@ class PgSQL implements Interface\SqlFunctions
|
||||
return $this->dbh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns last error for active cursor
|
||||
*
|
||||
* @return array{0:string,1:string} prefix, error string
|
||||
*/
|
||||
public function __dbPrintLastError(): array
|
||||
{
|
||||
if (is_bool($this->dbh)) {
|
||||
return ['', ''];
|
||||
}
|
||||
if (!empty($error_message = pg_last_error($this->dbh))) {
|
||||
return [
|
||||
'-PostgreSQL-Error-Last-',
|
||||
$error_message
|
||||
];
|
||||
}
|
||||
return ['', ''];
|
||||
}
|
||||
|
||||
/**
|
||||
* reads the last error for this cursor and returns
|
||||
* html formatted string with error name
|
||||
*
|
||||
* @param \PgSql\Result|false $cursor cursor
|
||||
* or null
|
||||
* @return string error string
|
||||
* or null
|
||||
* @return array{0:string,1:string} prefix, error string
|
||||
*/
|
||||
public function __dbPrintError(\PgSql\Result|false $cursor = false): string
|
||||
public function __dbPrintError(\PgSql\Result|false $cursor = false): array
|
||||
{
|
||||
if (is_bool($this->dbh)) {
|
||||
return '';
|
||||
return ['', ''];
|
||||
}
|
||||
// run the query again for the error result here
|
||||
if ((is_bool($cursor)) && $this->last_error_query) {
|
||||
@@ -485,10 +571,12 @@ class PgSQL implements Interface\SqlFunctions
|
||||
$cursor = pg_get_result($this->dbh);
|
||||
}
|
||||
if ($cursor && $error_str = pg_result_error($cursor)) {
|
||||
return '-PostgreSQL-Error- '
|
||||
. $error_str;
|
||||
return [
|
||||
'-PostgreSQL-Error-',
|
||||
$error_str
|
||||
];
|
||||
} else {
|
||||
return '';
|
||||
return ['', ''];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1954,7 +1954,7 @@ class Generate extends \CoreLibs\DB\Extended\ArrayIO
|
||||
if ($this->table_array[$key]['value']) {
|
||||
// use the better new passwordSet instead of crypt based
|
||||
$this->table_array[$key]['value'] =
|
||||
\CoreLibs\Check\Password::passwordSet($this->table_array[$key]['value']);
|
||||
\CoreLibs\Security\Password::passwordSet($this->table_array[$key]['value']);
|
||||
$this->table_array[$key]['HIDDEN_value'] = $this->table_array[$key]['value'];
|
||||
} else {
|
||||
// $this->table_array[$key]['HIDDEN_value'] =
|
||||
|
||||
61
src/Security/CreateKey.php
Normal file
61
src/Security/CreateKey.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* very simple symmetric encryption
|
||||
* better use: https://paragonie.com/project/halite
|
||||
*
|
||||
* this is for creating secret keys for
|
||||
* Security\SymmetricEncryption
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\Security;
|
||||
|
||||
class CreateKey
|
||||
{
|
||||
/**
|
||||
* Create a random key that is a hex string
|
||||
*
|
||||
* @return string Hex string key for encrypting
|
||||
*/
|
||||
public static function generateRandomKey(): string
|
||||
{
|
||||
return self::bin2hex(self::randomKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* create a random string as binary to encrypt data
|
||||
* to store it in clear text in some .env file use bin2hex
|
||||
*
|
||||
* @return string Binary string for encryption
|
||||
*/
|
||||
public static function randomKey(): string
|
||||
{
|
||||
return random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* convert binary key to hex string
|
||||
*
|
||||
* @param string $hex_key Convert binary key string to hex
|
||||
* @return string
|
||||
*/
|
||||
public static function bin2hex(string $hex_key): string
|
||||
{
|
||||
return sodium_bin2hex($hex_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* convert hex string to binary key
|
||||
*
|
||||
* @param string $string_key Convery hex key string to binary
|
||||
* @return string
|
||||
*/
|
||||
public static function hex2bin(string $string_key): string
|
||||
{
|
||||
return sodium_hex2bin($string_key);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
59
src/Security/Password.php
Normal file
59
src/Security/Password.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* core password set, check and rehash check wrapper functions
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\Security;
|
||||
|
||||
class Password
|
||||
{
|
||||
/**
|
||||
* creates the password hash
|
||||
*
|
||||
* @param string $password password
|
||||
* @return string hashed password
|
||||
*/
|
||||
public static function passwordSet(string $password): string
|
||||
{
|
||||
// always use the PHP default for the password
|
||||
// password options ca be set in the password init,
|
||||
// but should be kept as default
|
||||
return password_hash($password, PASSWORD_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if the entered password matches the hash
|
||||
*
|
||||
* @param string $password password
|
||||
* @param string $hash password hash
|
||||
* @return bool true or false
|
||||
*/
|
||||
public static function passwordVerify(string $password, string $hash): bool
|
||||
{
|
||||
if (password_verify($password, $hash)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if the password needs to be rehashed
|
||||
*
|
||||
* @param string $hash password hash
|
||||
* @return bool true or false
|
||||
*/
|
||||
public static function passwordRehashCheck(string $hash): bool
|
||||
{
|
||||
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
96
src/Security/SymmetricEncryption.php
Normal file
96
src/Security/SymmetricEncryption.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* very simple symmetric encryption
|
||||
* Better use: https://paragonie.com/project/halite
|
||||
*
|
||||
* current code is just to encrypt and decrypt
|
||||
*
|
||||
* must use a valid encryption key created with
|
||||
* Secruty\CreateKey class
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CoreLibs\Security;
|
||||
|
||||
use CoreLibs\Security\CreateKey;
|
||||
use SodiumException;
|
||||
|
||||
class SymmetricEncryption
|
||||
{
|
||||
/**
|
||||
* Encrypt a message
|
||||
*
|
||||
* @param string $message Message to encrypt
|
||||
* @param string $key Encryption key (as hex string)
|
||||
* @return string
|
||||
* @throws \RangeException
|
||||
*/
|
||||
public static function encrypt(string $message, string $key): string
|
||||
{
|
||||
try {
|
||||
$key = CreateKey::hex2bin($key);
|
||||
} catch (SodiumException $e) {
|
||||
throw new \Exception('Invalid hex key');
|
||||
}
|
||||
if (mb_strlen($key, '8bit') !== SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
|
||||
throw new \RangeException(
|
||||
'Key is not the correct size (must be '
|
||||
. 'SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes long).'
|
||||
);
|
||||
}
|
||||
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
|
||||
|
||||
$cipher = base64_encode(
|
||||
$nonce
|
||||
. sodium_crypto_secretbox(
|
||||
$message,
|
||||
$nonce,
|
||||
$key
|
||||
)
|
||||
);
|
||||
sodium_memzero($message);
|
||||
sodium_memzero($key);
|
||||
return $cipher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt a message
|
||||
*
|
||||
* @param string $encrypted Message encrypted with safeEncrypt()
|
||||
* @param string $key Encryption key (as hex string)
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function decrypt(string $encrypted, string $key): string
|
||||
{
|
||||
try {
|
||||
$key = CreateKey::hex2bin($key);
|
||||
} catch (SodiumException $e) {
|
||||
throw new \Exception('Invalid hex key');
|
||||
}
|
||||
$decoded = base64_decode($encrypted);
|
||||
$nonce = mb_substr($decoded, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
|
||||
$ciphertext = mb_substr($decoded, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
|
||||
|
||||
$plain = false;
|
||||
try {
|
||||
$plain = sodium_crypto_secretbox_open(
|
||||
$ciphertext,
|
||||
$nonce,
|
||||
$key
|
||||
);
|
||||
} catch (SodiumException $e) {
|
||||
throw new \Exception('Invalid ciphertext (too short)');
|
||||
}
|
||||
if (!is_string($plain)) {
|
||||
throw new \Exception('Invalid Key');
|
||||
}
|
||||
sodium_memzero($ciphertext);
|
||||
sodium_memzero($key);
|
||||
return $plain;
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
@@ -462,17 +462,19 @@ class SmartyExtend extends \Smarty
|
||||
* g_title :G_TITLE
|
||||
* stylesheet :STYLESHEET
|
||||
* javascript :JAVASCRIPT
|
||||
* @param \CoreLibs\Admin\Backend|null $cms Optinal Admin Backend for
|
||||
* smarty variables merge
|
||||
* @param array<string,mixed> $smarty_data array of three keys
|
||||
* that hold smarty set strings
|
||||
* HEADER, DATA, DEBUG_DATA
|
||||
* @return void
|
||||
*/
|
||||
public function setSmartyVarsFrontend(
|
||||
array $options,
|
||||
?\CoreLibs\Admin\Backend $cms = null
|
||||
array $smarty_data
|
||||
): void {
|
||||
$this->setSmartyVars(
|
||||
false,
|
||||
$cms,
|
||||
$smarty_data,
|
||||
null,
|
||||
$options['compile_dir'] ?? null,
|
||||
$options['cache_dir'] ?? null,
|
||||
$options['js'] ?? null,
|
||||
@@ -484,6 +486,7 @@ class SmartyExtend extends \Smarty
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
$options['stylesheet'] ?? null,
|
||||
$options['javascript'] ?? null
|
||||
);
|
||||
@@ -503,6 +506,7 @@ class SmartyExtend extends \Smarty
|
||||
* admin_stylesheet :ADMIN_STYLESHEET
|
||||
* admin_javascript :ADMIN_JAVASCRIPT
|
||||
* page_width :PAGE_WIDTH
|
||||
* content_path :CONTENT_PATH
|
||||
* user_name :_SESSION['USER_NAME']
|
||||
* @param \CoreLibs\Admin\Backend|null $cms Optinal Admin Backend for
|
||||
* smarty variables merge
|
||||
@@ -512,8 +516,18 @@ class SmartyExtend extends \Smarty
|
||||
array $options,
|
||||
?\CoreLibs\Admin\Backend $cms = null
|
||||
): void {
|
||||
// if we have cms data, check for array blocks and build
|
||||
$smarty_data = [];
|
||||
if ($cms !== null) {
|
||||
$smarty_data = [
|
||||
'HEADER' => $cms->HEADER,
|
||||
'DATA' => $cms->DATA,
|
||||
'DEBUG_DATA' => $cms->DEBUG_DATA
|
||||
];
|
||||
}
|
||||
$this->setSmartyVars(
|
||||
true,
|
||||
$smarty_data,
|
||||
$cms,
|
||||
$options['compile_dir'] ?? null,
|
||||
$options['cache_dir'] ?? null,
|
||||
@@ -525,6 +539,7 @@ class SmartyExtend extends \Smarty
|
||||
$options['admin_stylesheet'] ?? null,
|
||||
$options['admin_javascript'] ?? null,
|
||||
$options['page_width'] ?? null,
|
||||
$options['content_path'] ?? null,
|
||||
$options['user_name'] ?? null,
|
||||
null,
|
||||
null
|
||||
@@ -537,6 +552,7 @@ class SmartyExtend extends \Smarty
|
||||
*
|
||||
* @param bool $admin_call default false
|
||||
* will set admin only variables
|
||||
* @param array<string,mixed> $smarty_data smarty data to merge
|
||||
* @param \CoreLibs\Admin\Backend|null $cms Optinal Admin Backend for
|
||||
* smarty variables merge
|
||||
* @param string|null $compile_dir BASE . TEMPLATES_C
|
||||
@@ -549,6 +565,7 @@ class SmartyExtend extends \Smarty
|
||||
* @param string|null $set_admin_stylesheet ADMIN_STYLESHEET
|
||||
* @param string|null $set_admin_javascript ADMIN_JAVASCRIPT
|
||||
* @param string|null $set_page_width PAGE_WIDTH
|
||||
* @param string|null $set_content_path CONTENT_PATH (only if $cms set and admin)
|
||||
* @param string|null $set_user_name _SESSION['USER_NAME']
|
||||
* @param string|null $set_stylesheet STYLESHEET
|
||||
* @param string|null $set_javascript JAVASCRIPT
|
||||
@@ -556,6 +573,7 @@ class SmartyExtend extends \Smarty
|
||||
*/
|
||||
private function setSmartyVars(
|
||||
bool $admin_call,
|
||||
array $smarty_data = [],
|
||||
?\CoreLibs\Admin\Backend $cms = null,
|
||||
?string $compile_dir = null,
|
||||
?string $cache_dir = null,
|
||||
@@ -567,6 +585,7 @@ class SmartyExtend extends \Smarty
|
||||
?string $set_admin_stylesheet = null,
|
||||
?string $set_admin_javascript = null,
|
||||
?string $set_page_width = null,
|
||||
?string $set_content_path = null,
|
||||
?string $set_user_name = null,
|
||||
?string $set_stylesheet = null,
|
||||
?string $set_javascript = null,
|
||||
@@ -593,6 +612,9 @@ class SmartyExtend extends \Smarty
|
||||
$set_stylesheet === null ||
|
||||
$set_javascript === null
|
||||
)
|
||||
) ||
|
||||
(
|
||||
$admin_call === true && $cms !== null && $set_content_path === null
|
||||
)
|
||||
) {
|
||||
/** @deprecated setSmartyVars call without parameters */
|
||||
@@ -612,25 +634,12 @@ class SmartyExtend extends \Smarty
|
||||
$set_admin_stylesheet = $set_admin_stylesheet ?? ADMIN_STYLESHEET;
|
||||
$set_admin_javascript = $set_admin_javascript ?? ADMIN_JAVASCRIPT;
|
||||
$set_page_width = $set_page_width ?? PAGE_WIDTH;
|
||||
$set_content_path = $set_content_path ?? CONTENT_PATH;
|
||||
$set_stylesheet = $set_stylesheet ?? STYLESHEET;
|
||||
$set_javascript = $set_javascript ?? JAVASCRIPT;
|
||||
$set_user_name = $set_user_name ?? $_SESSION['USER_NAME'] ?? '';
|
||||
// depreacte call globals cms on null 4mcs
|
||||
if (
|
||||
$cms === null &&
|
||||
isset($GLOBALS['cms'])
|
||||
) {
|
||||
/** @deprecated setSmartyVars globals cms is deprecated */
|
||||
trigger_error(
|
||||
'Calling setSmartyVars without cms parameter when needed is deprecated',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
}
|
||||
// this is ugly
|
||||
$cms = $cms ?? $GLOBALS['cms'] ?? null;
|
||||
if ($cms instanceof \CoreLibs\Admin\Backend) {
|
||||
$this->mergeCmsSmartyVars($cms);
|
||||
}
|
||||
// merge additional smarty data
|
||||
$this->mergeCmsSmartyVars($smarty_data);
|
||||
|
||||
// trigger flags
|
||||
$this->HEADER['USE_PROTOTYPE'] = $this->USE_PROTOTYPE;
|
||||
@@ -672,12 +681,27 @@ class SmartyExtend extends \Smarty
|
||||
$this->DATA['FORM_ACTION'] = $this->FORM_ACTION;
|
||||
// special for admin
|
||||
if ($admin_call === true) {
|
||||
// depreacte call globals cms on null 4mcs
|
||||
if (
|
||||
$cms === null &&
|
||||
isset($GLOBALS['cms'])
|
||||
) {
|
||||
/** @deprecated setSmartyVars globals cms is deprecated */
|
||||
trigger_error(
|
||||
'Calling setSmartyVars without cms parameter when needed is deprecated',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
}
|
||||
// this is ugly
|
||||
$cms = $cms ?? $GLOBALS['cms'] ?? null;
|
||||
// set ACL extra show
|
||||
if ($cms instanceof \CoreLibs\Admin\Backend) {
|
||||
$this->DATA['show_ea_extra'] = $cms->acl['show_ea_extra'] ?? false;
|
||||
$this->DATA['ADMIN'] = $cms->acl['admin'] ?? 0;
|
||||
// top menu
|
||||
$this->DATA['nav_menu'] = $cms->adbTopMenu();
|
||||
$this->DATA['nav_menu'] = $cms->adbTopMenu(
|
||||
$set_content_path
|
||||
);
|
||||
$this->DATA['nav_menu_count'] = count($this->DATA['nav_menu']);
|
||||
// messages = ['msg' =>, 'class' => 'error/warning/...']
|
||||
$this->DATA['messages'] = $cms->messages;
|
||||
@@ -737,18 +761,18 @@ class SmartyExtend extends \Smarty
|
||||
/**
|
||||
* merge outside object HEADER/DATA/DEBUG_DATA vars into the smarty class
|
||||
*
|
||||
* @param \CoreLibs\Admin\Backend $cms object that has header/data/debug_data
|
||||
* @param array<string,mixed> $smarty_data array that has header/data/debug_data
|
||||
* @return void
|
||||
*/
|
||||
public function mergeCmsSmartyVars(\CoreLibs\Admin\Backend $cms): void
|
||||
public function mergeCmsSmartyVars(array $smarty_data): void
|
||||
{
|
||||
// array merge HEADER, DATA, DEBUG DATA
|
||||
foreach (['HEADER', 'DATA', 'DEBUG_DATA'] as $ext_smarty) {
|
||||
if (
|
||||
isset($cms->{$ext_smarty}) &&
|
||||
is_array($cms->{$ext_smarty})
|
||||
isset($smarty_data[$ext_smarty]) &&
|
||||
is_array($smarty_data[$ext_smarty])
|
||||
) {
|
||||
$this->{$ext_smarty} = array_merge($this->{$ext_smarty}, $cms->{$ext_smarty});
|
||||
$this->{$ext_smarty} = array_merge($this->{$ext_smarty}, $smarty_data[$ext_smarty]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,6 +267,8 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
'GROUP_ACL_LEVEL' => -1,
|
||||
'PAGES_ACL_LEVEL' => [],
|
||||
'USER_ACL_LEVEL' => -1,
|
||||
'USER_ADDITIONAL_ACL' => [],
|
||||
'GROUP_ADDITIONAL_ACL' => [],
|
||||
'UNIT_UID' => [
|
||||
'AdminAccess' => 1,
|
||||
],
|
||||
@@ -280,6 +282,7 @@ final class CoreLibsACLLoginTest extends TestCase
|
||||
'data' => [
|
||||
'test' => 'value',
|
||||
],
|
||||
'additional_acl' => []
|
||||
],
|
||||
],
|
||||
// 'UNIT_DEFAULT' => '',
|
||||
|
||||
@@ -31,6 +31,7 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase
|
||||
4,
|
||||
'b',
|
||||
'c' => 'test',
|
||||
'single' => 'single',
|
||||
'same' => 'same',
|
||||
'deep' => [
|
||||
'sub' => [
|
||||
@@ -288,6 +289,188 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function arraySearchKeyProvider(): array
|
||||
{
|
||||
/*
|
||||
0: search in array
|
||||
1: search keys
|
||||
2: flat flag
|
||||
3: prefix flag
|
||||
4: expected array
|
||||
*/
|
||||
return [
|
||||
// single
|
||||
'find single, standard' => [
|
||||
0 => self::$array,
|
||||
1 => ['single'],
|
||||
2 => null,
|
||||
3 => null,
|
||||
4 => [
|
||||
0 => [
|
||||
'value' => 'single',
|
||||
'path' => ['single'],
|
||||
],
|
||||
],
|
||||
],
|
||||
'find single, prefix' => [
|
||||
0 => self::$array,
|
||||
1 => ['single'],
|
||||
2 => null,
|
||||
3 => true,
|
||||
4 => [
|
||||
'single' => [
|
||||
0 => [
|
||||
'value' => 'single',
|
||||
'path' => ['single'],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'find single, flat' => [
|
||||
0 => self::$array,
|
||||
1 => ['single'],
|
||||
2 => true,
|
||||
3 => null,
|
||||
4 => [
|
||||
'single',
|
||||
],
|
||||
],
|
||||
'find single, flat, prefix' => [
|
||||
0 => self::$array,
|
||||
1 => ['single'],
|
||||
2 => true,
|
||||
3 => true,
|
||||
4 => [
|
||||
'single' => [
|
||||
'single',
|
||||
],
|
||||
],
|
||||
],
|
||||
// not found
|
||||
'not found, standard' => [
|
||||
0 => self::$array,
|
||||
1 => ['NOT FOUND'],
|
||||
2 => null,
|
||||
3 => null,
|
||||
4 => [],
|
||||
],
|
||||
'not found, standard, prefix' => [
|
||||
0 => self::$array,
|
||||
1 => ['NOT FOUND'],
|
||||
2 => null,
|
||||
3 => true,
|
||||
4 => [
|
||||
'NOT FOUND' => [],
|
||||
],
|
||||
],
|
||||
'not found, flat' => [
|
||||
0 => self::$array,
|
||||
1 => ['NOT FOUND'],
|
||||
2 => true,
|
||||
3 => null,
|
||||
4 => [],
|
||||
],
|
||||
'not found, flat, prefix' => [
|
||||
0 => self::$array,
|
||||
1 => ['NOT FOUND'],
|
||||
2 => true,
|
||||
3 => true,
|
||||
4 => [
|
||||
'NOT FOUND' => [],
|
||||
],
|
||||
],
|
||||
// multi
|
||||
'multiple found, standard' => [
|
||||
0 => self::$array,
|
||||
1 => ['same'],
|
||||
2 => null,
|
||||
3 => null,
|
||||
4 => [
|
||||
[
|
||||
'value' => 'same',
|
||||
'path' => ['a', 'same', ],
|
||||
],
|
||||
[
|
||||
'value' => 'same',
|
||||
'path' => ['same', ],
|
||||
],
|
||||
[
|
||||
'value' => 'same',
|
||||
'path' => ['deep', 'sub', 'same', ],
|
||||
],
|
||||
]
|
||||
],
|
||||
'multiple found, flat' => [
|
||||
0 => self::$array,
|
||||
1 => ['same'],
|
||||
2 => true,
|
||||
3 => null,
|
||||
4 => ['same', 'same', 'same', ],
|
||||
],
|
||||
// search with multiple
|
||||
'search multiple, standard' => [
|
||||
0 => self::$array,
|
||||
1 => ['single', 'nested'],
|
||||
2 => null,
|
||||
3 => null,
|
||||
4 => [
|
||||
[
|
||||
'value' => 'single',
|
||||
'path' => ['single'],
|
||||
],
|
||||
[
|
||||
'value' => 'bar',
|
||||
'path' => ['deep', 'sub', 'nested', ],
|
||||
],
|
||||
],
|
||||
],
|
||||
'search multiple, prefix' => [
|
||||
0 => self::$array,
|
||||
1 => ['single', 'nested'],
|
||||
2 => null,
|
||||
3 => true,
|
||||
4 => [
|
||||
'single' => [
|
||||
[
|
||||
'value' => 'single',
|
||||
'path' => ['single'],
|
||||
],
|
||||
],
|
||||
'nested' => [
|
||||
[
|
||||
'value' => 'bar',
|
||||
'path' => ['deep', 'sub', 'nested', ],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'search multiple, flat' => [
|
||||
0 => self::$array,
|
||||
1 => ['single', 'nested'],
|
||||
2 => true,
|
||||
3 => null,
|
||||
4 => [
|
||||
'single', 'bar',
|
||||
],
|
||||
],
|
||||
'search multiple, flat, prefix' => [
|
||||
0 => self::$array,
|
||||
1 => ['single', 'nested'],
|
||||
2 => true,
|
||||
3 => true,
|
||||
4 => [
|
||||
'single' => ['single', ],
|
||||
'nested' => ['bar', ],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* provides array listing for the merge test
|
||||
*
|
||||
@@ -691,6 +874,44 @@ final class CoreLibsCombinedArrayHandlerTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers::arraySearchKey
|
||||
* @dataProvider arraySearchKeyProvider
|
||||
* @testdox arraySearchKey Search array with keys and flat: $flat, prefix: $prefix [$_dataName]
|
||||
*
|
||||
* @param array $input
|
||||
* @param array $needles
|
||||
* @param bool|null $flat
|
||||
* @param bool|null $prefix
|
||||
* @param array $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testArraySearchKey(
|
||||
array $input,
|
||||
array $needles,
|
||||
?bool $flat,
|
||||
?bool $prefix,
|
||||
array $expected
|
||||
): void {
|
||||
if ($flat === null && $prefix === null) {
|
||||
$result = \CoreLibs\Combined\ArrayHandler::arraySearchKey($input, $needles);
|
||||
} elseif ($flat === null) {
|
||||
$result = \CoreLibs\Combined\ArrayHandler::arraySearchKey($input, $needles, prefix: $prefix);
|
||||
} elseif ($prefix === null) {
|
||||
$result = \CoreLibs\Combined\ArrayHandler::arraySearchKey($input, $needles, flat: $flat);
|
||||
} else {
|
||||
$result = \CoreLibs\Combined\ArrayHandler::arraySearchKey($input, $needles, $flat, $prefix);
|
||||
}
|
||||
// print "E: " . print_r($expected, true) . "\n";
|
||||
// print "R: " . print_r($result, true) . "\n";
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$result
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,11 +7,11 @@ namespace tests;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Test class for Check\Password
|
||||
* @coversDefaultClass \CoreLibs\Check\Password
|
||||
* @testdox \CoreLibs\Check\Password method tests
|
||||
* Test class for Security\Password
|
||||
* @coversDefaultClass \CoreLibs\Security\Password
|
||||
* @testdox \CoreLibs\Security\Password method tests
|
||||
*/
|
||||
final class CoreLibsCheckPasswordTest extends TestCase
|
||||
final class CoreLibsSecurityPasswordTest extends TestCase
|
||||
{
|
||||
public function passwordProvider(): array
|
||||
{
|
||||
@@ -46,7 +46,7 @@ final class CoreLibsCheckPasswordTest extends TestCase
|
||||
{
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Check\Password::passwordVerify($input, \CoreLibs\Check\Password::passwordSet($input_hash))
|
||||
\CoreLibs\Security\Password::passwordVerify($input, \CoreLibs\Security\Password::passwordSet($input_hash))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ final class CoreLibsCheckPasswordTest extends TestCase
|
||||
{
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
\CoreLibs\Check\Password::passwordRehashCheck($input)
|
||||
\CoreLibs\Security\Password::passwordRehashCheck($input)
|
||||
);
|
||||
}
|
||||
}
|
||||
172
test/phpunit/Security/CoreLibsSecuritySymmetricEncryption.php
Normal file
172
test/phpunit/Security/CoreLibsSecuritySymmetricEncryption.php
Normal file
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use CoreLibs\Security\CreateKey;
|
||||
use CoreLibs\Security\SymmetricEncryption;
|
||||
|
||||
/**
|
||||
* Test class for Security\SymmetricEncryption and Security\CreateKey
|
||||
* @coversDefaultClass \CoreLibs\Security\SymmetricEncryption
|
||||
* @testdox \CoreLibs\Security\SymmetricEncryption method tests
|
||||
*/
|
||||
final class CoreLibsSecuritySymmetricEncryption extends TestCase
|
||||
{
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerEncryptDecryptSuccess(): array
|
||||
{
|
||||
return [
|
||||
'valid string' => [
|
||||
'input' => 'I am a secret',
|
||||
'expected' => 'I am a secret',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* test encrypt/decrypt produce correct output
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptDecryptSuccess
|
||||
* @testdox encrypt/decrypt $input must be $expected [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $expected
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptDecryptSuccess(string $input, string $expected): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
$encrypted = SymmetricEncryption::encrypt($input, $key);
|
||||
$decrypted = SymmetricEncryption::decrypt($encrypted, $key);
|
||||
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
$decrypted
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerEncryptFailed(): array
|
||||
{
|
||||
return [
|
||||
'wrong decryption key' => [
|
||||
'input' => 'I am a secret',
|
||||
'excpetion_message' => 'Invalid Key'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test decryption with wrong key
|
||||
*
|
||||
* @covers ::generateRandomKey
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerEncryptFailed
|
||||
* @testdox decrypt with wrong key $input throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testEncryptFailed(string $input, string $exception_message): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
$encrypted = SymmetricEncryption::encrypt($input, $key);
|
||||
$wrong_key = CreateKey::generateRandomKey();
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::decrypt($encrypted, $wrong_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerWrongKey(): array
|
||||
{
|
||||
return [
|
||||
'not hex key' => [
|
||||
'key' => 'not_a_hex_key',
|
||||
'exception_message' => 'Invalid hex key'
|
||||
],
|
||||
'too short hex key' => [
|
||||
'key' => '1cabd5cba9e042f12522f4ff2de5c31d233b',
|
||||
'excpetion_message' => 'Key is not the correct size (must be '
|
||||
. 'SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes long).'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* test invalid key provided to decrypt or encrypt
|
||||
*
|
||||
* @covers ::encrypt
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongKey
|
||||
* @testdox wrong key $key throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongKey(string $key, string $exception_message): void
|
||||
{
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::encrypt('test', $key);
|
||||
// we must encrypt valid thing first so we can fail with the wrong kjey
|
||||
$enc_key = CreateKey::generateRandomKey();
|
||||
$encrypted = SymmetricEncryption::encrypt('test', $enc_key);
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::decrypt($encrypted, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerWrongCiphertext(): array
|
||||
{
|
||||
return [
|
||||
'too short ciphertext' => [
|
||||
'input' => 'short',
|
||||
'exception_message' => 'Invalid ciphertext (too short)'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @covers ::decrypt
|
||||
* @dataProvider providerWrongCiphertext
|
||||
* @testdox too short ciphertext $input throws $exception_message [$_dataName]
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $exception_message
|
||||
* @return void
|
||||
*/
|
||||
public function testWrongCiphertext(string $input, string $exception_message): void
|
||||
{
|
||||
$key = CreateKey::generateRandomKey();
|
||||
$this->expectExceptionMessage($exception_message);
|
||||
SymmetricEncryption::decrypt($input, $key);
|
||||
}
|
||||
}
|
||||
|
||||
// __END__
|
||||
Reference in New Issue
Block a user