URL API

Last modified by Admin on 2024/10/28 18:41

cogAllows configuration of the URL scheme used by XWiki to parse/serialize URLs and security management of external URLs
TypeJAR
CategoryAPI
Developed by

XWiki Development Team

Rating
0 Votes
LicenseGNU Lesser General Public License 2.1
Bundled With

XWiki Standard

Installable with the Extension Manager

Description

Even though it's already in use, this module is still in its infancy and the serialization part is not fully implemented yet.

This module is in charge of 

  • parsing/serializing XWiki URLs, based on a URL scheme specified in XWiki's configuration,
  • XWiki 12.10.7+, 13.3+ handling security of external URLs 

URLs schemes

In general terms the goal of this module is to allow implementing various URL schemes (i.e. various ways to specify XWiki URLs).

Specifically it uses the Resource API module and implements both a ResourceReferenceResolver and a ResourceReferenceSerializer to parse and serialize URLs. 

The following schemes are currently implemented:

  • standard
  • A filesystem URL Scheme has been started for exporting Resources to the filesystem and generating URLs to them (useful for the HTML Export for example). At the moment, only a few Resource Types are using it and most are still using the old XWikiURLFactory implementation classes.
  • A reference URL Scheme has been started but is not usable yet. It's meant to implement an Alternate Scheme being proposed in Design.

If you're looking to controlling the URLs you wish to use inside XWiki you should also check the Short URL tutorial, as one solution there is the use of a Rewrite Filter.

API Examples

Extracts the EntityReference pointed to by an XWiki URL, following the defined URL Scheme:

import org.xwiki.resource.entity.EntityResourceReference;

...
@Inject
private ResourceTypeResolver<ExtendedURL> typeResolver;

@Inject
private ResourceReferenceResolver<ExtendedURL> resourceResolver;
...

// context is an XWikiContext object
URL url = context.getURL();
ExtendedURL extendedURL = new ExtendedURL(url, context.getRequest().getContextPath());
ResourceType type = typeResolver.resolve(extendedURL, Collections.<String, Object>emptyMap());
ResourceReference reference = resourceResolver.resolve(extendedURL, type, Collections.<String, Object>emptyMap());

if (reference instanceof EntityResourceReference) {
    EntityReference entityReference = ((EntityResourceReference) reference).getEntityReference();
    ...
}

Tools

This module also offers some tools that can be reused. Specifically it provides:

  • An EntityReferenceResolver<String> resolver and an EntityReferenceSerializer<String> serializer implementations (with hint url) that can be used to parse/serialize EntityReference when used in URLs. The rationale is that Tomcat, for security reasons, doesn't support forward and backward slashes (/, \) in URLs by default. Thus, if a reference contains some reserved characters such as dot (.), colon (:), etc and you use a default serializer they'll be escaped using a backslash, leading to problems under Tomcat. So this resolver/serializer uses a different escape character (namely, it uses !). Example usage:
    @Inject
    @Named("url")
    private EntityReferenceResolver<String> urlResolver;

    @Inject
    @Named("url")
    private EntityReferenceSerializer<String> urlSerializer;

    @Inject
    private EntityReferenceSerializer<String> defaultSerializer;

    ...
    EntityReference reference = this.urlResolver.resolve("Some!.Page.Another page");
    assertEquals("Some!.Page.Another page", this.urlSerializer.serialize(reference));
    assertEquals("Some\.Page.Another page", this.defaultSerializer.serialize(reference));

Configuration

Scheme

The scheme to use is controlled by the url.format property in xwiki.properties:

#-# [Since 5.1M1]
#-# The id of the URL format to use. This allows to plug in different implementations and thus allows to completely
#-# control the format of XWiki URLs.
#-#
#-# The default is:
# url.format=standard

In addition, starting with XWiki 7.2M1 the URL scheme to use is set automatically in the Execution Context and can be modified at runtime. For example:

@Inject private URLContextManager urlContextManager;
...
urlContextManager.setURLFormatId("filesystem");
...
String formatId = urlContextManager.getURLFormatId();
...

Resource modification date

Starting with XWiki 11.1RC1 the modification date of local resources can be used as a query parameter in the generated URLs.
In some cases, this might impact the performances of the wiki. You can change this behaviour by editing the url.useResourceLastModificationDate in xwiki.properties:

#-# [Since 11.1RC1]
#-# Whether a the last modified date of the file to be loaded should be checked and put in the URL query parameter.
#-# Disabling this might improve a bit the performance on some old hard drives, or custom filesystem, however
#-# it might imply the need to force-reload some resources in the browser, when migrating.
#-#
#-# The default is:
# url.useResourceLastModificationDate=true

Security

XWiki 12.10.7+, 13.3+

This module is also in charge of handling the security of external URLs with a dedicated component URLSecurityManager:

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.xwiki.url;

import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;

import org.xwiki.component.annotation.Role;
import org.xwiki.stability.Unstable;

/**
 * Dedicated component to perform security checks on URLs.
 *
 * @version $Id$
 * @since 13.3RC1
 * @since 12.10.7
 */

@Role
public interface URLSecurityManager
{
   /**
     * Constant to be used in {@link org.xwiki.context.ExecutionContext} with the value {@code "true"} to bypass a
     * check of {@link #isDomainTrusted(URL)}.
     */

    String BYPASS_DOMAIN_SECURITY_CHECK_CONTEXT_PROPERTY = "bypassDomainSecurityCheck";

