Skip to content

Disabled public search as an option - #1896

Closed
denphi wants to merge 5 commits into
hubzero:2.4-mainfrom
denphi:dev
Closed

denphi wants to merge 5 commits into
hubzero:2.4-mainfrom
denphi:dev

Conversation

@denphi

@denphi denphi commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Searching Tags/Resources/Publications can be disabled for public users /bots

@denphi
denphi requested a review from nkissebe as a code owner June 15, 2026 16:08
@nkissebe
nkissebe force-pushed the 2.4-main branch 8 times, most recently from e96538d to 9680405 Compare September 16, 2026 18:23

@nkissebe nkissebe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this. Keeping bots off the expensive listing queries is a real need, but I don't think a per-component "allow public search" boolean is the right tool, and as submitted it doesn't close the door it means to. I'll make the change described at the end and open it as a PR that supersedes this one.

The guard only covers one entry point per component.

  • Resources: only browseTask is gated. browsetagsTask (the tag browser, on by default, so the return $this->browseTask() fallback never runs) and the AJAX browserTask still run keyword and tag searches for guests.
  • Tags: only viewTask. feedTask takes the same comma-separated list and returns the multi-tag result set as RSS.
  • The resources API list endpoint (?search=) and the site-search plugins for publications and resources still answer keyword queries for guests.

Behaviour problems.

  • A guest tag-filtered publications browse throws HTTP 410 Gone with a "you must log in" message instead of redirecting to login. 410 tells crawlers the URL is permanently gone even though it's valid for logged-in users and whenever the option is re-enabled.
  • The tags check counts the raw exploded list, so /tags/foo, or /tags/foo,foo or ?addtag=Foo look like multi-tag searches and bounce a guest to login for a single tag.
  • The QUERY_STRING regex is redundant with Request::getString('tag') and only differs for an empty ?tag=, which then gets the 410.
  • The same login-redirect block is copied into three components (publications already has _login() for this).

Proposal: make it a view access level, not a boolean. The hub already has view levels (Public, Registered, Special, plus whatever a hub defines) and com_projects already exposes a config field of type="accesslevel". So:

  • each component gets a browse_access (tags: search_access) field of type accesslevel, default Public, so nothing changes for hubs that don't touch it;
  • one helper on Hubzero\Component\SiteController (requireViewLevel($level, $message)) does the check against User::getAuthorisedViewLevels(), sending guests to log in with a return URL and giving logged-in users without the level a 403;
  • it's enforced on every listing/search path: publications browse; resources browse, tag browser and AJAX browser; tags view and feed (on the sanitized, de-duplicated tag count); the resources API list search; and the two search plugins.

"Registered only" then just means picking that level, and a hub can pick a narrower one. The default stays public, which matters because this hub's robots.txt and nofollow links are what actually keep crawlers off these pages today.


if (Request::getString('tag', '', 'request') || preg_match('/(?:^|[&;])(?:amp;)?tag=/i', $query))
{
throw new Exception(Lang::txt('COM_PUBLICATIONS_SEARCH_LOGIN_REQUIRED'), 410);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

410 Gone tells crawlers this URL is permanently dead, but it's valid for anyone logged in and whenever the option is re-enabled; and a guest gets an error page with no login link. The replacement sends guests to log in with a return URL, like the other guarded paths. (The QUERY_STRING regex above only differs from Request::getString('tag') for an empty ?tag=, which then lands here too.)

$tgs[] = $addtag;
}

if (!$this->config->get('allow_public_search', 1) && count($tgs) > 1 && User::isGuest())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

count($tgs) > 1 is the raw exploded list, so /tags/foo, or /tags/foo,foo or ?addtag=Foo bounce a guest for what is really a single tag. The de-duplicated $added after the sanitize loop is the right thing to count. feedTask also needs the same check; it takes the same list and returns the multi-tag results as RSS.

@nkissebe

nkissebe commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Merged via #1938: your three commits carried as-is, plus the rework proposed in the review above. The per-component boolean became a view access level (browse_access, tags search_access, default Public), checked through one SiteController::requireViewLevel() helper on every listing and search path: publications browse; resources browse, tag browser and AJAX browser; tags view and feed; the resources API list search; and both search plugins. To get the behaviour your option gave, set the component's browse access to Registered. Thanks for raising it.

@nkissebe nkissebe closed this Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants