Search code examples
couchdbcloudant

Managing Cloudant access through _users database


I have created the _users database as well as the _security document and set the security appropriately according to https://cloudant.com/for-developers/faq/auth/.

I have then created a user "test" with role "_reader" however when I try to log in to Cloudant it does not recognize the user.

_users database contains one document:

{
  "_id": "test",
  "_rev": "1-96973497bc9c89989c4beed02e3f0b96",
  "name": "test",
  "roles": [
    "_reader"
  ],
  "type": "user",
  "password": "password"
}

I was then trying to view a design document using username "test" and password "password", as created above, however it did not work.

Within the security document of the database where the design document is located I have:

{ "couchdb_auth_only": true, "members": { "names": [ "test" ], "roles": [ "_reader" ] } }

Any suggestions?

Thanks!


Solution

  • I haven't tested this with Cloudant, and I haven't verified the security of this approach with a cryptography expert (please do this), but hopefully it may point you in the right direction:

    First you need to generate a salt and a password_sha:

        import java.security.MessageDigest;
        import java.security.NoSuchAlgorithmException;
        import java.security.SecureRandom;
        import java.util.Random;
    
        public class CouchPassword {
    
            public static void main(String[] args) throws NoSuchAlgorithmException {
    
                String password = "123456"; // your password        
                String salt = genSalt();
                password = password + salt;
    
                MessageDigest md = MessageDigest.getInstance("SHA1");
                md.update(password.getBytes());
                byte byteData[] = md.digest();
    
                System.out.println("Password SHA:   " + byteToHexString(byteData) );
                System.out.println("Generated Salt: " + salt);
            }
            public static String genSalt() {
                Random ranGen = new SecureRandom();
                byte[] salt = new byte[16];
                ranGen.nextBytes(salt);
                return byteToHexString(salt);
            }
            public static String byteToHexString(byte[] b) {    
                StringBuffer sb = new StringBuffer();
                for (int i = 0; i < b.length; i++) {
                 sb.append(Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1));
                }
                return sb.toString();
            }
        }
        // There are libraries to help with generating SHAs and Hex Strings
        // but I have choosen not to use those here so the answer is more standalone.
    

    Set the above password string to the required value, and execute. E.g.

        Password SHA:   316ccb0df3c8bbd8f568347827803682d2c93849
        Generated Salt: bba45713ef54016c8ef4b56b6b147936
    

    Create the _user and _security document (option 1):

       // _user
        {
               "_id"          : "org.couchdb.user:joe",
               "type"         : "user",
               "name"         : "joe",
               "roles"        : ["standarduser"],
               "password_sha" : "316ccb0df3c8bbd8f568347827803682d2c93849",
              "salt"         : "bba45713ef54016c8ef4b56b6b147936"
        }
    

    The roles from the _users document map to roles defined in the _security document.

    // _security
    {
       "couchdb_auth_only": true,
       "members": {
         "names": [],
         "roles": ["standarduser"]
       },
       "admins": {}
     }
    

    From the CouchDB documentation

    • database members - Defined per database. They can read all types of documents from the DB, and they can write (and edit) documents to the DB except for design documents.

    • database admins - Defined per database. They have all the privileges members have plus the privileges: write (and edit) design documents, add/remove database admins and members, set the database revisions limit (/somedb/_revs_limit API) and execute temporary views against the database (/somedb/_temp_view API). They can not create a database and neither delete a database.

    In my example, my user joe has been given the role standarduser which maps to a database member for the database.

    Create the _user and _security document (option 2):

    Note that instead of using roles, I could have specified member.names:

    // _user document
    {
      "_id"          : "org.couchdb.user:joe",
      "type"         : "user",
      "name"         : "joe",
      "password_sha" : "316ccb0df3c8bbd8f568347827803682d2c93849",
      "salt"         : "bba45713ef54016c8ef4b56b6b147936"
    }
    
    // _security document
    {
      "couchdb_auth_only": true,
      "members": {
        "names": ["joe"],
        "roles": []
      },
      "admins": {}
    }
    

    Warning

    Ensure both member.names and member.roles parameters are not empty as this will grant read and write access to everyone:

    {
      "couchdb_auth_only": true,
      "members": {
        "names": [],
        "roles": []
      },
      "admins": {}
    }
    

    Having no members, any user can write regular documents (any non-design document) and read documents from the database.

    Source: http://wiki.apache.org/couchdb/Security_Features_Overview