Creating Hyperlambda from C#

I’ve just been refactoring and cleaning the code where I access my “auth” file in Phosphorus Five. The “auth” file contains the usernames/passwords and authorisation objects of Phosphorus Five. Since I am invoking Active Events parametrised from within these methods, I thought I’d take some time to explain this, since it provides an excellent use case of how to “create Hyperlambda” from C#.

The first thing you must realise about Hyperlambda, is that it’s simply a node structure. In fact, when your Hyperlambda has been parsed, its result is a simple graph object, encapsulated in the Node class of Phosphorus Five. This class contains lots of helper methods, but can be reduced down to the following pseudo code.

class Node {
    string Name;
    object Value;
    list Children;
}

The Children parts above is a list of Node children.

This trait of Hyperlambda, allows us to create Hyperlambda from C#. This might sound weird, but actually has lots of use cases. For instance, I wanted to encrypt my “auth” file when saving it, and decrypt it when loading it. I could of course have done this manually, by referencing BouncyCastle directly. However, I already have helper Active Events, that I can reuse, making my code much more condense, and allowing me to create more “DRY” code. DRY meaning “Don’t Repeat Yourself”. So what I ended up with was the following.


/*
 * Implementation of saving auth file.
 */
static void SaveAuthFileInternal (ApplicationContext context)
{
    // Getting path.
    string pwdFilePath = GetAuthFilePath (context);

    // Saving file, making sure we encrypt it in the process.
    using (TextWriter writer = new StreamWriter (File.Create (pwdFilePath))) {

        // Retrieving fingerprint from auth file, and removing the fingerprint node, since
        // it's not supposed to be save inside of the ecnrypted MIME part of our auth file's content.
        var fingerprint = _authFileContent ["gnupg-keypair"].UnTie ().Get<string> (context);

        try {

            // Writing fingerprint of PGP key used to encrypt auth file at the top of the file.
            writer.WriteLine (string.Format ("gnupg-keypair:{0}", fingerprint));

            // Encrypting auth file's content.
            var node = new Node ();
            node.Add ("text", "plain").LastChild
                .Add ("content", Utilities.Convert<string> (context, _authFileContent.Children))
                .Add ("encrypt").LastChild
                .Add ("fingerprint", fingerprint);
            context.RaiseEvent ("p5.mime.create", node);

            // Writing encrypted content to stream.
            writer.Write (node ["result"].Get<string> (context));

        } finally {

            // Adding back fingerprint to cached auth file.
            _authFileContent.Insert (0, new Node ("gnupg-keypair", fingerprint));
        }
    }
}

The above code is all the code I need to save my “auth” file, using a PGP key, stored in my GnuPG storage. Of course, if I was to use BouncyCastle directly, the above code would literally explode in size, and probably grow to at least one order of magnitude more in size. Instead I get to reuse my existing MIME Active Events, that allows me to simply supply a PGP key fingerprint, which will lookup my PGP key from my GnuPG storage, create a “text:plain” MIME envelope, encrypt that envelope, before it saves that encrypted envelope to disc. The thing you should pay particularly notice to above, is the parts where I construct my “node” object. The above code’s Hyperlambda equivalent would be the following.

p5.mime.create
  text:plain
    content:my-auth-file-content
    encrypt
      fingerprint:some-GnuPG-fingerprint

Of course, arguably using a MIME envelope to serialise my auth file to disc, arguably creates some overhead. However, this overhead is a small price to pay for the simplified code. In addition it also allows me to easily extend my logic later, to for instance create multipart MIME envelopes, wrapping multiple auth files, encrypted with their own unique PGP keys. In addition to that it allows me to serialise binary data later into my auth file, etc, etc, etc. So the added overhead of constructing a MIME envelope wrapping my auth file, is really quite insignificant compared to its gains. Reading the file from disc and decrypting it, is equally easy.


/*
 * Private implementation of retrieving auth file.
 */
static Node GetAuthFileInternal (ApplicationContext context)
{
    // Checking if we can return cached version.
    if (_authFileContent != null)
        return _authFileContent;

    // Getting path.
    string pwdFilePath = GetAuthFilePath (context);

    // Checking if file exist.
    if (!File.Exists (pwdFilePath)) {
        _authFileContent = new Node ("").Add ("users"); // First time retrieval of "auth" file.
        return _authFileContent;
    }

    // Reads auth file and decrypts it.
    using (TextReader reader = new StreamReader (File.OpenRead (pwdFilePath))) {

        // Retrieving fingerprint for PGP key to use to decrypt file.
        var fingerprintLine = reader.ReadLine ();
        var fingerprint = fingerprintLine.Split (':') [1];

        // Retrieving GnuPG key's password from web.config.
        var confNode = new Node ("", "gpg-server-keypair-password");
        var gnuPgPassword = context.RaiseEvent ("p5.config.get", confNode).FirstChild.Get<string> (context);
        
        // Retrieving the rest of the content of file.
        var fileContent = reader.ReadToEnd ();

        // Decrypting file's content with PGP key referenced at the top of the file.
        var node = new Node ("", fileContent);
        node.Add ("decrypt").LastChild
            .Add ("fingerprint", fingerprint).LastChild
            .Add ("password", gnuPgPassword);
        context.RaiseEvent ("p5.mime.parse", node);

        // Converting Hyperlambda content of file to a node, caching it, and returning it to caller.
        // Making sure we explicitly add the [gnupg-keypair] to the "auth" node first.
        _authFileContent = Utilities.Convert<Node> (context, node.FirstChild ["text"] ["content"].Value);
        _authFileContent.Add ("gnupg-keypair", fingerprint);
        return _authFileContent;
    }
}

The above code will in a similar fashion to its save counterpart, decrypt my file with a handful of lines of code. Probably saving me hundreds of lines of code, compared to if I were to explicitly implement decryption instead. Its Hyperlambda counterpart would resemble the following.

/*
 * Retrieving the password for our PGP key.
 */
p5.config.get:gpg-server-keypair-password
p5.mime.parse:auth-file-content-goes-here
  decrypt
    fingerprint:x:/@p5.config.get/*?value
      password:gnupg-password-goes-here

All in all saving me for hundreds, if not thousands of lines of code, allowing me to keep my code “DRY”, and reuse as much of my existing implementations as possible, without creating references between my two different projects. The last point is crucial. Because my auth file wrappers can be found in the project called “p5.auth”, while my MIME wrappers can be found in “p5.mime”. If you look at these projects in Visual Studio, you will notice that none of these projects have any references to the other, eliminating all the problems that such references might create, such as versioning problems, changing the signatures of my classes methods, etc, etc, etc. Resulting in a solution where I can reuse existing functionality from other projects, without any references between my projects at all!

I think that’s pretty cool! What do you think …?

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.