diff --git a/src/Core/Hooks/HookEventBridge.php b/src/Core/Hooks/HookEventBridge.php index 82a11f1f55..bc2262df55 100644 --- a/src/Core/Hooks/HookEventBridge.php +++ b/src/Core/Hooks/HookEventBridge.php @@ -81,6 +81,9 @@ final class HookEventBridge ArrayFilterEvent::BLOCK_CONTACT => 'block', ArrayFilterEvent::UNBLOCK_CONTACT => 'unblock', ArrayFilterEvent::AVATAR_LOOKUP => 'avatar_lookup', + ArrayFilterEvent::ACCOUNT_AUTHENTICATE => 'authenticate', + ArrayFilterEvent::ACCOUNT_REGISTER => 'register_account', + ArrayFilterEvent::ACCOUNT_REMOVE => 'remove_user', ArrayFilterEvent::EVENT_CREATED => 'event_created', ArrayFilterEvent::EVENT_UPDATED => 'event_updated', ArrayFilterEvent::ADD_WORKER_TASK => 'proc_run', @@ -149,6 +152,9 @@ final class HookEventBridge ArrayFilterEvent::BLOCK_CONTACT => 'onArrayFilterEvent', ArrayFilterEvent::UNBLOCK_CONTACT => 'onArrayFilterEvent', ArrayFilterEvent::AVATAR_LOOKUP => 'onArrayFilterEvent', + ArrayFilterEvent::ACCOUNT_AUTHENTICATE => 'onArrayFilterEvent', + ArrayFilterEvent::ACCOUNT_REGISTER => 'onAccountRegisterEvent', + ArrayFilterEvent::ACCOUNT_REMOVE => 'onAccountRemoveEvent', ArrayFilterEvent::EVENT_CREATED => 'onEventCreatedEvent', ArrayFilterEvent::EVENT_UPDATED => 'onEventUpdatedEvent', ArrayFilterEvent::ADD_WORKER_TASK => 'onArrayFilterEvent', @@ -295,6 +301,34 @@ final class HookEventBridge $event->setArray($data); } + /** + * Map the ACCOUNT_REGISTER event to `register_account` hook + */ + public static function onAccountRegisterEvent(ArrayFilterEvent $event): void + { + $data = $event->getArray(); + + $uid = (int) $data['uid'] ?? 0; + + $data['uid'] = static::callHook($event->getName(), $uid); + + $event->setArray($data); + } + + /** + * Map the ACCOUNT_REMOVE event to `remove_account` hook + */ + public static function onAccountRemoveEvent(ArrayFilterEvent $event): void + { + $data = $event->getArray(); + + $user = (array) $data['user'] ?? []; + + $data['user'] = static::callHook($event->getName(), $user); + + $event->setArray($data); + } + /** * Map the EVENT_CREATED event to `event_created` hook */ diff --git a/src/Event/ArrayFilterEvent.php b/src/Event/ArrayFilterEvent.php index 12105decc9..02a516bf61 100644 --- a/src/Event/ArrayFilterEvent.php +++ b/src/Event/ArrayFilterEvent.php @@ -112,6 +112,12 @@ final class ArrayFilterEvent extends Event public const AVATAR_LOOKUP = 'friendica.data.avatar_lookup'; + public const ACCOUNT_AUTHENTICATE = 'friendica.data.account_authenticate'; + + public const ACCOUNT_REGISTER = 'friendica.data.account_register'; + + public const ACCOUNT_REMOVE = 'friendica.data.account_remove'; + public const EVENT_CREATED = 'friendica.data.event_created'; public const EVENT_UPDATED = 'friendica.data.event_updated'; diff --git a/src/Model/User.php b/src/Model/User.php index 927a1a82bd..5195fb77a3 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -13,7 +13,6 @@ use ErrorException; use Exception; use Friendica\App; use Friendica\Content\Pager; -use Friendica\Core\Hook; use Friendica\Core\L10n; use Friendica\Core\Protocol; use Friendica\Core\Search; @@ -21,6 +20,7 @@ use Friendica\Core\System; use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\DI; +use Friendica\Event\ArrayFilterEvent; use Friendica\Module; use Friendica\Network\HTTPClient\Client\HttpClientAccept; use Friendica\Network\HTTPClient\Client\HttpClientOptions; @@ -758,12 +758,16 @@ class User 'user_record' => null ]; - /* + $eventDispatcher = DI::eventDispatcher(); + + /** * An addon indicates successful login by setting 'authenticated' to non-zero value and returning a user record * Addons should never set 'authenticated' except to indicate success - as hooks may be chained * and later addons should not interfere with an earlier one that succeeded. */ - Hook::callAll('authenticate', $addon_auth); + $addon_auth = $eventDispatcher->dispatch( + new ArrayFilterEvent(ArrayFilterEvent::ACCOUNT_AUTHENTICATE, $addon_auth), + )->getArray(); if ($addon_auth['authenticated'] && $addon_auth['user_record']) { return $addon_auth['user_record']['uid']; @@ -1460,11 +1464,20 @@ class User Contact::updateSelfFromUserID($uid, true); } - Hook::callAll('register_account', $uid); + $eventDispatcher = DI::eventDispatcher(); + + $hook_data = [ + 'uid' => $uid, + ]; + + $eventDispatcher->dispatch( + new ArrayFilterEvent(ArrayFilterEvent::ACCOUNT_REGISTER, $hook_data), + ); self::setRegisterMethodByUserCount(); $return['user'] = $user; + return $return; } @@ -1787,7 +1800,17 @@ class User throw new \RuntimeException(DI::l10n()->t("User with delegates can't be removed, please remove delegate users first")); } - Hook::callAll('remove_user', $user); + $eventDispatcher = DI::eventDispatcher(); + + $hook_data = [ + 'user' => $user, + ]; + + $hook_data = $eventDispatcher->dispatch( + new ArrayFilterEvent(ArrayFilterEvent::ACCOUNT_REMOVE, $hook_data), + )->getArray(); + + $user = $hook_data['user'] ?? $user; // save username (actually the nickname as it is guaranteed // unique), so it cannot be re-registered in the future. diff --git a/src/Security/BasicAuth.php b/src/Security/BasicAuth.php index fe89bf452e..52a0f75026 100644 --- a/src/Security/BasicAuth.php +++ b/src/Security/BasicAuth.php @@ -11,6 +11,7 @@ use Exception; use Friendica\Core\Hook; use Friendica\Database\DBA; use Friendica\DI; +use Friendica\Event\ArrayFilterEvent; use Friendica\Model\User; use Friendica\Network\HTTPException\UnauthorizedException; @@ -136,12 +137,16 @@ class BasicAuth 'user_record' => null, ]; - /* - * An addon indicates successful login by setting 'authenticated' to non-zero value and returning a user record - * Addons should never set 'authenticated' except to indicate success - as hooks may be chained - * and later addons should not interfere with an earlier one that succeeded. - */ - Hook::callAll('authenticate', $addon_auth); + $eventDispatcher = DI::eventDispatcher(); + + /** + * An addon indicates successful login by setting 'authenticated' to non-zero value and returning a user record + * Addons should never set 'authenticated' except to indicate success - as hooks may be chained + * and later addons should not interfere with an earlier one that succeeded. + */ + $addon_auth = $eventDispatcher->dispatch( + new ArrayFilterEvent(ArrayFilterEvent::ACCOUNT_AUTHENTICATE, $addon_auth), + )->getArray(); if ($addon_auth['authenticated'] && !empty($addon_auth['user_record'])) { $record = $addon_auth['user_record']; diff --git a/tests/Unit/Core/Hooks/HookEventBridgeTest.php b/tests/Unit/Core/Hooks/HookEventBridgeTest.php index e6ec58a27c..d47ea1cbf7 100644 --- a/tests/Unit/Core/Hooks/HookEventBridgeTest.php +++ b/tests/Unit/Core/Hooks/HookEventBridgeTest.php @@ -70,6 +70,9 @@ class HookEventBridgeTest extends TestCase ArrayFilterEvent::BLOCK_CONTACT => 'onArrayFilterEvent', ArrayFilterEvent::UNBLOCK_CONTACT => 'onArrayFilterEvent', ArrayFilterEvent::AVATAR_LOOKUP => 'onArrayFilterEvent', + ArrayFilterEvent::ACCOUNT_AUTHENTICATE => 'onArrayFilterEvent', + ArrayFilterEvent::ACCOUNT_REGISTER => 'onAccountRegisterEvent', + ArrayFilterEvent::ACCOUNT_REMOVE => 'onAccountRemoveEvent', ArrayFilterEvent::EVENT_CREATED => 'onEventCreatedEvent', ArrayFilterEvent::EVENT_UPDATED => 'onEventUpdatedEvent', ArrayFilterEvent::ADD_WORKER_TASK => 'onArrayFilterEvent', @@ -385,6 +388,40 @@ class HookEventBridgeTest extends TestCase HookEventBridge::onEventCreatedEvent($event); } + public function testOnAccountRegisterEventCallsHookWithCorrectValue(): void + { + $event = new ArrayFilterEvent(ArrayFilterEvent::ACCOUNT_REGISTER, ['uid' => 123]); + + $reflectionProperty = new \ReflectionProperty(HookEventBridge::class, 'mockedCallHook'); + $reflectionProperty->setAccessible(true); + + $reflectionProperty->setValue(null, function (string $name, int $data): int { + $this->assertSame('register_account', $name); + $this->assertSame(123, $data); + + return $data; + }); + + HookEventBridge::onAccountRegisterEvent($event); + } + + public function testOnAccountRemoveEventCallsHookWithCorrectValue(): void + { + $event = new ArrayFilterEvent(ArrayFilterEvent::ACCOUNT_REMOVE, ['user' => ['uid' => 123]]); + + $reflectionProperty = new \ReflectionProperty(HookEventBridge::class, 'mockedCallHook'); + $reflectionProperty->setAccessible(true); + + $reflectionProperty->setValue(null, function (string $name, array $data): array { + $this->assertSame('remove_user', $name); + $this->assertSame(['uid' => 123], $data); + + return $data; + }); + + HookEventBridge::onAccountRemoveEvent($event); + } + public function testOnEventUpdatedEventCallsHookWithCorrectValue(): void { $event = new ArrayFilterEvent(ArrayFilterEvent::EVENT_UPDATED, ['event' => ['id' => 123]]); @@ -427,7 +464,6 @@ class HookEventBridgeTest extends TestCase [ArrayFilterEvent::RENDER_LOCATION, 'render_location'], [ArrayFilterEvent::ITEM_PHOTO_MENU, 'item_photo_menu'], [ArrayFilterEvent::CONTACT_PHOTO_MENU, 'contact_photo_menu'], - [ArrayFilterEvent::PROFILE_SIDEBAR_ENTRY, 'profile_sidebar_enter'], [ArrayFilterEvent::PROFILE_SIDEBAR, 'profile_sidebar'], [ArrayFilterEvent::PAGE_INFO, 'page_info_data'], [ArrayFilterEvent::SMILEY_LIST, 'smilie'], @@ -441,6 +477,9 @@ class HookEventBridgeTest extends TestCase [ArrayFilterEvent::BLOCK_CONTACT, 'block'], [ArrayFilterEvent::UNBLOCK_CONTACT, 'unblock'], [ArrayFilterEvent::AVATAR_LOOKUP, 'avatar_lookup'], + [ArrayFilterEvent::ACCOUNT_AUTHENTICATE, 'authenticate'], + [ArrayFilterEvent::ACCOUNT_REGISTER, 'register_account'], + [ArrayFilterEvent::ACCOUNT_REMOVE, 'remove_user'], [ArrayFilterEvent::EVENT_CREATED, 'event_created'], [ArrayFilterEvent::EVENT_UPDATED, 'event_updated'], [ArrayFilterEvent::ADD_WORKER_TASK, 'proc_run'], diff --git a/tests/Unit/Event/ArrayFilterEventTest.php b/tests/Unit/Event/ArrayFilterEventTest.php index 85d90ba132..5bc71084c3 100644 --- a/tests/Unit/Event/ArrayFilterEventTest.php +++ b/tests/Unit/Event/ArrayFilterEventTest.php @@ -67,6 +67,9 @@ class ArrayFilterEventTest extends TestCase [ArrayFilterEvent::BLOCK_CONTACT, 'friendica.data.block_contact'], [ArrayFilterEvent::UNBLOCK_CONTACT, 'friendica.data.unblock_contact'], [ArrayFilterEvent::AVATAR_LOOKUP, 'friendica.data.avatar_lookup'], + [ArrayFilterEvent::ACCOUNT_AUTHENTICATE, 'friendica.data.account_authenticate'], + [ArrayFilterEvent::ACCOUNT_REGISTER, 'friendica.data.account_register'], + [ArrayFilterEvent::ACCOUNT_REMOVE, 'friendica.data.account_remove'], [ArrayFilterEvent::EVENT_CREATED, 'friendica.data.event_created'], [ArrayFilterEvent::EVENT_UPDATED, 'friendica.data.event_updated'], [ArrayFilterEvent::ADD_WORKER_TASK, 'friendica.data.add_worker_task'],