@@ -21,79 +21,79 @@ discard block |
||
21 | 21 | $nextcloudDir = dirname(__DIR__); |
22 | 22 | |
23 | 23 | class NextcloudNamespaceSkipVoter implements ClassNameImportSkipVoterInterface { |
24 | - private array $namespacePrefixes = [ |
|
25 | - 'OC', |
|
26 | - 'OCA', |
|
27 | - 'OCP', |
|
28 | - ]; |
|
29 | - private array $skippedClassNames = [ |
|
30 | - 'Backend', |
|
31 | - 'Connection', |
|
32 | - 'Exception', |
|
33 | - 'IManager', |
|
34 | - 'IProvider', |
|
35 | - 'Manager', |
|
36 | - 'Plugin', |
|
37 | - 'Provider', |
|
38 | - ]; |
|
39 | - public function shouldSkip(File $file, FullyQualifiedObjectType $fullyQualifiedObjectType, Node $node) : bool { |
|
40 | - if (in_array($fullyQualifiedObjectType->getShortName(), $this->skippedClassNames)) { |
|
41 | - // Skip common class names to avoid confusion |
|
42 | - return true; |
|
43 | - } |
|
44 | - foreach ($this->namespacePrefixes as $prefix) { |
|
45 | - if (str_starts_with($fullyQualifiedObjectType->getClassName(), $prefix . '\\')) { |
|
46 | - // Import Nextcloud namespaces |
|
47 | - return false; |
|
48 | - } |
|
49 | - } |
|
50 | - // Skip everything else |
|
51 | - return true; |
|
52 | - } |
|
24 | + private array $namespacePrefixes = [ |
|
25 | + 'OC', |
|
26 | + 'OCA', |
|
27 | + 'OCP', |
|
28 | + ]; |
|
29 | + private array $skippedClassNames = [ |
|
30 | + 'Backend', |
|
31 | + 'Connection', |
|
32 | + 'Exception', |
|
33 | + 'IManager', |
|
34 | + 'IProvider', |
|
35 | + 'Manager', |
|
36 | + 'Plugin', |
|
37 | + 'Provider', |
|
38 | + ]; |
|
39 | + public function shouldSkip(File $file, FullyQualifiedObjectType $fullyQualifiedObjectType, Node $node) : bool { |
|
40 | + if (in_array($fullyQualifiedObjectType->getShortName(), $this->skippedClassNames)) { |
|
41 | + // Skip common class names to avoid confusion |
|
42 | + return true; |
|
43 | + } |
|
44 | + foreach ($this->namespacePrefixes as $prefix) { |
|
45 | + if (str_starts_with($fullyQualifiedObjectType->getClassName(), $prefix . '\\')) { |
|
46 | + // Import Nextcloud namespaces |
|
47 | + return false; |
|
48 | + } |
|
49 | + } |
|
50 | + // Skip everything else |
|
51 | + return true; |
|
52 | + } |
|
53 | 53 | } |
54 | 54 | |
55 | 55 | $config = RectorConfig::configure() |
56 | - ->withPaths([ |
|
57 | - $nextcloudDir . '/apps', |
|
58 | - $nextcloudDir . '/core', |
|
59 | - $nextcloudDir . '/ocs', |
|
60 | - $nextcloudDir . '/ocs-provider', |
|
61 | - $nextcloudDir . '/console.php', |
|
62 | - $nextcloudDir . '/cron.php', |
|
63 | - $nextcloudDir . '/index.php', |
|
64 | - $nextcloudDir . '/occ', |
|
65 | - $nextcloudDir . '/public.php', |
|
66 | - $nextcloudDir . '/remote.php', |
|
67 | - $nextcloudDir . '/status.php', |
|
68 | - $nextcloudDir . '/version.php', |
|
69 | - $nextcloudDir . '/lib/private/Share20/ProviderFactory.php', |
|
70 | - $nextcloudDir . '/tests', |
|
71 | - // $nextcloudDir . '/config', |
|
72 | - // $nextcloudDir . '/lib', |
|
73 | - // $nextcloudDir . '/themes', |
|
74 | - ]) |
|
75 | - ->withSkip([ |
|
76 | - $nextcloudDir . '/apps/*/3rdparty/*', |
|
77 | - $nextcloudDir . '/apps/*/build/stubs/*', |
|
78 | - $nextcloudDir . '/apps/*/composer/*', |
|
79 | - $nextcloudDir . '/apps/*/config/*', |
|
80 | - ]) |
|
81 | - // uncomment to reach your current PHP version |
|
82 | - // ->withPhpSets() |
|
83 | - ->withImportNames(importShortClasses:false) |
|
84 | - ->withTypeCoverageLevel(0) |
|
85 | - ->withRules([ |
|
86 | - UseSpecificWillMethodRector::class, |
|
87 | - StaticDataProviderClassMethodRector::class, |
|
88 | - DataProviderAnnotationToAttributeRector::class, |
|
89 | - ]) |
|
90 | - ->withConfiguredRule(ClassPropertyAssignToConstructorPromotionRector::class, [ |
|
91 | - 'inline_public' => true, |
|
92 | - 'rename_property' => true, |
|
93 | - ]) |
|
94 | - ->withSets([ |
|
95 | - NextcloudSets::NEXTCLOUD_25, |
|
96 | - ]); |
|
56 | + ->withPaths([ |
|
57 | + $nextcloudDir . '/apps', |
|
58 | + $nextcloudDir . '/core', |
|
59 | + $nextcloudDir . '/ocs', |
|
60 | + $nextcloudDir . '/ocs-provider', |
|
61 | + $nextcloudDir . '/console.php', |
|
62 | + $nextcloudDir . '/cron.php', |
|
63 | + $nextcloudDir . '/index.php', |
|
64 | + $nextcloudDir . '/occ', |
|
65 | + $nextcloudDir . '/public.php', |
|
66 | + $nextcloudDir . '/remote.php', |
|
67 | + $nextcloudDir . '/status.php', |
|
68 | + $nextcloudDir . '/version.php', |
|
69 | + $nextcloudDir . '/lib/private/Share20/ProviderFactory.php', |
|
70 | + $nextcloudDir . '/tests', |
|
71 | + // $nextcloudDir . '/config', |
|
72 | + // $nextcloudDir . '/lib', |
|
73 | + // $nextcloudDir . '/themes', |
|
74 | + ]) |
|
75 | + ->withSkip([ |
|
76 | + $nextcloudDir . '/apps/*/3rdparty/*', |
|
77 | + $nextcloudDir . '/apps/*/build/stubs/*', |
|
78 | + $nextcloudDir . '/apps/*/composer/*', |
|
79 | + $nextcloudDir . '/apps/*/config/*', |
|
80 | + ]) |
|
81 | + // uncomment to reach your current PHP version |
|
82 | + // ->withPhpSets() |
|
83 | + ->withImportNames(importShortClasses:false) |
|
84 | + ->withTypeCoverageLevel(0) |
|
85 | + ->withRules([ |
|
86 | + UseSpecificWillMethodRector::class, |
|
87 | + StaticDataProviderClassMethodRector::class, |
|
88 | + DataProviderAnnotationToAttributeRector::class, |
|
89 | + ]) |
|
90 | + ->withConfiguredRule(ClassPropertyAssignToConstructorPromotionRector::class, [ |
|
91 | + 'inline_public' => true, |
|
92 | + 'rename_property' => true, |
|
93 | + ]) |
|
94 | + ->withSets([ |
|
95 | + NextcloudSets::NEXTCLOUD_25, |
|
96 | + ]); |
|
97 | 97 | |
98 | 98 | $config->registerService(NextcloudNamespaceSkipVoter::class, tag:ClassNameImportSkipVoterInterface::class); |
99 | 99 | |
@@ -105,11 +105,11 @@ discard block |
||
105 | 105 | $ignoredEntries = array_values($ignoredEntries); |
106 | 106 | |
107 | 107 | foreach ($ignoredEntries as $ignoredEntry) { |
108 | - if (str_ends_with($ignoredEntry, '/')) { |
|
109 | - $config->withSkip([$ignoredEntry . '*']); |
|
110 | - } else { |
|
111 | - $config->withSkip([$ignoredEntry . '/*']); |
|
112 | - } |
|
108 | + if (str_ends_with($ignoredEntry, '/')) { |
|
109 | + $config->withSkip([$ignoredEntry . '*']); |
|
110 | + } else { |
|
111 | + $config->withSkip([$ignoredEntry . '/*']); |
|
112 | + } |
|
113 | 113 | } |
114 | 114 | |
115 | 115 | return $config; |
@@ -31,7 +31,7 @@ discard block |
||
31 | 31 | use Psr\Log\LoggerInterface; |
32 | 32 | |
33 | 33 | if (!str_contains(@ini_get('disable_functions'), 'set_time_limit')) { |
34 | - @set_time_limit(0); |
|
34 | + @set_time_limit(0); |
|
35 | 35 | } |
36 | 36 | |
37 | 37 | require_once '../../lib/base.php'; |
@@ -46,117 +46,117 @@ discard block |
||
46 | 46 | $eventSource->send('success', $l->t('Preparing update')); |
47 | 47 | |
48 | 48 | if (Util::needUpgrade()) { |
49 | - $config = Server::get(SystemConfig::class); |
|
50 | - if ($config->getValue('upgrade.disable-web', false)) { |
|
51 | - $eventSource->send('failure', $l->t('Please use the command line updater because updating via browser is disabled in your config.php.')); |
|
52 | - $eventSource->close(); |
|
53 | - exit(); |
|
54 | - } |
|
49 | + $config = Server::get(SystemConfig::class); |
|
50 | + if ($config->getValue('upgrade.disable-web', false)) { |
|
51 | + $eventSource->send('failure', $l->t('Please use the command line updater because updating via browser is disabled in your config.php.')); |
|
52 | + $eventSource->close(); |
|
53 | + exit(); |
|
54 | + } |
|
55 | 55 | |
56 | - // if a user is currently logged in, their session must be ignored to |
|
57 | - // avoid side effects |
|
58 | - \OC_User::setIncognitoMode(true); |
|
56 | + // if a user is currently logged in, their session must be ignored to |
|
57 | + // avoid side effects |
|
58 | + \OC_User::setIncognitoMode(true); |
|
59 | 59 | |
60 | - $config = Server::get(IConfig::class); |
|
61 | - $updater = new Updater( |
|
62 | - Server::get(ServerVersion::class), |
|
63 | - $config, |
|
64 | - Server::get(IAppConfig::class), |
|
65 | - Server::get(Checker::class), |
|
66 | - Server::get(LoggerInterface::class), |
|
67 | - Server::get(Installer::class) |
|
68 | - ); |
|
69 | - $incompatibleApps = []; |
|
70 | - $incompatibleOverwrites = $config->getSystemValue('app_install_overwrite', []); |
|
60 | + $config = Server::get(IConfig::class); |
|
61 | + $updater = new Updater( |
|
62 | + Server::get(ServerVersion::class), |
|
63 | + $config, |
|
64 | + Server::get(IAppConfig::class), |
|
65 | + Server::get(Checker::class), |
|
66 | + Server::get(LoggerInterface::class), |
|
67 | + Server::get(Installer::class) |
|
68 | + ); |
|
69 | + $incompatibleApps = []; |
|
70 | + $incompatibleOverwrites = $config->getSystemValue('app_install_overwrite', []); |
|
71 | 71 | |
72 | - /** @var IEventDispatcher $dispatcher */ |
|
73 | - $dispatcher = Server::get(IEventDispatcher::class); |
|
74 | - $dispatcher->addListener( |
|
75 | - MigratorExecuteSqlEvent::class, |
|
76 | - function (MigratorExecuteSqlEvent $event) use ($eventSource, $l): void { |
|
77 | - $eventSource->send('success', $l->t('[%d / %d]: %s', [$event->getCurrentStep(), $event->getMaxStep(), $event->getSql()])); |
|
78 | - } |
|
79 | - ); |
|
80 | - $feedBack = new FeedBackHandler($eventSource, $l); |
|
81 | - $dispatcher->addListener(RepairStartEvent::class, [$feedBack, 'handleRepairFeedback']); |
|
82 | - $dispatcher->addListener(RepairAdvanceEvent::class, [$feedBack, 'handleRepairFeedback']); |
|
83 | - $dispatcher->addListener(RepairFinishEvent::class, [$feedBack, 'handleRepairFeedback']); |
|
84 | - $dispatcher->addListener(RepairStepEvent::class, [$feedBack, 'handleRepairFeedback']); |
|
85 | - $dispatcher->addListener(RepairInfoEvent::class, [$feedBack, 'handleRepairFeedback']); |
|
86 | - $dispatcher->addListener(RepairWarningEvent::class, [$feedBack, 'handleRepairFeedback']); |
|
87 | - $dispatcher->addListener(RepairErrorEvent::class, [$feedBack, 'handleRepairFeedback']); |
|
72 | + /** @var IEventDispatcher $dispatcher */ |
|
73 | + $dispatcher = Server::get(IEventDispatcher::class); |
|
74 | + $dispatcher->addListener( |
|
75 | + MigratorExecuteSqlEvent::class, |
|
76 | + function (MigratorExecuteSqlEvent $event) use ($eventSource, $l): void { |
|
77 | + $eventSource->send('success', $l->t('[%d / %d]: %s', [$event->getCurrentStep(), $event->getMaxStep(), $event->getSql()])); |
|
78 | + } |
|
79 | + ); |
|
80 | + $feedBack = new FeedBackHandler($eventSource, $l); |
|
81 | + $dispatcher->addListener(RepairStartEvent::class, [$feedBack, 'handleRepairFeedback']); |
|
82 | + $dispatcher->addListener(RepairAdvanceEvent::class, [$feedBack, 'handleRepairFeedback']); |
|
83 | + $dispatcher->addListener(RepairFinishEvent::class, [$feedBack, 'handleRepairFeedback']); |
|
84 | + $dispatcher->addListener(RepairStepEvent::class, [$feedBack, 'handleRepairFeedback']); |
|
85 | + $dispatcher->addListener(RepairInfoEvent::class, [$feedBack, 'handleRepairFeedback']); |
|
86 | + $dispatcher->addListener(RepairWarningEvent::class, [$feedBack, 'handleRepairFeedback']); |
|
87 | + $dispatcher->addListener(RepairErrorEvent::class, [$feedBack, 'handleRepairFeedback']); |
|
88 | 88 | |
89 | - $updater->listen('\OC\Updater', 'maintenanceEnabled', function () use ($eventSource, $l): void { |
|
90 | - $eventSource->send('success', $l->t('Turned on maintenance mode')); |
|
91 | - }); |
|
92 | - $updater->listen('\OC\Updater', 'maintenanceDisabled', function () use ($eventSource, $l): void { |
|
93 | - $eventSource->send('success', $l->t('Turned off maintenance mode')); |
|
94 | - }); |
|
95 | - $updater->listen('\OC\Updater', 'maintenanceActive', function () use ($eventSource, $l): void { |
|
96 | - $eventSource->send('success', $l->t('Maintenance mode is kept active')); |
|
97 | - }); |
|
98 | - $updater->listen('\OC\Updater', 'dbUpgradeBefore', function () use ($eventSource, $l): void { |
|
99 | - $eventSource->send('success', $l->t('Updating database schema')); |
|
100 | - }); |
|
101 | - $updater->listen('\OC\Updater', 'dbUpgrade', function () use ($eventSource, $l): void { |
|
102 | - $eventSource->send('success', $l->t('Updated database')); |
|
103 | - }); |
|
104 | - $updater->listen('\OC\Updater', 'upgradeAppStoreApp', function ($app) use ($eventSource, $l): void { |
|
105 | - $eventSource->send('success', $l->t('Update app "%s" from App Store', [$app])); |
|
106 | - }); |
|
107 | - $updater->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($eventSource, $l): void { |
|
108 | - $eventSource->send('success', $l->t('Checking whether the database schema for %s can be updated (this can take a long time depending on the database size)', [$app])); |
|
109 | - }); |
|
110 | - $updater->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($eventSource, $l): void { |
|
111 | - $eventSource->send('success', $l->t('Updated "%1$s" to %2$s', [$app, $version])); |
|
112 | - }); |
|
113 | - $updater->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use (&$incompatibleApps, &$incompatibleOverwrites): void { |
|
114 | - if (!in_array($app, $incompatibleOverwrites)) { |
|
115 | - $incompatibleApps[] = $app; |
|
116 | - } |
|
117 | - }); |
|
118 | - $updater->listen('\OC\Updater', 'failure', function ($message) use ($eventSource, $config): void { |
|
119 | - $eventSource->send('failure', $message); |
|
120 | - $eventSource->close(); |
|
121 | - $config->setSystemValue('maintenance', false); |
|
122 | - }); |
|
123 | - $updater->listen('\OC\Updater', 'setDebugLogLevel', function ($logLevel, $logLevelName) use ($eventSource, $l): void { |
|
124 | - $eventSource->send('success', $l->t('Set log level to debug')); |
|
125 | - }); |
|
126 | - $updater->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use ($eventSource, $l): void { |
|
127 | - $eventSource->send('success', $l->t('Reset log level')); |
|
128 | - }); |
|
129 | - $updater->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use ($eventSource, $l): void { |
|
130 | - $eventSource->send('success', $l->t('Starting code integrity check')); |
|
131 | - }); |
|
132 | - $updater->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use ($eventSource, $l): void { |
|
133 | - $eventSource->send('success', $l->t('Finished code integrity check')); |
|
134 | - }); |
|
89 | + $updater->listen('\OC\Updater', 'maintenanceEnabled', function () use ($eventSource, $l): void { |
|
90 | + $eventSource->send('success', $l->t('Turned on maintenance mode')); |
|
91 | + }); |
|
92 | + $updater->listen('\OC\Updater', 'maintenanceDisabled', function () use ($eventSource, $l): void { |
|
93 | + $eventSource->send('success', $l->t('Turned off maintenance mode')); |
|
94 | + }); |
|
95 | + $updater->listen('\OC\Updater', 'maintenanceActive', function () use ($eventSource, $l): void { |
|
96 | + $eventSource->send('success', $l->t('Maintenance mode is kept active')); |
|
97 | + }); |
|
98 | + $updater->listen('\OC\Updater', 'dbUpgradeBefore', function () use ($eventSource, $l): void { |
|
99 | + $eventSource->send('success', $l->t('Updating database schema')); |
|
100 | + }); |
|
101 | + $updater->listen('\OC\Updater', 'dbUpgrade', function () use ($eventSource, $l): void { |
|
102 | + $eventSource->send('success', $l->t('Updated database')); |
|
103 | + }); |
|
104 | + $updater->listen('\OC\Updater', 'upgradeAppStoreApp', function ($app) use ($eventSource, $l): void { |
|
105 | + $eventSource->send('success', $l->t('Update app "%s" from App Store', [$app])); |
|
106 | + }); |
|
107 | + $updater->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($eventSource, $l): void { |
|
108 | + $eventSource->send('success', $l->t('Checking whether the database schema for %s can be updated (this can take a long time depending on the database size)', [$app])); |
|
109 | + }); |
|
110 | + $updater->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($eventSource, $l): void { |
|
111 | + $eventSource->send('success', $l->t('Updated "%1$s" to %2$s', [$app, $version])); |
|
112 | + }); |
|
113 | + $updater->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use (&$incompatibleApps, &$incompatibleOverwrites): void { |
|
114 | + if (!in_array($app, $incompatibleOverwrites)) { |
|
115 | + $incompatibleApps[] = $app; |
|
116 | + } |
|
117 | + }); |
|
118 | + $updater->listen('\OC\Updater', 'failure', function ($message) use ($eventSource, $config): void { |
|
119 | + $eventSource->send('failure', $message); |
|
120 | + $eventSource->close(); |
|
121 | + $config->setSystemValue('maintenance', false); |
|
122 | + }); |
|
123 | + $updater->listen('\OC\Updater', 'setDebugLogLevel', function ($logLevel, $logLevelName) use ($eventSource, $l): void { |
|
124 | + $eventSource->send('success', $l->t('Set log level to debug')); |
|
125 | + }); |
|
126 | + $updater->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use ($eventSource, $l): void { |
|
127 | + $eventSource->send('success', $l->t('Reset log level')); |
|
128 | + }); |
|
129 | + $updater->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use ($eventSource, $l): void { |
|
130 | + $eventSource->send('success', $l->t('Starting code integrity check')); |
|
131 | + }); |
|
132 | + $updater->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use ($eventSource, $l): void { |
|
133 | + $eventSource->send('success', $l->t('Finished code integrity check')); |
|
134 | + }); |
|
135 | 135 | |
136 | - try { |
|
137 | - $updater->upgrade(); |
|
138 | - } catch (\Exception $e) { |
|
139 | - Server::get(LoggerInterface::class)->error( |
|
140 | - $e->getMessage(), |
|
141 | - [ |
|
142 | - 'exception' => $e, |
|
143 | - 'app' => 'update', |
|
144 | - ]); |
|
145 | - $eventSource->send('failure', get_class($e) . ': ' . $e->getMessage()); |
|
146 | - $eventSource->close(); |
|
147 | - exit(); |
|
148 | - } |
|
136 | + try { |
|
137 | + $updater->upgrade(); |
|
138 | + } catch (\Exception $e) { |
|
139 | + Server::get(LoggerInterface::class)->error( |
|
140 | + $e->getMessage(), |
|
141 | + [ |
|
142 | + 'exception' => $e, |
|
143 | + 'app' => 'update', |
|
144 | + ]); |
|
145 | + $eventSource->send('failure', get_class($e) . ': ' . $e->getMessage()); |
|
146 | + $eventSource->close(); |
|
147 | + exit(); |
|
148 | + } |
|
149 | 149 | |
150 | - $disabledApps = []; |
|
151 | - foreach ($incompatibleApps as $app) { |
|
152 | - $disabledApps[$app] = $l->t('%s (incompatible)', [$app]); |
|
153 | - } |
|
150 | + $disabledApps = []; |
|
151 | + foreach ($incompatibleApps as $app) { |
|
152 | + $disabledApps[$app] = $l->t('%s (incompatible)', [$app]); |
|
153 | + } |
|
154 | 154 | |
155 | - if (!empty($disabledApps)) { |
|
156 | - $eventSource->send('notice', $l->t('The following apps have been disabled: %s', [implode(', ', $disabledApps)])); |
|
157 | - } |
|
155 | + if (!empty($disabledApps)) { |
|
156 | + $eventSource->send('notice', $l->t('The following apps have been disabled: %s', [implode(', ', $disabledApps)])); |
|
157 | + } |
|
158 | 158 | } else { |
159 | - $eventSource->send('notice', $l->t('Already up to date')); |
|
159 | + $eventSource->send('notice', $l->t('Already up to date')); |
|
160 | 160 | } |
161 | 161 | |
162 | 162 | $eventSource->send('done', ''); |
@@ -27,139 +27,139 @@ |
||
27 | 27 | * @group DB |
28 | 28 | */ |
29 | 29 | class AccountMigratorTest extends TestCase { |
30 | - private IUserManager $userManager; |
|
31 | - private IAvatarManager $avatarManager; |
|
32 | - private AccountMigrator $migrator; |
|
33 | - private IImportSource&MockObject $importSource; |
|
34 | - private IExportDestination&MockObject $exportDestination; |
|
35 | - private OutputInterface&MockObject $output; |
|
36 | - |
|
37 | - private const ASSETS_DIR = __DIR__ . '/assets/'; |
|
38 | - |
|
39 | - private const REGEX_ACCOUNT_FILE = '/^' . Application::APP_ID . '\/' . '[a-z]+\.json' . '$/'; |
|
40 | - |
|
41 | - private const REGEX_AVATAR_FILE = '/^' . Application::APP_ID . '\/' . 'avatar\.(jpg|png)' . '$/'; |
|
42 | - |
|
43 | - private const REGEX_CONFIG_FILE = '/^' . Application::APP_ID . '\/' . '[a-z]+\.json' . '$/'; |
|
44 | - |
|
45 | - protected function setUp(): void { |
|
46 | - parent::setUp(); |
|
47 | - |
|
48 | - $app = new App(Application::APP_ID); |
|
49 | - $container = $app->getContainer(); |
|
50 | - $container->get(IConfig::class)->setSystemValue('has_internet_connection', false); |
|
51 | - |
|
52 | - $this->userManager = $container->get(IUserManager::class); |
|
53 | - $this->avatarManager = $container->get(IAvatarManager::class); |
|
54 | - $this->migrator = $container->get(AccountMigrator::class); |
|
55 | - |
|
56 | - $this->importSource = $this->createMock(IImportSource::class); |
|
57 | - $this->exportDestination = $this->createMock(IExportDestination::class); |
|
58 | - $this->output = $this->createMock(OutputInterface::class); |
|
59 | - } |
|
60 | - |
|
61 | - protected function tearDown(): void { |
|
62 | - Server::get(IConfig::class)->setSystemValue('has_internet_connection', true); |
|
63 | - parent::tearDown(); |
|
64 | - } |
|
65 | - |
|
66 | - public static function dataImportExportAccount(): array { |
|
67 | - return array_map( |
|
68 | - static function (string $filename): array { |
|
69 | - $dataPath = static::ASSETS_DIR . $filename; |
|
70 | - // For each account json file there is an avatar image and a config json file with the same basename |
|
71 | - $basename = pathinfo($filename, PATHINFO_FILENAME); |
|
72 | - $avatarPath = static::ASSETS_DIR . (file_exists(static::ASSETS_DIR . "$basename.jpg") ? "$basename.jpg" : "$basename.png"); |
|
73 | - $configPath = static::ASSETS_DIR . "$basename-config." . pathinfo($filename, PATHINFO_EXTENSION); |
|
74 | - return [ |
|
75 | - UUIDUtil::getUUID(), |
|
76 | - json_decode(file_get_contents($dataPath), true, 512, JSON_THROW_ON_ERROR), |
|
77 | - $avatarPath, |
|
78 | - json_decode(file_get_contents($configPath), true, 512, JSON_THROW_ON_ERROR), |
|
79 | - ]; |
|
80 | - }, |
|
81 | - array_filter( |
|
82 | - scandir(static::ASSETS_DIR), |
|
83 | - fn (string $filename) => pathinfo($filename, PATHINFO_EXTENSION) === 'json' && mb_strpos(pathinfo($filename, PATHINFO_FILENAME), 'config') === false, |
|
84 | - ), |
|
85 | - ); |
|
86 | - } |
|
87 | - |
|
88 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataImportExportAccount')] |
|
89 | - public function testImportExportAccount(string $userId, array $importData, string $avatarPath, array $importConfig): void { |
|
90 | - $user = $this->userManager->createUser($userId, 'topsecretpassword'); |
|
91 | - $avatarExt = pathinfo($avatarPath, PATHINFO_EXTENSION); |
|
92 | - $exportData = $importData; |
|
93 | - $exportConfig = $importConfig; |
|
94 | - // Verification status of email will be set to in progress on import so we set the export data to reflect that |
|
95 | - $exportData[IAccountManager::PROPERTY_EMAIL]['verified'] = IAccountManager::VERIFICATION_IN_PROGRESS; |
|
96 | - |
|
97 | - $this->importSource |
|
98 | - ->expects($this->once()) |
|
99 | - ->method('getMigratorVersion') |
|
100 | - ->with($this->migrator->getId()) |
|
101 | - ->willReturn(1); |
|
102 | - |
|
103 | - $calls = [ |
|
104 | - [static::REGEX_ACCOUNT_FILE, json_encode($importData)], |
|
105 | - [static::REGEX_CONFIG_FILE, json_encode($importConfig)], |
|
106 | - ]; |
|
107 | - $this->importSource |
|
108 | - ->expects($this->exactly(2)) |
|
109 | - ->method('getFileContents') |
|
110 | - ->willReturnCallback(function ($path) use (&$calls) { |
|
111 | - $expected = array_shift($calls); |
|
112 | - $this->assertMatchesRegularExpression($expected[0], $path); |
|
113 | - return $expected[1]; |
|
114 | - }); |
|
115 | - |
|
116 | - $this->importSource |
|
117 | - ->expects($this->once()) |
|
118 | - ->method('getFolderListing') |
|
119 | - ->with(Application::APP_ID . '/') |
|
120 | - ->willReturn(["avatar.$avatarExt"]); |
|
121 | - |
|
122 | - $this->importSource |
|
123 | - ->expects($this->once()) |
|
124 | - ->method('getFileAsStream') |
|
125 | - ->with($this->matchesRegularExpression(static::REGEX_AVATAR_FILE)) |
|
126 | - ->willReturn(fopen($avatarPath, 'r')); |
|
127 | - |
|
128 | - $this->migrator->import($user, $this->importSource, $this->output); |
|
129 | - |
|
130 | - $importedAvatar = $this->avatarManager->getAvatar($user->getUID()); |
|
131 | - $this->assertTrue($importedAvatar->isCustomAvatar()); |
|
132 | - |
|
133 | - /** |
|
134 | - * Avatar images are re-encoded on import therefore JPEG images which use lossy compression cannot be checked for equality |
|
135 | - * @see https://github.com/nextcloud/server/blob/9644b7e505dc90a1e683f77ad38dc6dc4e90fa2f/lib/private/legacy/OC_Image.php#L383-L390 |
|
136 | - */ |
|
137 | - |
|
138 | - if ($avatarExt !== 'jpg') { |
|
139 | - $this->assertStringEqualsFile( |
|
140 | - $avatarPath, |
|
141 | - $importedAvatar->getFile(-1)->getContent(), |
|
142 | - ); |
|
143 | - } |
|
144 | - |
|
145 | - $calls = [ |
|
146 | - [static::REGEX_ACCOUNT_FILE, new JsonMatches(json_encode($importData))], |
|
147 | - [static::REGEX_CONFIG_FILE,new JsonMatches(json_encode($importConfig))], |
|
148 | - ]; |
|
149 | - $this->exportDestination |
|
150 | - ->expects($this->exactly(2)) |
|
151 | - ->method('addFileContents') |
|
152 | - ->willReturnCallback(function ($path) use (&$calls) { |
|
153 | - $expected = array_shift($calls); |
|
154 | - $this->assertMatchesRegularExpression($expected[0], $path); |
|
155 | - return $expected[1]; |
|
156 | - }); |
|
157 | - |
|
158 | - $this->exportDestination |
|
159 | - ->expects($this->once()) |
|
160 | - ->method('addFileAsStream') |
|
161 | - ->with($this->matchesRegularExpression(static::REGEX_AVATAR_FILE), $this->isType('resource')); |
|
162 | - |
|
163 | - $this->migrator->export($user, $this->exportDestination, $this->output); |
|
164 | - } |
|
30 | + private IUserManager $userManager; |
|
31 | + private IAvatarManager $avatarManager; |
|
32 | + private AccountMigrator $migrator; |
|
33 | + private IImportSource&MockObject $importSource; |
|
34 | + private IExportDestination&MockObject $exportDestination; |
|
35 | + private OutputInterface&MockObject $output; |
|
36 | + |
|
37 | + private const ASSETS_DIR = __DIR__ . '/assets/'; |
|
38 | + |
|
39 | + private const REGEX_ACCOUNT_FILE = '/^' . Application::APP_ID . '\/' . '[a-z]+\.json' . '$/'; |
|
40 | + |
|
41 | + private const REGEX_AVATAR_FILE = '/^' . Application::APP_ID . '\/' . 'avatar\.(jpg|png)' . '$/'; |
|
42 | + |
|
43 | + private const REGEX_CONFIG_FILE = '/^' . Application::APP_ID . '\/' . '[a-z]+\.json' . '$/'; |
|
44 | + |
|
45 | + protected function setUp(): void { |
|
46 | + parent::setUp(); |
|
47 | + |
|
48 | + $app = new App(Application::APP_ID); |
|
49 | + $container = $app->getContainer(); |
|
50 | + $container->get(IConfig::class)->setSystemValue('has_internet_connection', false); |
|
51 | + |
|
52 | + $this->userManager = $container->get(IUserManager::class); |
|
53 | + $this->avatarManager = $container->get(IAvatarManager::class); |
|
54 | + $this->migrator = $container->get(AccountMigrator::class); |
|
55 | + |
|
56 | + $this->importSource = $this->createMock(IImportSource::class); |
|
57 | + $this->exportDestination = $this->createMock(IExportDestination::class); |
|
58 | + $this->output = $this->createMock(OutputInterface::class); |
|
59 | + } |
|
60 | + |
|
61 | + protected function tearDown(): void { |
|
62 | + Server::get(IConfig::class)->setSystemValue('has_internet_connection', true); |
|
63 | + parent::tearDown(); |
|
64 | + } |
|
65 | + |
|
66 | + public static function dataImportExportAccount(): array { |
|
67 | + return array_map( |
|
68 | + static function (string $filename): array { |
|
69 | + $dataPath = static::ASSETS_DIR . $filename; |
|
70 | + // For each account json file there is an avatar image and a config json file with the same basename |
|
71 | + $basename = pathinfo($filename, PATHINFO_FILENAME); |
|
72 | + $avatarPath = static::ASSETS_DIR . (file_exists(static::ASSETS_DIR . "$basename.jpg") ? "$basename.jpg" : "$basename.png"); |
|
73 | + $configPath = static::ASSETS_DIR . "$basename-config." . pathinfo($filename, PATHINFO_EXTENSION); |
|
74 | + return [ |
|
75 | + UUIDUtil::getUUID(), |
|
76 | + json_decode(file_get_contents($dataPath), true, 512, JSON_THROW_ON_ERROR), |
|
77 | + $avatarPath, |
|
78 | + json_decode(file_get_contents($configPath), true, 512, JSON_THROW_ON_ERROR), |
|
79 | + ]; |
|
80 | + }, |
|
81 | + array_filter( |
|
82 | + scandir(static::ASSETS_DIR), |
|
83 | + fn (string $filename) => pathinfo($filename, PATHINFO_EXTENSION) === 'json' && mb_strpos(pathinfo($filename, PATHINFO_FILENAME), 'config') === false, |
|
84 | + ), |
|
85 | + ); |
|
86 | + } |
|
87 | + |
|
88 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataImportExportAccount')] |
|
89 | + public function testImportExportAccount(string $userId, array $importData, string $avatarPath, array $importConfig): void { |
|
90 | + $user = $this->userManager->createUser($userId, 'topsecretpassword'); |
|
91 | + $avatarExt = pathinfo($avatarPath, PATHINFO_EXTENSION); |
|
92 | + $exportData = $importData; |
|
93 | + $exportConfig = $importConfig; |
|
94 | + // Verification status of email will be set to in progress on import so we set the export data to reflect that |
|
95 | + $exportData[IAccountManager::PROPERTY_EMAIL]['verified'] = IAccountManager::VERIFICATION_IN_PROGRESS; |
|
96 | + |
|
97 | + $this->importSource |
|
98 | + ->expects($this->once()) |
|
99 | + ->method('getMigratorVersion') |
|
100 | + ->with($this->migrator->getId()) |
|
101 | + ->willReturn(1); |
|
102 | + |
|
103 | + $calls = [ |
|
104 | + [static::REGEX_ACCOUNT_FILE, json_encode($importData)], |
|
105 | + [static::REGEX_CONFIG_FILE, json_encode($importConfig)], |
|
106 | + ]; |
|
107 | + $this->importSource |
|
108 | + ->expects($this->exactly(2)) |
|
109 | + ->method('getFileContents') |
|
110 | + ->willReturnCallback(function ($path) use (&$calls) { |
|
111 | + $expected = array_shift($calls); |
|
112 | + $this->assertMatchesRegularExpression($expected[0], $path); |
|
113 | + return $expected[1]; |
|
114 | + }); |
|
115 | + |
|
116 | + $this->importSource |
|
117 | + ->expects($this->once()) |
|
118 | + ->method('getFolderListing') |
|
119 | + ->with(Application::APP_ID . '/') |
|
120 | + ->willReturn(["avatar.$avatarExt"]); |
|
121 | + |
|
122 | + $this->importSource |
|
123 | + ->expects($this->once()) |
|
124 | + ->method('getFileAsStream') |
|
125 | + ->with($this->matchesRegularExpression(static::REGEX_AVATAR_FILE)) |
|
126 | + ->willReturn(fopen($avatarPath, 'r')); |
|
127 | + |
|
128 | + $this->migrator->import($user, $this->importSource, $this->output); |
|
129 | + |
|
130 | + $importedAvatar = $this->avatarManager->getAvatar($user->getUID()); |
|
131 | + $this->assertTrue($importedAvatar->isCustomAvatar()); |
|
132 | + |
|
133 | + /** |
|
134 | + * Avatar images are re-encoded on import therefore JPEG images which use lossy compression cannot be checked for equality |
|
135 | + * @see https://github.com/nextcloud/server/blob/9644b7e505dc90a1e683f77ad38dc6dc4e90fa2f/lib/private/legacy/OC_Image.php#L383-L390 |
|
136 | + */ |
|
137 | + |
|
138 | + if ($avatarExt !== 'jpg') { |
|
139 | + $this->assertStringEqualsFile( |
|
140 | + $avatarPath, |
|
141 | + $importedAvatar->getFile(-1)->getContent(), |
|
142 | + ); |
|
143 | + } |
|
144 | + |
|
145 | + $calls = [ |
|
146 | + [static::REGEX_ACCOUNT_FILE, new JsonMatches(json_encode($importData))], |
|
147 | + [static::REGEX_CONFIG_FILE,new JsonMatches(json_encode($importConfig))], |
|
148 | + ]; |
|
149 | + $this->exportDestination |
|
150 | + ->expects($this->exactly(2)) |
|
151 | + ->method('addFileContents') |
|
152 | + ->willReturnCallback(function ($path) use (&$calls) { |
|
153 | + $expected = array_shift($calls); |
|
154 | + $this->assertMatchesRegularExpression($expected[0], $path); |
|
155 | + return $expected[1]; |
|
156 | + }); |
|
157 | + |
|
158 | + $this->exportDestination |
|
159 | + ->expects($this->once()) |
|
160 | + ->method('addFileAsStream') |
|
161 | + ->with($this->matchesRegularExpression(static::REGEX_AVATAR_FILE), $this->isType('resource')); |
|
162 | + |
|
163 | + $this->migrator->export($user, $this->exportDestination, $this->output); |
|
164 | + } |
|
165 | 165 | } |
@@ -28,36 +28,36 @@ |
||
28 | 28 | * @group DB |
29 | 29 | */ |
30 | 30 | class ApplicationTest extends TestCase { |
31 | - protected Application $app; |
|
32 | - protected IAppContainer $container; |
|
33 | - |
|
34 | - protected function setUp(): void { |
|
35 | - parent::setUp(); |
|
36 | - $this->app = new Application(); |
|
37 | - $this->container = $this->app->getContainer(); |
|
38 | - } |
|
39 | - |
|
40 | - public function testContainerAppName(): void { |
|
41 | - $this->app = new Application(); |
|
42 | - $this->assertEquals('settings', $this->container->getAppName()); |
|
43 | - } |
|
44 | - |
|
45 | - public static function dataContainerQuery(): array { |
|
46 | - return [ |
|
47 | - [AdminSettingsController::class, Controller::class], |
|
48 | - [AppSettingsController::class, Controller::class], |
|
49 | - [AuthSettingsController::class, Controller::class], |
|
50 | - [CheckSetupController::class, Controller::class], |
|
51 | - [LogSettingsController::class, Controller::class], |
|
52 | - [MailSettingsController::class, Controller::class], |
|
53 | - [UsersController::class, Controller::class], |
|
54 | - |
|
55 | - [SubadminMiddleware::class, Middleware::class], |
|
56 | - ]; |
|
57 | - } |
|
58 | - |
|
59 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataContainerQuery')] |
|
60 | - public function testContainerQuery(string $service, string $expected): void { |
|
61 | - $this->assertTrue($this->container->query($service) instanceof $expected); |
|
62 | - } |
|
31 | + protected Application $app; |
|
32 | + protected IAppContainer $container; |
|
33 | + |
|
34 | + protected function setUp(): void { |
|
35 | + parent::setUp(); |
|
36 | + $this->app = new Application(); |
|
37 | + $this->container = $this->app->getContainer(); |
|
38 | + } |
|
39 | + |
|
40 | + public function testContainerAppName(): void { |
|
41 | + $this->app = new Application(); |
|
42 | + $this->assertEquals('settings', $this->container->getAppName()); |
|
43 | + } |
|
44 | + |
|
45 | + public static function dataContainerQuery(): array { |
|
46 | + return [ |
|
47 | + [AdminSettingsController::class, Controller::class], |
|
48 | + [AppSettingsController::class, Controller::class], |
|
49 | + [AuthSettingsController::class, Controller::class], |
|
50 | + [CheckSetupController::class, Controller::class], |
|
51 | + [LogSettingsController::class, Controller::class], |
|
52 | + [MailSettingsController::class, Controller::class], |
|
53 | + [UsersController::class, Controller::class], |
|
54 | + |
|
55 | + [SubadminMiddleware::class, Middleware::class], |
|
56 | + ]; |
|
57 | + } |
|
58 | + |
|
59 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataContainerQuery')] |
|
60 | + public function testContainerQuery(string $service, string $expected): void { |
|
61 | + $this->assertTrue($this->container->query($service) instanceof $expected); |
|
62 | + } |
|
63 | 63 | } |
@@ -17,103 +17,103 @@ |
||
17 | 17 | use Test\TestCase; |
18 | 18 | |
19 | 19 | class ForwardedForHeadersTest extends TestCase { |
20 | - private IL10N $l10n; |
|
21 | - private IConfig $config; |
|
22 | - private IURLGenerator $urlGenerator; |
|
23 | - private IRequest $request; |
|
24 | - private ForwardedForHeaders $check; |
|
20 | + private IL10N $l10n; |
|
21 | + private IConfig $config; |
|
22 | + private IURLGenerator $urlGenerator; |
|
23 | + private IRequest $request; |
|
24 | + private ForwardedForHeaders $check; |
|
25 | 25 | |
26 | - protected function setUp(): void { |
|
27 | - parent::setUp(); |
|
26 | + protected function setUp(): void { |
|
27 | + parent::setUp(); |
|
28 | 28 | |
29 | - $this->l10n = $this->createMock(IL10N::class); |
|
30 | - $this->l10n->expects($this->any()) |
|
31 | - ->method('t') |
|
32 | - ->willReturnCallback(function ($message, array $replace) { |
|
33 | - return vsprintf($message, $replace); |
|
34 | - }); |
|
35 | - $this->config = $this->getMockBuilder(IConfig::class)->getMock(); |
|
36 | - $this->urlGenerator = $this->getMockBuilder(IURLGenerator::class)->getMock(); |
|
37 | - $this->request = $this->getMockBuilder(IRequest::class)->getMock(); |
|
38 | - $this->check = new ForwardedForHeaders( |
|
39 | - $this->l10n, |
|
40 | - $this->config, |
|
41 | - $this->urlGenerator, |
|
42 | - $this->request, |
|
43 | - ); |
|
44 | - } |
|
29 | + $this->l10n = $this->createMock(IL10N::class); |
|
30 | + $this->l10n->expects($this->any()) |
|
31 | + ->method('t') |
|
32 | + ->willReturnCallback(function ($message, array $replace) { |
|
33 | + return vsprintf($message, $replace); |
|
34 | + }); |
|
35 | + $this->config = $this->getMockBuilder(IConfig::class)->getMock(); |
|
36 | + $this->urlGenerator = $this->getMockBuilder(IURLGenerator::class)->getMock(); |
|
37 | + $this->request = $this->getMockBuilder(IRequest::class)->getMock(); |
|
38 | + $this->check = new ForwardedForHeaders( |
|
39 | + $this->l10n, |
|
40 | + $this->config, |
|
41 | + $this->urlGenerator, |
|
42 | + $this->request, |
|
43 | + ); |
|
44 | + } |
|
45 | 45 | |
46 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataForwardedForHeadersWorking')] |
|
47 | - public function testForwardedForHeadersWorking(array $trustedProxies, string $remoteAddrNotForwarded, string $remoteAddr, string $result): void { |
|
48 | - $this->config->expects($this->once()) |
|
49 | - ->method('getSystemValue') |
|
50 | - ->with('trusted_proxies', []) |
|
51 | - ->willReturn($trustedProxies); |
|
52 | - $this->request->expects($this->atLeastOnce()) |
|
53 | - ->method('getHeader') |
|
54 | - ->willReturnMap([ |
|
55 | - ['REMOTE_ADDR', $remoteAddrNotForwarded], |
|
56 | - ['X-Forwarded-Host', ''] |
|
57 | - ]); |
|
58 | - $this->request->expects($this->any()) |
|
59 | - ->method('getRemoteAddress') |
|
60 | - ->willReturn($remoteAddr); |
|
46 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataForwardedForHeadersWorking')] |
|
47 | + public function testForwardedForHeadersWorking(array $trustedProxies, string $remoteAddrNotForwarded, string $remoteAddr, string $result): void { |
|
48 | + $this->config->expects($this->once()) |
|
49 | + ->method('getSystemValue') |
|
50 | + ->with('trusted_proxies', []) |
|
51 | + ->willReturn($trustedProxies); |
|
52 | + $this->request->expects($this->atLeastOnce()) |
|
53 | + ->method('getHeader') |
|
54 | + ->willReturnMap([ |
|
55 | + ['REMOTE_ADDR', $remoteAddrNotForwarded], |
|
56 | + ['X-Forwarded-Host', ''] |
|
57 | + ]); |
|
58 | + $this->request->expects($this->any()) |
|
59 | + ->method('getRemoteAddress') |
|
60 | + ->willReturn($remoteAddr); |
|
61 | 61 | |
62 | - $this->assertEquals( |
|
63 | - $result, |
|
64 | - $this->check->run()->getSeverity() |
|
65 | - ); |
|
66 | - } |
|
62 | + $this->assertEquals( |
|
63 | + $result, |
|
64 | + $this->check->run()->getSeverity() |
|
65 | + ); |
|
66 | + } |
|
67 | 67 | |
68 | - public static function dataForwardedForHeadersWorking(): array { |
|
69 | - return [ |
|
70 | - // description => trusted proxies, getHeader('REMOTE_ADDR'), getRemoteAddr, expected result |
|
71 | - 'no trusted proxies' => [[], '2.2.2.2', '2.2.2.2', SetupResult::SUCCESS], |
|
72 | - 'trusted proxy, remote addr not trusted proxy' => [['1.1.1.1'], '2.2.2.2', '2.2.2.2', SetupResult::SUCCESS], |
|
73 | - 'trusted proxy, remote addr is trusted proxy, x-forwarded-for working' => [['1.1.1.1'], '1.1.1.1', '2.2.2.2', SetupResult::SUCCESS], |
|
74 | - 'trusted proxy, remote addr is trusted proxy, x-forwarded-for not set' => [['1.1.1.1'], '1.1.1.1', '1.1.1.1', SetupResult::WARNING], |
|
75 | - ]; |
|
76 | - } |
|
68 | + public static function dataForwardedForHeadersWorking(): array { |
|
69 | + return [ |
|
70 | + // description => trusted proxies, getHeader('REMOTE_ADDR'), getRemoteAddr, expected result |
|
71 | + 'no trusted proxies' => [[], '2.2.2.2', '2.2.2.2', SetupResult::SUCCESS], |
|
72 | + 'trusted proxy, remote addr not trusted proxy' => [['1.1.1.1'], '2.2.2.2', '2.2.2.2', SetupResult::SUCCESS], |
|
73 | + 'trusted proxy, remote addr is trusted proxy, x-forwarded-for working' => [['1.1.1.1'], '1.1.1.1', '2.2.2.2', SetupResult::SUCCESS], |
|
74 | + 'trusted proxy, remote addr is trusted proxy, x-forwarded-for not set' => [['1.1.1.1'], '1.1.1.1', '1.1.1.1', SetupResult::WARNING], |
|
75 | + ]; |
|
76 | + } |
|
77 | 77 | |
78 | - public function testForwardedHostPresentButTrustedProxiesNotAnArray(): void { |
|
79 | - $this->config->expects($this->once()) |
|
80 | - ->method('getSystemValue') |
|
81 | - ->with('trusted_proxies', []) |
|
82 | - ->willReturn('1.1.1.1'); |
|
83 | - $this->request->expects($this->atLeastOnce()) |
|
84 | - ->method('getHeader') |
|
85 | - ->willReturnMap([ |
|
86 | - ['REMOTE_ADDR', '1.1.1.1'], |
|
87 | - ['X-Forwarded-Host', 'nextcloud.test'] |
|
88 | - ]); |
|
89 | - $this->request->expects($this->any()) |
|
90 | - ->method('getRemoteAddress') |
|
91 | - ->willReturn('1.1.1.1'); |
|
78 | + public function testForwardedHostPresentButTrustedProxiesNotAnArray(): void { |
|
79 | + $this->config->expects($this->once()) |
|
80 | + ->method('getSystemValue') |
|
81 | + ->with('trusted_proxies', []) |
|
82 | + ->willReturn('1.1.1.1'); |
|
83 | + $this->request->expects($this->atLeastOnce()) |
|
84 | + ->method('getHeader') |
|
85 | + ->willReturnMap([ |
|
86 | + ['REMOTE_ADDR', '1.1.1.1'], |
|
87 | + ['X-Forwarded-Host', 'nextcloud.test'] |
|
88 | + ]); |
|
89 | + $this->request->expects($this->any()) |
|
90 | + ->method('getRemoteAddress') |
|
91 | + ->willReturn('1.1.1.1'); |
|
92 | 92 | |
93 | - $this->assertEquals( |
|
94 | - SetupResult::ERROR, |
|
95 | - $this->check->run()->getSeverity() |
|
96 | - ); |
|
97 | - } |
|
93 | + $this->assertEquals( |
|
94 | + SetupResult::ERROR, |
|
95 | + $this->check->run()->getSeverity() |
|
96 | + ); |
|
97 | + } |
|
98 | 98 | |
99 | - public function testForwardedHostPresentButTrustedProxiesEmpty(): void { |
|
100 | - $this->config->expects($this->once()) |
|
101 | - ->method('getSystemValue') |
|
102 | - ->with('trusted_proxies', []) |
|
103 | - ->willReturn([]); |
|
104 | - $this->request->expects($this->atLeastOnce()) |
|
105 | - ->method('getHeader') |
|
106 | - ->willReturnMap([ |
|
107 | - ['REMOTE_ADDR', '1.1.1.1'], |
|
108 | - ['X-Forwarded-Host', 'nextcloud.test'] |
|
109 | - ]); |
|
110 | - $this->request->expects($this->any()) |
|
111 | - ->method('getRemoteAddress') |
|
112 | - ->willReturn('1.1.1.1'); |
|
99 | + public function testForwardedHostPresentButTrustedProxiesEmpty(): void { |
|
100 | + $this->config->expects($this->once()) |
|
101 | + ->method('getSystemValue') |
|
102 | + ->with('trusted_proxies', []) |
|
103 | + ->willReturn([]); |
|
104 | + $this->request->expects($this->atLeastOnce()) |
|
105 | + ->method('getHeader') |
|
106 | + ->willReturnMap([ |
|
107 | + ['REMOTE_ADDR', '1.1.1.1'], |
|
108 | + ['X-Forwarded-Host', 'nextcloud.test'] |
|
109 | + ]); |
|
110 | + $this->request->expects($this->any()) |
|
111 | + ->method('getRemoteAddress') |
|
112 | + ->willReturn('1.1.1.1'); |
|
113 | 113 | |
114 | - $this->assertEquals( |
|
115 | - SetupResult::ERROR, |
|
116 | - $this->check->run()->getSeverity() |
|
117 | - ); |
|
118 | - } |
|
114 | + $this->assertEquals( |
|
115 | + SetupResult::ERROR, |
|
116 | + $this->check->run()->getSeverity() |
|
117 | + ); |
|
118 | + } |
|
119 | 119 | } |
@@ -19,58 +19,58 @@ |
||
19 | 19 | use Test\TestCase; |
20 | 20 | |
21 | 21 | class LoggingLevelTest extends TestCase { |
22 | - private IL10N&MockObject $l10n; |
|
23 | - private IConfig&MockObject $config; |
|
24 | - private IURLGenerator&MockObject $urlGenerator; |
|
22 | + private IL10N&MockObject $l10n; |
|
23 | + private IConfig&MockObject $config; |
|
24 | + private IURLGenerator&MockObject $urlGenerator; |
|
25 | 25 | |
26 | - protected function setUp(): void { |
|
27 | - parent::setUp(); |
|
26 | + protected function setUp(): void { |
|
27 | + parent::setUp(); |
|
28 | 28 | |
29 | - $this->l10n = $this->createMock(IL10N::class); |
|
30 | - $this->l10n->expects($this->any()) |
|
31 | - ->method('t') |
|
32 | - ->willReturnCallback(function ($message, array $replace) { |
|
33 | - return vsprintf($message, $replace); |
|
34 | - }); |
|
35 | - $this->config = $this->createMock(IConfig::class); |
|
36 | - $this->urlGenerator = $this->createMock(IURLGenerator::class); |
|
37 | - } |
|
29 | + $this->l10n = $this->createMock(IL10N::class); |
|
30 | + $this->l10n->expects($this->any()) |
|
31 | + ->method('t') |
|
32 | + ->willReturnCallback(function ($message, array $replace) { |
|
33 | + return vsprintf($message, $replace); |
|
34 | + }); |
|
35 | + $this->config = $this->createMock(IConfig::class); |
|
36 | + $this->urlGenerator = $this->createMock(IURLGenerator::class); |
|
37 | + } |
|
38 | 38 | |
39 | - public static function dataRun(): array { |
|
40 | - return [ |
|
41 | - [ILogger::INFO, SetupResult::SUCCESS], |
|
42 | - [ILogger::WARN, SetupResult::SUCCESS], |
|
43 | - [ILogger::ERROR, SetupResult::SUCCESS], |
|
44 | - [ILogger::FATAL, SetupResult::SUCCESS], |
|
39 | + public static function dataRun(): array { |
|
40 | + return [ |
|
41 | + [ILogger::INFO, SetupResult::SUCCESS], |
|
42 | + [ILogger::WARN, SetupResult::SUCCESS], |
|
43 | + [ILogger::ERROR, SetupResult::SUCCESS], |
|
44 | + [ILogger::FATAL, SetupResult::SUCCESS], |
|
45 | 45 | |
46 | - // Debug is valid but will result in an warning |
|
47 | - [ILogger::DEBUG, SetupResult::WARNING], |
|
46 | + // Debug is valid but will result in an warning |
|
47 | + [ILogger::DEBUG, SetupResult::WARNING], |
|
48 | 48 | |
49 | - // negative - invalid range |
|
50 | - [-1, SetupResult::ERROR], |
|
51 | - // string value instead of number |
|
52 | - ['1', SetupResult::ERROR], |
|
53 | - // random string value |
|
54 | - ['error', SetupResult::ERROR], |
|
55 | - // PSR logger value |
|
56 | - [LogLevel::ALERT, SetupResult::ERROR], |
|
57 | - // out of range |
|
58 | - [ILogger::FATAL + 1, SetupResult::ERROR], |
|
59 | - ]; |
|
60 | - } |
|
49 | + // negative - invalid range |
|
50 | + [-1, SetupResult::ERROR], |
|
51 | + // string value instead of number |
|
52 | + ['1', SetupResult::ERROR], |
|
53 | + // random string value |
|
54 | + ['error', SetupResult::ERROR], |
|
55 | + // PSR logger value |
|
56 | + [LogLevel::ALERT, SetupResult::ERROR], |
|
57 | + // out of range |
|
58 | + [ILogger::FATAL + 1, SetupResult::ERROR], |
|
59 | + ]; |
|
60 | + } |
|
61 | 61 | |
62 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataRun')] |
|
63 | - public function testRun(string|int $value, string $expected): void { |
|
64 | - $this->urlGenerator->method('linkToDocs')->willReturn('admin-logging'); |
|
62 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataRun')] |
|
63 | + public function testRun(string|int $value, string $expected): void { |
|
64 | + $this->urlGenerator->method('linkToDocs')->willReturn('admin-logging'); |
|
65 | 65 | |
66 | - $this->config->expects(self::once()) |
|
67 | - ->method('getSystemValue') |
|
68 | - ->with('loglevel', ILogger::WARN) |
|
69 | - ->willReturn($value); |
|
66 | + $this->config->expects(self::once()) |
|
67 | + ->method('getSystemValue') |
|
68 | + ->with('loglevel', ILogger::WARN) |
|
69 | + ->willReturn($value); |
|
70 | 70 | |
71 | - $check = new LoggingLevel($this->l10n, $this->config, $this->urlGenerator); |
|
71 | + $check = new LoggingLevel($this->l10n, $this->config, $this->urlGenerator); |
|
72 | 72 | |
73 | - $result = $check->run(); |
|
74 | - $this->assertEquals($expected, $result->getSeverity()); |
|
75 | - } |
|
73 | + $result = $check->run(); |
|
74 | + $this->assertEquals($expected, $result->getSeverity()); |
|
75 | + } |
|
76 | 76 | } |
@@ -29,7 +29,7 @@ discard block |
||
29 | 29 | $this->l10n = $this->createMock(IL10N::class); |
30 | 30 | $this->l10n->expects($this->any()) |
31 | 31 | ->method('t') |
32 | - ->willReturnCallback(function ($message, array $replace) { |
|
32 | + ->willReturnCallback(function($message, array $replace) { |
|
33 | 33 | return vsprintf($message, $replace); |
34 | 34 | }); |
35 | 35 | $this->config = $this->createMock(IConfig::class); |
@@ -60,7 +60,7 @@ discard block |
||
60 | 60 | } |
61 | 61 | |
62 | 62 | #[\PHPUnit\Framework\Attributes\DataProvider('dataRun')] |
63 | - public function testRun(string|int $value, string $expected): void { |
|
63 | + public function testRun(string | int $value, string $expected): void { |
|
64 | 64 | $this->urlGenerator->method('linkToDocs')->willReturn('admin-logging'); |
65 | 65 | |
66 | 66 | $this->config->expects(self::once()) |
@@ -20,98 +20,98 @@ |
||
20 | 20 | use Test\TestCase; |
21 | 21 | |
22 | 22 | class DataDirectoryProtectedTest extends TestCase { |
23 | - private IL10N&MockObject $l10n; |
|
24 | - private IConfig&MockObject $config; |
|
25 | - private IURLGenerator&MockObject $urlGenerator; |
|
26 | - private IClientService&MockObject $clientService; |
|
27 | - private LoggerInterface&MockObject $logger; |
|
28 | - private DataDirectoryProtected&MockObject $setupcheck; |
|
29 | - |
|
30 | - protected function setUp(): void { |
|
31 | - parent::setUp(); |
|
32 | - |
|
33 | - $this->l10n = $this->createMock(IL10N::class); |
|
34 | - $this->l10n->expects($this->any()) |
|
35 | - ->method('t') |
|
36 | - ->willReturnCallback(function ($message, array $replace) { |
|
37 | - return vsprintf($message, $replace); |
|
38 | - }); |
|
39 | - |
|
40 | - $this->config = $this->createMock(IConfig::class); |
|
41 | - $this->urlGenerator = $this->createMock(IURLGenerator::class); |
|
42 | - $this->clientService = $this->createMock(IClientService::class); |
|
43 | - $this->logger = $this->createMock(LoggerInterface::class); |
|
44 | - |
|
45 | - $this->setupcheck = $this->getMockBuilder(DataDirectoryProtected::class) |
|
46 | - ->onlyMethods(['runRequest']) |
|
47 | - ->setConstructorArgs([ |
|
48 | - $this->l10n, |
|
49 | - $this->config, |
|
50 | - $this->urlGenerator, |
|
51 | - $this->clientService, |
|
52 | - $this->logger, |
|
53 | - ]) |
|
54 | - ->getMock(); |
|
55 | - } |
|
56 | - |
|
57 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTestStatusCode')] |
|
58 | - public function testStatusCode(array $status, string $expected, bool $hasBody): void { |
|
59 | - $responses = array_map(function ($state) use ($hasBody) { |
|
60 | - $response = $this->createMock(IResponse::class); |
|
61 | - $response->expects($this->any())->method('getStatusCode')->willReturn($state); |
|
62 | - $response->expects(($this->atMost(1)))->method('getBody')->willReturn($hasBody ? '# Nextcloud data directory' : 'something else'); |
|
63 | - return $response; |
|
64 | - }, $status); |
|
65 | - |
|
66 | - $this->setupcheck |
|
67 | - ->expects($this->once()) |
|
68 | - ->method('runRequest') |
|
69 | - ->will($this->generate($responses)); |
|
70 | - |
|
71 | - $this->config |
|
72 | - ->expects($this->once()) |
|
73 | - ->method('getSystemValueString') |
|
74 | - ->willReturn(''); |
|
75 | - |
|
76 | - $result = $this->setupcheck->run(); |
|
77 | - $this->assertEquals($expected, $result->getSeverity()); |
|
78 | - } |
|
79 | - |
|
80 | - public static function dataTestStatusCode(): array { |
|
81 | - return [ |
|
82 | - 'success: forbidden access' => [[403], SetupResult::SUCCESS, true], |
|
83 | - 'success: forbidden access with redirect' => [[200], SetupResult::SUCCESS, false], |
|
84 | - 'error: can access' => [[200], SetupResult::ERROR, true], |
|
85 | - 'error: one forbidden one can access' => [[403, 200], SetupResult::ERROR, true], |
|
86 | - 'warning: connection issue' => [[], SetupResult::WARNING, true], |
|
87 | - ]; |
|
88 | - } |
|
89 | - |
|
90 | - public function testNoResponse(): void { |
|
91 | - $response = $this->createMock(IResponse::class); |
|
92 | - $response->expects($this->any())->method('getStatusCode')->willReturn(200); |
|
93 | - |
|
94 | - $this->setupcheck |
|
95 | - ->expects($this->once()) |
|
96 | - ->method('runRequest') |
|
97 | - ->will($this->generate([])); |
|
98 | - |
|
99 | - $this->config |
|
100 | - ->expects($this->once()) |
|
101 | - ->method('getSystemValueString') |
|
102 | - ->willReturn(''); |
|
103 | - |
|
104 | - $result = $this->setupcheck->run(); |
|
105 | - $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); |
|
106 | - $this->assertMatchesRegularExpression('/^Could not check/', $result->getDescription()); |
|
107 | - } |
|
108 | - |
|
109 | - /** |
|
110 | - * Helper function creates a nicer interface for mocking Generator behavior |
|
111 | - */ |
|
112 | - protected function generate(array $yield_values) { |
|
113 | - return $this->returnCallback(function () use ($yield_values) { |
|
114 | - yield from $yield_values; |
|
115 | - }); |
|
116 | - } |
|
23 | + private IL10N&MockObject $l10n; |
|
24 | + private IConfig&MockObject $config; |
|
25 | + private IURLGenerator&MockObject $urlGenerator; |
|
26 | + private IClientService&MockObject $clientService; |
|
27 | + private LoggerInterface&MockObject $logger; |
|
28 | + private DataDirectoryProtected&MockObject $setupcheck; |
|
29 | + |
|
30 | + protected function setUp(): void { |
|
31 | + parent::setUp(); |
|
32 | + |
|
33 | + $this->l10n = $this->createMock(IL10N::class); |
|
34 | + $this->l10n->expects($this->any()) |
|
35 | + ->method('t') |
|
36 | + ->willReturnCallback(function ($message, array $replace) { |
|
37 | + return vsprintf($message, $replace); |
|
38 | + }); |
|
39 | + |
|
40 | + $this->config = $this->createMock(IConfig::class); |
|
41 | + $this->urlGenerator = $this->createMock(IURLGenerator::class); |
|
42 | + $this->clientService = $this->createMock(IClientService::class); |
|
43 | + $this->logger = $this->createMock(LoggerInterface::class); |
|
44 | + |
|
45 | + $this->setupcheck = $this->getMockBuilder(DataDirectoryProtected::class) |
|
46 | + ->onlyMethods(['runRequest']) |
|
47 | + ->setConstructorArgs([ |
|
48 | + $this->l10n, |
|
49 | + $this->config, |
|
50 | + $this->urlGenerator, |
|
51 | + $this->clientService, |
|
52 | + $this->logger, |
|
53 | + ]) |
|
54 | + ->getMock(); |
|
55 | + } |
|
56 | + |
|
57 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestStatusCode')] |
|
58 | + public function testStatusCode(array $status, string $expected, bool $hasBody): void { |
|
59 | + $responses = array_map(function ($state) use ($hasBody) { |
|
60 | + $response = $this->createMock(IResponse::class); |
|
61 | + $response->expects($this->any())->method('getStatusCode')->willReturn($state); |
|
62 | + $response->expects(($this->atMost(1)))->method('getBody')->willReturn($hasBody ? '# Nextcloud data directory' : 'something else'); |
|
63 | + return $response; |
|
64 | + }, $status); |
|
65 | + |
|
66 | + $this->setupcheck |
|
67 | + ->expects($this->once()) |
|
68 | + ->method('runRequest') |
|
69 | + ->will($this->generate($responses)); |
|
70 | + |
|
71 | + $this->config |
|
72 | + ->expects($this->once()) |
|
73 | + ->method('getSystemValueString') |
|
74 | + ->willReturn(''); |
|
75 | + |
|
76 | + $result = $this->setupcheck->run(); |
|
77 | + $this->assertEquals($expected, $result->getSeverity()); |
|
78 | + } |
|
79 | + |
|
80 | + public static function dataTestStatusCode(): array { |
|
81 | + return [ |
|
82 | + 'success: forbidden access' => [[403], SetupResult::SUCCESS, true], |
|
83 | + 'success: forbidden access with redirect' => [[200], SetupResult::SUCCESS, false], |
|
84 | + 'error: can access' => [[200], SetupResult::ERROR, true], |
|
85 | + 'error: one forbidden one can access' => [[403, 200], SetupResult::ERROR, true], |
|
86 | + 'warning: connection issue' => [[], SetupResult::WARNING, true], |
|
87 | + ]; |
|
88 | + } |
|
89 | + |
|
90 | + public function testNoResponse(): void { |
|
91 | + $response = $this->createMock(IResponse::class); |
|
92 | + $response->expects($this->any())->method('getStatusCode')->willReturn(200); |
|
93 | + |
|
94 | + $this->setupcheck |
|
95 | + ->expects($this->once()) |
|
96 | + ->method('runRequest') |
|
97 | + ->will($this->generate([])); |
|
98 | + |
|
99 | + $this->config |
|
100 | + ->expects($this->once()) |
|
101 | + ->method('getSystemValueString') |
|
102 | + ->willReturn(''); |
|
103 | + |
|
104 | + $result = $this->setupcheck->run(); |
|
105 | + $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); |
|
106 | + $this->assertMatchesRegularExpression('/^Could not check/', $result->getDescription()); |
|
107 | + } |
|
108 | + |
|
109 | + /** |
|
110 | + * Helper function creates a nicer interface for mocking Generator behavior |
|
111 | + */ |
|
112 | + protected function generate(array $yield_values) { |
|
113 | + return $this->returnCallback(function () use ($yield_values) { |
|
114 | + yield from $yield_values; |
|
115 | + }); |
|
116 | + } |
|
117 | 117 | } |
@@ -33,7 +33,7 @@ discard block |
||
33 | 33 | $this->l10n = $this->createMock(IL10N::class); |
34 | 34 | $this->l10n->expects($this->any()) |
35 | 35 | ->method('t') |
36 | - ->willReturnCallback(function ($message, array $replace) { |
|
36 | + ->willReturnCallback(function($message, array $replace) { |
|
37 | 37 | return vsprintf($message, $replace); |
38 | 38 | }); |
39 | 39 | |
@@ -56,7 +56,7 @@ discard block |
||
56 | 56 | |
57 | 57 | #[\PHPUnit\Framework\Attributes\DataProvider('dataTestStatusCode')] |
58 | 58 | public function testStatusCode(array $status, string $expected, bool $hasBody): void { |
59 | - $responses = array_map(function ($state) use ($hasBody) { |
|
59 | + $responses = array_map(function($state) use ($hasBody) { |
|
60 | 60 | $response = $this->createMock(IResponse::class); |
61 | 61 | $response->expects($this->any())->method('getStatusCode')->willReturn($state); |
62 | 62 | $response->expects(($this->atMost(1)))->method('getBody')->willReturn($hasBody ? '# Nextcloud data directory' : 'something else'); |
@@ -110,7 +110,7 @@ discard block |
||
110 | 110 | * Helper function creates a nicer interface for mocking Generator behavior |
111 | 111 | */ |
112 | 112 | protected function generate(array $yield_values) { |
113 | - return $this->returnCallback(function () use ($yield_values) { |
|
113 | + return $this->returnCallback(function() use ($yield_values) { |
|
114 | 114 | yield from $yield_values; |
115 | 115 | }); |
116 | 116 | } |
@@ -20,177 +20,177 @@ |
||
20 | 20 | use Test\TestCase; |
21 | 21 | |
22 | 22 | class SecurityHeadersTest extends TestCase { |
23 | - private IL10N&MockObject $l10n; |
|
24 | - private IConfig&MockObject $config; |
|
25 | - private IURLGenerator&MockObject $urlGenerator; |
|
26 | - private IClientService&MockObject $clientService; |
|
27 | - private LoggerInterface&MockObject $logger; |
|
28 | - private SecurityHeaders&MockObject $setupcheck; |
|
29 | - |
|
30 | - protected function setUp(): void { |
|
31 | - parent::setUp(); |
|
32 | - |
|
33 | - $this->l10n = $this->createMock(IL10N::class); |
|
34 | - $this->l10n->expects($this->any()) |
|
35 | - ->method('t') |
|
36 | - ->willReturnCallback(function ($message, array $replace) { |
|
37 | - return vsprintf($message, $replace); |
|
38 | - }); |
|
39 | - |
|
40 | - $this->config = $this->createMock(IConfig::class); |
|
41 | - $this->urlGenerator = $this->createMock(IURLGenerator::class); |
|
42 | - $this->clientService = $this->createMock(IClientService::class); |
|
43 | - $this->logger = $this->createMock(LoggerInterface::class); |
|
44 | - |
|
45 | - $this->setupcheck = $this->getMockBuilder(SecurityHeaders::class) |
|
46 | - ->onlyMethods(['runRequest']) |
|
47 | - ->setConstructorArgs([ |
|
48 | - $this->l10n, |
|
49 | - $this->config, |
|
50 | - $this->urlGenerator, |
|
51 | - $this->clientService, |
|
52 | - $this->logger, |
|
53 | - ]) |
|
54 | - ->getMock(); |
|
55 | - } |
|
56 | - |
|
57 | - public function testInvalidStatusCode(): void { |
|
58 | - $this->setupResponse(500, []); |
|
59 | - |
|
60 | - $result = $this->setupcheck->run(); |
|
61 | - $this->assertMatchesRegularExpression('/^Could not check that your web server serves security headers correctly/', $result->getDescription()); |
|
62 | - $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); |
|
63 | - } |
|
64 | - |
|
65 | - public function testAllHeadersMissing(): void { |
|
66 | - $this->setupResponse(200, []); |
|
67 | - |
|
68 | - $result = $this->setupcheck->run(); |
|
69 | - $this->assertMatchesRegularExpression('/^Some headers are not set correctly on your instance/', $result->getDescription()); |
|
70 | - $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); |
|
71 | - } |
|
72 | - |
|
73 | - public function testSomeHeadersMissing(): void { |
|
74 | - $this->setupResponse( |
|
75 | - 200, |
|
76 | - [ |
|
77 | - 'X-Robots-Tag' => 'noindex, nofollow', |
|
78 | - 'X-Frame-Options' => 'SAMEORIGIN', |
|
79 | - 'Strict-Transport-Security' => 'max-age=15768000;preload', |
|
80 | - 'X-Permitted-Cross-Domain-Policies' => 'none', |
|
81 | - 'Referrer-Policy' => 'no-referrer', |
|
82 | - ] |
|
83 | - ); |
|
84 | - |
|
85 | - $result = $this->setupcheck->run(); |
|
86 | - $this->assertEquals( |
|
87 | - "Some headers are not set correctly on your instance\n- The `X-Content-Type-Options` HTTP header is not set to `nosniff`. This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.\n", |
|
88 | - $result->getDescription() |
|
89 | - ); |
|
90 | - $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); |
|
91 | - } |
|
92 | - |
|
93 | - public static function dataSuccess(): array { |
|
94 | - return [ |
|
95 | - // description => modifiedHeaders |
|
96 | - 'basic' => [[]], |
|
97 | - 'no-space-in-x-robots' => [['X-Robots-Tag' => 'noindex,nofollow']], |
|
98 | - 'strict-origin-when-cross-origin' => [['Referrer-Policy' => 'strict-origin-when-cross-origin']], |
|
99 | - 'referrer-no-referrer-when-downgrade' => [['Referrer-Policy' => 'no-referrer-when-downgrade']], |
|
100 | - 'referrer-strict-origin' => [['Referrer-Policy' => 'strict-origin']], |
|
101 | - 'referrer-strict-origin-when-cross-origin' => [['Referrer-Policy' => 'strict-origin-when-cross-origin']], |
|
102 | - 'referrer-same-origin' => [['Referrer-Policy' => 'same-origin']], |
|
103 | - 'hsts-minimum' => [['Strict-Transport-Security' => 'max-age=15552000']], |
|
104 | - 'hsts-include-subdomains' => [['Strict-Transport-Security' => 'max-age=99999999; includeSubDomains']], |
|
105 | - 'hsts-include-subdomains-preload' => [['Strict-Transport-Security' => 'max-age=99999999; preload; includeSubDomains']], |
|
106 | - ]; |
|
107 | - } |
|
108 | - |
|
109 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataSuccess')] |
|
110 | - public function testSuccess(array $headers): void { |
|
111 | - $headers = array_merge( |
|
112 | - [ |
|
113 | - 'X-Content-Type-Options' => 'nosniff', |
|
114 | - 'X-Robots-Tag' => 'noindex, nofollow', |
|
115 | - 'X-Frame-Options' => 'SAMEORIGIN', |
|
116 | - 'Strict-Transport-Security' => 'max-age=15768000', |
|
117 | - 'X-Permitted-Cross-Domain-Policies' => 'none', |
|
118 | - 'Referrer-Policy' => 'no-referrer', |
|
119 | - ], |
|
120 | - $headers |
|
121 | - ); |
|
122 | - $this->setupResponse( |
|
123 | - 200, |
|
124 | - $headers |
|
125 | - ); |
|
126 | - |
|
127 | - $result = $this->setupcheck->run(); |
|
128 | - $this->assertEquals( |
|
129 | - 'Your server is correctly configured to send security headers.', |
|
130 | - $result->getDescription() |
|
131 | - ); |
|
132 | - $this->assertEquals(SetupResult::SUCCESS, $result->getSeverity()); |
|
133 | - } |
|
134 | - |
|
135 | - public static function dataFailure(): array { |
|
136 | - return [ |
|
137 | - // description => modifiedHeaders |
|
138 | - 'x-robots-none' => [['X-Robots-Tag' => 'none'], "- The `X-Robots-Tag` HTTP header is not set to `noindex,nofollow`. This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.\n"], |
|
139 | - 'referrer-origin' => [['Referrer-Policy' => 'origin'], "- The `Referrer-Policy` HTTP header is not set to `no-referrer`, `no-referrer-when-downgrade`, `strict-origin`, `strict-origin-when-cross-origin` or `same-origin`. This can leak referer information. See the {w3c-recommendation}.\n"], |
|
140 | - 'referrer-origin-when-cross-origin' => [['Referrer-Policy' => 'origin-when-cross-origin'], "- The `Referrer-Policy` HTTP header is not set to `no-referrer`, `no-referrer-when-downgrade`, `strict-origin`, `strict-origin-when-cross-origin` or `same-origin`. This can leak referer information. See the {w3c-recommendation}.\n"], |
|
141 | - 'referrer-unsafe-url' => [['Referrer-Policy' => 'unsafe-url'], "- The `Referrer-Policy` HTTP header is not set to `no-referrer`, `no-referrer-when-downgrade`, `strict-origin`, `strict-origin-when-cross-origin` or `same-origin`. This can leak referer information. See the {w3c-recommendation}.\n"], |
|
142 | - 'hsts-missing' => [['Strict-Transport-Security' => ''], "- The `Strict-Transport-Security` HTTP header is not set (should be at least `15552000` seconds). For enhanced security, it is recommended to enable HSTS.\n"], |
|
143 | - 'hsts-too-low' => [['Strict-Transport-Security' => 'max-age=15551999'], "- The `Strict-Transport-Security` HTTP header is not set to at least `15552000` seconds (current value: `15551999`). For enhanced security, it is recommended to use a long HSTS policy.\n"], |
|
144 | - 'hsts-malformed' => [['Strict-Transport-Security' => 'iAmABogusHeader342'], "- The `Strict-Transport-Security` HTTP header is malformed: `iAmABogusHeader342`. For enhanced security, it is recommended to enable HSTS.\n"], |
|
145 | - ]; |
|
146 | - } |
|
147 | - |
|
148 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataFailure')] |
|
149 | - public function testFailure(array $headers, string $msg): void { |
|
150 | - $headers = array_merge( |
|
151 | - [ |
|
152 | - 'X-Content-Type-Options' => 'nosniff', |
|
153 | - 'X-Robots-Tag' => 'noindex, nofollow', |
|
154 | - 'X-Frame-Options' => 'SAMEORIGIN', |
|
155 | - 'Strict-Transport-Security' => 'max-age=15768000', |
|
156 | - 'X-Permitted-Cross-Domain-Policies' => 'none', |
|
157 | - 'Referrer-Policy' => 'no-referrer', |
|
158 | - ], |
|
159 | - $headers |
|
160 | - ); |
|
161 | - $this->setupResponse( |
|
162 | - 200, |
|
163 | - $headers |
|
164 | - ); |
|
165 | - |
|
166 | - $result = $this->setupcheck->run(); |
|
167 | - $this->assertEquals( |
|
168 | - 'Some headers are not set correctly on your instance' . "\n$msg", |
|
169 | - $result->getDescription() |
|
170 | - ); |
|
171 | - $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); |
|
172 | - } |
|
173 | - |
|
174 | - protected function setupResponse(int $statuscode, array $headers): void { |
|
175 | - $response = $this->createMock(IResponse::class); |
|
176 | - $response->expects($this->atLeastOnce())->method('getStatusCode')->willReturn($statuscode); |
|
177 | - $response->expects($this->any())->method('getHeader') |
|
178 | - ->willReturnCallback( |
|
179 | - fn (string $header): string => $headers[$header] ?? '' |
|
180 | - ); |
|
181 | - |
|
182 | - $this->setupcheck |
|
183 | - ->expects($this->atLeastOnce()) |
|
184 | - ->method('runRequest') |
|
185 | - ->willReturnOnConsecutiveCalls($this->generate([$response])); |
|
186 | - } |
|
187 | - |
|
188 | - /** |
|
189 | - * Helper function creates a nicer interface for mocking Generator behavior |
|
190 | - */ |
|
191 | - protected function generate(array $yield_values) { |
|
192 | - return $this->returnCallback(function () use ($yield_values) { |
|
193 | - yield from $yield_values; |
|
194 | - }); |
|
195 | - } |
|
23 | + private IL10N&MockObject $l10n; |
|
24 | + private IConfig&MockObject $config; |
|
25 | + private IURLGenerator&MockObject $urlGenerator; |
|
26 | + private IClientService&MockObject $clientService; |
|
27 | + private LoggerInterface&MockObject $logger; |
|
28 | + private SecurityHeaders&MockObject $setupcheck; |
|
29 | + |
|
30 | + protected function setUp(): void { |
|
31 | + parent::setUp(); |
|
32 | + |
|
33 | + $this->l10n = $this->createMock(IL10N::class); |
|
34 | + $this->l10n->expects($this->any()) |
|
35 | + ->method('t') |
|
36 | + ->willReturnCallback(function ($message, array $replace) { |
|
37 | + return vsprintf($message, $replace); |
|
38 | + }); |
|
39 | + |
|
40 | + $this->config = $this->createMock(IConfig::class); |
|
41 | + $this->urlGenerator = $this->createMock(IURLGenerator::class); |
|
42 | + $this->clientService = $this->createMock(IClientService::class); |
|
43 | + $this->logger = $this->createMock(LoggerInterface::class); |
|
44 | + |
|
45 | + $this->setupcheck = $this->getMockBuilder(SecurityHeaders::class) |
|
46 | + ->onlyMethods(['runRequest']) |
|
47 | + ->setConstructorArgs([ |
|
48 | + $this->l10n, |
|
49 | + $this->config, |
|
50 | + $this->urlGenerator, |
|
51 | + $this->clientService, |
|
52 | + $this->logger, |
|
53 | + ]) |
|
54 | + ->getMock(); |
|
55 | + } |
|
56 | + |
|
57 | + public function testInvalidStatusCode(): void { |
|
58 | + $this->setupResponse(500, []); |
|
59 | + |
|
60 | + $result = $this->setupcheck->run(); |
|
61 | + $this->assertMatchesRegularExpression('/^Could not check that your web server serves security headers correctly/', $result->getDescription()); |
|
62 | + $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); |
|
63 | + } |
|
64 | + |
|
65 | + public function testAllHeadersMissing(): void { |
|
66 | + $this->setupResponse(200, []); |
|
67 | + |
|
68 | + $result = $this->setupcheck->run(); |
|
69 | + $this->assertMatchesRegularExpression('/^Some headers are not set correctly on your instance/', $result->getDescription()); |
|
70 | + $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); |
|
71 | + } |
|
72 | + |
|
73 | + public function testSomeHeadersMissing(): void { |
|
74 | + $this->setupResponse( |
|
75 | + 200, |
|
76 | + [ |
|
77 | + 'X-Robots-Tag' => 'noindex, nofollow', |
|
78 | + 'X-Frame-Options' => 'SAMEORIGIN', |
|
79 | + 'Strict-Transport-Security' => 'max-age=15768000;preload', |
|
80 | + 'X-Permitted-Cross-Domain-Policies' => 'none', |
|
81 | + 'Referrer-Policy' => 'no-referrer', |
|
82 | + ] |
|
83 | + ); |
|
84 | + |
|
85 | + $result = $this->setupcheck->run(); |
|
86 | + $this->assertEquals( |
|
87 | + "Some headers are not set correctly on your instance\n- The `X-Content-Type-Options` HTTP header is not set to `nosniff`. This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.\n", |
|
88 | + $result->getDescription() |
|
89 | + ); |
|
90 | + $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); |
|
91 | + } |
|
92 | + |
|
93 | + public static function dataSuccess(): array { |
|
94 | + return [ |
|
95 | + // description => modifiedHeaders |
|
96 | + 'basic' => [[]], |
|
97 | + 'no-space-in-x-robots' => [['X-Robots-Tag' => 'noindex,nofollow']], |
|
98 | + 'strict-origin-when-cross-origin' => [['Referrer-Policy' => 'strict-origin-when-cross-origin']], |
|
99 | + 'referrer-no-referrer-when-downgrade' => [['Referrer-Policy' => 'no-referrer-when-downgrade']], |
|
100 | + 'referrer-strict-origin' => [['Referrer-Policy' => 'strict-origin']], |
|
101 | + 'referrer-strict-origin-when-cross-origin' => [['Referrer-Policy' => 'strict-origin-when-cross-origin']], |
|
102 | + 'referrer-same-origin' => [['Referrer-Policy' => 'same-origin']], |
|
103 | + 'hsts-minimum' => [['Strict-Transport-Security' => 'max-age=15552000']], |
|
104 | + 'hsts-include-subdomains' => [['Strict-Transport-Security' => 'max-age=99999999; includeSubDomains']], |
|
105 | + 'hsts-include-subdomains-preload' => [['Strict-Transport-Security' => 'max-age=99999999; preload; includeSubDomains']], |
|
106 | + ]; |
|
107 | + } |
|
108 | + |
|
109 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataSuccess')] |
|
110 | + public function testSuccess(array $headers): void { |
|
111 | + $headers = array_merge( |
|
112 | + [ |
|
113 | + 'X-Content-Type-Options' => 'nosniff', |
|
114 | + 'X-Robots-Tag' => 'noindex, nofollow', |
|
115 | + 'X-Frame-Options' => 'SAMEORIGIN', |
|
116 | + 'Strict-Transport-Security' => 'max-age=15768000', |
|
117 | + 'X-Permitted-Cross-Domain-Policies' => 'none', |
|
118 | + 'Referrer-Policy' => 'no-referrer', |
|
119 | + ], |
|
120 | + $headers |
|
121 | + ); |
|
122 | + $this->setupResponse( |
|
123 | + 200, |
|
124 | + $headers |
|
125 | + ); |
|
126 | + |
|
127 | + $result = $this->setupcheck->run(); |
|
128 | + $this->assertEquals( |
|
129 | + 'Your server is correctly configured to send security headers.', |
|
130 | + $result->getDescription() |
|
131 | + ); |
|
132 | + $this->assertEquals(SetupResult::SUCCESS, $result->getSeverity()); |
|
133 | + } |
|
134 | + |
|
135 | + public static function dataFailure(): array { |
|
136 | + return [ |
|
137 | + // description => modifiedHeaders |
|
138 | + 'x-robots-none' => [['X-Robots-Tag' => 'none'], "- The `X-Robots-Tag` HTTP header is not set to `noindex,nofollow`. This is a potential security or privacy risk, as it is recommended to adjust this setting accordingly.\n"], |
|
139 | + 'referrer-origin' => [['Referrer-Policy' => 'origin'], "- The `Referrer-Policy` HTTP header is not set to `no-referrer`, `no-referrer-when-downgrade`, `strict-origin`, `strict-origin-when-cross-origin` or `same-origin`. This can leak referer information. See the {w3c-recommendation}.\n"], |
|
140 | + 'referrer-origin-when-cross-origin' => [['Referrer-Policy' => 'origin-when-cross-origin'], "- The `Referrer-Policy` HTTP header is not set to `no-referrer`, `no-referrer-when-downgrade`, `strict-origin`, `strict-origin-when-cross-origin` or `same-origin`. This can leak referer information. See the {w3c-recommendation}.\n"], |
|
141 | + 'referrer-unsafe-url' => [['Referrer-Policy' => 'unsafe-url'], "- The `Referrer-Policy` HTTP header is not set to `no-referrer`, `no-referrer-when-downgrade`, `strict-origin`, `strict-origin-when-cross-origin` or `same-origin`. This can leak referer information. See the {w3c-recommendation}.\n"], |
|
142 | + 'hsts-missing' => [['Strict-Transport-Security' => ''], "- The `Strict-Transport-Security` HTTP header is not set (should be at least `15552000` seconds). For enhanced security, it is recommended to enable HSTS.\n"], |
|
143 | + 'hsts-too-low' => [['Strict-Transport-Security' => 'max-age=15551999'], "- The `Strict-Transport-Security` HTTP header is not set to at least `15552000` seconds (current value: `15551999`). For enhanced security, it is recommended to use a long HSTS policy.\n"], |
|
144 | + 'hsts-malformed' => [['Strict-Transport-Security' => 'iAmABogusHeader342'], "- The `Strict-Transport-Security` HTTP header is malformed: `iAmABogusHeader342`. For enhanced security, it is recommended to enable HSTS.\n"], |
|
145 | + ]; |
|
146 | + } |
|
147 | + |
|
148 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataFailure')] |
|
149 | + public function testFailure(array $headers, string $msg): void { |
|
150 | + $headers = array_merge( |
|
151 | + [ |
|
152 | + 'X-Content-Type-Options' => 'nosniff', |
|
153 | + 'X-Robots-Tag' => 'noindex, nofollow', |
|
154 | + 'X-Frame-Options' => 'SAMEORIGIN', |
|
155 | + 'Strict-Transport-Security' => 'max-age=15768000', |
|
156 | + 'X-Permitted-Cross-Domain-Policies' => 'none', |
|
157 | + 'Referrer-Policy' => 'no-referrer', |
|
158 | + ], |
|
159 | + $headers |
|
160 | + ); |
|
161 | + $this->setupResponse( |
|
162 | + 200, |
|
163 | + $headers |
|
164 | + ); |
|
165 | + |
|
166 | + $result = $this->setupcheck->run(); |
|
167 | + $this->assertEquals( |
|
168 | + 'Some headers are not set correctly on your instance' . "\n$msg", |
|
169 | + $result->getDescription() |
|
170 | + ); |
|
171 | + $this->assertEquals(SetupResult::WARNING, $result->getSeverity()); |
|
172 | + } |
|
173 | + |
|
174 | + protected function setupResponse(int $statuscode, array $headers): void { |
|
175 | + $response = $this->createMock(IResponse::class); |
|
176 | + $response->expects($this->atLeastOnce())->method('getStatusCode')->willReturn($statuscode); |
|
177 | + $response->expects($this->any())->method('getHeader') |
|
178 | + ->willReturnCallback( |
|
179 | + fn (string $header): string => $headers[$header] ?? '' |
|
180 | + ); |
|
181 | + |
|
182 | + $this->setupcheck |
|
183 | + ->expects($this->atLeastOnce()) |
|
184 | + ->method('runRequest') |
|
185 | + ->willReturnOnConsecutiveCalls($this->generate([$response])); |
|
186 | + } |
|
187 | + |
|
188 | + /** |
|
189 | + * Helper function creates a nicer interface for mocking Generator behavior |
|
190 | + */ |
|
191 | + protected function generate(array $yield_values) { |
|
192 | + return $this->returnCallback(function () use ($yield_values) { |
|
193 | + yield from $yield_values; |
|
194 | + }); |
|
195 | + } |
|
196 | 196 | } |
@@ -20,196 +20,196 @@ |
||
20 | 20 | use Test\TestCase; |
21 | 21 | |
22 | 22 | class WellKnownUrlsTest extends TestCase { |
23 | - private IL10N&MockObject $l10n; |
|
24 | - private IConfig&MockObject $config; |
|
25 | - private IURLGenerator&MockObject $urlGenerator; |
|
26 | - private IClientService&MockObject $clientService; |
|
27 | - private LoggerInterface&MockObject $logger; |
|
28 | - private WellKnownUrls&MockObject $setupcheck; |
|
29 | - |
|
30 | - protected function setUp(): void { |
|
31 | - parent::setUp(); |
|
32 | - |
|
33 | - /** @var IL10N&MockObject */ |
|
34 | - $this->l10n = $this->createMock(IL10N::class); |
|
35 | - $this->l10n->expects($this->any()) |
|
36 | - ->method('t') |
|
37 | - ->willReturnCallback(function ($message, array $replace) { |
|
38 | - return vsprintf($message, $replace); |
|
39 | - }); |
|
40 | - |
|
41 | - $this->config = $this->createMock(IConfig::class); |
|
42 | - $this->urlGenerator = $this->createMock(IURLGenerator::class); |
|
43 | - $this->clientService = $this->createMock(IClientService::class); |
|
44 | - $this->logger = $this->createMock(LoggerInterface::class); |
|
45 | - |
|
46 | - $this->setupcheck = $this->getMockBuilder(WellKnownUrls::class) |
|
47 | - ->onlyMethods(['runRequest']) |
|
48 | - ->setConstructorArgs([ |
|
49 | - $this->l10n, |
|
50 | - $this->config, |
|
51 | - $this->urlGenerator, |
|
52 | - $this->clientService, |
|
53 | - $this->logger, |
|
54 | - ]) |
|
55 | - ->getMock(); |
|
56 | - } |
|
57 | - |
|
58 | - /** |
|
59 | - * Test that the SetupCheck is skipped if the system config is set |
|
60 | - */ |
|
61 | - public function testDisabled(): void { |
|
62 | - $this->config |
|
63 | - ->expects($this->once()) |
|
64 | - ->method('getSystemValueBool') |
|
65 | - ->with('check_for_working_wellknown_setup') |
|
66 | - ->willReturn(false); |
|
67 | - |
|
68 | - $this->setupcheck |
|
69 | - ->expects($this->never()) |
|
70 | - ->method('runRequest'); |
|
71 | - |
|
72 | - $result = $this->setupcheck->run(); |
|
73 | - $this->assertEquals(SetupResult::INFO, $result->getSeverity()); |
|
74 | - $this->assertMatchesRegularExpression('/check was skipped/', $result->getDescription()); |
|
75 | - } |
|
76 | - |
|
77 | - /** |
|
78 | - * Test what happens if the local server could not be reached (no response from the requests) |
|
79 | - */ |
|
80 | - public function testNoResponse(): void { |
|
81 | - $this->config |
|
82 | - ->expects($this->once()) |
|
83 | - ->method('getSystemValueBool') |
|
84 | - ->with('check_for_working_wellknown_setup') |
|
85 | - ->willReturn(true); |
|
86 | - |
|
87 | - $this->setupcheck |
|
88 | - ->expects($this->once()) |
|
89 | - ->method('runRequest') |
|
90 | - ->will($this->generate([])); |
|
91 | - |
|
92 | - $result = $this->setupcheck->run(); |
|
93 | - $this->assertEquals(SetupResult::INFO, $result->getSeverity()); |
|
94 | - $this->assertMatchesRegularExpression('/^Could not check/', $result->getDescription()); |
|
95 | - } |
|
96 | - |
|
97 | - /** |
|
98 | - * Test responses |
|
99 | - */ |
|
100 | - #[\PHPUnit\Framework\Attributes\DataProvider('dataTestResponses')] |
|
101 | - public function testResponses($responses, string $expectedSeverity): void { |
|
102 | - $this->config |
|
103 | - ->expects($this->once()) |
|
104 | - ->method('getSystemValueBool') |
|
105 | - ->with('check_for_working_wellknown_setup') |
|
106 | - ->willReturn(true); |
|
107 | - |
|
108 | - $this->setupcheck |
|
109 | - ->expects($this->atLeastOnce()) |
|
110 | - ->method('runRequest') |
|
111 | - ->willReturnOnConsecutiveCalls(...$responses); |
|
112 | - |
|
113 | - $result = $this->setupcheck->run(); |
|
114 | - $this->assertEquals($expectedSeverity, $result->getSeverity()); |
|
115 | - } |
|
116 | - |
|
117 | - public function dataTestResponses(): array { |
|
118 | - $createResponse = function (int $statuscode, array $header = []): IResponse&MockObject { |
|
119 | - $response = $this->createMock(IResponse::class); |
|
120 | - $response->expects($this->any()) |
|
121 | - ->method('getStatusCode') |
|
122 | - ->willReturn($statuscode); |
|
123 | - $response->expects($this->any()) |
|
124 | - ->method('getHeader') |
|
125 | - ->willReturnCallback(fn ($name) => $header[$name] ?? ''); |
|
126 | - return $response; |
|
127 | - }; |
|
128 | - |
|
129 | - $wellKnownHeader = ['X-NEXTCLOUD-WELL-KNOWN' => 'yes']; |
|
130 | - |
|
131 | - return [ |
|
132 | - 'expected codes' => [ |
|
133 | - [ |
|
134 | - $this->generate([$createResponse(200, $wellKnownHeader)]), |
|
135 | - $this->generate([$createResponse(200, $wellKnownHeader)]), |
|
136 | - $this->generate([$createResponse(207)]), |
|
137 | - $this->generate([$createResponse(207)]), |
|
138 | - ], |
|
139 | - SetupResult::SUCCESS, |
|
140 | - ], |
|
141 | - 'late response with expected codes' => [ |
|
142 | - [ |
|
143 | - $this->generate([$createResponse(404), $createResponse(200, $wellKnownHeader)]), |
|
144 | - $this->generate([$createResponse(404), $createResponse(200, $wellKnownHeader)]), |
|
145 | - $this->generate([$createResponse(404), $createResponse(207)]), |
|
146 | - $this->generate([$createResponse(404), $createResponse(207)]), |
|
147 | - ], |
|
148 | - SetupResult::SUCCESS, |
|
149 | - ], |
|
150 | - 'working but disabled webfinger' => [ |
|
151 | - [ |
|
152 | - $this->generate([$createResponse(404, $wellKnownHeader)]), |
|
153 | - $this->generate([$createResponse(404, $wellKnownHeader)]), |
|
154 | - $this->generate([$createResponse(207)]), |
|
155 | - $this->generate([$createResponse(207)]), |
|
156 | - ], |
|
157 | - SetupResult::SUCCESS, |
|
158 | - ], |
|
159 | - 'unauthorized webdav but with correct configured redirect' => [ |
|
160 | - [ |
|
161 | - $this->generate([$createResponse(404, $wellKnownHeader)]), |
|
162 | - $this->generate([$createResponse(404, $wellKnownHeader)]), |
|
163 | - $this->generate([$createResponse(401, ['X-Guzzle-Redirect-History' => 'https://example.com,https://example.com/remote.php/dav/'])]), |
|
164 | - $this->generate([$createResponse(401, ['X-Guzzle-Redirect-History' => 'https://example.com/remote.php/dav/'])]), |
|
165 | - ], |
|
166 | - SetupResult::SUCCESS, |
|
167 | - ], |
|
168 | - 'not configured path' => [ |
|
169 | - [ |
|
170 | - $this->generate([$createResponse(404)]), |
|
171 | - $this->generate([$createResponse(404)]), |
|
172 | - $this->generate([$createResponse(404)]), |
|
173 | - $this->generate([$createResponse(404)]), |
|
174 | - ], |
|
175 | - SetupResult::WARNING, |
|
176 | - ], |
|
177 | - 'Invalid webfinger' => [ |
|
178 | - [ |
|
179 | - $this->generate([$createResponse(404)]), |
|
180 | - $this->generate([$createResponse(404, $wellKnownHeader)]), |
|
181 | - $this->generate([$createResponse(207)]), |
|
182 | - $this->generate([$createResponse(207)]), |
|
183 | - ], |
|
184 | - SetupResult::WARNING, |
|
185 | - ], |
|
186 | - 'Invalid nodeinfo' => [ |
|
187 | - [ |
|
188 | - $this->generate([$createResponse(404, $wellKnownHeader)]), |
|
189 | - $this->generate([$createResponse(404)]), |
|
190 | - $this->generate([$createResponse(207)]), |
|
191 | - $this->generate([$createResponse(207)]), |
|
192 | - ], |
|
193 | - SetupResult::WARNING, |
|
194 | - ], |
|
195 | - 'Invalid caldav' => [ |
|
196 | - [ |
|
197 | - $this->generate([$createResponse(404, $wellKnownHeader)]), |
|
198 | - $this->generate([$createResponse(404, $wellKnownHeader)]), |
|
199 | - $this->generate([$createResponse(404)]), |
|
200 | - $this->generate([$createResponse(207)]), |
|
201 | - ], |
|
202 | - SetupResult::WARNING, |
|
203 | - ], |
|
204 | - ]; |
|
205 | - } |
|
206 | - |
|
207 | - /** |
|
208 | - * Helper function creates a nicer interface for mocking Generator behavior |
|
209 | - */ |
|
210 | - protected function generate(array $yield_values) { |
|
211 | - return $this->returnCallback(function () use ($yield_values) { |
|
212 | - yield from $yield_values; |
|
213 | - }); |
|
214 | - } |
|
23 | + private IL10N&MockObject $l10n; |
|
24 | + private IConfig&MockObject $config; |
|
25 | + private IURLGenerator&MockObject $urlGenerator; |
|
26 | + private IClientService&MockObject $clientService; |
|
27 | + private LoggerInterface&MockObject $logger; |
|
28 | + private WellKnownUrls&MockObject $setupcheck; |
|
29 | + |
|
30 | + protected function setUp(): void { |
|
31 | + parent::setUp(); |
|
32 | + |
|
33 | + /** @var IL10N&MockObject */ |
|
34 | + $this->l10n = $this->createMock(IL10N::class); |
|
35 | + $this->l10n->expects($this->any()) |
|
36 | + ->method('t') |
|
37 | + ->willReturnCallback(function ($message, array $replace) { |
|
38 | + return vsprintf($message, $replace); |
|
39 | + }); |
|
40 | + |
|
41 | + $this->config = $this->createMock(IConfig::class); |
|
42 | + $this->urlGenerator = $this->createMock(IURLGenerator::class); |
|
43 | + $this->clientService = $this->createMock(IClientService::class); |
|
44 | + $this->logger = $this->createMock(LoggerInterface::class); |
|
45 | + |
|
46 | + $this->setupcheck = $this->getMockBuilder(WellKnownUrls::class) |
|
47 | + ->onlyMethods(['runRequest']) |
|
48 | + ->setConstructorArgs([ |
|
49 | + $this->l10n, |
|
50 | + $this->config, |
|
51 | + $this->urlGenerator, |
|
52 | + $this->clientService, |
|
53 | + $this->logger, |
|
54 | + ]) |
|
55 | + ->getMock(); |
|
56 | + } |
|
57 | + |
|
58 | + /** |
|
59 | + * Test that the SetupCheck is skipped if the system config is set |
|
60 | + */ |
|
61 | + public function testDisabled(): void { |
|
62 | + $this->config |
|
63 | + ->expects($this->once()) |
|
64 | + ->method('getSystemValueBool') |
|
65 | + ->with('check_for_working_wellknown_setup') |
|
66 | + ->willReturn(false); |
|
67 | + |
|
68 | + $this->setupcheck |
|
69 | + ->expects($this->never()) |
|
70 | + ->method('runRequest'); |
|
71 | + |
|
72 | + $result = $this->setupcheck->run(); |
|
73 | + $this->assertEquals(SetupResult::INFO, $result->getSeverity()); |
|
74 | + $this->assertMatchesRegularExpression('/check was skipped/', $result->getDescription()); |
|
75 | + } |
|
76 | + |
|
77 | + /** |
|
78 | + * Test what happens if the local server could not be reached (no response from the requests) |
|
79 | + */ |
|
80 | + public function testNoResponse(): void { |
|
81 | + $this->config |
|
82 | + ->expects($this->once()) |
|
83 | + ->method('getSystemValueBool') |
|
84 | + ->with('check_for_working_wellknown_setup') |
|
85 | + ->willReturn(true); |
|
86 | + |
|
87 | + $this->setupcheck |
|
88 | + ->expects($this->once()) |
|
89 | + ->method('runRequest') |
|
90 | + ->will($this->generate([])); |
|
91 | + |
|
92 | + $result = $this->setupcheck->run(); |
|
93 | + $this->assertEquals(SetupResult::INFO, $result->getSeverity()); |
|
94 | + $this->assertMatchesRegularExpression('/^Could not check/', $result->getDescription()); |
|
95 | + } |
|
96 | + |
|
97 | + /** |
|
98 | + * Test responses |
|
99 | + */ |
|
100 | + #[\PHPUnit\Framework\Attributes\DataProvider('dataTestResponses')] |
|
101 | + public function testResponses($responses, string $expectedSeverity): void { |
|
102 | + $this->config |
|
103 | + ->expects($this->once()) |
|
104 | + ->method('getSystemValueBool') |
|
105 | + ->with('check_for_working_wellknown_setup') |
|
106 | + ->willReturn(true); |
|
107 | + |
|
108 | + $this->setupcheck |
|
109 | + ->expects($this->atLeastOnce()) |
|
110 | + ->method('runRequest') |
|
111 | + ->willReturnOnConsecutiveCalls(...$responses); |
|
112 | + |
|
113 | + $result = $this->setupcheck->run(); |
|
114 | + $this->assertEquals($expectedSeverity, $result->getSeverity()); |
|
115 | + } |
|
116 | + |
|
117 | + public function dataTestResponses(): array { |
|
118 | + $createResponse = function (int $statuscode, array $header = []): IResponse&MockObject { |
|
119 | + $response = $this->createMock(IResponse::class); |
|
120 | + $response->expects($this->any()) |
|
121 | + ->method('getStatusCode') |
|
122 | + ->willReturn($statuscode); |
|
123 | + $response->expects($this->any()) |
|
124 | + ->method('getHeader') |
|
125 | + ->willReturnCallback(fn ($name) => $header[$name] ?? ''); |
|
126 | + return $response; |
|
127 | + }; |
|
128 | + |
|
129 | + $wellKnownHeader = ['X-NEXTCLOUD-WELL-KNOWN' => 'yes']; |
|
130 | + |
|
131 | + return [ |
|
132 | + 'expected codes' => [ |
|
133 | + [ |
|
134 | + $this->generate([$createResponse(200, $wellKnownHeader)]), |
|
135 | + $this->generate([$createResponse(200, $wellKnownHeader)]), |
|
136 | + $this->generate([$createResponse(207)]), |
|
137 | + $this->generate([$createResponse(207)]), |
|
138 | + ], |
|
139 | + SetupResult::SUCCESS, |
|
140 | + ], |
|
141 | + 'late response with expected codes' => [ |
|
142 | + [ |
|
143 | + $this->generate([$createResponse(404), $createResponse(200, $wellKnownHeader)]), |
|
144 | + $this->generate([$createResponse(404), $createResponse(200, $wellKnownHeader)]), |
|
145 | + $this->generate([$createResponse(404), $createResponse(207)]), |
|
146 | + $this->generate([$createResponse(404), $createResponse(207)]), |
|
147 | + ], |
|
148 | + SetupResult::SUCCESS, |
|
149 | + ], |
|
150 | + 'working but disabled webfinger' => [ |
|
151 | + [ |
|
152 | + $this->generate([$createResponse(404, $wellKnownHeader)]), |
|
153 | + $this->generate([$createResponse(404, $wellKnownHeader)]), |
|
154 | + $this->generate([$createResponse(207)]), |
|
155 | + $this->generate([$createResponse(207)]), |
|
156 | + ], |
|
157 | + SetupResult::SUCCESS, |
|
158 | + ], |
|
159 | + 'unauthorized webdav but with correct configured redirect' => [ |
|
160 | + [ |
|
161 | + $this->generate([$createResponse(404, $wellKnownHeader)]), |
|
162 | + $this->generate([$createResponse(404, $wellKnownHeader)]), |
|
163 | + $this->generate([$createResponse(401, ['X-Guzzle-Redirect-History' => 'https://example.com,https://example.com/remote.php/dav/'])]), |
|
164 | + $this->generate([$createResponse(401, ['X-Guzzle-Redirect-History' => 'https://example.com/remote.php/dav/'])]), |
|
165 | + ], |
|
166 | + SetupResult::SUCCESS, |
|
167 | + ], |
|
168 | + 'not configured path' => [ |
|
169 | + [ |
|
170 | + $this->generate([$createResponse(404)]), |
|
171 | + $this->generate([$createResponse(404)]), |
|
172 | + $this->generate([$createResponse(404)]), |
|
173 | + $this->generate([$createResponse(404)]), |
|
174 | + ], |
|
175 | + SetupResult::WARNING, |
|
176 | + ], |
|
177 | + 'Invalid webfinger' => [ |
|
178 | + [ |
|
179 | + $this->generate([$createResponse(404)]), |
|
180 | + $this->generate([$createResponse(404, $wellKnownHeader)]), |
|
181 | + $this->generate([$createResponse(207)]), |
|
182 | + $this->generate([$createResponse(207)]), |
|
183 | + ], |
|
184 | + SetupResult::WARNING, |
|
185 | + ], |
|
186 | + 'Invalid nodeinfo' => [ |
|
187 | + [ |
|
188 | + $this->generate([$createResponse(404, $wellKnownHeader)]), |
|
189 | + $this->generate([$createResponse(404)]), |
|
190 | + $this->generate([$createResponse(207)]), |
|
191 | + $this->generate([$createResponse(207)]), |
|
192 | + ], |
|
193 | + SetupResult::WARNING, |
|
194 | + ], |
|
195 | + 'Invalid caldav' => [ |
|
196 | + [ |
|
197 | + $this->generate([$createResponse(404, $wellKnownHeader)]), |
|
198 | + $this->generate([$createResponse(404, $wellKnownHeader)]), |
|
199 | + $this->generate([$createResponse(404)]), |
|
200 | + $this->generate([$createResponse(207)]), |
|
201 | + ], |
|
202 | + SetupResult::WARNING, |
|
203 | + ], |
|
204 | + ]; |
|
205 | + } |
|
206 | + |
|
207 | + /** |
|
208 | + * Helper function creates a nicer interface for mocking Generator behavior |
|
209 | + */ |
|
210 | + protected function generate(array $yield_values) { |
|
211 | + return $this->returnCallback(function () use ($yield_values) { |
|
212 | + yield from $yield_values; |
|
213 | + }); |
|
214 | + } |
|
215 | 215 | } |