From c71464b76322aa03fa5c35329913ae51973479d3 Mon Sep 17 00:00:00 2001 From: Bubka <858858+Bubka@users.noreply.github.com> Date: Mon, 16 Jun 2025 08:44:42 +0200 Subject: [PATCH] Add fallback to regular variant & Add the Strict fetch user preference --- app/Services/LogoLib/AbstractLogoLib.php | 20 +++++++++++- config/2fauth.php | 1 + resources/js/views/settings/Options.vue | 9 ++++-- resources/lang/en/settings.php | 6 +++- tests/Feature/Services/LogoLibsTest.php | 41 ++++++++++++++++++++++-- 5 files changed, 70 insertions(+), 7 deletions(-) diff --git a/app/Services/LogoLib/AbstractLogoLib.php b/app/Services/LogoLib/AbstractLogoLib.php index 458a935d..247a674f 100644 --- a/app/Services/LogoLib/AbstractLogoLib.php +++ b/app/Services/LogoLib/AbstractLogoLib.php @@ -43,12 +43,20 @@ abstract class AbstractLogoLib implements LogoLibInterface $this->setVariant($variant); $logoFilename = $this->getLogo(strval($serviceName)); - if (!$logoFilename) { + if (! $logoFilename && $this->format != 'png') { // maybe the svg is not available, we try to get the png format $this->format = 'png'; $logoFilename = $this->getLogo(strval($serviceName)); } + if (! $logoFilename && $this->variant != 'regular' && ! $this->strictFetch()) { + // maybe the variant is not available, we try to get the regular svg variant + $this->format = 'svg'; + if ($icon = $this->getIcon($serviceName)) { + return $icon; + } + } + if ($logoFilename) { $iconFilename = \Illuminate\Support\Str::random(40) . '.' . $this->format; @@ -72,6 +80,16 @@ abstract class AbstractLogoLib implements LogoLibInterface } } + /** + * Should we fallback to the regular variant + */ + protected function strictFetch() : bool + { + return Auth::user() + ? (bool)Auth::user()->preferences['iconVariantStrictFetch'] + : false; + } + /** * Return the logo's filename for a given service * diff --git a/config/2fauth.php b/config/2fauth.php index b59eb42e..9ab6a979 100644 --- a/config/2fauth.php +++ b/config/2fauth.php @@ -15,6 +15,7 @@ $preferences = [ 'showAccountsIcons' => envUnlessEmpty('USERPREF_DEFAULT__SHOW_ACCOUNTS_ICONS', true), 'iconCollection' => envUnlessEmpty('USERPREF_DEFAULT__ICON_COLLECTION', 'selfh'), 'iconVariant' => envUnlessEmpty('USERPREF_DEFAULT__ICON_VARIANT', 'regular'), + 'iconVariantStrictFetch' => envUnlessEmpty('USERPREF_DEFAULT__ICON_VARIANT_STRICT_FETCH', false), 'kickUserAfter' => envUnlessEmpty('USERPREF_DEFAULT__KICK_USER_AFTER', 15), 'activeGroup' => 0, 'rememberActiveGroup' => envUnlessEmpty('USERPREF_DEFAULT__REMEMBER_ACTIVE_GROUP', true), diff --git a/resources/js/views/settings/Options.vue b/resources/js/views/settings/Options.vue index e87eb8a0..2cc7d8cb 100644 --- a/resources/js/views/settings/Options.vue +++ b/resources/js/views/settings/Options.vue @@ -38,7 +38,10 @@ { text: 'commons.regular', value: 'regular' }, { text: 'settings.forms.light', value: 'light' }, { text: 'settings.forms.dark', value: 'dark' }, - ] + ], + tfa: [ + { text: 'commons.regular', value: 'regular' }, + ], } const passwordFormats = [ { text: '12 34 56', value: 2, legend: 'settings.forms.pair', title: 'settings.forms.pair_legend' }, @@ -199,7 +202,9 @@ - + + + diff --git a/resources/lang/en/settings.php b/resources/lang/en/settings.php index b07c5912..22e3d138 100644 --- a/resources/lang/en/settings.php +++ b/resources/lang/en/settings.php @@ -131,7 +131,11 @@ return [ ], 'icon_variant' => [ 'label' => 'Icon variant', - 'help' => 'Some icons may be available in several flavors to best suit dark or light UIs. Set the one you want to look for first.' + 'help' => 'Some icons are available in different flavors to best suit dark or light user interfaces. Set the one you want to look for first. The regular variant will automatically be fetched as a fallback.' + ], + 'icon_variant_strict_fetch' => [ + 'label' => 'Strict fetch', + 'help' => 'Narrow the fetch to the specified variant. If the variant is missing, 2FAuth will not try to fallback to the regular variant.' ], 'auto_lock' => [ 'label' => 'Auto lock', diff --git a/tests/Feature/Services/LogoLibsTest.php b/tests/Feature/Services/LogoLibsTest.php index 763ceedd..b4c0a5d4 100644 --- a/tests/Feature/Services/LogoLibsTest.php +++ b/tests/Feature/Services/LogoLibsTest.php @@ -122,6 +122,41 @@ class LogoLibsTest extends FeatureTestCase $this->assertStringEndsWith('.png', $icon); } + #[Test] + public function test_getIcon_fallbacks_to_regular_when_requested_variant_is_not_available() + { + Http::preventStrayRequests(); + Http::fake([ + CommonDataProvider::SELFH_URL_ROOT . 'svg/myservice-dark.svg' => Http::response('not found', 404), + CommonDataProvider::SELFH_URL_ROOT . 'png/myservice-dark.png' => Http::response('not found', 404), + CommonDataProvider::SELFH_URL_ROOT . 'svg/myservice.svg' => Http::response(HttpRequestTestData::SVG_LOGO_BODY_VARIANT_REGULAR, 200), + ]); + + $this->logoLib = $this->app->make(SelfhLogoLib::class); + $icon = $this->logoLib->getIcon('myservice', 'dark'); + + $this->assertEquals(trim(Storage::disk('icons')->get($icon)), ' ' . HttpRequestTestData::SVG_LOGO_BODY_VARIANT_REGULAR); + } + + #[Test] + public function test_getIcon_does_not_fallback_to_regular_when_requested_variant_is_not_available() + { + $user = User::factory()->create(); + $user['preferences->iconVariantStrictFetch'] = true; + $user->save(); + + Http::preventStrayRequests(); + Http::fake([ + CommonDataProvider::SELFH_URL_ROOT . 'svg/myservice-dark.svg' => Http::response('not found', 404), + CommonDataProvider::SELFH_URL_ROOT . 'png/myservice-dark.png' => Http::response('not found', 404), + ]); + + $this->logoLib = $this->app->make(SelfhLogoLib::class); + $icon = $this->actingAs($user)->logoLib->getIcon('myservice', 'dark'); + + $this->assertNull($icon); + } + #[Test] public function test_getIcon_fallbacks_to_user_preferences_when_variant_is_omitted() { @@ -186,7 +221,7 @@ class LogoLibsTest extends FeatureTestCase $this->logoLib = $this->app->make(TfalogoLib::class); $icon = $this->logoLib->getIcon('service'); - $this->assertEquals(null, $icon); + $this->assertNull($icon); } #[Test] @@ -204,7 +239,7 @@ class LogoLibsTest extends FeatureTestCase $this->logoLib = $this->app->make($iconProvider['class']); $icon = $this->logoLib->getIcon('service'); - $this->assertEquals(null, $icon); + $this->assertNull($icon); } #[Test] @@ -219,7 +254,7 @@ class LogoLibsTest extends FeatureTestCase $this->logoLib = $this->app->make(TfalogoLib::class); $icon = $this->logoLib->getIcon('no_logo_should_exists_with_this_name'); - $this->assertEquals(null, $icon); + $this->assertNull($icon); } #[Test]