Skip to content

Commit 0feba24

Browse files
committed
Enhance name normalization and validation with logging and additional tests
1 parent 0c58c28 commit 0feba24

3 files changed

Lines changed: 70 additions & 24 deletions

File tree

src/Commands/Backend/CreateUsersCommand.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,10 @@ private function get_backends_users(array $backends, array &$map): array
254254

255255
// -- this was source of lots of bugs and confusion for users,
256256
// -- we decided to normalize the user-names early in the process.
257-
$user['name'] = normalizeName((string)$user['name']);
257+
$user['name'] = normalizeName((string)$user['name'], $this->logger, [
258+
'log_message' => "Normalized '{backend}: {name}' to '{backend}: {new_name}'",
259+
'context' => [ 'backend' => $backedName ],
260+
]);
258261

259262
// -- run map actions.
260263
$this->map_actions($backedName, $user, $map);
@@ -277,7 +280,7 @@ private function get_backends_users(array $backends, array &$map): array
277280
$info['backendName'] = normalizeName(r("{backend}_{user}", [
278281
'backend' => $backedName,
279282
'user' => $user['name']
280-
]));
283+
]),$this->logger);
281284

282285
if (false === isValidName($info['backendName'])) {
283286
$this->logger->error(
@@ -357,7 +360,7 @@ private function create_user(iInput $input, array $users): void
357360

358361
foreach ($users as $user) {
359362
// -- User subdirectory name.
360-
$userName = normalizeName(ag($user, 'name', 'unknown'));
363+
$userName = normalizeName(ag($user, 'name', 'unknown'), $this->logger);
361364

362365
if (false === isValidName($userName)) {
363366
$this->logger->error(

src/Libs/helpers.php

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,7 @@ function makeBackend(array $backend, string|null $name = null, array $options =
730730
backendUrl: new Uri(ag($backend, 'url')),
731731
cache: $cache,
732732
userContext: $userContext ?? Container::get(UserContext::class),
733-
logger: ag($options, iLogger::class, fn() => Container::get(iLogger::class)),
733+
logger: ag($options, iLogger::class, fn () => Container::get(iLogger::class)),
734734
backendId: ag($backend, 'uuid', null),
735735
backendToken: ag($backend, 'token', null),
736736
backendUser: ag($backend, 'user', null),
@@ -909,10 +909,6 @@ function getAppVersion(): string
909909
*/
910910
function isValidName(string $name): bool
911911
{
912-
if (true === ctype_digit($name[0])) {
913-
return false;
914-
}
915-
916912
return 1 === preg_match('/^[a-z_0-9]+$/', $name);
917913
}
918914
}
@@ -949,7 +945,7 @@ function formatDuration(int|float $milliseconds): string
949945
*/
950946
function array_keys_diff(array $base, array $list, bool $has = true): array
951947
{
952-
return array_filter($base, fn($key) => $has === in_array($key, $list), ARRAY_FILTER_USE_KEY);
948+
return array_filter($base, fn ($key) => $has === in_array($key, $list), ARRAY_FILTER_USE_KEY);
953949
}
954950
}
955951

@@ -1354,7 +1350,7 @@ function getSystemMemoryInfo(string $memFile = '/proc/meminfo'): array
13541350
function parseConfigValue(mixed $value, Closure|null $callback = null): mixed
13551351
{
13561352
if (is_string($value) && preg_match('#%{(.+?)}#s', $value)) {
1357-
$val = preg_replace_callback('#%{(.+?)}#s', fn($match) => Config::get($match[1], $match[1]), $value);
1353+
$val = preg_replace_callback('#%{(.+?)}#s', fn ($match) => Config::get($match[1], $match[1]), $value);
13581354
return null !== $callback && null !== $val ? $callback($val) : $val;
13591355
}
13601356

@@ -1389,7 +1385,7 @@ function checkIgnoreRule(string $guid, UserContext|null $userContext = null): bo
13891385
if (false === in_array('guid_' . $db, $sources)) {
13901386
throw new RuntimeException(r("Invalid db source name '{db}' was given. Expected values are '{dbs}'.", [
13911387
'db' => $db,
1392-
'dbs' => implode(', ', array_map(fn($f) => after($f, 'guid_'), $sources)),
1388+
'dbs' => implode(', ', array_map(fn ($f) => after($f, 'guid_'), $sources)),
13931389
]));
13941390
}
13951391

@@ -2112,8 +2108,8 @@ function registerEvents(bool $ignoreCache = false): void
21122108
/** @var array<ScannerItem> $list */
21132109
$list = cacheableItem(
21142110
'event_listeners',
2115-
fn() => AttributesScanner::scan(Config::get('events.listeners.locations', []))->for(EventListener::class),
2116-
Config::get('events.listeners.cache', fn() => new DateInterval('PT1H')),
2111+
fn () => AttributesScanner::scan(Config::get('events.listeners.locations', []))->for(EventListener::class),
2112+
Config::get('events.listeners.cache', fn () => new DateInterval('PT1H')),
21172113
$ignoreCache
21182114
);
21192115

@@ -2147,7 +2143,7 @@ function registerEvents(bool $ignoreCache = false): void
21472143
*/
21482144
function queueEvent(string $event, array $data = [], array $opts = []): EventInfo
21492145
{
2150-
$repo = ag($opts, EventsRepository::class, fn() => Container::get(EventsRepository::class));
2146+
$repo = ag($opts, EventsRepository::class, fn () => Container::get(EventsRepository::class));
21512147
assert($repo instanceof EventsRepository);
21522148

21532149
$item = null;
@@ -2428,7 +2424,7 @@ function perUserCacheAdapter(string $user): CacheInterface
24282424
}
24292425

24302426
$ns = getAppVersion();
2431-
$ns .= isValidName($user) ? '.' . $user : '.' . md5($user);
2427+
$ns .= isValidName($user) ? ".{$user}" : '.' . md5($user);
24322428

24332429
try {
24342430
$backend = new RedisAdapter(redis: Container::get(Redis::class), namespace: $ns);
@@ -2731,13 +2727,28 @@ function deletePath(string $path, iLogger|null $logger = null, bool $dryRun = fa
27312727
* Normalize the name to be in [a-z_0-9] format.
27322728
*
27332729
* @param string $name The name to normalize.
2730+
* @param iLogger |null $logger (Optional) The logger instance.
27342731
*
27352732
* @return string The normalized name.
27362733
*/
2737-
function normalizeName(string $name): string
2734+
function normalizeName(string $name, iLogger|null $logger = null,array $opts=[]): string
27382735
{
2739-
$name = strtolower($name);
2740-
$name = preg_replace('/[^a-z0-9_]/', '_', $name);
2741-
return trim($name);
2736+
if (true === ctype_digit($name)) {
2737+
$newName = 'user_' . $name;
2738+
} else {
2739+
$newName = strtolower($name);
2740+
$newName = preg_replace('/[^a-z0-9_]/', '_', $newName);
2741+
$newName = trim($newName);
2742+
}
2743+
2744+
if ($newName !== $name && null !== $logger) {
2745+
$logger->notice(ag($opts, 'log_message', "Normalized '{name}' to '{new_name}'."), [
2746+
'name' => $name,
2747+
'new_name' => $newName,
2748+
...ag($opts,'context',[])
2749+
]);
2750+
}
2751+
2752+
return $newName;
27422753
}
27432754
}

tests/Libs/HelpersTest.php

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -678,12 +678,12 @@ public function __toString(): string
678678

679679
public function test_isValidName(): void
680680
{
681-
$this->assertTrue(isValidName('foo'), 'When name is valid, true is returned.');
682-
$this->assertTrue(isValidName('foo_bar'), 'When name is valid, true is returned.');
683-
$this->assertFalse(isValidName('foo_baR'), 'When name is invalid, false is returned.');
684-
$this->assertFalse(isValidName('3oo_bar'), 'When name is invalid, false is returned.');
681+
$validNames = ['foo', '123', 'foo_bar', '1foo_bar'];
682+
$invalidNames = ['foo bar', 'foo-bar', 'foo/bar', 'foo?bar', 'foo*bar', 'foo_baR', 'FOOBAR'];
685683

686-
$invalidNames = ['foo bar', 'foo-bar', 'foo/bar', 'foo?bar', 'foo*bar', '1foo', 'foo_baR', 'FOOBAR'];
684+
foreach ($validNames as $name) {
685+
$this->assertTrue(isValidName($name), "When given name is '{$name}', true should be is returned.");
686+
}
687687

688688
foreach ($invalidNames as $name) {
689689
$this->assertFalse(isValidName($name), "When given name is '{$name}', false should be is returned.");
@@ -1632,4 +1632,36 @@ public function test_commandContext()
16321632
);
16331633
unset($_ENV['IN_CONTAINER']);
16341634
}
1635+
1636+
public function test_normalizeName()
1637+
{
1638+
$isValid = ['foo','foo_bar','0user','user_123','user_123_foo','user_123_foo_bar'];
1639+
foreach ($isValid as $name) {
1640+
$this->assertSame(
1641+
$name,
1642+
normalizeName($name),
1643+
"When valid name '{$name}' is passed, it should return same string."
1644+
);
1645+
}
1646+
1647+
$this->assertSame(
1648+
'user_123',
1649+
normalizeName('123'),
1650+
'When name is made entirely of numbers, it should prepend user_ to it.'
1651+
);
1652+
1653+
$invalidNames = [
1654+
'foo bar',
1655+
'foo-bar',
1656+
'foo@baR',
1657+
];
1658+
1659+
foreach ($invalidNames as $name) {
1660+
$this->assertSame(
1661+
'foo_bar',
1662+
normalizeName($name),
1663+
"When invalid name '{$name}' is passed, it should return same string with underscores."
1664+
);
1665+
}
1666+
}
16351667
}

0 commit comments

Comments
 (0)