Skip to content

Commit

Permalink
XWIKI-21411: Improve check of translation document author rights
Browse files Browse the repository at this point in the history
* Check both author and content author and improve JavaDoc

(cherry picked from commit c4c8d61)
  • Loading branch information
pjeanjean authored and michitux committed Nov 16, 2023
1 parent 7525c13 commit 2a9ce88
Show file tree
Hide file tree
Showing 4 changed files with 310 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -138,39 +138,57 @@ protected void setReference(DocumentReference reference)
setId(this.idPrefix + this.serializer.serialize(reference));
}

protected LocalizedTranslationBundle loadDocumentLocaleBundle(Locale locale) throws Exception
/**
* Gets the document that defines the translation bundle for a given locale.
*
* @param locale the requested locale
* @return the document defining the translation bundle, or null if it could not be fetched yet and requires a retry
*/
protected XWikiDocument getDocumentLocaleBundle(Locale locale) throws Exception
{
XWikiContext context = this.contextProvider.get();

if (context == null) {
// No context for some reason, lets try later
// No context for some reason, let's try later.
return null;
}

XWiki xwiki = context.getWiki();

if (xwiki == null) {
// No XWiki instance ready, lets try later
// No XWiki instance ready, let's try later.
return null;
}

XWikiDocument document = xwiki.getDocument(this.documentReference, context);

if (locale != null && !locale.equals(Locale.ROOT) && !locale.equals(document.getDefaultLocale())) {
document = xwiki.getDocument(new DocumentReference(document.getDocumentReference(), locale), context);
}

if (document.isNew()) {
// No document found for this locale
return LocalizedTranslationBundle.EMPTY;
}
return document;
}

protected LocalizedTranslationBundle loadDocumentLocaleBundle(Locale locale) throws Exception
{
XWikiDocument document = getDocumentLocaleBundle(locale);

if (document == null) {
// Either no context or XWiki instance not ready, let's try later.
return null;
}

if (document.isNew()) {
// No document found for this locale.
return LocalizedTranslationBundle.EMPTY;
}

String content = document.getContent();

Properties properties = new Properties();
properties.load(new StringReader(content));

// Convert to LocalBundle
// Convert to LocalBundle.
DefaultLocalizedTranslationBundle localeBundle = new DefaultLocalizedTranslationBundle(this, locale);

TranslationMessageParser parser = getTranslationMessageParser();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
*/
package org.xwiki.localization.wiki.internal;

import java.util.Locale;

import org.xwiki.bridge.event.WikiDeletedEvent;
import org.xwiki.component.descriptor.ComponentDescriptor;
import org.xwiki.component.manager.ComponentLookupException;
Expand All @@ -27,6 +29,11 @@
import org.xwiki.localization.message.TranslationMessageParser;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.observation.event.Event;
import org.xwiki.security.authorization.AccessDeniedException;

import com.xpn.xwiki.XWiki;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.doc.XWikiDocument;

/**
* Component wiki document based implementation of Bundle.
Expand All @@ -37,6 +44,8 @@
*/
public class ComponentDocumentTranslationBundle extends AbstractDocumentTranslationBundle
{
private DocumentTranslationBundleFactory factory;

private ComponentDescriptor<TranslationBundle> descriptor;

/**
Expand All @@ -45,17 +54,54 @@ public class ComponentDocumentTranslationBundle extends AbstractDocumentTranslat
* @param componentManager used to lookup components needed to manipulate wiki documents
* @param translationMessageParser the parser to use for each message
* @param descriptor the component descriptor used to unregister the bundle
* @param factory the factory
* @throws ComponentLookupException failed to lookup some required components
*/
public ComponentDocumentTranslationBundle(String idPrefix, DocumentReference documentReference,
ComponentManager componentManager, TranslationMessageParser translationMessageParser,
ComponentDescriptor<TranslationBundle> descriptor) throws ComponentLookupException
ComponentDescriptor<TranslationBundle> descriptor, DocumentTranslationBundleFactory factory)
throws ComponentLookupException
{
super(idPrefix, documentReference, componentManager, translationMessageParser);

this.factory = factory;
this.descriptor = descriptor;
}

/**
* {@inheritDoc}
* This overrides the default implementation to first check the author rights for the document.
*
* @param locale the requested locale
* @return the document defining the translation bundle if its author has the necessary rights, the default locale
* otherwise, and fallback on the original implementation if the document doesn't exist or could not be fetched
*/
@Override
protected XWikiDocument getDocumentLocaleBundle(Locale locale) throws Exception
{
XWikiDocument document = super.getDocumentLocaleBundle(locale);

if (document != null && !document.isNew()) {
XWikiContext context = this.contextProvider.get();
XWiki xwiki = context.getWiki();
XWikiDocument defaultLocaleDocument = xwiki.getDocument(this.documentReference, context);

if (defaultLocaleDocument != document) {
// We only need to check rights for non-default locales.
try {
this.factory.checkRegistrationAuthorizationForDocumentLocaleBundle(document, defaultLocaleDocument);
} catch (AccessDeniedException e) {
this.logger.warn("Failed to load and register the translation for locale [{}] from document [{}]. "
+ "Falling back to default locale.", locale, document.getDocumentReference());
// We return the default translation bundle if the requested one has permission issues.
return defaultLocaleDocument;
}
}
}

return document;
}

@Override
public void onEvent(Event event, Object source, Object data)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.xwiki.localization.wiki.internal.TranslationDocumentModel.Scope;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.DocumentReferenceResolver;
import org.xwiki.model.reference.EntityReference;
import org.xwiki.model.reference.EntityReferenceSerializer;
import org.xwiki.model.reference.WikiReference;
import org.xwiki.observation.EventListener;
Expand Down Expand Up @@ -324,7 +325,7 @@ private ComponentDocumentTranslationBundle createComponentDocumentBundle(XWikiDo
try {
documentBundle =
new ComponentDocumentTranslationBundle(ID_PREFIX, document.getDocumentReference(),
this.componentManagerProvider.get(), this.translationParser, descriptor);
this.componentManagerProvider.get(), this.translationParser, descriptor, this);
} catch (ComponentLookupException e) {
throw new TranslationBundleDoesNotExistsException("Failed to create document bundle", e);
}
Expand Down Expand Up @@ -434,6 +435,23 @@ private void registerTranslationBundle(XWikiDocument document) throws Translatio
}
}

