![]() ![]() |
||||||||||||||||||||||||
|
||||||||||||||||||||||||
Here
we complete the description of the security level plug-in and use it to introduce the
following OSATE API capabilities, which are more fully described in subsequent sections:
The
security level analysis runs in two passes:
We
need two passes because we cannot check connections until we are sure that all
components have a security level associated with them. We must, therefore, visit all the
components first to make sure that any inferred property values have bubbled all the way
up
the component containment hierarchy. We implement each pass as a separate subclass of
AadlProcessingSwitch. |
||||||||||||||||||||||||
The CheckSecurity class, our Eclipse action class for driving the analysis,
is shown below.
The class demonstrates how to initialize static references to property definitions once at the
beginning of an analysis. This avoids having to find the property definition every time it is
needed, which in this case would be every time a component is visited by the analysis. Class
AaxlReadOnlyAction (superclass of AaxlModifyAction, which our action extends)
declares the method initPropertyReferences(). This method is called before
doAaxlAction is called. The default implementation does nothing; actions that use properties,
however, should override this method to initialize references to any property definitions, types,
and constants used by the analysis. The recommended pattern is for references to these
elements to be public static volatile fields of the action class. That way they
can be easily
referenced from instances of the model traversal classes and other helper classes.
Property definitions, types, and constants are looked up using the methods
AaxlReadOnlyAction.lookupPropertyDefinition,
AaxlReadOnlyAction.lookupPropertyType, and
AaxlReadOnlyAction.lookupPropertyConstant which are summarized in the table below.
If the definition is not found, these methods return null and also update an internal list of
unfound definitions. The methods lookupOptionalPropertyDefinition,
lookupOptionalPropertyType, and lookupOptionalPropertyConstant return the
definition, or null if not found, but do not update the list of unfound definitions. It
is assumed
the plug-in is written to function correctly when an optional definition is absent. After
initPropertyReferences returns, if any of the items are not found, then instead of calling
doAaxlAction, a dialog box is presented to the user listing those elements that were not
found; see Figure 27 for an example. These methods are implemented using calls to the
property lookup methods in OsateResourceManager that are described in Section 5.8.2
Getting PropertyDefinition Objects.
![]() Figure
27: Example error dialog reporting that property set elements needed by a plug-in could not be found.
If your plug-in has other preconditions that it checks before performing its
analysis, and you
want to report all the errors together, the default error reporting can be suppressed by
overriding the method suppressErrorMessages() so that it returns true. In this case,
doAaxlAction will always be called, and it is your responsibility to check whether there were
any errors in initPropertyReferences and to react appropriately. You can check for errors
using the method hasPropertyLookupErrors(). You can get a list of the property set
elements that were not found by calling getPropertyLookupErrors(), which returns a List
of Strings identifying those elements that were not found. The strings are of the form
"property definition propertySet::name", "property type propertySet::name",
and
"property constant propertySet::name" as appropriate.
Our security level analysis is implemented as an AaxlModifyAction, because
it may modify
the model by inserting inferred property values. It uses the default error handling mechanism
because it has no other preconditions to check. The action uses two different model traversal
classes, one for each pass described above. For the first pass, analysis invokes the
processBottomUpComponentImpl method, which visits the components in an order that
insures that a component is visited before any of the components that reference it are. For the
second pass, a normal pre-order traversal is sufficient.
public
class CheckSecurity extends AaxlModifyAction {
private static final String SECURITYLEVEL = "SecurityLevel";
private static final String SEI_PACKAGE = "SEI";
public static volatile PropertyDefinition securityLevel = null;
protected void initPropertyReferences() {
// Initialize our one property reference
securityLevel =
lookupPropertyDefinition(SEI_PACKAGE, SECURITYLEVEL);
}
public void doAaxlAction(AObject obj) {
if (obj == null) return;
final AObject as = obj.getAObjectRoot();
/* Ensure that enclosing component security level encompasses
* contained security levels.
*/
if (as instanceof AadlSpec) {
final ComponentSecuritySwitch componentSecuritySwitch =
new ComponentSecuritySwitch(getMarkerReporter());
// Walk up the component implementation reference hierarchy
componentSecuritySwitch.processBottomUpComponentImpl(
(AadlSpec) as);
// Check security along connections
final ConnectionSecuritySwitch connectionSecuritySwitch =
new ConnectionSecuritySwitch(getMarkerReporter());
connectionSecuritySwitch.processPreOrderAll(as);
}
}
} |
||||||||||||||||||||||||
Checking
the security levels of the declarative components is a straightforward task: get the
security level of the component and compare it against the security levels of its subcomponents.
Because we are also interested in being able to correct the security level of the components,
we actually first find the maximum security level of the subcomponents, and then compare that
value against the components value. If the components value is non-existent or less
than the
maximum subcomponent value, then we upgrade the components SecurityLevel property
association.
To
get the property values we use the method
PropertyHolder.getSimplePropertyValue(PropertyDefinition), which is a convenience
method for getting a property value that is not expected to be modal and is not list-valued. The
method returns a PropertyValue object, or null if the property value is undefined, is
a list, or
depends on modes. We reference the property declaration of the SecurityLevel property
using the previously initialized static reference CheckSecurity.securityLevel. Once we
have
the property value, we check that it is an IntegerValue, which also checks that the value is
non-null, and get the value as a long by casting the object back to an IntegerValue
and
invoking getValue().
When
the security level of the component must be set, either because no value was found or
because the value is not high enough, we create a new IntegerValue object, set its value, and
then set the property value. We always create a new PropertyValue object rather than
modifying the object obtained from looking up the property value, because we do not know
from where in the modelif at allthe property value object comes, and modifying it directly
may have unintended consequences; see Section 5.5 Getting Simple Property Values for
more information. We set the value of the IntegerValue object using the method
setNewValue(long), which makes sure the values numeric and string representations are
consistent; see Section 5.6.6 Setting Number Values. Finally, we associate the new
value
with the property for the component using
PropertyHolder.setPropertyValue(PropertyDefinition, PropertyValue). This method is
one of several property setting methods described in more detail in Section 5.8.5.1 Setting
Property Associations.
public class ComponentSecuritySwitch
extends AadlProcessingSwitch {
public ComponentSecuritySwitch(MarkerReporter
er) {
setReporter(er);
}
protected final void initSwitches()
{
componentSwitch
= new ComponentSwitch() {
public
Object caseComponentImpl(ComponentImpl ci) {
// Get my security level, if declared
final PropertyValue cipv =
ci.getSimplePropertyValue(CheckSecurity.securityLevel);
long cilv = 0;
if (cipv instanceof IntegerValue) {
cilv = ((IntegerValue) cipv).getValue();
}
// Get the max security level of my subcomponents
long maxslv = 0;
final EList subs = ci.getXAllSubcomponent();
for (Iterator it = subs.iterator(); it.hasNext();) {
final Subcomponent sub = (Subcomponent) it.next();
final ComponentImpl sci = sub.getComponentImpl();
if (sci != null) {
PropertyValue scipv =
sci.getSimplePropertyValue(CheckSecurity.securityLevel);
if (scipv instanceof IntegerValue) {
long slv = ((IntegerValue) scipv).getValue();
// Update max subcomponent security level
if (slv > maxslv) maxslv = slv;
}
}
}
if (maxslv > cilv) {
/* Subcomponents have higher security level than me.
* Update my declared security level.
*/
if (cipv != null) { // My declared level is wrong
reportWarning(ci,
"Security level updated from " +
cilv + " to the maximum of the subcomponent values: " +
maxslv);
} else { // Didn't have a declared level
reportWarning(ci,
"Security level set to the maximum of the " +
"subcomponent values: " + maxslv);
}
// Create new property value: An Integer value
final IntegerValue newpv =
PropertyFactory.eINSTANCE.createIntegerValue();
// Set to max security level
newpv.setNewValue(maxslv);
// Set the property association
ci.setPropertyValue(CheckSecurity.securityLevel, newpv);
}
return DONE;
}
};
}
} |
||||||||||||||||||||||||
To
check a connection we need the security levels of the connections end points. These are
retrieved from the source and destination context components of the connections. The
methods Connection.getAllSrcContextComponent() and
Connection.getAllDestContextComponent() get the Subcomponent or ComponentImpl
that is the source or destination of the connection. It is not sufficient to use the
Connection.getXAllSrc() and Connection.getXAllDest() methods, because when the
connection is made through a port group, the latter pair of methods return the port group
features that participate in the connection, whereas the former pair of methods search beyond
the port group to find the component that contains the port group.
Once
we have the end points of the connection, we get the property value associated with
SecurityLevel as described above, and
compare the level of the source component against
the level of the destination component.
public class ConnectionSecuritySwitch
extends AadlProcessingSwitch {
public ConnectionSecuritySwitch(MarkerReporter
er) {
setReporter(er);
}
protected final void initSwitches()
{
connectionSwitch
= new ConnectionSwitch() {
public
Object caseConnection(final Connection conn) {
// Ignore access connections
if (conn instanceof DataAccessConnection
|| conn instanceof BusAccessConnection) {
return DONE;
}
// Get the connection contexts
final PropertyHolder scxt = conn.getAllSrcContextComponent();
final PropertyHolder dcxt = conn.getAllDstContextComponent();
if (scxt == null || dcxt == null) return DONE;
// Get the security levels of the end points
final PropertyValue spv =
scxt.getSimplePropertyValue(CheckSecurity.securityLevel);
final PropertyValue dpv =
dcxt.getSimplePropertyValue(CheckSecurity.securityLevel);
long slv = 0;
if (spv instanceof IntegerValue) {
slv = ((IntegerValue) spv).getValue();
}
long dlv = 0;
if (dpv instanceof IntegerValue) {
dlv = ((IntegerValue) dpv).getValue();
}
// Error if source level is higher than the dest level
if (slv > dlv) {
reportError(conn,
"Security level violation: Source has level " + slv +
" and destination has level " + dlv);
}
return DONE;
}
};
}
} |
||||||||||||||||||||||||