|
| 1 | +# Extending the Apache Felix Web Console |
| 2 | + |
| 3 | +The Apache Felix Web Console is extensible in various ways. |
| 4 | + |
| 5 | +## Providing Web Console Plugins |
| 6 | + |
| 7 | +The Web Console can be extended by registering an OSGi service for the interface `jakarta.servlet.Servlet`. The respective service is called a Web Console Plugin or a plugin for short. |
| 8 | + |
| 9 | +The following table shows the required and optional registration properties. |
| 10 | + |
| 11 | +| Property | Description | |
| 12 | +|---|---| |
| 13 | +|`felix.webconsole.label`| A required string property with the label. The label is also used as a path segment in the URL to address the plugin. | |
| 14 | +|`felix.webconsole.title`| A required string property for the title.| |
| 15 | +|`felix.webconsole.css`|A single string, an array of strings or a collection of strings to be used as additional CSS resources which are automatically included in the head of the html page for the plugin. This property is optional.| |
| 16 | + |
| 17 | +If there are more than one plugin registered with the same label, the one with the highest service ranking is used. |
| 18 | + |
| 19 | +Before calling the servlet the web console sets these request attributes helping the plugin rendering the response: |
| 20 | + |
| 21 | +| Property | Description | |
| 22 | +|---|---| |
| 23 | +| `felix.webconsole.appRoot` | This request attribute of type `String` provides the absolute path of the Web Console root. This path consists of the servlet context path (from `ServletRequest.getContextPath()`) and the Web Console servlet path (from `HttpServletRequest.getServletPath()`, `/system/console` by default). This attribute can be used to provide absolute links to resources (images, CSS, scripts, etc.) or other plugins. This request attribute is available to client side JavaScript as the global _`appRoot`_ variable.| |
| 24 | +| `felix.webconsole.pluginRoot` | This request attribute of type `String` provides the absolute path of the current plugin. This path consists of the servlet context path (from `ServletRequest.getContextPath()`), the Web Console servlet path (from `HttpServletRequest.getServletPath()`, `/system/console` by default) and the plugin label. This attribute can be used to provide absolute links to the plugin itself. This request attribute is available to client side JavaScript as the global _`pluginRoot`_ variable.| |
| 25 | + |
| 26 | +The most basic plugin is a plain old Servlet whose `service(ServletRequest, ServletResponse)` method is called by the Apache Felix Web Console. For support is provided by extending the `org.apache.felix.webconsole.servlet.AbstractServlet` base class. In general the first approach is the preferred option as it does not create a dependency on web console API. |
| 27 | + |
| 28 | +### Plugin Logging |
| 29 | + |
| 30 | +The web console does not provide anything special for logging from within a plugin. As the web console is using slf4j, it is advisable to use the same logging mechanism. However, plugins are free to use other logging APIs. |
| 31 | + |
| 32 | +### Providing Resources |
| 33 | + |
| 34 | +All requests that are targetted at a plugin where the path starts with `/res/` are automatically handled as requests to resources. In this case the resources are searched inside the bundle providing the plugin. |
| 35 | + |
| 36 | +### Web Console Output Templating |
| 37 | + |
| 38 | +Templating and Internationalization support of the Web Console is based on Java Resource Bundles loaded from the plugin bundles and is transparent to the plugin itself. |
| 39 | + |
| 40 | +All html output generated by the plugin is filtered by the web console. This writer filter recognizes variables of the pattern `${name}` and tries to replace that part of the output with another string: |
| 41 | + |
| 42 | +* If a variable of that name exists, the value of that variable is used. See Variable Resolution below. |
| 43 | +* Otherwise if a resource bundle provides a translated string for the name, that string is used. See Resource Bundles below. |
| 44 | +* Otherwise the name itself is just placed in the output. |
| 45 | + |
| 46 | +#### Example |
| 47 | + |
| 48 | +Consider the plugin bundle provides a localization for the default german locale `de`: |
| 49 | + |
| 50 | +``` |
| 51 | +OSGI-INF/l10n/bundle_de.properties: |
| 52 | +Hello = Guten Tag |
| 53 | +``` |
| 54 | + |
| 55 | +And the plugin defines a variable replacement and writes output with the following code: |
| 56 | + |
| 57 | +```java |
| 58 | + this.getVariableResolver().put("world", "Schweiz"); |
| 59 | + response.getWriter().println("${Hello} ${world}"); |
| 60 | +``` |
| 61 | + |
| 62 | +The response sent to the client whose primary locale is `de` is then filtered to be: |
| 63 | + |
| 64 | +> Guten Tag Schweiz |
| 65 | +
|
| 66 | +#### Resource Bundles |
| 67 | + |
| 68 | +Resources for the Resource Bundles is provided by the Web Console bundle on the one hand and by the bundle providing the plugin on the other hand. Resources are identified inside a bundle with the `Bundle-Localization` manifest header as described in Section 3.10 Localization in the Core Specification. |
| 69 | + |
| 70 | +This also means, that additional translations may be provided by fragment bundles. |
| 71 | + |
| 72 | +During request processing the `Locale` of the request (`ServletRequest.getLocale()`) is used to identify the actual resources to use. From this information a `ResourceBundle` is constructed from a collection of the resources provided by the plugin bundle and the resources provided by the Web Console. |
| 73 | + |
| 74 | +#### Web Console Localization |
| 75 | + |
| 76 | +The Web Console contains a single localization file `OSGI-INF/l10n/bundle.properties`. Fragments attached to the Web Console bundle may provide translations for these resources. All plugins of the Web Console itself will use a ReosurceBundle, which is only based on the localization of the Web Console itself. |
| 77 | + |
| 78 | +#### Using Templating |
| 79 | + |
| 80 | +To use the described templating, the plugin developer may provide the following: |
| 81 | + |
| 82 | +- Use templated strings in the generated response. Such templated strings will be replaced with variable values or localization strings as available. |
| 83 | +- Set variable mappings in a `VariableResolver`. The simplest thing is to get a default `VariableResolver` calling the `this.getVariableResolver(ServletRequest)` method. |
| 84 | +- Provide localization files and optionally set the `Bundle-Localization` header if the base file name is not the default `OSGI-INF/l10n/bundle`. |
| 85 | + |
| 86 | +## Legacy Plugin Support |
| 87 | + |
| 88 | +The Web Console supports plugins written for older versions of the Web Console leveraging the `javax.servlet.Servlet` interface. The support requires the `javax.servlet` API to be available at runtime as well as the Apache Felix Http Wrappers API. If both dependencies are provided at runtime, the legacy support is automatically enabled. |
| 89 | + |
| 90 | +For more information refer to the [legacy plugins](https://felix.apache.org/documentation/subprojects/apache-felix-web-console/extending-the-apache-felix-web-console/providing-web-console-plugins.html). |
| 91 | + |
| 92 | +## Branding the Web Console |
| 93 | + |
| 94 | +Branding the Web Consle mainly concerns hooking into the looks of the Web Console providing vendor-provided setup like CSS, Logo, Main Title, Vendor URL, etc. |
| 95 | + |
| 96 | +Branding for the Web Console can be provided in two ways: By registering a `BrandingPlugin` service or by providing a branding properties files. The Web Console uses the branding from the `BrandingPlugin` service registered with the highest ranking. |
| 97 | + |
| 98 | +The `BrandingPlugin` interface provides the following information used for branding: |
| 99 | + |
| 100 | +```java |
| 101 | +// Returns an indicative name of the branding plugin |
| 102 | +// This value is used as the Window/Page title together with the |
| 103 | +// title of the respective plugin |
| 104 | +String getBrandName(); |
| 105 | + |
| 106 | +// Returns the name of the product in which the web console is contained |
| 107 | +// and to which the web console is branded. |
| 108 | +String getProductName(); |
| 109 | + |
| 110 | +// Returns an (absolute) URL to a web site representing the product to |
| 111 | +// which the web console is branded. |
| 112 | +String getProductURL(); |
| 113 | + |
| 114 | +// Returns an absolute path to an image to be rendered as the logo of the |
| 115 | +// branding product. |
| 116 | +String getProductImage(); |
| 117 | + |
| 118 | +// Returns the name of the branding product vendor. |
| 119 | +String getVendorName(); |
| 120 | + |
| 121 | +// Returns an (absolute) URL to the web site of the branding product |
| 122 | +// vendor. |
| 123 | +String getVendorURL(); |
| 124 | + |
| 125 | +// Returns an absolute path to an image to be rendered as the logo of the |
| 126 | +// branding product vendor. |
| 127 | +String getVendorImage(); |
| 128 | + |
| 129 | +// Returns the absolute path to an icon to be used as the web console |
| 130 | +// "favicon". |
| 131 | +String getFavIcon(); |
| 132 | + |
| 133 | +// Returns the absolute path to a CSS file to be used as the main CSS for |
| 134 | +// the basic admin site. |
| 135 | +String getMainStyleSheet(); |
| 136 | +``` |
| 137 | + |
| 138 | +If no `BrandingPlugin` service is registered, the `DefaultBrandingPlugin` is used. |
| 139 | + |
| 140 | +The `DefaultBrandingPlugin` reads the `/META-INF/webconsole.properties` from the web console bundle to setup the branding using the following properties: |
| 141 | + |
| 142 | +|Propery Name| Default Value | `BrandingPlugin` method name| |
| 143 | +|---|---|---| |
| 144 | +| `webconsole.brand.name`| Apache Felix Web Console| `getBrandName()`| |
| 145 | +| `webconsole.product.name`| Apache Felix| `getProductName()`| |
| 146 | +| `webconsole.product.url`|| https://felix.apache.org| `getProductURL()`| |
| 147 | +| `webconsole.product.image`|| /res/imgs/logo.png| `getProductImage()`| |
| 148 | +| `webconsole.vendor.name`| The Apache Software Foundation| `getVendorName()`| |
| 149 | +| `webconsole.vendor.url`| https://www.apache.org| `getVendorURL()`| |
| 150 | +| `webconsole.vendor.image`| /res/imgs/logo.png| `getVendorImage()`| |
| 151 | +| `webconsole.favicon`| /res/imgs/favicon.ico| `getFavIcon()`| |
| 152 | +| `webconsole.stylesheet`| /res/ui/webconsole.css| `getMainStyleSheet()`| |
| 153 | + |
| 154 | +_Note:_ The `/META-INF/webconsole.properties` file is not contained in the Apache Felix Web Console bundle itself. It may be provided by a Fragment Bundle attaching to the Apache Felix Web Console bundle. |
| 155 | + |
| 156 | +## Web Console Security Provider |
| 157 | + |
| 158 | +An additional service controlling the access to the web console can be deployed by registering a service implementing the `org.apache.felix.webconsole.spi.SecurityProvider` interface. |
| 159 | + |
| 160 | +It is possible to _require_ a SecurityProvider service to be present in order for the Web Console to become active. This can be used to prevent bypassing the security provider if this is provided asynchronously. To do this, the SecurityProvider service needs to be registered with a _Service Registration_ property to identify itself: |
| 161 | + |
| 162 | +> "webconsole.security.provider.id" = "my.security.provider" |
| 163 | +
|
| 164 | +Then the Web Console can be informed about the security providers it needs to operate by setting the following _OSGi Framework_ property: |
| 165 | + |
| 166 | +> "felix.webconsole.security.providers" = "my.security.provider" |
| 167 | +
|
| 168 | +With this property specified, the Web Console will not activate until the service with the specified property is present. Local login as typically done with `admin/password` is disabled when this property is specified. |
| 169 | + |
| 170 | +### The SecurityProvider interfacew |
| 171 | + |
| 172 | +The `SecurityProvider` interface defines three methods: |
| 173 | + |
| 174 | +```java |
| 175 | +boolean authorize( Object user, String role ); |
| 176 | + |
| 177 | +Object authenticate( HttpServletRequest request, HttpServletResponse response ); |
| 178 | + |
| 179 | +void logout(HttpServletRequest request, HttpServletResponse response); |
| 180 | +``` |
| 181 | + |
| 182 | +Both, the `authenticate` as well as the `logout` method are in full control of authentication and thus have to extract the user credentials from the request and can also fail the request. |
| 183 | + |
| 184 | +The method `authorize` is currently not used. |
| 185 | + |
| 186 | +### Sample |
| 187 | + |
| 188 | +A sample of a `SecurityProvider` service (using the legacy interface) is in the Apache Sling implementation [SlingWebConsoleSecurityProvider2](https://github.com/apache/sling-org-apache-sling-extensions-webconsolesecurityprovider/blob/master/src/main/java/org/apache/sling/extensions/webconsolesecurityprovider/internal/SlingWebConsoleSecurityProvider2.java). |
| 189 | +This implementation uses a JCR implementation to login to the repository and thus validate the credentials. |
0 commit comments