Skip to content
Snippets Groups Projects
auth_helper.php 8.23 KiB
Newer Older
  • Learn to ignore specific revisions
  • <?php
    
    declare(strict_types=1);
    
    /**
     * @copyright  2020 Ad Aures
     * @license    https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
     * @link       https://castopod.org/
     */
    
    use App\Entities\Podcast;
    use App\Models\ActorModel;
    use App\Models\PodcastModel;
    use CodeIgniter\Shield\Entities\User;
    use Modules\Auth\Auth;
    use Modules\Fediverse\Entities\Actor;
    use Modules\Fediverse\Models\NotificationModel;
    
    if (! function_exists('auth')) {
        /**
         * Provides convenient access to the main Auth class for CodeIgniter Shield.
         *
         * @param string|null $alias Authenticator alias
         */
        function auth(?string $alias = null): Auth
        {
            /** @var Auth $auth */
            $auth = service('auth');
    
            return $auth->setAuthenticator($alias);
        }
    }
    
    if (! function_exists('set_interact_as_actor')) {
        /**
         * Sets the actor id of which the user is acting as
         */
        function set_interact_as_actor(int $actorId): void
        {
            if (auth()->loggedIn()) {
                session()
                    ->set('interact_as_actor_id', $actorId);
            }
        }
    }
    
    if (! function_exists('remove_interact_as_actor')) {
        /**
         * Removes the actor id of which the user is acting as
         */
        function remove_interact_as_actor(): void
        {
            session()->remove('interact_as_actor_id');
        }
    }
    
    if (! function_exists('interact_as_actor_id')) {
        /**
         * Sets the podcast id of which the user is acting as
         */
        function interact_as_actor_id(): ?int
        {
            return session()->get('interact_as_actor_id');
        }
    }
    
    if (! function_exists('interact_as_actor')) {
        /**
         * Get the actor the user is currently interacting as
         */
        function interact_as_actor(): Actor | false
        {
            if (! auth()->loggedIn()) {
                return false;
            }
    
            $session = session();
            if (! $session->has('interact_as_actor_id')) {
                return false;
            }
    
            return model(ActorModel::class, false)->getActorById($session->get('interact_as_actor_id'));
        }
    }
    
    if (! function_exists('can_user_interact')) {
        function can_user_interact(): bool
        {
            return (bool) interact_as_actor();
        }
    }
    
    if (! function_exists('add_podcast_group')) {
        function add_podcast_group(User $user, int $podcastId, string $group): User
        {
            $podcastGroup = 'podcast#' . $podcastId . '-' . $group;
    
            return $user->addGroup($podcastGroup);
        }
    }
    
    if (! function_exists('get_instance_group')) {
        function get_instance_group(User $user): ?string
        {
            $instanceGroups = array_filter($user->getGroups() ?? [], static function ($group): bool {
                return ! str_starts_with($group, 'podcast#');
            });
    
            if ($instanceGroups === []) {
                return null;
            }
    
            $instanceGroup = array_shift($instanceGroups);
    
            // Verify that a user belongs to one group only!
            if ($instanceGroups !== []) {
                // remove any other group the user belongs to
                $user->removeGroup(...$instanceGroups);
            }
    
            return $instanceGroup;
        }
    }
    
    if (! function_exists('set_instance_group')) {
        function set_instance_group(User $user, string $group): User
        {
            // remove old instance group
            if (get_instance_group($user)) {
                $user->removeGroup(get_instance_group($user));
            }
    
            // set new group
            return $user->addGroup($group);
        }
    }
    
    if (! function_exists('get_podcast_group')) {
        function get_podcast_group(User $user, int $podcastId): ?string
        {
            $podcastGroups = array_filter($user->getGroups() ?? [], static function ($group) use ($podcastId): bool {
                return str_starts_with($group, "podcast#{$podcastId}");
            });
    
            if ($podcastGroups === []) {
                return null;
            }
    
            $podcastGroup = array_shift($podcastGroups);
    
            // Verify that a user belongs to one group only!
            if ($podcastGroups !== []) {
                // remove any other group the user belongs to
                $user->removeGroup(...$podcastGroups);
            }
    
            // strip the `podcast#{id}.` prefix when returning group
    
            return substr((string) $podcastGroup, strlen('podcast#' . $podcastId . '-'));
    
        }
    }
    
    if (! function_exists('set_podcast_group')) {
        function set_podcast_group(User $user, int $podcastId, string $group): User
        {
            // remove old instance group
            $user->removeGroup("podcast#{$podcastId}-" . get_podcast_group($user, $podcastId));
    
            // set new group
            return add_podcast_group($user, $podcastId, $group);
        }
    }
    
    if (! function_exists('get_podcast_groups')) {
        /**
         * @return string[]
         */
        function get_user_podcast_ids(User $user): array
        {
            $podcastGroups = array_filter($user->getGroups() ?? [], static function ($group): bool {
                return str_starts_with($group, 'podcast#');
            });
    
            $userPodcastIds = [];
            // extract all podcast ids from groups
            foreach ($podcastGroups as $podcastGroup) {
    
                // extract podcast id from group and add it to the list of ids
                preg_match('~podcast#([0-9]+)-[a-z]+~', $podcastGroup, $matches);
                $userPodcastIds[] = $matches[1];
    
            }
    
            return $userPodcastIds;
        }
    }
    
    if (! function_exists('can_podcast')) {
        function can_podcast(User $user, int $podcastId, string $permission): bool
        {
            return $user->can('podcast#' . $podcastId . '.' . $permission);
        }
    }
    
    if (! function_exists('get_user_podcasts')) {
        /**
         * Returns the podcasts the user is contributing to
         *
         * @return Podcast[]
         */
        function get_user_podcasts(User $user): array
        {
            return (new PodcastModel())->getUserPodcasts($user->id, get_user_podcast_ids($user));
        }
    }
    
    if (! function_exists('get_podcasts_user_can_interact_with')) {
        /**
         * @return Podcast[]
         */
        function get_podcasts_user_can_interact_with(User $user): array
        {
            $userPodcasts = (new PodcastModel())->getUserPodcasts($user->id, get_user_podcast_ids($user));
    
            $hasInteractAsPrivilege = interact_as_actor_id() === null;
    
            if ($userPodcasts === []) {
                if ($hasInteractAsPrivilege) {
                    remove_interact_as_actor();
                }
    
                return [];
            }
    
            $isInteractAsPrivilegeLost = true;
            $podcastsUserCanInteractWith = [];
            foreach ($userPodcasts as $userPodcast) {
                if (can_podcast($user, $userPodcast->id, 'interact-as')) {
                    if (interact_as_actor_id() === $userPodcast->actor_id) {
                        $isInteractAsPrivilegeLost = false;
                    }
    
                    $podcastsUserCanInteractWith[] = $userPodcast;
                }
            }
    
            if ($podcastsUserCanInteractWith === []) {
                if (interact_as_actor_id() !== null) {
                    remove_interact_as_actor();
                }
    
                return [];
            }
    
            // check if user has lost the interact as privilege for current podcast actor.
            // --> Remove interact as if there's no podcast actor to interact as
            // or set the first podcast actor the user can interact as
            if ($isInteractAsPrivilegeLost) {
                set_interact_as_actor($podcastsUserCanInteractWith[0]->actor_id);
            }
    
            return $podcastsUserCanInteractWith;
        }
    }
    
    if (! function_exists('get_actor_ids_with_unread_notifications')) {
        /**
         * Returns the ids of the user's actors that have unread notifications
         *
         * @return int[]
         */
        function get_actor_ids_with_unread_notifications(User $user): array
        {
            if (($userPodcasts = get_user_podcasts($user)) === []) {
                return [];
            }
    
            $unreadNotifications = (new NotificationModel())->whereIn(
                'target_actor_id',
                array_column($userPodcasts, 'actor_id')
            )
                ->where('read_at', null)
                ->findAll();
    
            return array_column($unreadNotifications, 'target_actor_id');
        }
    }
    
    if (! function_exists('get_group_title')) {
        /**
         * @return array<'title'|'description', string>
         */
        function get_group_info(string $group, ?int $podcastId = null): array
        {
            if ($podcastId === null) {
                return setting('AuthGroups.instanceGroups')[$group];
            }
    
            return setting('AuthGroups.podcastGroups')[$group];
        }
    }