| <?xml version="1.0" encoding="utf-8"?> |
| <!-- |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| |
| --> |
| <section xmlns="http://docbook.org/ns/docbook" |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| xsi:schemaLocation="http://docbook.org/ns/docbook http://www.docbook.org/xml/5.0/xsd/docbook.xsd" |
| version="5.0" |
| label="Security-Plugins-Documentation"> |
| <title>Security Plugins Documentation</title> |
| |
| <section role="h2" label="SPD-Introduction"> |
| <title>Introduction</title> |
| <para> |
| This document describes the structure and design of Qpid security plugins, for |
| the Java broker. In particular, the new <emphasis>Access Control</emphasis> plugin, which implements |
| the same ACL file syntax as the C++ broker, is examined in detail. The security |
| plugins use the broker's OSGi bundle functionality to manage their lifecycle, |
| and the <code>ConfigurationPlugin</code> mechanism to manage their configuration via the |
| Apache commons configuration XML configuration file. |
| </para> |
| <para> |
| The Java interfaces and packages used by the security plugins are described here, |
| although the Javadoc documentation generated from the source should also be |
| consulted, and as always reading the source should provide further insight and information. |
| </para> |
| </section> |
| <section role="h2" label="SPD-Use-Cases"> |
| <title>Use Cases</title> |
| <para> |
| The following use cases were identified and used to drive the design and development |
| of both the security plugin mechanism in general, and the access control plugin in particular. |
| </para> |
| <itemizedlist> |
| <listitem> |
| <para> |
| Allow access to broker functions to be controlled by an ACL, with the checks being |
| carried out independently of the mechanism used to access the broker. This would |
| mean that a single <literal>CREATE </literal> permission would apply whether the queue was |
| created when a user logged in and used it, or if that user connected to the broker |
| via JMX or QMF and used the management operations to create the queue. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| Permissions must be definable at a virtualhost level, with fallback to global |
| permissions. This allows access to be granted for operations only on a certain |
| host, while global operations such as broker administration can be defined at |
| the global level. It also allows default behaviour to be specified globally and |
| then overridden on a per-host basis. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| The ACL mechanism controls access to operations on particular objects for all users, |
| if at least one user has a rule controlling access to that operation on that type of |
| object. This means that all users requiring access to a particular operation must be |
| configured. The default behaviour will be to deny access. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| It should be possible for the addition of one access control rule to trigger the |
| addition of other rules, to simplify creation of rulesets. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| The behaviour of the access control mechanism should be configurable. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| The Java and C++ brokers should share a common configuration file format. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| It should be possible to configure access to not just internal broker application |
| objects, but to the management operations and attributes of the broker, as well |
| as to external objects such as plugins. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| As long as a suitably authenticated channel is used to connect, access control |
| rules should be applied when performing operations on broker objects. This does |
| not hold when, for example, an operator has local access and is using JConsole |
| to manage the broker. |
| </para> |
| </listitem> |
| </itemizedlist> |
| </section> |
| <section role="h2" label="SPD-Java-Interfaces-Packages-Classes"> |
| <title>Java Interfaces, Packages and Classes</title> |
| <para> |
| This section describes the Java artifacts that are involved in security plugin development. |
| They are mostly contained in the package <code>org.apache.qpid.server.security</code> which |
| is part of the broker code. It is recommended that a package prefix is chosen for new |
| security plugins, and this should be used to form the packages for the implementing classes. |
| </para> |
| <para> |
| In general, when creating a new plugin, you need three classes. These would be the main |
| <code>PluginName</code> class, which should implement the <code>SecurityPlugin</code> |
| interface and have a public static instance of an anonymous internal classes that implements |
| <code>SecurityPluginfactory</code>. Additionally, the <code>PluginNameConfiguration</code> |
| class, which should implement the <code>ConfigurationPlugin</code> interface and have a |
| public static instance of an anonymous internal classes that implements |
| <code>ConfigurationPluginfactory</code>, and finally the <code>PluginNameActivator</code> |
| class, which should extend the <code>SecurityPluginActivator</code> abstract class |
| and implement the required methods exposing the factories from the other classes. |
| </para> |
| <para> |
| These classes need to be visible from the broker, so they should be placed in the |
| <code>org.apache.qpid.server.security.pluginname.plugins</code> package, which should be |
| listed in the manifest file. Any internal classes for the plugin should be placed in |
| the <code>org.apache.qpid.server.security.pluginname.config</code> package which |
| should be marked as provate in the manifest. |
| </para> |
| <para> |
| If logging using the actor and subject framework is required, the property file should |
| be located in the <code>org.apache.qpid.server.security.pluginname.logging</code> |
| package, and this should also be exported in the manifest file. |
| </para> |
| <section role="h3" label="SPD-OSGi"> |
| <title>OSGi</title> |
| <para> |
| The security plugins are now loaded using the <emphasis>Felix</emphasis> OSGi container, which is started |
| as an embedded process inside the broker. This loads all plugin .jar files from the |
| directory named in the <code>plugin-directory</code> configuration element, cacheing them in the |
| <code>cache-directory</code> directory. Note that, at present, the cache directory is cleared at |
| startup, although this behaviour may change. To create OSGi plugin bundles, a manifest |
| file - <code>MANIFEST.MF</code> is created that specifies certain attributes of the bundle. A |
| sample manifest file for one of the security plugins is shown below. |
| </para> |
| <programlisting> |
| Manifest-Version: 1.0 |
| Bundle-ManifestVersion: 2 |
| Bundle-Name: Qpid Broker-Plugins PluginName |
| Bundle-SymbolicName: broker-plugins-pluginname |
| Bundle-Description: Name description. |
| Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt |
| Bundle-DocURL: http://www.apache.org/qpid/pluginname.html |
| Bundle-Version: 1.0.0 |
| Bundle-Activator: org.apache.qpid.server.security.pluginname.plugins.PluginNameActivator |
| Bundle-RequiredExecutionEnvironment: JavaSE-1.5 |
| Bundle-ActivationPolicy: lazy |
| Import-Package: org.apache.qpid |
| Private-Package: org.apache.qpid.server.security.pluginname.config, |
| org.apache.qpid.server.security.pluginname.logging |
| Export-Package: org.apache.qpid.server.security.pluginname.plugins |
| </programlisting> |
| <para> |
| The complete list of packages to import will be determined by the actual operation of |
| the plugin, however the number of exported packages should be kept to a minimum. |
| </para> |
| </section> |
| <section role="h3" label="SPD-Plugin"> |
| <title>Plugin</title> |
| <para> |
| This is the main interface to be extended by all plugins. It contains a |
| method that allows configuration via the <code>ConfigurationPlugin</code> |
| mechanism. |
| </para> |
| <programlisting> |
| public void configure(ConfigurationPlugin config); |
| </programlisting> |
| </section> |
| <section role="h3" label="SPD-PluginFactory"> |
| <title>PluginFactory and SecurityPluginFactory</title> |
| <para> |
| These factories are used to initialise instances of plugins and configure them appropriately. |
| The factories are managed by the OSGI framework started by the <code>PluginManager</code>, |
| which is also used to retrieve the instances. |
| </para> |
| <programlisting> |
| public Class<P> getPluginClass(); |
| public String getPluginName(); |
| public P newInstance(ConfigurationPlugin config) throws ConfigurationException; |
| </programlisting> |
| </section> |
| <section role="h3" label="SPD-SecurityPlugin"> |
| <title>SecurityPlugin</title> |
| <para> |
| This is the interface that defines security plugins. The <code>getDefault</code> method |
| returns the default result for the plugin when no configuration is found for some |
| situation. |
| </para> |
| <para> |
| The <code>authorise</code> method is the main entry-point to the plugin, and is called |
| by the <code>SecurityManager</code> with the relevant paramaters. Similarly, the |
| <code>access</code> method is used for the special case of controlling access to |
| the entire virtual host, and the <code></code> |
| </para> |
| <programlisting> |
| Result getDefault(); |
| Result access(ObjectType objectType, Object instance); |
| Result authorise(Operation operation, ObjectType objectType, ObjectProperties properties); |
| </programlisting> |
| </section> |
| <section role="h3" label="SPD-SecurityPluginActivator"> |
| <title>SecurityPluginActivator</title> |
| <para> |
| The activator registers the factories with the OSGI framework, based on the |
| implementations of the abstract methods. |
| </para> |
| <programlisting> |
| public abstract SecurityPluginFactory getFactory(); |
| public abstract ConfigurationPluginFactory getConfigurationFactory(); |
| </programlisting> |
| </section> |
| <section role="h3" label="SPD-AbstractPlugin"> |
| <title>AbstractPlugin</title> |
| <para> |
| This is a simple parent class, which allows a common point of extension |
| for shared plugin code. Currently it simply implements the interface with |
| abstract methods. |
| </para> |
| <programlisting> |
| public abstract Result access(ObjectType object, Object instance); |
| public abstract Result authorise(Operation operation, ObjectType object, ObjectProperties properties); |
| </programlisting> |
| </section> |
| <section role="h3" label="SPD-AbstractProxyPlugin"> |
| <title>AbstractProxyPlugin</title> |
| <para> |
| This class is designed to be extended by plugins that only wish to take part in a subset |
| of the possible security descisions. Normally, a call to the <code>authorise</code> method |
| is proxied to one of the provided methods, based on the operation, for example a <literal>CONSUME</literal> |
| access control check would be proxied to the <code>authoriseConsume</code> method with |
| the appropriate paramaters set. The default behaviour is to return <literal>ABSTAIN</literal>, meaning |
| the plugin does not handle this type of operation. If a method is overridden, it can then perform |
| whatever security checks are required and return <literal>ALLOWED</literal> or <literal>DENIED</literal> |
| as appropriate. |
| </para> |
| <programlisting> |
| public Result authoriseConsume(ObjectType object, ObjectProperties properties); |
| public Result authorisePublish(ObjectType object, ObjectProperties properties); |
| public Result authoriseCreate(ObjectType object, ObjectProperties properties); |
| public Result authoriseAccess(ObjectType object, ObjectProperties properties); |
| public Result authoriseBind(ObjectType object, ObjectProperties properties); |
| public Result authoriseUnbind(ObjectType object, ObjectProperties properties); |
| public Result authoriseDelete(ObjectType object, ObjectProperties properties); |
| public Result authorisePurge(ObjectType object, ObjectProperties properties); |
| public Result authoriseExecute(ObjectType object, ObjectProperties properties); |
| public Result authoriseUpdate(ObjectType object, ObjectProperties properties); |
| public Result accessVirtualhost(Object instance); |
| </programlisting> |
| </section> |
| </section> |
| <section role="h2" label="SPD-Access-Control-Security-Plugin"> |
| <title>Access Control Security Plugin</title> |
| <para> |
| This security plugin implements access control using the same configuration file syntax as the |
| C++ broker. The classes are all in sub-packages of the <code>org.apache.qpid.server.security.access</code> |
| package. The exposed classes consist of the plugoin itself, its OSGi activator and the configuration |
| plugin, as well as the properties file and generated code for logging. The private, internal classes, |
| consist of the ruleset implementation for managing access control list rules. The plugin also makes |
| extensive use of the enumerations provided by the broker as part of the security plugin interfaces, |
| for operations, objects and permissions. |
| </para> |
| <section role="h3" label="SPD-ACL-Enumerations"> |
| <title>Enumerations</title> |
| <para> |
| These enumerations are used to define exactly what a security plugin can control. |
| </para> |
| <para> |
| The <code>ObjectProperties</code> and <code>ObjectProperties.Property</code> lalala |
| </para> |
| <para> |
| The <code>ObjectType</code> |
| </para> |
| <para> |
| The <code>Operation</code> |
| </para> |
| <para> |
| The <code>Permission</code> |
| </para> |
| </section> |
| <section role="h3" label="SPD-ACL-Configuration"> |
| <title>Configuration</title> |
| <para> |
| Security plugins are configurable using the Qpid XML configuration file, under the <code><security></code> |
| element. This can be either inside the main <code><broker /></code> element, as a global plugin affecting |
| all virtual hosts, or under a <code><virtualhosts><virtualhost><name></code> element, where |
| the <code><name></code> element is the name of the virtual host that is to be configured. Each security |
| plugin must register the elements it expects to process using a <code>ConfigurationPlugin</code>, which is |
| documented elsewhere. |
| </para> |
| <para> |
| The plugins are checked in order, first for the virtual host, then globally, and the first <literal>ALLOWED</literal> or |
| <literal>DENIED</literal> response is used. |
| </para> |
| <para> |
| The ACL configuration file is specified via the contents of the <code><aclv2></code> element. This is simply |
| the path to the file, which is a plain text format, and is parseable by both Java and C++ brokers. The path can be |
| specified with embedded property value interpolation, for environment variables or other properties defined in the |
| configuration file. |
| </para> |
| <programlisting> |
| <![CDATA[ |
| <security> |
| <!-- global access control configuration file --> |
| <aclv2>${QPID_HOME}/etc/global-security-config.txt</aclv2> |
| </security> |
| ]]> |
| </programlisting> |
| <section role="h4" label="SPD-ACL-File-Format"> |
| <title>File Format</title> |
| <para> |
| The file format is described below. |
| </para> |
| <itemizedlist> |
| <listitem> |
| <para> |
| Whitespace is considered to be any ASCII |
| byte with a value below <literal>0x20</literal>, and is |
| ignored when it occurs between tokens. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| Continuations using the <literal>\</literal> |
| character (ASCII <literal>0x5c</literal>) are allowed |
| anywhere on a line, and |
| can consist of a blank line with a continuation |
| character as the last non-whitespace token |
| </para> |
| <programlisting> |
| group group1 name1 name2 \ |
| name3 name4 \ |
| name5 |
| acl allow group1 create queue \ |
| property1 = "value1" \ |
| property2 \ |
| = "value2" |
| </programlisting> |
| </listitem> |
| <listitem> |
| <para> |
| Comments are line-style comments, and any text after |
| an un-quoted <literal>#</literal> (ASCII <literal>0x23</literal>) |
| are ignored, including continuations. The <literal>#</literal> |
| charater may appear in a quoted string. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| Quoted strings consist of any ASCII inside matching pairs of |
| <literal>'</literal> or <literal>"</literal> (ASCII <literal>0x27</literal> |
| and <literal>0x22</literal>) characters, including any |
| otherwise special characters. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| Tokens are <emphasis>NOT</emphasis> case sensitive, but quoted |
| strings <emphasis>ARE</emphasis>. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| The <literal>=</literal> (ASCII <literal>0x3d</literal>) character |
| is special, and is used to indicate property value assignment. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| Wildcards are specified using the <literal>*</literal> (ASCII |
| <literal>0x2a</literal>) character in a property value string, |
| which may be quoted. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| Empty lines and lines that contain only whitespace are ignored. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| The keyword <literal>all</literal> is reserved, and matches all individuals, |
| groups and actions. It may be used in place of a group or |
| individual name and/or an action - eg <literal>acl allow all all</literal>, |
| <literal>acl deny all all</literal> or <literal>acl deny user1 all</literal>. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| Rules are interpreted from the top of the file down until the |
| name match is obtained; at which point processing stops. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| The last line of the file (whether present or not) will be |
| assumed to be <literal>acl deny all all</literal>. If present in the file, any |
| lines below this one are ignored. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| Names and group names may contain only <literal>a-z</literal>, |
| <literal>A-Z</literal>, <literal>0-9</literal>, |
| <literal>-</literal>, <literal>@</literal>, <literal>/</literal> |
| or <literal>_</literal>. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| Rules must be preceded by any configuration and group definitions they may use; |
| any name not previously defined as a group will be assumed to be |
| that of an individual user. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| <literal>CONFIG</literal> lines must have the following tokens in order: |
| </para> |
| <itemizedlist> |
| <listitem> |
| <para>The string literal <literal>config</literal></para> |
| </listitem> |
| <listitem> |
| <para>One or more property name-value pairs, in the form <literal>property = value</literal> |
| where value is the token <literal>true</literal> or <literal>false</literal></para> |
| </listitem> |
| </itemizedlist> |
| </listitem> |
| <listitem> |
| <para> |
| GROUP lines must have the following tokens in order: |
| </para> |
| <itemizedlist> |
| <listitem> |
| <para>The string literal <literal>group</literal></para> |
| </listitem> |
| <listitem> |
| <para>The name of the group, which cannot contain <literal>@</literal> or |
| <literal>/</literal> characters</para> |
| </listitem> |
| <listitem> |
| <para>A whitespace separated list of user and group names. User names are formatted |
| as <literal>username/domain@realm</literal> and group names must have been defined |
| earlier in the file</para> |
| </listitem> |
| </itemizedlist> |
| </listitem> |
| <listitem> |
| <para> |
| ACL rules must have the following tokens in order: |
| </para> |
| <itemizedlist> |
| <listitem> |
| <para>An optional rule number, which should be expressible as a positive Java integer</para> |
| </listitem> |
| <listitem> |
| <para>The string literal <literal>acl</literal></para> |
| </listitem> |
| <listitem> |
| <para>The permission, one of <literal>allow</literal>, <literal>allow-log</literal>, |
| <literal>deny</literal> or <literal>deny-log</literal></para> |
| </listitem> |
| <listitem> |
| <para>The name of a single group or individual or the keyword <literal>all</literal></para> |
| </listitem> |
| <listitem> |
| <para>The name of an operation, which should be one of <literal>consume</literal>, |
| <literal>publish</literal>, <literal>create</literal>, <literal>access</literal>, |
| <literal>bind</literal>, <literal>unbind</literal>, <literal>delete</literal>, |
| <literal>purge</literal>, <literal>update</literal>, <literal>execute</literal> |
| or the keyword <literal>all</literal></para> |
| </listitem> |
| <listitem> |
| <para>Optionally, a single object type or the keyword <literal>all</literal></para> |
| <para>Objects allowed are <literal>virtualhost</literal>, <literal>queue</literal>, |
| <literal>topic</literal> and <literal>exchange</literal></para> |
| <para>Objects allowed are <literal>virtualhost</literal>, <literal>queue</literal>, |
| <literal>topic</literal>, <literal>exchange</literal>, <literal>link</literal>, |
| <literal>route</literal>, <literal>method</literal> and <literal>object</literal></para> |
| </listitem> |
| <listitem> |
| <para>If the object is present, then optionally one or more property name-value pairs in the form |
| <literal>property=value</literal>. The property and value can be separated from the |
| <literal>=</literal> charater by any amount of whitespace, and the calue can be quoted if |
| it contains special characters or whitespace.</para> |
| </listitem> |
| <listitem> |
| <para>Property values can add the wildcard <literal>*</literal> character at the end of the string |
| to indicate that any string beginning with the characters up to the wildcard will match, or |
| if the wildcard is the only character, that any string will match</para> |
| </listitem> |
| </itemizedlist> |
| </listitem> |
| </itemizedlist> |
| <para> |
| This allows a rather looser and more readable style for ACL files, |
| while still retaining the ability to read the stricter files accepted |
| by the C++ broker. Bear in mind that the group declarations are to be |
| deprecated, in favour of an external directory service, using a plugin |
| mechanism. |
| </para> |
| <para> |
| The initial number is used to allow rulesets to be created which allow |
| individual rules to be enabled and disabled using an admin interface, |
| and an ACL file using numbered lines would be restricted to having |
| increasing numbers per rule, although gaps would be allowed to enable |
| rules to be inserted later, again using an admin interface. This |
| administrative interface would also allow saving of a modified ruleset |
| and re-loading. |
| </para> |
| </section> |
| <section role="h4" label="SPD-ACL-Broker-Access-Control"> |
| <title>Broker Access Control</title> |
| <para> |
| The Java broker access control mechanism is used to protect internal |
| entities used by the broker. These are virtual hosts, queues, topics |
| and exchanges. The actual access control checks take place in the |
| methods that carry out the operations on these objects, in order to |
| ensure thatsecurity is both mechanism and protocol agnostic. |
| </para> |
| <para> |
| <emphasis>The Java broker does not support <code>LINK</code> or |
| <code>ROUTE</code> object types.</emphasis> |
| </para> |
| <para> |
| An example of the various rules that can be specified follows: |
| </para> |
| <programlisting> |
| acl allow robot create exchange name="robot.*" |
| acl deny kitten create queue |
| acl allow guest bind exchange name=amq.topic routingkey="kitten.#" |
| acl allow all create queue name="tmp.*" |
| acl allow guest publish all durable="false" |
| acl allow robot create queue name="robot" |
| acl allow kitten consume queue durable="true" |
| acl allow guest create all |
| </programlisting> |
| </section> |
| <section role="h4" label="SPD-ACL-Management-Access-Control"> |
| <title>Management Access Control</title> |
| <para> |
| The management of the broker using JMX is also protected by the security |
| plugins, in two ways. If the management interface is used to perform |
| operations that would be access controlled normally, the same rules |
| would still apply and be applied. However, this only occurs when the |
| JMX connection was authenticated. If JConsole is used to connect directly |
| to a broker process started by the same user, then no extra checks are made. |
| </para> |
| <para> |
| The management operations themselves are also able to be access controlled. |
| This is done using the <code>METHOD</code> object type. A component name |
| and method name are specified as properties, and these indicate the MBean |
| type name and JMX method name respectively. If the operation is set to |
| <code>ALL</code> then reading JMX attributes, writing JMX attributes and |
| invoking JMX operations are controlled by the rule. Otherwise, the three |
| operations <code>ACCESS</code>, <code>UPDATE</code> and <code>EXECUTE</code> |
| control reading, writing and invocation respectively. |
| </para> |
| <programlisting> |
| ACL ALLOW user ALL METHOD |
| ACL ALLOW user ALL METHOD name="method" |
| ACL ALLOW user ALL METHOD name="prefix*" |
| ACL ALLOW user ALL METHOD component="MBean" name="method" |
| ACL ALLOW user ACCESS METHOD component="MBean" |
| ACL ALLOW user UPDATE METHOD component="MBean" |
| ACL ALLOW user EXECUTE METHOD component="MBean" |
| </programlisting> |
| </section> |
| <section role="h4" label="SPD-ACL-External-Object-Access-Control"> |
| <title>External Object Access Control</title> |
| <para> |
| At the moment the C++ broker has an extension point to allow access control |
| of external objects. This will be provided in the Java broker as well, using the |
| <code>ACCESS OBJECT</code> rule, with package name and class name properties. |
| The external object must be able to retrieve a reference to the virtual host |
| it is running on, and then call the <code>accessObject</code> method. This |
| must be the responsibility of the external object. |
| </para> |
| <para> |
| <emphasis>Note that this is not currently implemented in the <code>SecurityManager</code>.</emphasis> |
| </para> |
| <programlisting> |
| ACL ALLOW user ACCESS OBJECT package="com.example.application" class="Extension" |
| </programlisting> |
| <programlisting> |
| if (!_vhost.getSecurityManager().accessObject("com.example.application", "Extension")) |
| { |
| // TODO reject access somehow - exception |
| } |
| </programlisting> |
| </section> |
| </section> |
| </section> |
| </section> |