Features on MindTouch Dream use the C# native protection levels to specify who can access them:
- Public – everyone
- Internal – only Parent, Child or services that were provided the Internal or Private key as a service-key cookie
- Protected – same as Internal
- Private – only Parent or services that were provided the Private key as a service-key cookie
Initial grants are according to relationships at service creation, but if you need to give someone special access you can provide them the appropriate service-key cookie and they are able to access the features matching the key provided.
But sometimes you want more granular access than “you there, feel free to call all my internal features” or you actually want to lock down access on more liberal features according to a configuration value. Luckily, the access protection defined by the C# protection levels is completely customizable at run time.
A Quick Diversion: Dream access & cookies
When a request comes in to Dream and the Feature called is not public, Dream will check for a cookie called service-key. This cookie is compared to the service’s InternalAccessKey and PrivateAccessKey to determine what access level should be granted. By default the parent of a service (i.e. the service that created it) is automatically provided the PrivateAccessKey, as is the host service. In addition, the InternalAccessKey is provided to child services. By manually providing these cookies via set-cookie to callers, a service can easily provide safe access to other services.
Just like in the browser use-case, cookies are used in Dream to attach path specific data that allows service calls to be made without having to worry about all sorts of ancillary information to be passed in the query or custom headers. In the browser, cookie management and persistence is an automatic core feature. Often times REST based systems skip cookies, relying more on custom headers, since the calling system might be a simple curl wrapper that doesn’t worry about persisting cookie jars. However most http request systems offer some capability to handle cookies rather than leaving the busywork of dealing with the headers to the caller, making cookies simpler than X-* headers in the general case. When writing Dream Services, cookies are managed by the currently active DreamContext and therefore are mostly invisible. When calling services from code outside of a DreamService, you can leverage the Plug class for your service calls and have it manage the cookie jar for you.
With cookies granting access to an internal Feature becomes as simple as setting the appropriate service-key in a cookie. But the use of the service-key cookie is not restricted to InternalAccessKey or PrivateAccessKey, nor is authorization limited to the use of cookies in general. By hooking into the access system of Dream, virtually any type of access security can be added to a Feature. It should be kept in mind that violating the access levels published in the service blueprints should be done with care, as it can make for user unfriendly services.
Granting granular access
Most of the time the type of access modification needed is that of opening up access to some features that are by default locked down. To intercept the authorization for these features, you can override DetermineAccess.
public override DreamAccess DetermineAccess(DreamMessage request) {
return base.DetermineAccess(request);
}
In order to grant access to a Feature marked internal, DetermineAccess must return that level of DreamAccess or higher. If you simply have a custom header, that, if set, allows the caller to access any internal feature, then the code could be as simple as this:
public override DreamAccess DetermineAccess(DreamMessage request) {
if(request.Headers["X-Custom-Internal-Auth"] == _customInternalAuthKey) {
return DreamAccess.Internal;
}
return base.DetermineAccess(request);
}
However, if the grant is more granular, say specific to a particular feature, you need to inspect the context. DetermineAccess isn’t passed a context like it is for a feature, but it is still accessible via DreamContext.Current (note: with Dream 1.6 the context will be passed to DetermineAccess). In particular, you can inspect the current Feature via DreamContext.Current.Feature. There are a number of ways to determine whether the called feature is the appropriate one, from looking at the fully qualified method name in DreamContext.Current.Feature.MainStage to looking at the signature components. For this example, we’re going to match on signature, since it is more likely to survive code refactoring than a method name and we will only use our custom header to grant access on posts to foo:
public override DreamAccess DetermineAccess(DreamMessage request) {
if(DreamContext.Current.Feature.VerbSignature == "POST:foo" &&
request.Headers["X-Custom-Internal-Auth"] == _customInternalAuthKey) {
return DreamAccess.Internal;
}
return base.DetermineAccess(request);
}
Locking down default access
Locking down access from the default should be considered an edge case, since you are now going back on what you promised in your blueprint. I.e. a service at a known location really should not restrict features previously advertised as public.
One scenario in which locking down is appropriate is a service that may be instantiated as public and discoverable in some cases but as a private child service for exclusive use by the parent in other cases. In this scenario, previously public features may need to be locked down to add security beyond the obscurity of the path at which the parent created the service. An example of this scenario is the StorageService in Dream 1.6. By default it is a public service, but if instantiated via the private SID, it turns into a private service, generally used as a child service for private service storage. This scenario cannot be accommodated by an override to DetermineAccess, since it only allows the easing of restrictions. Instead, the Prologue system of Dream comes into play.
Dream contains feature intercepts for both pre- and post-feature invocation. These are named Prologues and Epilogues, respectively and are exposed as properties on DreamService. These interceptors are implemented as feature stages, i.e. they have the same signature as a normal feature. At feature invocation time, the list of prologues are each called before the feature is called, while the list of epilogues are called after the feature. The default implementation in a service returns null for either, although there are also host specified prologues and epilogues that wrap feature invocations.
In order to add a prologue, we first must override Prologues to return the interceptor we want called. The prologue/feature/epilogue chain is built up at service start, which means that the override of Prologues has to generate a static list without knowledge of the invocation. That moves responsibility of whether the prologue is appropriate for the current feature to a runtime responsibility of the prologue. The same is true for epilogues. The most basic implementation of a prologue/epilogue must at pass the request through, i.e.
private Yield DummyPrologue(
DreamContext context,
DreamMessage request,
Result response
) {
response.Return(request);
yield break;
}
For illustration, we will consider the use case of the Storage Service as implemented in Dream 1.6, in which we have set a _private flag in the service at Start time depending on the SID that was used to create the service. First we add our prologue to every Feature invocation by overriding Prologues:
public override DreamFeatureStage[] Prologues {
get {
return new DreamFeatureStage[] {
new DreamFeatureStage(
"check-private-storage-access",
this.ProloguePrivateStorage,
DreamAccess.Public
)
};
}
}
This created an interceptor named “check-private-storage-access” (the name is purely informational in this scenario) that calls ProloguePrivateStorage on any call (DreamAccess.Public indicates that it applies to public, internal and private invocations). Now for the implementation of ProloguePrivateStorage:
private Yield ProloguePrivateStorage(
DreamContext context,
DreamMessage request,
Result response
) {
//check if this services is private
if(_private) {
Cookie cookie = request.HasCookies
? HttpUtil.GetCookie(request.Cookies, "service-key")
: null;
if(cookie == null || cookie.Value != PrivateAccessKey) {
throw new DreamForbiddenException("insufficient access privileges");
}
}
response.Return(request);
yield break;
}
ProloguePrivateStorage checks if the service is marked as private and checks for the service-key and whether it matches our PrivateAccessKey before allowing the call to continue to the next DreamFeatureStage.
Between cookies, DetermineAccess and the Prologue/Epilogue systems, Dream provides very simple and versatile methods for access control without having to front load the business logic of the Features themselves with authorization concerns. This keeps Features focused on their task and allows for Service wide access control independent of Feature development and refactoring.
Note: For reference this article along with a number of other Dream related Tutorials can also be found here. If you have any problem with the examples, check the wiki version, as it may reflect changes in Dream since this writing.





Autogenerate Docs from Source code. Hello mindtouch.doc; Goodbye NDoc, Javadoc…
Recently I’ve had several users and customers ask me about auto-generating documentation from source code comments. I talked to the some of the engineers here at MindTouch and I was informed of a tool that they developed a while back named mindtouch.doc. Indeed, our DReAM framework documentation was created by this tool.
We’re making mindtouch.doc available for free as open source. It’s basically a much better alternative to Javadoc or similar doc generation tools. The auto-generated doc output can be pushed directly into your MindTouch or you can produce a MindTouch Archive (mtarc) and import this to your MindTouch site. I’m told it supports all the C# compiler supported tags for documentation comments.
Read more…