The need for what we call the security model of Guaranį, in a reflective architecture, is the same as for data encapsulation in an object-oriented architecture. By completely hiding from the base level details of the implementation of the meta level, we help ensure a proper separation of concerns between the application level and the management level. Furthermore, by preventing arbitrary interactions of the base level with the meta level, we make it possible to implement meta-objects and verify their correctness, no matter what base-level object they are associated with. For the same reasoning, we forbid meta-objects from accessing objects they are not associated with.
Some design decisions that support these goals have been exposed already:
The hierarchical organization of meta-objects in a meta-configuration may be seen as directed graph, but it is likely to be an acyclic one, and most likely it will be a tree. The primary meta-object is the root of the tree, and composers are parents of meta-objects they delegate to. This view suggests a natural definition of a hierarchy of control of meta-level processing. Meta-objects closer to the root of the tree are able to filter out messages, operations, results and reconfiguration requests, preventing that they reach untrustworthy components of their subtrees. Furthermore, in the operation and result handling protocol, a meta-object that is higher in the hierarchy may decide not to accept a replacement operation or a result provided by a meta-object lower in the hierarchy.
There is an additional issue regarding meta-level security, that has to do with the ability to create meta-level representations of operations addressed to a base-level object. In principle, only component meta-objects of the meta-configuration of an object should be able to create operations addressed to that object. On one hand, this provides an apparently reasonable security constraint that prevents any object from gaining privileged access to any other object. On the other hand, this model may be excessively restrictive on some situations, because such meta-objects might prefer to delegate their security privileges to other meta-level objects. Furthermore, this model would provide an all-or-nothing authorization control: the meta-objects of an object would be able to create any operation addressed to the base-level object; that might be undesirable if we would rather restrict the set of operations available for meta-objects.
Given this analysis, we have come up with a solution based on operation factories. The primary meta-object of an object is given an operation factory, an object that is capable of creating any operation addressed to the base-level object the meta-object is associated with. It may distribute this operation factory to other meta-objects it delegates to, or to other meta-level objects that may need to create operations addressed to the base-level object. However, it may create another operation factory that refuses to create certain kinds of operations, but that delegates valid requests to the operation factory it had access to. Then, it may distribute such restricted operation factories to its sub-meta-objects.
With this model, the mechanism that controls the creation of operations resembles the hierarchy of meta-objects, without requiring lower meta-objects to have references to higher ones. A similar mechanism for result objects is not necessary, since results are always returned by lower meta-objects to higher ones, whereas operations may be created and performed.
An important fact to note is that an operation created in the meta-level is never performed unless the primary meta-object associated with its target object determines so, when requested to handle it. This is important because, even if an operation factory becomes invalid--it does so whenever the primary meta-object changes--, an evil meta-object might have created an operation while the operation factory was still valid. This operation will not be delivered to the target object before it is handled by the current object's meta-configuration. If the primary meta-object of a meta-configuration changes while an operation is being handled, an abort result will be presented to the previous primary meta-object, and the operation handling is restarted with the new primary meta-object.
It is possible to create invalid operations--that refer to inexistent methods or fields, or whose types or argument counts do not match the expected ones. Such operations might have been rejected at operation creation time, but we decided to postpone this verification to the moment just before the operation is delivered, or whenever a meta-object requests so. The rationale is that one may create additional pseudo-fields or pseudo-methods in an object, that are only accessible from the meta level. Such invalid operations will never be delivered to the target object: meta-objects should create suitable results or replacement operations but, if they do not, the operation will fail, and its result will be an exception indicating this failure. This feature has proven to be useful for creating pseudo arrays that map into databases, for providing advanced overload resolution mechanisms and for sharing data among meta-objects in a safer way than using broadcast messages--because only the components of the meta-configuration or objects authorized by them may create such operations.
An operation factory can be used to create do-nothing operations, that act as mere placeholders until they reach a particular meta-object that replaces it with another operation. This makes it possible for a meta-object to create an operation that will only be observed by meta-objects that are placed after itself in a sequence of meta-objects.