Table of Contents
This chapter describes how to develop custom security services, which is a key feature of the JEUS security system.
Using this feature, users can easily integrate the JEUS security system with various kinds of external security systems or security data repositories.
The following sections describe how to develop the custom security services.
The most basic class of the pluggable security architecture is the classjeus.security.base.Service class.
The Service class is an abstract class that must be extended by all security service implementation classes. Currently, all available security SPI classes in the eus.security.spi package extend this class.
The following is the class diagram for the Service class.
The Service class provides a few basic things that apply to all security services.
A Service class provides a string type description that briefly explains what the service does. The description can be obtained using the getDescription() method.
Each Service instance will be assigned to a domain when the service instance is created at runtime. So a domain will be essentially a collection of service instances. The domain of the service can be obtained using the getDomain() method.
Each Service implementation class has a single type. The type acts like a marker so that a specific service instance can be obtained from a set of service instances.
For example, if a Service implementation class implements some authentication functions, other security services need to request the domain for the class in order to use this authentication service class. To do so, they can pass the authentication type of the class so that the domain can return the correct service instance.
The service type can be obtained by using the getType() method. However, since getType() is an abstract method, it must be implemented by the sub-classes. The service type is actually a java.lang.Class, an instance of the SPI class that is included in the jeus.security.spi package. Each SPI class implements the getType() method as final. That is, the getType() method cannot be redefined in a sub-class of SPI.
The service name can be obtained using the getName() method.
The service class is associated with a JMX MBean, which is used to manage a particular service instance.
The MBean can be obtained using the getMBean() method. Sub-classes must override this method in order to return a different MBean instead of the default one.
By default, MBean is created based on the information that is returned from the getMBeanInfo() method that is declared as a protected method. Sub-classes can redefine this method to return a different MBeanInfo instance instead of the default one. To do so, the overriding implementation must include the MBeanInfo of the super class.
Each Service implementation class is in one of the two states, created or destroyed. The state can be changed by invoking create() and destroy(). These methods delegate the actual service to the abstract protected methods, doCreate() and doDestroy(). It is required that all service implementation classes must implement both doCreate() and doDestroy() methods.
The doCreate() and doDestroy() methods include the code that starts and terminates the service. The typical doCreate() method contains the code that acquires some resources like DB connections, while the doDestroy() method contains the code that releases those resources.
The current status of the service class can be obtained using the isCreate() and isDestroyed() methods. There is also the method getState() that returns the current status as a string.
The following is the status-chart for the Service class.
The service class has a name-value pair property. This property can be set or retrieved using the jeus.security.base.PropertyHolder interface. The service implementation class must implement the interface.
The properties are used to initialize service instances. The properties are set before a service instance is created, and then the create() method is called. After the properties have been set, the service instances can be initialized by calling the doCreate() method to retrieve the property values.
This section briefly describes how to implement the custom security services.
The following are required to implement a customized security service.
Users must have a general understanding of the security system and its architecture.
Choose the security functions that the custom security service needs to provide.
Choose the SPI class that has the necessary features. For more information about main SPI classes, refer to "5.4. SPI Class".
Refer to the documents for information on SPI class. (Refer to Javadoc, References, and "5.4. SPI Class".)
Make a sub-class of the selected SPI class and implement the following methods. An empty public constructor without parameter must be provided.
Optionally define a set of properties that are used to initialize services. Each property is a public static final string type and what each property represents must be documented.
Implement the doCreate() method. This method will be called once when the security service is started and it performs general initialization operations such as resource allocation. This method reads the property values using the getProperty() method. The parameter of the getProperty() method is the property name that was defined in the previous step.
Implement the doDestroy() method. This method is called once when the service is terminated. The method should release any resources that were allocated during the doCreate() method call and also perform clean-up operations such as writing logs to a certain file.
Implement all abstract methods in the selected SPI class as specified in the Javadoc.
Optionally implement the methods to be used for managing the service through JMX. Implement the getMBean() or getMBeanInfo() method.
Compile the class that implements SPI.
Register the new security service with JEUS as described in "Chapter 2. Configuring the Security System".
The following SPI classes are defined in the jeus.security.spi package.
The following is the list of SPI classes defined in the jeus.security.spi package. The cardinality indicates how many SPI instances can exist for each security domain.
[Table 5.1] The standard SPI classes defined in the package jeus.security.spi
class name | Purpose | Cardinality |
---|---|---|
SecurityInstaller | Installs and uninstalls the security system. Only one must exist for the entire JVM. | 1 |
SubjectValidationService | Checks if the credential of the subject is valid prior to logging in. | 0 or more |
SubjectFactoryService | Creates the custom subject prior to logging in. | 1 |
AuthenticationService | Authenticates the subject prior to logging in. | 1 |
AuthenticationRepositoryService | Add, get, or remove a subject from its repository. | 1 |
IdentityAssertionService | Maps credentials to subject using the cert-user-map.xml file. | 0 or more |
CredentialMappingService | Maps credentials to subject using the truststore information. | 0 or more |
CredentialVerificationService | Verifies that at least one of the credentials for the subject is valid. | 0 or more |
AuthorizationService | Checks whether the subject has permission to access a role or resource. | 1 |
AuthorizationRepositoryService | Add, get, or remove Policies from the repository. | 1 |
EventHandlingService | Implements custom event handler for security events and various security audits. | 0 or more |
Fore more information about the SPI class, refer to References.
The jeus.security.spi.SubjectValidationService SPI is used to check whether all the credentials held by a subject are valid. This means that there should be no invalid credentials for any reason. An invalid credential means that the Subject is invalid and consequently that the subject should not be allowed to log in.
A typical use for the SubjectValidationService instance is to check whether the subject contains a lock credential. If so, it can be concluded that the subject is locked out and that any login process should not be allowed to proceed.
Thus, the SubjectValidationService.checkValidity(Subject) method is usually called during the login process. If a SecurityException occurs, the login process will fail. EventHandlingService class is used to automatically configure a lock credential for the subject. For more information, refer to "5.4.10. EventHandlingService SPI".
Note that the issue of authentication (verifying whether the returned subject corresponds to the actual subject) is different from the validation of the Subject. Authentication and validation are not dependent upon one another. However, login is dependent on both, as already mentioned.
ZERO OR MORE SubjectValidationService instances can be configured for each domain. If no SecurityException occurs during the SubjectValidationService process, the subject is regarded as valid, and the login process is allowed to proceed. However, if at least one SecurityException occurs, the overall validation will fail and the login must NOT be allowed to proceed.
SubjectValidationService SPI class is functionally different from the CredentialVerificationService class.
SubjectValidationService checks the credential to determine the validity of the subject. CredentialVerificationService checks if the subject has at least one credential to prove its authenticity. So the SubjectValidationService class is used after the subject has been successfully authenticated, while CredentialVerificationService class is used during the process of authentication in the AuthenticationService class.
jeus.security.spi.SubjectFactoryService SPI is a special SPI class used to create a subject without any external information.
SubjectFactoryService SPI is used to create a subject using the SubjectFactoryService class without receiving any parameters. On the other hand, regular implementation classes receive the username and password from the prompt and create a subject based on the information.
The SubjectFactoryService class is created in the login mechanism to support other credential types other than the password.
The jeus.security.spi.AuthenticationService class is used to authenticate a Subject. It is used to verify that the subject, which was received as a parameter, indeed corresponds to the subject that is registered with the subject repository.
The authentication procedure is as follows:
Calls the jeus.security.spi.CredentialMappingService.getSubjectName(Object) in the login mechanism to check for the user the credential of the subject is mapped to.
Calls the jeus.security.spi.AuthenticationRepository.getSubject(String username) method to copy the registered subject from the subject repository to the local server. This is called a local subject.
If the local subject is not null, check whether the credential of the local subject matches that of the subject that needs to be authenticated. The comparison can be done by calling the SPI method, jeus.security.spi.CredentialVerificationService.verifyCredentials(Subject, Subject).
In some cases, equals(Object) method is used to compare the credentials, but it lacks flexibility.
Finally, if all of the previous steps were completed successfully, the local Subject is returned from the authenticate(Subject) method and eventually, that Subject will be used to log into the SecurityCommonService.
ONE OR MORE AuthenticationService instances can be configured for each domain. If at least one of the configured AuthenticationService instances is successfully authenticated, the entire AuthenticationService will regard the authentication as successful. If one instance successfully authenticates the subject, no additional AuthenticationService will be queried.
jeus.security.spi.AuthenticationRepositoryService SPI has methods that add a subject to the repository and delete or get a subject from the repository. This SPI is used in the classes that implement the AuthenticationService class, or it can be used directly within the J2EE application code.
For example, this SPI must be used to write code that automatically adds a subject to the subject repository whenever a new user registers through a web site.
The main purpose of the AuthenticationRepositoryService class is to store a jeus.security.base.Subject instance in a specified repository type at runtime. The typical repository types are XML file and database.
The implementation and use of AuthenticationRepositoryService SPI is optional. However, it must be provided in the case when the default AuthenticationService is used (since it calls the AuthenticationRepositoryService.getSubject(String) method during the authentication), or if it is directly used in a J2EE application code.
Only one AuthenticationRepositoryService instance can be specified for a domain. Currently, multiple AuthenticationRepositoryService's cannot be used.
jeus.security.spi.IdentityAssertionService SPI is necessary in the case when the username of a subject is unknown, which means that the main principal is set to null. In this case, it needs to extract the credentials of the subject and try to match these credentials to a username based on the information in cert-user-map.xml. This username is later used to perform authentication.
A typical example is the use of a java.security.Certificate instance. The instance uses a certificate credential without the main principal. In such case, during the authentication, it receives the certificate credential from the subject and passes it as a parameter of the IdentityAssertionService.getIdentity(Object) method.
The method calls the IdentityAssertionService.doIdentity(Object) method again. In the method, the user name will be traced back using the attribute key value of the certificate defined in the cert-user-map.xml file and sent as a return value.
jeus.security.spi.CredentialMappingService is necessary in the case when the username of a subject is unknown, which means that the main principal is set to null. In this case, it needs to extract the credentials of the subject and try to match these credentials to a username, which may be used later to perform authentication.
A typical example is the use of a java.security.Certificate instance. The instance uses certificate credential without the main principal. In such case, during the authentication, it receives the certificate credential from the subject and passes it as a parameter of the CredentialMappingService.getSubjectName(Object) method.
The method calls the CredentialMappingService.doGetSubjectName(Object) method again. In the method, the user name will be traced back using the attribute key value of the certificate defined in the cert-user-map.xml file and sent as a return value.
jeus.security.spi.CredentialVerificationService SPI is used to support a new type of Proof Credential.
Proof Credential is a kind of credential that may be used to prove the authenticity of the Subject that is returned as a parameter, which means that it corresponds to an existing subject. A typical example of the proof credential is a password that is implemented by the jeus.security.resource.Password class.
CredentialVerificationService SPI essentially declares the doVerifyCredentials(Subject, Subject) method, which is one method that must be implemented by its sub-class. The first subject in the signature of this method is the reference Subject, which contains the credentials of the actual subject that is registered with the subject repository. The second argument is the proof Subject, which contains the proof credentials. The doVerifyCredentials(Subject, Subject) method compares the credentials of the two subjects and checks for a match.
The matching between credentials may be done in a number of different ways (just using the equals(Object) method may not be enough). If the credentials of any two subjects match, the method will return, otherwise, a jeus.security.base.SecurityException will occur.
In some cases, it is not necessary to actually use the information of the reference subject. Certain proof credentials can be checked by using only the information of the proof Subject, such as a Certificate Credential.
The CredentialVerificationService that matches with the jeus.security.resource.Password is the jeus.security.impl.verification.PasswordVerificationService class.
ZERO OR MORE CredentialVerificationService's can be configured for each domain. If at least one match is found in the CredentialVerificationService, the entire CredentialVerificationService verification is regarded as a success. Otherwise, a SecurityException occurs and the entire verification is regarded as a failure, and the authentication will fail.
jeus.security.spi.AuthorizationService SPI defines the methods for authorization of the security system. The purpose of this SPI is to answer the question, “Does the Subject S have the permission to perform the action A?”.
A typical implementation class calls the jeus.security.spi.AuthorizationRepositoryService.getPolicy(contextId) method and analyzes the returned jeus.security.base.Policy object to obtain the answer to the previous question. It is also possible to use other implementations that do not use the AuthorizationRepositoryService SPI.
ONE OR MORE AuthorizationService can be configured for each domain. If at least one AuthorizationService returns a positive value, the entire authorization will be regarded as a success. Additional AuthorizationService objects will not receive the permission query if one successfully authorizes the permission.
jeus.security.spi.AuthorizationRepositoryService SPI is used to add, remove, or get a jeus.security.base.Policy (which represents authorization policy) instance to/from the Policy repository. This SPI may be used in the AuthorizationService implementation classes. It can also be used directly in J2EE application code to directly add or remove Policy to/from the Policy repository when a particular event occurs.
The main purpose of AuthorizationRepositoryService is to store a jeus.security.base.Policy instance to a certain repository type at runtime. The typical repository types are XML file, database, and LDAP server.
The implementation and use of AuthorizationRepositoryServiceSPI is optional, but it must be provided in the case when the default AuthorizationService is used (since it calls the AuthorizationRepositoryService.getPolicy(String) method during authentication), or if it is directly used in J2EE application code.
Only one AuthorizationRepositoryService instance can be specified for a domain. Currently, multiple AuthorizationRepositoryService's cannot be used.
EventHandlingService SPI is an interface that captures and processes various security events.
Using the EventHandlingService SPI, users can easily implement operations, including logging and sending a notification email to the administrator when a security event occurs, setting a lock on a subject, etc.
The basic concepts are very simple: the occurrences of jeus.security.base.Event's in the security service will be notified to all EventHandlingService's in the domain. Then EventHandlingService handles the event according to its content.
For example, an EventHandlingService can be used when a lock needs to be applied to a Subject. Whenever the AuthenticationService fails to authenticate a Subject, it will generate a security.authentication.failed Event. The lockout EventHandlingService catches this event and executes the handleEvent(Event) method. In the method, a login counter is kept and when it reaches a certain value (such as 3), a lock credential is applied to the Subject.
Then SubjectValidationService confirms that the subject is locked, and as a result, all attempts to log in using this Subject will fail. Thus, by using EventHandlingService with SubjectValidationService, user can implement a function that prohibits a subject from logging into the system if it fails to log in three times in a row.
However, the most common use of the EventHandlingService SPI is probably to implement various kinds of security loggers that record events. The events can be written in a text file, an XML file, or sometimes in an encrypted file to prevent repudiation.
ZERO OR MORE EventHandlingService's can be configured for each security domain.
There may be a dependency between different SPI classes. For example, the AuthenticationService class depends on the AuthenticationRepositoryService class.
Here, dependency is simply the fact that one SPI implementation class calls a static method of another SPI implementation class. If the implementation class of AuthenticationService is AuthenticationServiceImpl, the class can obtain the information about the subject by calling the AuthenticationRepositoryService.getSubject() method.
Note that the fact that a dependency between SPI classes "may" exists implies that the dependency could exist but is not mandatory. It is dependent on the actual implementation of the SPI class. No SPI class directly calls the methods of another SPI class (with a few minor exceptions). In general, the dependency is generated when a SPI implementation class calls a method of a different SPI implementation class.
The following shows the dependencies between SPI implementations and SPI static methods in a default security system implementation.
After a new SPI implementation class is compiled, the class must be registered with the JEUS security system. For more information, refer to "2.3. Configuring Security Domain Components".