Jaunder

#haskell

Posts on this instance
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.