Skip to content

Instantly share code, notes, and snippets.

@isaacs
Last active August 29, 2015 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save isaacs/8926985 to your computer and use it in GitHub Desktop.
Save isaacs/8926985 to your computer and use it in GitHub Desktop.
{
"_id": "_design/_auth",
"language": "javascript",
"lists": {
"index": "function (head,req) {\n var row\n , out = {}\n , id, data\n while (row = getRow()) {\n id = row.id.replace(/^org\\.couchdb\\.user:/, '')\n data = row.value\n delete data._id\n delete data._rev\n delete data.salt\n delete data.password_sha\n delete data.type\n delete data.roles\n delete data._deleted_conflicts\n out[id] = data\n }\n send(toJSON(out))\n }",
"email": "function (head, req) {\n var row\n , data\n , id\n , email = req.query.email || undefined\n , out = []\n while (row = getRow()) {\n id = row.id.replace(/^org\\.couchdb\\.user:/, '')\n data = row.value\n var dm = data.email || undefined\n if (data.email !== email) continue\n out.push(row.value.name)\n }\n send(toJSON(out))\n }"
},
"validate_doc_update": "function (newDoc, oldDoc, userCtx, secObj) {\n if (newDoc._deleted === true) {\n // allow deletes by admins\n if ((userCtx.roles.indexOf('_admin') !== -1)) {\n return;\n } else {\n throw({forbidden: 'Only admins may delete user docs.'});\n }\n }\n\n if ((oldDoc && oldDoc.type !== 'user') || newDoc.type !== 'user') {\n throw({forbidden : 'doc.type must be user'});\n } // we only allow user docs for now\n\n if (!newDoc.name) {\n throw({forbidden: 'doc.name is required'});\n }\n\n if (newDoc.roles && !isArray(newDoc.roles)) {\n throw({forbidden: 'doc.roles must be an array'});\n }\n\n if (newDoc._id !== ('org.couchdb.user:' + newDoc.name)) {\n throw({\n forbidden: 'Doc ID must be of the form org.couchdb.user:name'\n });\n }\n\n if (newDoc.name !== newDoc.name.toLowerCase()) {\n throw({\n forbidden: 'Name must be lower-case'\n })\n }\n\n if (newDoc.name !== encodeURIComponent(newDoc.name)) {\n throw({\n forbidden: 'Name cannot contain non-url-safe characters'\n })\n }\n\n if (newDoc.name.charAt(0) === '.') {\n throw({\n forbidden: 'Name cannot start with .'\n })\n }\n\n if (!(newDoc.email && newDoc.email.match(/^.+@.+\\..+$/))) {\n throw({forbidden: 'Email must be an email address'})\n }\n\n if (oldDoc) { // validate all updates\n if (oldDoc.name !== newDoc.name) {\n throw({forbidden: 'Usernames can not be changed.'});\n }\n }\n\n if (newDoc.password_sha && !newDoc.salt) {\n throw({\n forbidden: 'Users with password_sha must have a salt.'\n });\n }\n\n var is_server_or_database_admin = function(userCtx, secObj) {\n // see if the user is a server admin\n if(userCtx.roles.indexOf('_admin') !== -1) {\n return true; // a server admin\n }\n\n // see if the user a database admin specified by name\n if(secObj && secObj.admins && secObj.admins.names) {\n if(secObj.admins.names.indexOf(userCtx.name) !== -1) {\n return true; // database admin\n }\n }\n\n // see if the user a database admin specified by role\n if(secObj && secObj.admins && secObj.admins.roles) {\n var db_roles = secObj.admins.roles;\n for(var idx = 0; idx < userCtx.roles.length; idx++) {\n var user_role = userCtx.roles[idx];\n if(db_roles.indexOf(user_role) !== -1) {\n return true; // role matches!\n }\n }\n }\n\n return false; // default to no admin\n }\n\n if (newDoc.name.length > 50) {\n throw({\n forbidden: 'Username is too long. Pick a shorter one.'\n })\n }\n\n if (!is_server_or_database_admin(userCtx, secObj)) {\n if (oldDoc) { // validate non-admin updates\n if (userCtx.name !== newDoc.name) {\n throw({\n forbidden: 'You may only update your own user document.'\n });\n }\n if (oldDoc.email !== newDoc.email) {\n throw({\n forbidden: 'You may not change your email address\\n' +\n 'Please visit https://npmjs.org/email-edit to do so.'\n })\n }\n // validate role updates\n var oldRoles = oldDoc.roles.sort();\n var newRoles = newDoc.roles.sort();\n\n if (oldRoles.length !== newRoles.length) {\n throw({forbidden: 'Only _admin may edit roles'});\n }\n\n for (var i = 0; i < oldRoles.length; i++) {\n if (oldRoles[i] !== newRoles[i]) {\n throw({forbidden: 'Only _admin may edit roles'});\n }\n }\n } else if (newDoc.roles.length > 0) {\n throw({forbidden: 'Only _admin may set roles'});\n }\n }\n\n // no system roles in users db\n for (var i = 0; i < newDoc.roles.length; i++) {\n if (newDoc.roles[i][0] === '_') {\n throw({\n forbidden: 'No system roles (starting with underscore) in users db.'\n });\n }\n }\n\n // no system names as names\n if (newDoc.name[0] === '_') {\n throw({forbidden: 'Username may not start with underscore.'});\n }\n\n var badUserNameChars = [':'];\n\n for (var i = 0; i < badUserNameChars.length; i++) {\n if (newDoc.name.indexOf(badUserNameChars[i]) >= 0) {\n throw({forbidden: 'Character `' + badUserNameChars[i] +\n '` is not allowed in usernames.'});\n }\n }\n}",
"views": {
"listAll": {
"map": "function (doc) { return emit(doc._id, doc) }"
},
"invalidUser": {
"map": "function (doc) {\n var errors = []\n if (doc.type !== 'user') {\n errors.push('doc.type must be user')\n }\n\n if (!doc.name) {\n errors.push('doc.name is required')\n }\n\n if (doc.roles && !isArray(doc.roles)) {\n errors.push('doc.roles must be an array')\n }\n\n if (doc._id !== ('org.couchdb.user:' + doc.name)) {\n errors.push('Doc ID must be of the form org.couchdb.user:name')\n }\n\n if (doc.name !== doc.name.toLowerCase()) {\n errors.push('Name must be lower-case')\n }\n\n if (doc.name !== encodeURIComponent(doc.name)) {\n errors.push('Name cannot contain non-url-safe characters')\n }\n\n if (doc.name.charAt(0) === '.') {\n errors.push('Name cannot start with .')\n }\n\n if (!(doc.email && doc.email.match(/^.+@.+\\..+$/))) {\n errors.push('Email must be an email address')\n }\n\n if (doc.password_sha && !doc.salt) {\n errors.push('Users with password_sha must have a salt.')\n }\n if (!errors.length) return\n emit([doc.name, doc.email], errors)\n }"
},
"invalid": {
"map": "function (doc) {\n if (doc.type !== 'user') {\n return emit(['doc.type must be user', doc.email, doc.name], 1)\n }\n\n if (!doc.name) {\n return emit(['doc.name is required', doc.email, doc.name], 1)\n }\n\n if (doc.roles && !isArray(doc.roles)) {\n return emit(['doc.roles must be an array', doc.email, doc.name], 1)\n }\n\n if (doc._id !== ('org.couchdb.user:' + doc.name)) {\n return emit(['Doc ID must be of the form org.couchdb.user:name', doc.email, doc.name], 1)\n }\n\n if (doc.name !== doc.name.toLowerCase()) {\n return emit(['Name must be lower-case', doc.email, doc.name], 1)\n }\n\n if (doc.name !== encodeURIComponent(doc.name)) {\n return emit(['Name cannot contain non-url-safe characters', doc.email, doc.name], 1)\n }\n\n if (doc.name.charAt(0) === '.') {\n return emit(['Name cannot start with .', doc.email, doc.name], 1)\n }\n\n if (!(doc.email && doc.email.match(/^.+@.+\\..+$/))) {\n return emit(['Email must be an email address', doc.email, doc.name], 1)\n }\n\n if (doc.password_sha && !doc.salt) {\n return emit(['Users with password_sha must have a salt.', doc.email, doc.name], 1)\n }\n }",
"reduce": "_sum"
},
"conflicts": {
"map": "function (doc) {\n if (doc._conflicts) {\n for (var i = 0; i < doc._conflicts.length; i++) {\n emit([doc._id, doc._conflicts[i]], 1)\n }\n }\n}",
"reduce": "_sum"
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment