Author: jawi
Date: Wed Apr 25 12:57:35 2012
New Revision: 1330254
URL: http://svn.apache.org/viewvc?rev=1330254&view=rev
Log:
Formatting updated.
Modified:
ace/site/trunk/content/dev-doc/design/ace-authentication.mdtext
Modified: ace/site/trunk/content/dev-doc/design/ace-authentication.mdtext
URL:
http://svn.apache.org/viewvc/ace/site/trunk/content/dev-doc/design/ace-authentication.mdtext?rev=1330254&r1=1330253&r2=1330254&view=diff
==============================================================================
--- ace/site/trunk/content/dev-doc/design/ace-authentication.mdtext (original)
+++ ace/site/trunk/content/dev-doc/design/ace-authentication.mdtext Wed Apr 25
12:57:35 2012
@@ -1,11 +1,11 @@
_Enabling authentication in ACE_
-last updated: April 24th, 2012
+last updated: April 25th, 2012
## Introduction
-When provisioning software (partly) to targets, one has to rely upon the
trustworthiness of both the network and the target. Even if everything is under
your control and governance, one cannot entirely be sure that unwanted access
takes place. A first step in order to prevent unwanted access is
*authentication*, which gives you the ability to verify the identity of
someone. Once the identity is known, one can apply *authorization* in order to
determine what actions are allowed and which are not.
+When provisioning software (partly) to targets, one has to rely upon the
trustworthiness of both the network and the target. Even if everything is under
your control and governance, one cannot entirely be sure that unwanted access
takes place. A first step in order to prevent unwanted access is
*authentication*, which gives you the ability to verify the identity of
someone. Once the identity is known, one can apply *authentication* in order to
determine what actions are allowed and which are not.
In this article, the recently added authentication layer of ACE is explained
in more depth, and some details on how extensions can be written for additional
mechanisms are given. The remainder of this article assumes the reader has
basic knowledge of the principles behind ACE, and has sufficient programming
skills. For this article, the latest code of ACE (0.8.1-SNAPSHOT, rev.1329269)
was used.
@@ -13,7 +13,8 @@ In this article, the recently added auth
Before going in more depth on the authentication layer of ACE, we first need
to pinpoint all places were authentication is to be applied. The following
figure shows the main components in ACE and their communication paths,
providing a global overview of where authentication is applicable to ACE.
-
+
+Figure 1: Overview of components and communication paths.
In figure 1, several communication paths exists (denoted by the circled
digits):
@@ -40,21 +41,23 @@ The high-level design for security in AC
Based on these requirements, the design of the authentication layer is
represented in the following figure:
-
+
+Figure 2: Authentication layer class diagram.
-The `AuthenticationService` is responsible for authenticating a user based on
some piece of information. This piece of information can be a username/password
combination, a `HttpServletRequest` containing authentication request headers,
or any other set of information capable of uniquely identifying a user. The
actual authentication itself is delegated to one or more
`AuthenticationProcessor`s, which know how to handle a given set of
information (e.g., `HttpServletRequest`) and can map this information to a
particular user. In more detail, the calling sequence of
`AuthenticationService#authenticate` would be:
+The <tt>AuthenticationService</tt> is responsible for authenticating a user
based on some piece of information. This piece of information can be a
username/password combination, a <tt>HttpServletRequest</tt> containing
authentication request headers, or any other set of information capable of
uniquely identifying a user. The actual authentication itself is delegated to
one or more <tt>AuthenticationProcessor</tt>s, which know how to handle a
given set of information (e.g., <tt>HttpServletRequest</tt>) and can map this
information to a particular user. In more detail, the calling sequence of
<tt>AuthenticationService#authenticate</tt> would be:
-1. `AuthenticationService#authenticate` is called with a blob of data, for
example the `HttpServletRequest`;
-2. for each known `AuthenticationProcessor`:
- - `AuthenticationProcessor#canHandle` is called with that blob of data. An
authentication processor can decide whether the given blob is something it can
handle or not;
- - if it can be handled, the `AuthenticationProcessor#authenticate` is
called with that blob of data, along with an instance of the UserAdmin service.
The authentication processor is now responsible for converting the blob of data
to an authenticated user, if possible.
-3. if a `User` object is returned from the authentication service[^3], the
authentication phase will be regarded as successful. If *no* `User` object is
returned, the authentication phase will be regarded unsuccessful.
+1. <tt>AuthenticationService#authenticate</tt> is called with a blob of data,
for example the <tt>HttpServletRequest</tt>;
+2. for each known <tt>AuthenticationProcessor</tt>:
+ - <tt>AuthenticationProcessor#canHandle</tt> is called with that blob of
data. An authentication processor can decide whether the given blob is
something it can handle or not;
+ - if it can be handled, the <tt>AuthenticationProcessor#authenticate</tt>
is called with that blob of data, along with an instance of the UserAdmin
service. The authentication processor is now responsible for converting the
blob of data to an authenticated user, if possible.
+3. if a <tt>User</tt> object is returned from the authentication service[^3],
the authentication phase will be regarded as successful. If *no* <tt>User</tt>
object is returned, the authentication phase will be regarded unsuccessful.
This is only half the story for authentication. As stated before, ACE
internally also communicates through HTTP to access certain services. Without
any changes, all those remote calls will fail due to missing credentials. If we
want to leave those means of communications as-is, we would need to track down
all places where remote calls are being made and inject the proper credentials
at those places. However, doing this is not only *very* invasive and error
prone but also not very developer friendly from a service-oriented perspective.
Alternatively, we could try to include the credentials in the URL itself,
making it self-contained. Not only would this approach limit our ability to use
any kind of authentication mechanism (it only works for username/password
combos), it also required us to supply the credentials manually each and every
time we want to create a remote connection. Instead, we would like to refrain
from passing around credentials, and leverage the service orien
ted aspects of OSGi to create remote connections for us. This service could
then be responsible for adding the right credentials for us, leaving the
calling party totally unaware about the fact authentication might be used. Such
a service is denoted in the following figure:
-
+
+Figure 3: Connection Factory class diagram.
-The `ConnectionFactory` is responsible for creating `URLConnection`s, given a
"plain" URL. So, instead of calling `URL#openConnection()` or
`URL#openStream()`, we'll now have to call
`ConnectionFactory#createConnection(url)` instead. But, what advantage does
this bring us? In order to allow the connection factory to supply the
credentials to `URLConnection`s, it is also registered as
`ManagedServiceFactory` that enables us to provide multiple configurations of
which credentials should be supplied to what (sets of) URLs. The introduction
of the connection factory allows us to abstract the creation of a connection
and passing of credentials to it from the URL. Internally, the connection
factory will match each URL given in `createConnection` with the URLs it is
configured with. If a matching URL is found, it will use the credentials in
that configuration to supply to the `URLConnection`.
+The <tt>ConnectionFactory</tt> is responsible for creating
<tt>URLConnection</tt>s, given a "plain" URL. So, instead of calling
<tt>URL#openConnection()</tt> or <tt>URL#openStream()</tt>, we'll now have to
call <tt>ConnectionFactory#createConnection(url)</tt> instead. But, what
advantage does this bring us? In order to allow the connection factory to
supply the credentials to <tt>URLConnection</tt>s, it is also registered as
<tt>ManagedServiceFactory</tt> that enables us to provide multiple
configurations of which credentials should be supplied to what (sets of) URLs.
The introduction of the connection factory allows us to abstract the creation
of a connection and passing of credentials to it from the URL. Internally, the
connection factory will match each URL given in <tt>createConnection</tt> with
the URLs it is configured with. If a matching URL is found, it will use the
credentials in that configuration to supply to the <tt>URLConnection</tt>.
We've now closed the circle: we not only have defined how remote endpoints can
apply authentication, but also how all calling parties can remain using these
remote endpoints without having to be aware of authentication.
@@ -65,31 +68,31 @@ Before continuing on the details of conf
### Remote services
-All remote services in ACE are configurable with respect to the endpoint
through which they can be accessed. The following table shows an overview of
the remote services, including the default endpoint they use:
+All remote services in ACE are configurable with respect to the endpoint they
can be accessed. The following table shows an overview of the remote services,
including the default endpoint they use:
Name | Description | Default endpoint | Configuration PID
------- | --------------- | --------------------- | ---------------------
-`BundleServlet` | provides access to the OBR (bundle repository) of ACE |
`/obr` | `org.apache.ace.obr.servlet`
-`DeploymentServlet` | handles the actual provisioning of deployment packages
to a target | `/deployment` |`org.apache.ace.deployment.servlet`
-`LogServlet` | allows any number of logs for a target to be synchronized and
accessed | `/auditlog`[^4] |
`org.apache.ace.server.log.servlet.factory`<br/>**note: this is a configuration
factory!**
-`RepositoryServlet` | provides access to the various
(artifact/feature/distribution/target) internal repositories of ACE |
`/repository` | `org.apache.ace.repository.servlet.RepositoryServlet`
-`RepositoryReplicationServlet` | allows *relay nodes* to replicate the
internal repositories of ACE | `/replication` |
`org.apache.ace.repository.servlet.RepositoryReplicationServlet`
-`RESTClientServlet` | provides the RESTful interface to ACE |`/client` |
`org.apache.ace.client.rest`
-`VaadinServlet` | provides the Vaadin web interface | `/ace` |
`org.apache.ace.webui.vaadin`
+<tt>BundleServlet</tt> | provides access to the OBR (bundle repository) of ACE
| <tt>/obr</tt> | <tt>org.apache.ace.obr.servlet</tt>
+<tt>DeploymentServlet</tt> | handles the actual provisioning of deployment
packages to a target | <tt>/deployment</tt>
|<tt>org.apache.ace.deployment.servlet</tt>
+<tt>LogServlet</tt> | allows any number of logs for a target to be
synchronized and accessed | <tt>/auditlog</tt>[^4] |
<tt>org.apache.ace.server.log.servlet.factory</tt><br/>**note: this is a
configuration factory!**
+<tt>RepositoryServlet</tt> | provides access to the various
(artifact/feature/distribution/target) internal repositories of ACE |
<tt>/repository</tt> |
<tt>org.apache.ace.repository.servlet.RepositoryServlet</tt>
+<tt>RepositoryReplicationServlet</tt> | allows *relay nodes* to replicate the
internal repositories of ACE | <tt>/replication</tt> |
<tt>org.apache.ace.repository.servlet.RepositoryReplicationServlet</tt>
+<tt>RESTClientServlet</tt> | provides the RESTful interface to ACE
|<tt>/client</tt> | <tt>org.apache.ace.client.rest</tt>
+<tt>VaadinServlet</tt> | provides the Vaadin web interface | <tt>/ace</tt> |
<tt>org.apache.ace.webui.vaadin</tt>
### Configuring authentication for remote services
-In the section on the design of the authentication layer, we've mentioned that
if a remote service wants to make use of authentication, it can make use of the
`AuthenticationService`. However, one of the design requirements was that
authentication should be optional as well. In order to enable or disable
authentication, each remote service needs to do the following:
+In the section on the design of the authentication layer, we've mentioned that
if a remote service wants to make use of authentication, it can make use of the
<tt>AuthenticationService</tt>. However, one of the design requirements was
that authentication should be optional as well. In order to enable or disable
authentication, each remote service needs to do the following:
1. add a **mandatory** configuration property `authentication.enabled =
false|true` to their configuration. Although any kind of name for this
configuration property can be used, it is *strongly* advised to stick to the
same name for all services;
-2. when the configuration of a remote service is updated, it should add a
service dependency to the `AuthenticationService`. By making this service
*required* when authentication is enabled, and *optional* when authentication
is disabled, we can adhere to the requirement of optionality for authentication;
-3. in case authentication is *enabled*, each request the service obtains needs
to be passed to the `AuthenticationService` first, and depending on its
outcome, the request can continue or not.
+2. when the configuration of a remote service is updated, it should add a
service dependency to the <tt>AuthenticationService</tt>. By making this
service *required* when authentication is enabled, and *optional* when
authentication is disabled, we can adhere to the requirement of optionality for
authentication;
+3. in case authentication is *enabled*, each request the service obtains needs
to be passed to the <tt>AuthenticationService</tt> first, and depending on its
outcome, the request can continue or not.
-To make this more concrete, an example of how the `BundleServlet` is to be
configured:
+To make this more concrete, an example of how the <tt>BundleServlet</tt> is to
be configured:
#### Service configuration
-The service configuration, located in `org.apache.ace.obr.servlet.cfg`, looks
like:
+The service configuration, located in <tt>org.apache.ace.obr.servlet.cfg</tt>,
looks like:
:::properties
# Endpoint for this servlet
@@ -97,7 +100,7 @@ The service configuration, located in `o
# Whether or not authentication is to be used
authentication.enabled = true
-In `BundleServlet` we add the following code:
+In <tt>BundleServlet</tt> we add the following code:
:::java
private volatile boolean m_useAuth;
@@ -130,10 +133,10 @@ In `BundleServlet` we add the following
.setService(AuthenticationService.class)
.setRequired(m_useAuth)
.setInstanceBound(true)
- );
+ );
}
-As almost all of the services in ACE are managed by the Dependency Manager, we
can leverage its dynamics to inject our `BundleServlet` with an instance of the
`AuthenticationService` and provide us with a configuration[^5].
+As almost all of the services in ACE are managed by the Dependency Manager, we
can leverage its dynamics to inject our <tt>BundleServlet</tt> with an instance
of the <tt>AuthenticationService</tt> and provide us with a configuration[^5].
#### Implemention
@@ -152,21 +155,21 @@ The actual authentication implementation
}
private boolean authenticate(HttpServletRequest request) {
- User user = null;
if (m_useAuth) {
User user = m_authService.authenticate(request);
+ if (user == null) {
+ m_log.log(LogService.LOG_INFO, "Authentication failure!");
+ }
+ return (user != null);
}
- if (user == null) {
- m_log.log(LogService.LOG_INFO, "Authentication failure!");
- }
- return (user != null);
+ return true;
}
-Note that this implementation does not tell *how* the authentication should
occur, only that it should occur. How the authentication is performed, is
determined internally by the `AuthenticationService`, with the help of the
registered `AuthenticationProcessor`s.
+Note that this implementation does not tell *how* the authentication should
occur, only that it should occur. How the authentication is performed, is
determined internally by the <tt>AuthenticationService</tt>, with the help of
the registered <tt>AuthenticationProcessor</tt>s.
### Configuring the connection factory
-Now that the remote service itself is no longer accepting unauthenticated
requests, we need to supply the credentials to access this service to the
`ConnectionFactory` service. This service can be configured using the PID
`org.apache.ace.connectionfactory` (*note that it is a configuration
factory!*), which would result in the following configuration for accessing our
`BundleServlet`:
+Now that the remote service itself is no longer accepting unauthenticated
requests, we need to supply the credentials to access this service to the
<tt>ConnectionFactory</tt> service. This service can be configured using the
PID <tt>org.apache.ace.connectionfactory</tt> (*note that it is a configuration
factory!*), which would result in the following configuration for accessing our
<tt>BundleServlet</tt>:
:::properties
# What kind of authentication should we supply
@@ -177,7 +180,7 @@ Now that the remote service itself is no
# What is the base URL that these credentials apply to:
authentication.baseURL = http://localhost:8080/obr/
-When this configuration is supplied to the `ConnectionFactory`, it will
provide a basic HTTP authentication header to each connection created for any
URL starting with "http://localhost:8080/obr/"[^6].
+When this configuration is supplied to the <tt>ConnectionFactory</tt>, it will
provide a basic HTTP authentication header to each connection created for any
URL starting with "<tt>http://localhost:8080/obr/</tt>"[^6].
### Configuring the management agent
@@ -188,10 +191,10 @@ When this configuration is supplied to t
[^2]: Assuming that all components in the ACE server are trusted and obtained
from trusted sources. If untrusted components would be allowed, we need to add
authentication to these communication paths as well.
-[^3]: It is up to the implementation of `AuthenticationService` whether the
*first* found user is returned, or whether it checks if all authentication
processors yield the *same* user, or any other strategy that is desired.
+[^3]: It is up to the implementation of <tt>AuthenticationService</tt> whether
the *first* found user is returned, or whether it checks if all authentication
processors yield the *same* user, or any other strategy that is desired.
[^4]: Amongst others, any number of log-endpoints can be defined, at least one
is needed for the audit log to be synchronized between target and ACE server.
[^5]: Note that we're using a configuration dependency for this service. This
way, the configuration **must** be present before the service itself is
registered, which allows us to determine if authentication should be used or
not.
-[^6]: Currently, a simple `String#startsWith()` is used to determine whether
or not a URL matches a configuration. This might change in the future when a
more sophisticated URL-matching strategy is needed.
+[^6]: Currently, a simple <tt>String#startsWith()</tt> is used to determine
whether or not a URL matches a configuration. This might change in the future
when a more sophisticated URL-matching strategy is needed.