Pre-signed S3 URLs with Clojure and ClojureScript

Dashman uses S3 for storing the encrypted screenshots. They can go directly from the renderer application to S3 and from there to the displayer application as there’s no need for the server application to ever see them. They are end-to-end encrypted anyway.

My needs in this regard were a bit unique. I have a server component that can generate whatever tokens and signatures are needed and then send it to both applications. The applications are written in ClojureScript, so, essentially, JavaScript, but unlike most direct-to-S3 upload solutions, I don’t have a web site with a form, so, I don’t need to fiddle with CORS or forms.

The two libraries

I found two libraries that could have helped me. The big one is amazonica, a Clojure library to interact with AWS. It looks very complete but when I tried to use the com.amazonaws.services.s3/generate-presigned-url function, I got this error:

The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256.

There are two versions of signatures, version 2 and 4. Version 2 is deprecated, it might or might not work on some data centers but it definitely doesn’t work on all. I refused to believe it at first because I was using very recent version of all libraries, but yup, it was generating version 2 of the signature with signed URLs that look something like this:

https://bucket-name.s3.amazonaws.com/key-name?AWSAccessKeyId=AKI...OA&Expires=1494190327&Signature=ct...3D

Digging further I found this snippet of code:

(System/setProperty SDKGlobalConfiguration/ENFORCE_S3_SIGV4_SYSTEM_PROPERTY "true")

But for me, it made no difference in my case. For the record I reported the issue and it is supposed to work, but it doesn’t for me.

Then there’s s3-beam, that looked promising, but it seems targeted towards forms, not just plain PUT requests from the clients. On top of that, I didn’t want to mess with async, I didn’t need it and it would be over-engineering for my case. Lastly, it uses it’s own custom way of creating the signatures instead of Amazon’s SDK and that code looks like it’s generating the old signatures (sha1 instead of sha256). I ended up not even trying it.

Instead, I decided to do my own custom option, which was not hard and worked straight away. This post is about sharing that code.

Custom solution using the AWS Java SDK

Starting on the server/Clojure side of things, I added the dependency:

[com.amazonaws/aws-java-sdk "1.11.126"]

With only that I was getting this unexpected error when trying to use the AWS SDK:

ERROR com.fasterxml.jackson.databind.ObjectMapper.enable([Lcom/fasterxml/jackson/core/JsonParser$Feature;)Lcom/fasterxml/jackson/databind/ObjectMapper;
java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.ObjectMapper.enable([Lcom/fasterxml/jackson/core/JsonParser$Feature;)Lcom/fasterxml/jackson/databind/ObjectMapper;
ERROR Could not initialize class com.amazonaws.partitions.PartitionsLoader
which I resolved by adding these other dependencies as well (mostly about getting the latest version):
[com.fasterxml.jackson.core/jackson-core "2.8.8"]
[com.fasterxml.jackson.core/jackson-databind "2.8.8"]
[com.fasterxml.jackson.core/jackson-annotations "2.8.8"]

I wrote a function to generate the S3 client:

(defn aws-s3-client []
  (let [region (Regions/fromName (config/env :aws-s3-region))
        credentials (BasicAWSCredentials. (config/env :aws-access-key-id)
                                          (config/env :aws-secret-access-key))]
    (-> (AmazonS3ClientBuilder/standard)
        (.withRegion region)
        (.withCredentials (AWSStaticCredentialsProvider. credentials))
        .build)))

config/env just fetches configuration values, powered by cprop and mount. This is just standard stuff from Luminus. With

(defn- pre-signed-url [key & [verb]]
  (let [expiration (tc/to-date (-> 24 t/hours t/from-now))
        generate-presigned-url-request (doto (GeneratePresignedUrlRequest. (config/env :aws-s3-bucket-name) key)
                                         (.setMethod (if (= verb :put)
                                                       HttpMethod/PUT
                                                       HttpMethod/GET))
                                         (.setExpiration expiration))]
    (.toString (.generatePresignedUrl (aws-s3-client) generate-presigned-url-request))))

The function generates either a URL for reading, :get, or for writing, :put, depending on the parameter verb. key is just the path part of the URL (remember, S3 is actually a key/value storage). All URLs will last for 24hs, something you can tweak as well.

And that’s it, generating the URL is done, now we have to use it.

Reading and writing from ClojureScript

If you have a form, then, you might need to deal with CORS and other issues. I suggest you look at s3-beam at least for inspiration. For me, it was just a matter of generating the requests from the ClojureScript app, which uses cljs-ajax. Reading was straightforward enough:

(ajax/GET get-url
          {:handler (fn [value]
                      (do-something value))})

although the handler might have to deal with encoding issues. Writing is similar, but again, you may need to fiddle with encoding issues. Since what I was sending is a JSON payload (an encrypted screenshot), this worked for me:

(ajax/PUT put-url 
          {:format :json
           :params payload})

If you are uploading binary data, I recommend you play with :format :raw and the :body attribute.

How to work with a private library in ClojureScript

Dashman is composed of many components  including three desktop apps written in ClojureScript using Electron that share code through a private library (as in, it’s not open source).

To have continuous integration and a set up that is easy to boostrap, that library should be deployed to a private repository. I achieved that using the plug in s3-wagon-private so that the project.clj of the common library looks like this:

:repositories [["private" {:url           "s3p://s3-bucket-for-my-project/releases/"
                           :username      "not-telling-you"
                           :passphrase    "not-telling-you-either"
                           :sign-releases false}]]

:plugins [;...
          [s3-wagon-private "1.3.0"]]

You should never have production or even staging credentials on your source code repository; but those credentials are neither. They allow to compile the project so, pretty much everybody that has access to the source code also needs access to that private repository. Having them in project.clj simplifies CI, on-boarding developers, etc.

One you have that you can deploy the library by executing:

lein deploy private

The project of the apps using the common library look similar:

:repositories [["private" {:url           "s3p://s3-bucket-for-my-project/releases/"
                           :username      "not-telling-you"
                           :passphrase    "not-telling-you-either"
                           :sign-releases false}]]
:dependencies [;...
               [tech.dashman/clientcommon "0.1.0-SNAPSHOT"]]

:plugins [;...
          [s3-wagon-private "1.3.0"]]

Yes… very similar.

The next issue is compiling and re-loading code. You could modify the common library, install it, re-compile the apps, and re-run the apps. And you could do it while wearing a mullet and watching MacGyver (the original one). This is not the 80s! You want code reloading and all the goodies.

If you are using Clojure, you should look into checkouts and maybe lein-checkout. In ClojureScript you can just add the path of the source code to your project even if it’s outside the project, with one gotcha. You might be used to a config like this:

:cljsbuild {:builds {:app {:source-paths ["src/app"]}}}

which would imply this might work:

:cljsbuild {:builds {:app {:source-paths ["src/app" "../clientcommon/src"]}}}

It might on non-Windows platforms, on Windows it doesn’t. On Windows you want this:

:cljsbuild {:builds {:app {:source-paths ["src/app" "..\\clientcommon\\src"]}}}

Since you should probably never commit that anyway, it’s not a big deal.

ClojureScript debouncer

I’m developing Dashman in ClojureScript (and Clojure) and as I was developing the cookie handler I found sites that constantly modify the cookies. As in, many times per second. Obviously I don’t want to encrypt the cookies and send them to the server that often, but even without taking that into account, just storing them to keep track of them in an atom, as in:

(reset! cookies new-cookies)

was slowing down the app considerably. I found myself needing to debounce so many calls as there’s no value in the middle calls, only the last one. Let’s backtrack a little bit here.

The term to debounce comes from electronics:

To remove the small ripple of current that forms when a mechanical switch is pushed in an electrical circuit and makes a series of short contacts.

In programming it refers to a signal that gets emitted often and you want to process it but you don’t care about processing every intermediate value. To tie into electronics imagine you have a program that receives a message every time the temperature changes and the message contains all the temperature changes for the day. Clearly each message doesn’t have to be processed, only one, only the latest.

Here’s where a software debouncer can help. It’s a function that gets a function and returns another function. When you call the resulting function 10 times, it will only call the original function once, with the last value it received.

I found this debouncer written by David Nolan: https://gist.github.com/swannodette/5888989 and in the comments you can start to see that writing a debouncer might not be a trivial as you may think. One of the comments point to this other version: https://gist.github.com/scttnlsn/9744501. The comment implies this is a more correct version but I haven’t verified it myself. In that last page you can see someone found a bug and posted an improved version.

For me, when a supposedly simple code snippet generates so many comments and versions, it’s better to find a library that implements it than to copy and paste and never maintain it again. Unfortunately, I couldn’t find a ClojureScript debouncer library but I got pointed to goog.function.debounce which you can use this way;

(ns whatever
  (:require [goog.functions]))

(def debounced-println (goog.functions.debounce println))

Free-form version 0.2.0 released

We are very happy to announce version 0.2.0 of our form building library Free-form. This version includes:

The Bootstrap 3 support means that you can have whole fields defined as succinctly as:

[:free-form/field {:type        :email
                   :key         :email
                   :label       "Email"}]]

Enjoy!

 

Prerenderer 0.2.0 released

We are proud to announce the release of version 0.2.0 of our ClojureScript library Prerenderer, a library to do server side pre-rendering of single page applications. In this release, we include:

The two first items in the changelog came hand in hand and they are the biggest changes to keep in mind if you are upgrading. We are very happy that we no longer need a fork of re-frame and we would like to extend our gratitude to Mike Thompson for working with us on having the appropriate API to make this happen.

The change in API means that your Prerenderer module now would look something like this:

(ns projectx.node
  (:require [prerenderer.core :as prerenderer]))

(defn render-and-send [page-path send-to-browser]
  (send-to-browser (render page-path)))

(set! *main-cli-fn* (prerenderer/create render-and-send "ProjectX"))

instead of:

(ns projectx.node
  (:require [cljs.nodejs :as nodejs]
            [prerenderer.core :as prerenderer]))

(defn render [req res]
  (let [page-path (.-path (.parse url (.-url (.-query req))))]
    (.send res (render page-path))))

(set! *main-cli-fn* (prerenderer/create render "ProjectX"))

Enjoy!

Review of Clojure Exchange 2015 London

I recently attended Clojure Exchange 2015 London, the conference organized by Skills Matter for Clojurians. Like many other attendees I was impressed by the quality of the talks and as a presenter, I was particularly pleased that only a few hours later my presentation, What is a Macro?, was already published, in video form, for everybody to see.

Some presentations I found particularly interesting were:

Yada for RESTfull APIs

Malcolm Sparks presenting Yada in RESTfull web service in Clojure, two different approaches. Yada is a library to create RESTfull APIs that focus on succinctness and on doing as much work for you as possible so you only focus on your business model. Yada is also async-ready and you can stream results. We will consider using it instead of Compojure-API in the future, although we still have to explore how to integrate it with other Clojure components. One limitation it has is that it can only work with Aleph, because the other web services don’t provide back pressure.

Clojurescript: Architecting for Scale

Kris Jenkins presenting his pattern in ClojureScript: Architecting for Scale. Kris shows us how he writes ClojureScript single page applications so he doesn’t end up with a spaghetti of code. The pattern is implemented as a library that he just released for the conference, called Petrol. We are happy to see how close the pattern is to our favorite one, as provided by Re-frame, that we use in Ninja Tools and we plan on using in future projects. Clearly the reactive pattern seems the way to go to write client applications beyond Hello World.

Duct, Covered with James Reeves

James Reeves presenting his aggressively simple framework for writing web applications with Clojure in Duct, Covered. You might know James as weavejester, the author of compojure, environ and so many other super popular libraries. Duct is his take on the web framework arena. It can be said to be similar to Luminus, but its emphasis is in the set up of a componentized system. Something that is easier to ignore at first and comes back to bite you later on. I had a private conversation with James after his presentation and I’m really excited about the future of Duct.

Compared to other conferences I’ve been to, it surprised me how many authors of popular open source libraries and tools we had on stage and that made me wonder how many were in the audience that I didn’t know about. I wished I had better visibility into this as I think cooperation makes for a better ecosystem.

One problem I see in the Clojure world right now is fragmentation; we are all inventing our own ways of doing things instead of compromising a bit, cooperating, and making some ways of doing things faster, better, more tested, friendlier, better documented, and so on.

Saying that, the experience was brilliant and I already bought my ticket for next year’s Clojure Exchange at the bargain price of £95.00+VAT. Do you have yours?

 

Tour of the Source Code of Ninja Tools

Notes and links

  • What is a Single Page Application? A previous screencast describing what a single page application is and why they are the future.
  • Ninja Tools: discover tools that work with your current tools, learn about better ways to use them, get alerts, etc.
  • Clojure: the programming language we are using.
  • ClojureScript: the programming language that is saving is from JavaScript.
  • Luminus: a template to get started with web applications without having to reinvent the wheel.
  • Yesql: a SQL interfacing library for Clojure.
  • Migratus: a migration library for Clojure, to modify the database schema.
  • Conman: a connection manager for Yesql.
  • React: Facebook’s library to develop JavaScript UIs.
  • Reagent: a wrapper for React to use it in ClojureScript.
  • Re-frame: a library to develop web UIs with the reactive pattern.
  • Validateur: validation library for Clojure and ClojureScript.