/**
* Checks that the author of a document defining a translation bundle has the necessary rights to make it
* available, based on the scope of the default locale translation bundle.
*
* @param document the document defining the translation bundle to check
* @param defaultLocaleDocument the document containing the default locale translation bundle
* @throws AccessDeniedException when the document author does not have enough rights for the defined scope
*/
protected void checkRegistrationAuthorizationForDocumentLocaleBundle(XWikiDocument document,
XWikiDocument defaultLocaleDocument) throws AccessDeniedException
{
Scope scope = getScope(defaultLocaleDocument);
if (scope != null && scope != Scope.ON_DEMAND) {
checkRegistrationAuthorization(document, scope);
}
}

/**
* @param document the translation document
* @param scope the scope
Expand All @@ -442,18 +460,24 @@ private void registerTranslationBundle(XWikiDocument document) throws Translatio
*/
private void checkRegistrationAuthorization(XWikiDocument document, Scope scope) throws AccessDeniedException
{
EntityReference entityReference;
switch (scope) {
case GLOBAL:
this.authorizationManager.checkAccess(Right.PROGRAM, document.getAuthorReference(), null);
this.authorizationManager.checkAccess(Right.PROGRAM, document.getContentAuthorReference(), null);
break;
case WIKI:
this.authorizationManager.checkAccess(Right.ADMIN, document.getAuthorReference(), document
.getDocumentReference().getWikiReference());
entityReference = document.getDocumentReference().getWikiReference();
this.authorizationManager.checkAccess(Right.ADMIN, document.getAuthorReference(), entityReference);
this.authorizationManager.checkAccess(Right.ADMIN, document.getContentAuthorReference(),
entityReference);
break;
case USER:
if (this.configuration.isRestrictUserTranslations()) {
this.authorizationManager.checkAccess(Right.SCRIPT, document.getAuthorReference(),
document.getDocumentReference());
entityReference = document.getDocumentReference();
this.authorizationManager.checkAccess(Right.SCRIPT, document.getAuthorReference(), entityReference);
this.authorizationManager.checkAccess(Right.SCRIPT, document.getContentAuthorReference(),
entityReference);
}
break;
default:
Expand Down
Loading

0 comments on commit 2a9ce88

Please sign in to comment.