|
Our first step in designing the RIF for validation is to limit what parts of the code base that will support validation activities. In the three-layer architecture, validation will be divided between the business concept layer and the data storage layer. Although validation code could be included in the presentation layer, there are two reasons for not placing any emphasis of validation in this layer:
Given that we anticipate future development of front end applications to be volatile, we believe it is best to conserve as much business logic for validation within the business concept and data storage layers.
Validation-1 : Assume the client applications will do no validation. Validation code will be distributed between the business concept and data storage layers of the architecture.
Now that we have limited our concerns about validation to the business concept and data storage layers, we now look for other ways to economise our efforts. In the general design of the architecture, we decided to limit paths of execution wherever possible. Two general design decisions help make it easy to know where validation efforts should be focused:
General Design-4 : Wherever possible, limit the paths of execution that are likely to occur.
General Design-7 : Encapsulate business concept and data storage layers of the architecture through service APIs. Do not allow clients to know which class is implementing the service interfaces.
The emphasis for validation should focus on validating data passed by the client:
Validation-2 : Limit validation support to execution paths that support service API methods. Specifically, focus validation efforts on validating values the client application passes to methods of service APIs.
Validation-3 : By default, the fields in business classes should be of type String.
Concurrency-9 : Perform a deep-copy of any mutable object that is passed to a service method by a client application. This action will prevent the client from altering parameter values while they are being processed by the middleware.
Once the parameter values have been safe-copied, we can be assured that values will remain the same when they are being validated. The next step is to identify and prioritise what validation checks are applied to the parameter values of service methods. The order should ensure that a service request uses the least amount of the system resources. If an attempt to call a service method is going to fail, it should fail as quickly as possible and minimise how much work it makes the database do.
Failing fast can be more important in some scenarios than others. For example, as soon as a malicious code attack is detected in any field of any parameter object, the method should fail as fast as possible. Likewise, the code should react most quickly if an invalid user or one previously associated with attacks is associated with a service call. It is more important that these checks are done before checks that test whether a field contains a valid data type or a value within some range.
Validation-4 : Identify and prioritise the validation checks that are applied to the parameter values of service methods. Order the validation checks so they
- cause validation to fail as quickly as possible and
- wherever possible, give highest priority to fail a security validation check over other types of checks
- minimise the load placed on the database.
We identified and ordered a number of validation checks for a typical service call and
described them in the table below. As well as the order, the table describes what class
is responsible for holding the code for a check and the classes that actually call that code.
For example, the code to determine whether the fields of a User
are valid is
inside the checkErrors()
method in that class. However, user.checkErrors()
is actually invoked in the service methods that appear in the services classes such as
rifServices.dataStorageLayer.AbstractRIFStudyRetrievalService
and
rifServices.dataStorageLayer.AbstractRIFStudySubmissionService
. Where the code
is owned and where it is called help show how validation duties are distributed in the layers
of the code base.
The table also shows whether a check requires the database. Checking whether the field values of
a Geography
has a valid value for its 'name' field does not require a call to the
database. However, knowing whether the geography "SAHSU" is known does require a call. Note
that in the starred item marked '**', users are checked against a black list that is managed in
memory, not in the database.
Priority Order | Description | Where code for check exists | Where check is invoked | Need access to the database |
1 | Check that the user object has not been previously black listed due to previous security violations. Reject if user object is null. |
SQLConnectionManager
|
service classes | No ** |
2 | Check that parameter values are not null |
FieldValidationUtility
|
service classes | No |
3 | Check that the user Object contains no malicious field values. |
User
|
service classes | No |
4 | Check that all the field values in the User object are valid. | business classes | service classes | No |
5 | Check that the userID is currently registered. |
SQLConnectionManager
|
service classes | No |
6 | Check that none of the fields of any of the other business objects besides User has no malicious field values | business classes | service classes | No |
7 |
Check that the fields of other business objects besides User:
|
business classes | manager classes | No |
8 | In some cases, check whether parameter values exist in the database | manager classes | manager classes | Yes |