Privacy for first and last name on public wikis

Hello everyone,

Is it possible for registered users on public wikis to opt for their first name and last name to not be displayed to unregistered users? Is there any standard feature for this or this would require a specific script to be made?

Thank you!

Hello @DorinaAnton

I don’t know any option to do that. I’ve asked CC and here’s its answer.

There’s no standard feature for this today, and unfortunately the workaround you’d expect — restricting the user profile page — doesn’t actually hide the name.

Why restricting the profile page isn’t enough

The pretty user name is computed by XWiki#getUserName(), which loads the user profile document and reads the first_name/last_name properties without checking view rights. So even when a guest has no view right on XWiki.JohnDoe, the name still shows up in the “Last modified by” line of the page header, in comments, in the history tab, in Live Data author columns, in the REST API (authorName, creatorName, modifierName on pages, comments and search results) and in the Solr index (author_display, creator_display), which feeds the search UI.

Denying view to unregistered users on the profile pages is still worth doing — it hides the profile itself (email, address, phone, avatar) and keeps profiles out of guest search results and out of the user directory — but it doesn’t hide the names attached to content.

What XWiki does have

Only for email addresses: “Obfuscate Email Addresses” (Administration → Mail → General, or mail.general.obfuscate in xwiki.properties, since XWiki 12.4). When enabled, emails are displayed as a...@domain.org to users without edit rights and are excluded from the Solr index. There is no equivalent for the first and last name.

What you can do today

  • Encourage pseudonymous accounts: when first_name and last_name are empty, XWiki falls back to displaying the profile page name, so the username is what everyone sees. This hides the real name from everybody though, not only from guests.
  • Deny view to unregistered users on the user profile pages, as described above — partial, but useful.
  • A custom solution can override the skin templates and sheets that call $xwiki.getUserName() to display an alias when the current user is a guest. This is fragile and incomplete: there is no extension point for user name display, so it won’t cover REST, the search results, exports or third-party extensions.

What should happen

A proper opt-in (“don’t display my name to unregistered users”) needs platform support, most naturally by generalizing the existing obfuscation mechanism to the name fields, so that it applies in getUserName()/getPlainUserName() and in the Solr metadata extractor. I couldn’t find an existing issue for it in JIRA.

Would that match your needs? Is the need only for unregistered users or could it be extended to specific users or groups or users for ex?

Another thing that we could do is evaluate the possibility of having a single place in the code to display user names (using a component + script api for ex) and making sure all code displaying user names use it. That would make it easy to override it and decide what to display.

Thx

Disclaimer: Analysis done by CC, to be verified (could be wrong, but gives a good idea).

I had a look at the code to check how realistic this is. Short version: it’s doable and mostly a convergence job rather than a design problem — and we already have a partial precedent.

Where we are today

There are three parallel ways of displaying a user name, and none of them is authoritative:

  • XWiki#getUserName() / #getPlainUserName() / #getLocalUserName() in oldcore (XWiki.java), exposed as $xwiki.getUserName(). This is the de-facto standard and it’s used everywhere.
  • The #displayUser($arg $options) Velocity macro in templates/macros.vm, which does the nicer job (avatar, link, handles both users and groups) but is Velocity-only and is used in about 19 files.
  • Direct reads of the first_name/last_name xproperties, or of UserProperties#getFirstName()/getLastName(), scattered around.

Note that $services.user (UserScriptService) has no display method at all today, which is exactly the gap.

How much code is concerned

Roughly 215 matching lines across ~94 files in xwiki-platform-core + xwiki-platform-distribution (main code only, tests excluded):

  • Java, ~28 files: oldcore (XWiki, api/XWiki with 22 overloads, api/Document#getAuthorName()/getCreatorName()/getContentAuthorName(), UsersClass — the displayer of the Users property type —, XWikiLock, LockAction/EditAction/CancelAction/AdminAction), REST (ModelFactory, DomainObjectFactory, the search sources, UsersClassPropertyValuesProvider, DocumentUserReferenceModelSerializer), Solr (DocumentSolrMetadataExtractor, AttachmentSolrMetadataExtractor for author_display/creator_display/attachment_author_display), UserMentionsFormatter (which reimplements the name building with its own FIRST_NAME/LOGIN/FULL_NAME styles), the feed plugin, the notifications RSS renderer, the extension repository and the reset-password manager.
  • Velocity and wiki pages, 66 files. The main ones: macros.vm, flamingo’s contentheader.vm, commentsinline.vm, historyinline.vm, drawer.vm, restore.vm, editinline.vm, renameStatus.vm, copy.vm; getdocuments.vm, getusers.vm, getgroupmembers.vm, uorgsuggest.vm, recyclebinlist.vm, getdeleteddocuments.vm, diff_macros.vm, attachment_macros.vm, pdfcover.vm/pdffooter.vm, notification/macros.vm, notification/email/macros.vm; and wiki pages such as LiveTableResultsMacros.xml, SharePage.xml (16 occurrences on its own), WikiUsers.xml, UserDirectoryMacros.xml, XWikiUserProfileSheet.xml, PageAuthorsUIX.xml, SolrUserFacet.xml, Stats/Macros.xml, Invitation*, AnnotationCode/Macros.xml, Panels/Members.xml.
  • And a handful of places that bypass both APIs and read first_name/last_name directly for display: likers.vm (it builds a Live Data JSON with first_name/last_name columns), uorgsuggest.vm, getusers.vm, UserDirectoryMacros.xml, AdminUsersSheet.xml, SearchSuggestConfig.xml. These are precisely the ones that any display-level override would silently miss today.

What the API could look like

A new component role in xwiki-platform-user-api:

@Role
public interface UserDisplayer
{
    Block display(UserReference user, Map<String, Object> parameters);
    String displayPlain(UserReference user);
}

with the default implementation reading UserProperties and handling the guest/unknown/empty-name fallbacks, exposed as $services.user.display(...), with #displayUser delegating to it and XWiki#getUserName() and friends kept but reimplemented on top of it (they’re used by a large part of the extension ecosystem, so removing them is out of the question). Returning a Block instead of a String would also let us get rid of the current link/escapeXML boolean matrix, which is a recurring source of escaping bugs.

The tricky parts

  1. Solr is index-time, not display-time. author_display and creator_display are stored fields, so a display-level API can’t retroactively hide anything there. We’d need to follow what we did for emails (exclude from indexing when obfuscation is enabled) or filter at query time. The same applies to any name already serialized into a cached JSON payload.
  2. getUserName(user, format, ...) evaluates arbitrary Velocity with every XWikiUsers property bound to the context. That’s a public API contract that’s painful to reproduce in a clean component, and it’s a privacy hole in itself since a format script can read email. I’d deprecate it rather than port it.
  3. The JS consumers (Live Data, notifications, mentions autocomplete, the user picker, search suggest) receive names as JSON, so they have to be fed from the server-side API. likers.vm shipping raw first_name/last_name columns is a good illustration of what breaks otherwise.
  4. Users versus groups: #displayUser already unifies them, and the new API should too, otherwise it’ll be re-forked immediately.
  5. Volume: ~94 files is a lot of churn, much of it in wiki pages and templates that are hard to test. It has to be incremental — introduce the API, migrate oldcore, REST and the skin templates first, and leave the long tail for later.

One extra argument in favour, independent of privacy: getUserName() loads the full user document on every single call, so a single API is also the natural place to add caching, which would help on any page listing many authors.

So I’d suggest two issues: one for the UserDisplayer API and the migration of the call sites, and one for the privacy feature itself (opt-in “don’t display my name to unregistered users”), the latter depending on the former for anything beyond the obvious display points.