Jaunder

#haskell

Posts by ~mdorman
M
Michael Alan Dorman@mdorman

Using user authentication with CouchDB and couchdb-conduit

Handling user-level securiy with couchdb-conduit

I've started getting back into working on my Sekrit Haskell Application, for which I plan on using a CouchDB back-end.

This left me with a choice between couchdb-conduit and CoucbDB. I went with couchdb-conduit for a couple of reasons.

A large part of it was simply that conduit is a well-supported, fairly well-documented base for building this sort of higher-level library. The other part was that the older CouchDB library was using the json library, and I wanted to use aeson.

(I freely concede that some of this is about libraries that are mindshare-winners, rather than necessarily the best from a technical standpoint; I recognize that pipes may have a better theoretical foundation than counduit, and I don't have a clear idea which is more sophisticated between json and aeson.)

Anyway, I found myself having to contribute a couple of fixes to couchdb-conduit to bring it up to date with the 1.0 release of conduit. And then yesterday, I noticed that it wasn't setting up per-user authentication properly. But at this point, I believe that things are set up to work.

So, just for reference, here's the code I wrote to actually do something. I can create a database, named for the user, with authentication open to just that user, with the passed in password:

data UserCredentials = UserCredentials {
    credentialEmail :: ByteString, -- ^The email address of the new user
    credentialPassword :: ByteString  -- ^The password for the new user
} deriving (Show)

connection :: CouchConnection
connection = def {couchLogin = "administrator", couchPass = "ThisIsn'tReallyThePassword"}

userDb :: ByteString -> ByteString
userDb = (intercalate "/") . reverse . splitWith (`elem` "@.")

authId :: ByteString -> ByteString
authId email = concat ["org.couchdb.user:", email]

authRecord :: AntilibrationCredentials -> Value
authRecord (AntilibrationCredentials email password) = object ["name" .= email, "roles" .= ([] :: [ByteString]), "type" .= ("user" :: ByteString), "password" .= password]

createUserDB :: UserCredentials -> IO ()
createUserDB credentials =
  runCouch connection $ do
    _ <- couchPut "_users" (authId $ credentialEmail credentials) "" [] (authRecord credentials)
    couchPutDB_ (userDb $ credentialEmail credentials)
    couchSecureDB (userDb $ credentialEmail credentials) [] [] [] [(credentialEmail credentials)]

It's taken me a while to feel comfortable enough with Haskell to get to where I could write this code, but now that it's done, I'm impressed with how straightforward it ends up being.

M
Michael Alan Dorman@mdorman

Choosing a new language

I have been programming primarily--for long stretches, almost exclusively--in Perl for the last 17 years or so. I seem to remember starting to use it around mid-1995, with 5.001--during that long, awkward time between when Perl 5 came out and when the 2nd edition of Programming Perl finally arrived in late 1996.

I've kept with it because I'm fluent in it, I am productive in it, and at this point, I can make it do some fairly absurd things (ask me about writing event-driven servers in Perl, I dare you). In fact, I like the language. I understand the complaints people have about it, but the subset in which I write these days is pretty clear while remaining concise and expressive, and the ecosystem that exists around it is simply unparalleled.

Nonetheless, I think the time has come to move on. The downsides of the language--speed, largely, and lack of good language support for expressing things like parallelism--have started to wear at me. I'm tired of the hoops I have to jump through to do the things I want to do.

So for the last 18 months or so, I've been reading a lot about a number of languages. I don't think I've rejected any out of hand except PHP, though I certainly have some biases. For instance, I am looking for a mainstream language--something like IO, though interesting, does not qualify.

But mainstream isn't everything--I want something that is going to open up new options, that's going to be fun to get immersed in; so I'm not considering things like Ruby or Python because for the most part I think they recapitulate most of the problems I have with Perl (speed, concurrency support) just with different syntax.

In the end, I came down to three options. Node.js, Scala and Haskell. I find that as I've been sitting with the question for the last couple of weeks, though, I've stopped thinking about Node.js as a real option. Though it's fast, and it's got a great ecosystem of software surrounding it, raw event-driven programming doesn't really engage me any more. It was fun for the first year or two I did it, but the idea of moving to an environment where Everything Is A Callback leaves me cold.

So it's down to Scala and Haskell, I think.

As a consequence, I've spent the last week reading Programming in Scala: A Comprehensive Step-by-Step Guide, 2nd Edition by Odersky, Spoon and Venners, and before that I got most of the way through Learn You a Haskell for Great Good by Miran Lipovaca (though I'm going to go back through it now and finish it).

I intend, over the next couple of weeks, to post about my experiences working on using each to write a couple of short (but non-trivial) programs with both of them--ones that, incidentally, I have implemented in Perl already, so I can do a real comparison of code.