Introduction
As mentioned, the xmlBlaster security system is not and cannot be a completely implemented piece of
software, which refers all aspects of security. The only thing it does, is to support the fundamentals
as a framework.
In most cases, security functions require extra jobs (e.g. encryption - decryption)
to be done on client side as well as on server side. Thus the framework can be divided into two main
aspect: Server side and client side.
But because of the fact, that a developer of an xmlBlaster client program doesn't need to use xmlBlaster
client helper classes. Security functions cannot be guarenteed for client developers to be transparent.
Thus a security plugin developer has to implement the server part, but it makes no sense to bind the
developer to implement the client plugin, too. Although it is optional, I advice to do it.
Anyhow, this part of the xmlBlaster security howto describes only the server part.
Preparations
The first and most important step of developing a security plugin is to get the requirements straight.
Therefore you should be able to answer following questions:
- What is the general goal? What do you want to protect?
- Which kinds of threads do you suppose?
- Which of them do you want to prevent? (Possibly, the benefit won't justify the costs!)
- What are the inherited security functions? (Authentication, authorization, ...)
- What are the characteristics of the security functions? (E.g. a more or less strong encryption)
- How do you want to implement these functions? (E.g. authentication via password or fingerprint)
- Do you want to integrate existing services? (Kerberos, passwd-files, ...)
- ...
In general, you must know what you want and how to achieve this. If so, there won't be a problem to
integrate all that into the xmlBlaster. The only thing you have to do, is to implement the following interfaces:
-
org.xmlBlaster.authentication.plugins.I_Manager
-
org.xmlBlaster.authentication.plugins.I_Session
-
org.xmlBlaster.authentication.plugins.I_Subject
-
org.xmlBlaster.authentication.plugins.I_SecurityQos
What is the intention of the interfaces, methods and values?
org.xmlBlaster.authentication.plugins.I_Manager
The implementation of I_Manager
is the central class loaded by the plugin loader. Therefore
I_Manager
extends I_Plugin
. First of all, the methods defined in this interface
have to be implemented to be able to load the plugin via the plugin loader. In a second step the methods of
I_Manager
must be implemented. Namely:
-
public I_Session reserveSession(String sessionId) throws XmlBlasterException
This method will be called from xmlBlasted while a user (subject) tries to log on. The only
parameter sessionId
is a unique id for each session, generated by the xmlBlaster.
The result is a session of an unauthenticated subject. In order to authenticate the subject,
the xmlBlaster will call I_Session.init(String)
.
-
public void releaseSession(String sessionId, String qos_literal) throws XmlBlasterException
This is the counter part of reserveSession(...)
. It's called while a logout e.g.
to remove the session from the plugins internal session list (if it has one).
The first parameter contains the id of the session. The second parameter
is an optional qos literal. The plugin could use it e.g. to proof the authenticity of the
source of the logout/disconnect request.
-
public I_Session getSessionById(String _id) throws XmlBlasterException
The last method serves a way to determine a sessions security context via a textual session id.
It must return the corresponding session, if it exists. Otherwise, it return null
org.xmlBlaster.authentication.plugins.I_Session
I_Session
hides the implementation of session specific methods. Exactly one object
exists per session. The follow methods have to be implemented:
-
public String init(String securityQos) throws XmlBlasterException
init(...)
is called as part of the login process. In the first step,
the xmlBlaser reserves an I_Session
-Object via
I_Manager.reserveSession(String sessionId)
. If this happened without an
exception, I_Session
will be initialized using this method.
The implementor may use it e.g. to authenticate subjects, log information to make
subjects accountable for actions, etc..
The parameter securityQos
is part of the login qos, provided by the client.
It can be uses for exchange of crypto keys, transmission of information (e.g.
user and password) for authemtication ...
The result is a qos literal, too. It may be used e.g. to return keys, the session id,
etc..
-
public String init(I_SecurityQos securityQos) throws XmlBlasterException
This is basically the same as the last method. Its difference is only the type of the
parameter securityQos
. The qos has already been mapped to an object.
-
public I_Subject getSubject()
Returns the corresponding subject.
-
public I_Manager getManager()
Returns the responsible I_Manager
.
-
public void changeSecretSessionId(String sessionId) throws XmlBlasterException
Because the current implementation of org.xmlBlaster.authentication.Authenticate#connect(org.xmlBlaster.util.qos.ConnectQos, String)
cannot provide a correct session id when an object of I_Session
is instantiated,
the id must be changed later. Therefore I_Session
provides this method.
-
public String getSecretSessionId()
Returns the id of this session.
org.xmlBlaster.authentication.plugins.I_MsgSecurityInterceptor
I_MsgSecurityInterceptor
hides the implementation of message encryption and decryption.
The follow methods have to be implemented:
-
public MsgUnitRaw importMessage(MsgUnitRaw msg, MethodName action) throws XmlBlasterException
public String importMessage(String xmlMsg) throws XmlBlasterException
public byte[] importMessage(byte[] content) throws XmlBlasterException
The job of these methods is the import of messages. Therefore all incomming messages and
message components (like qos without key and content) will be routed through one of these methods.
In an implementation, they can be used to decrypt the information, verify a signature, log some
information, and so on.
-
public MsgUnitRaw exportMessage(MsgUnitRaw msg) throws XmlBlasterException
public String exportMessage(String xmlMsg) throws XmlBlasterException
public byte[] exportMessage(byte[] content) throws XmlBlasterException
These are the counterparts of of MsgUnitRaw importMessage(MsgUnitRaw msg, MethodName action)
and
String importMessage(String xmlMsg)
. Thus, they are the right location to encrypt,
sign, log, etc. outgoing messages.
org.xmlBlaster.authentication.plugins.I_SecurityQos
Implementations of I_SecurityQos
are simple wrappers for security related qos information.
Both ways (XML to object and object to XML) have to be implemented.
Following methods must be implemented:
-
public void parse(String xml) throws XmlBlasterException
Parses the given XML literal and turns it into an object. Note: The XML literal doesn't represent
a complete qos. It covers only the security related part of the qos, which is always enclosed
by <securityService type='' version=''>...</securityService>
. Example:
A typical qos looks like this:
<qos>
...
<securityService type='htpasswd' version='1.0'>
<![CDATA[
<user>fred</user>
<passwd>secret</passwd>
]]\>
</securityService>
...
</qos>
The attributes of <securityService>
are also mandatory. They are used by the qos-parser
to determine the right plugin.
-
public void setUserId(String userId)
Used to replace the userId.
-
public String getUserId()
Has to return a previously set or parsed userId.
-
public void setCredential(String cred)
This is used to set the credential. E.g. a password, signature, etc.
-
public String getPluginType()
Must return the type of the plugin (e.g. 'htpasswd').
-
public String getPluginVersion()
Must return the plugins version (e.g. '1.0').
-
public void toXml(String extraOffset)
This is the counterpart of public void parse(String xml) throws XmlBlasterException
.
So it must serialize the data into an xml-stream. The purpose of the only parameter is to beautify
the returned stream by indention.
org.xmlBlaster.authentication.plugins.I_Subject
Implementations of I_Subject
user related attributes and methods.
Following methods have to be implemented:
How it all fits together - the passwd example
This chapter shows how it all fits together using probably the simples example. A plugin that logs some information and authenticates
subjects via a Unix passwd file.
Initialization and connecting
Considered from the clients point of view, everything begins with establishing a xmlBlaster connection and the succeeding login.
Therefore, the server requires a loginQos. This contains also security related information embedded in:
<securityService type='passwd' version='1.0'>
<![CDATA[
...
]]\>
</securityService>
The server parses the qos end extracts this part including the securityService-tag. In a next step, the xmlBlaster tries to find
a plugin with the type "passwd" and version "1.0" (PluginManager.getManager(...)
) in its local cache respectively in the xmlBlaster.properties and will probably
find there the following lines:
Security.Server.Plugin[passwd][1.0]=org.xmlBlaster.authentication.plugins.passwd.PasswdManager
If so, it will try to instanciate the class. Therefore PasswdManager
has to implement the I_Manager
interface
which extends the I_Plugin
interface. After creating an object, the objects construct will be call. That permits us i.e.
to read config. data, open the passwd file and store user name and password as key-value pairs in a Hashtable, etc.. After this has taken place, Autheticate.connect(...)
will call PasswdManager.reserveSession(...)
.
It simply creates a new object of PasswdSession and stores it in a Hashtable. It is necessary to store each valid session, because
getSessionById(...)
may be called. Moreover, it permits us to implement some timeout behaviour.
Hashtable sessions = new Hashtable();
...
public I_Session reserveSession(String sessionId) {
Session session = new Session(this, sessionId);
synchronized(sessions) {
sessions.put(sessionId, session);
}
return session;
}
Autheticate.connect(...)
will subsequently call init(...)
on the just created object.
This is the right place to identify the subject. Thus, it is necessary to get the PasswdSubject object which corresponds
to the userId. If such an object doesn't exist, we know that the user is known and a "Access Denied" exception needs to be thrown.
Otherwise the returned object will help us to authenticate the user.
But before that takes place, we need a method that serves the PasswdSubject objects. The best place to implement it is the PasswdManager:
Hashtable allSubjects = new Hashtable();
...
PasswdSubject getSubject(String _userId) {
String password;
PasswdSubject subject;
password = allSubjects.get(_userId);
if (password==null) { // user is unknown
subject = null
}
else {
subject = new PasswdSubject(_userId, password);
// create new Object and fill it with user and password data
}
return subject;
}
Now, it is possible to check the users authenticity from PasswdSession.init(...)
with PasswdSubject.authenticate(...)
.
String username;
String password;
...
public boolean authenticate(String _passwd) {
String encryptedPasswd = .... ; // generate unix encrypted passwd
return (encryptedPasswd == password);
}
The PasswdSession.init(...)
-method itself:
PasswdSubject subject = null;
PasswdManager mgr;
String sessionId;
boolean authenticated = false; // successfully authenticated?
// Constructor
public Session(PasswdManager _mgr, String sessionId) {
mgr = _mgr;
sessionId = sessionId;
}
public String init(String xmlQos_literal) throws
XmlBlasterException
{
return init(new SecurityQos(xmlQos_literal));
}
public String init(I_SecurityQos securityQos)
throws XmlBlasterException
{
authenticated = false;
// Security function: IDENTIFICATION
subject = mgr.getSubject(((SecurityQos)securityQos).getCredential());
if (subject==null)
throw new XmlBlasterException("Access denied!"); // User unknown
// Security Function: AUTHENTICATION
authenticated = subject.authenticate(securityQos.getUserId());
if (!authenticated)
throw new XmlBlasterException("Access denied!"); // Wrong password
return null; // we don't support a return qos (e.g. helpful for key-exchange)
}
Finally Autheticate.connect(...)
calls getSubject()
on the just initialized I_Session
object,
before it returns and the user has logged on.
Publishing a message
Okay, so far so good. The user successfully connected to the xmlBlaster. But what happens if the user wants i.e. to publish a message?
After the user has called the servers publish method, it imports (e.g. decrypts, unseals, logs, ...) the message and checks the permission.
It does this by calling the local method private MsgUnit checkMessage(SessionInfo sessionInfo, MsgUnit msgUnit, String action) throws XmlBlasterException
,
which fetches the security session context (the responsible I_Session instance) from the xmlBlaster session, which will be determined by
the session id.
Now, the I_Session
instance can be used to log, encrypt or unseal the message. In our implementation (PasswdSession) we don't
support these features and return the message as it was given.
Additionally, I_Session
will be used to get the Subject via getSubject()
. This is necessary to check the permissions
in a final step. Therefore, I_Subject.isAuthoriued(...)
will be call. But because our driver doesn't support this, too,
I_Subject.isAuthoriued(...)
returns true
in each case.
If these actions took place without problems, the xmlBlaster will continue with publishing the message. Otherwise it exits with an XmlBlasterException
.
Other action like subscribtions, erasing messages, and so on follow the same pattern.
Conclusion
You've got a first impression what a developer of a security plugin has to do. The next step (if necessary) should be the examination
of one of the existing security plugins.