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.