   /**
     * Check if the given {@link URL} can be trusted based on the trusted domains of the wiki.
     * This method check on both the list of trusted domains given by the configuration
     * (see {@link URLConfiguration#getTrustedDomains()}) and the list of aliases used by the wiki descriptors.
     * Note that this method always returns {@code true} if {@link URLConfiguration#isTrustedDomainsEnabled()} returns
     * {@code true}. Also the method will return {@code true} whenever the {@link org.xwiki.context.ExecutionContext}
     * contains a property named {@link #BYPASS_DOMAIN_SECURITY_CHECK_CONTEXT_PROPERTY} with the value {@code "true"}.
     *
     * @param urlToCheck the URL for which we want to know if the domain is trusted or not.
     * @return {@code true} if the URL domain can be trusted or if the check is skipped, {@code false} otherwise
     */

   boolean isDomainTrusted(URL urlToCheck);

   /**
     * Check if the given URI can be trusted.
     * A URI can be trusted if:
     * <ul>
     *     <li>it's not opaque (see {@link URI} documentation for definition of opaque URI. TL;DR: a URI without
     *     {@code //} is opaque): note that following this, any URI such as {@code mailto:acme@foo.org} won't be
     *     trusted</li>
     *     <li>it refers to a specific domain and this domain is trusted (see {@link #isDomainTrusted(URL)})</li>
     *     <li>it's completely relative: it doesn't refer to an external domain</li>
     * </ul>
     *
     * @param uri the URI to check if it can be trusted or not
     * @return {@code true} only if the URI can be trusted per the criteria given in the above documentation
     * @since 14.10.4
     * @since 15.0
     */

   default boolean isURITrusted(URI uri)
   {
       return false;
   }

   /**
     * Parse the given string to create a URI that is safe to use.
     * This method throws a {@link SecurityException} if the parsed URI is not safe to use according to
     * {@link #isURITrusted(URI)}. It might also throw a {@link URISyntaxException} if the parameter cannot be properly
     * parsed.
     * Note that this method might try to "repair" URI that are not parsed correctly by {@link URI#URI(String)}
     * (e.g. serialized uri containing spaces).
     *
     * @param serializedURI a string representing a URI that needs to be parsed.
     * @return a URI safe to use
     * @throws URISyntaxException if the given parameter cannot be properly parsed
     * @throws SecurityException if the parsed URI is not safe according to {@link #isURITrusted(URI)}
     * @since 14.10.4
     * @since 15.0
     */

   default URI parseToSafeURI(String serializedURI) throws URISyntaxException, SecurityException
   {
       throw new SecurityException("Cannot guarantee safeness of " + serializedURI);
   }

   /**
     * Parse the given string to create a URI that is safe to use.
     * This method throws a {@link SecurityException} if the parsed URI is not safe to use according to
     * {@link #isURITrusted(URI)}. It might also throw a {@link URISyntaxException} if the parameter cannot be properly
     * parsed.
     * Note that this method might try to "repair" URI that are not parsed correctly by {@link URI#URI(String)}
     * (e.g. serialized uri containing spaces).
     *
     * @param serializedURI a string representing a URI that needs to be parsed.
     * @param requestHost the host the current request, this host will be added to the safe domains, when omitted this
     * host is extracted from the context, this parameter exists for cases where the context is not available
     * @return a URI safe to use
     * @throws URISyntaxException if the given parameter cannot be properly parsed
     * @throws SecurityException if the parsed URI is not safe according to {@link #isURITrusted(URI)}
     * @since 16.8.0
     * @since 16.4.4
     * @since 15.10.13
     */

   @Unstable
   default URI parseToSafeURI(String serializedURI, String requestHost) throws URISyntaxException, SecurityException
   {
       throw new SecurityException("Cannot guarantee that " + serializedURI + " is safe.");
   }
}

Script service

 

XWiki 15.0+, 14.10.4+

A dedicated script service has been introduced to allow access of the security URL APIs: 

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.xwiki.url.script;

import java.net.URI;
import java.net.URISyntaxException;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.slf4j.Logger;
import org.xwiki.component.annotation.Component;
import org.xwiki.script.service.ScriptService;
import org.xwiki.url.URLSecurityManager;

/**
 * Script service for security related checks from URL and URI.
 *
 * @version $Id$
 * @since 14.10.4
 * @since 15.0
 */

@Component
@Named("security.url")
@Singleton
public class URLSecurityScriptService implements ScriptService
{
   @Inject
   private URLSecurityManager urlSecurityManager;

   @Inject
   private Logger logger;

   /**
     * Parse the given string to create a URI that is safe to use.
     * This method returns null if the parsed URI is not safe to use according to
     * {@link URLSecurityManager#isURITrusted(URI)}. It might also throw a {@link URISyntaxException} if the parameter
     * cannot be properly parsed.
     *
     * @param uriRepresentation a string representing a URI that needs to be parsed.
     * @return a URI safe to use or {@code null}
     * @throws URISyntaxException if the given parameter cannot be properly parsed
     * @see URLSecurityManager#parseToSafeURI(String)
     */

   public URI parseToSafeURI(String uriRepresentation) throws URISyntaxException, SecurityException
   {
       try {
           return this.urlSecurityManager.parseToSafeURI(uriRepresentation);
       } catch (SecurityException e)
       {
           this.logger.info("The URI [{}] is considered not safe: [{}]", uriRepresentation, e.getMessage());
           this.logger.debug("Security exception stack trace: ", e);
           return null;
       }
   }
}

Prerequisites & Installation Instructions

We recommend using the Extension Manager to install this extension (Make sure that the text "Installable with the Extension Manager" is displayed at the top right location on this page to know if this extension can be installed with the Extension Manager).

You can also use the manual method which involves dropping the JAR file and all its dependencies into the WEB-INF/lib folder and restarting XWiki.

Dependencies

Dependencies for this extension (org.xwiki.platform:xwiki-platform-url-api 16.9.0):

Get Connected