Created
October 31, 2016 17:26
-
-
Save tleyden/f260b2d9b2ef828fadfad462f0014aed to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"interface":":4984", | |
"log": ["HTTP", "Auth"], | |
"databases": { | |
"todo": { | |
"server": "http://couchbaseserver:8091", | |
"bucket": "todo", | |
"users": { | |
"user1": {"password": "pass"}, | |
"user2": {"password": "pass"} | |
}, | |
"sync": ` | |
function(doc, oldDoc){ | |
/* Validate */ | |
if (isCreate()) { | |
// Don't allow creating a document without a type. | |
validateNotEmpty("type", doc.type); | |
} else if (isUpdate()) { | |
// Don't allow changing the type of any document. | |
validateReadOnly("type", doc.type, oldDoc.type); | |
} | |
if (doc.type == "moderator") { | |
/* Control Write Access */ | |
// Only allow admins to add/remove moderators. | |
requireRole("admin"); | |
/* Validate */ | |
if (!isDelete()) { | |
// Validate required fields. | |
validateNotEmpty("username", doc.username); | |
if (isCreate()) { | |
// We use a key pattern to ensure unique moderators within the system, | |
// so we need to ensure that doc._id matches the pattern | |
// moderator.{username}. | |
if (doc._id != "moderator." + doc.username) { | |
throw({forbidden: "_id must match the pattern moderator.{username}."}); | |
} | |
} else { | |
// doc._id is tied to username, validated during create, and must remain this | |
// way to ensure unique moderators within the system. | |
validateReadOnly("username", doc.username, oldDoc.username); | |
} | |
} | |
/* Route */ | |
if (!isDelete()) { | |
// Add user to moderator role. | |
role(doc.username, "role:moderator"); | |
} | |
// Add doc to the user's channel. | |
channel(doc.username); | |
/* Grant Read Access */ | |
if (!isDelete()) { | |
// Grant user access to moderators channel. | |
access(doc.username, "moderators"); | |
} | |
// Grant user access to their channel. | |
access(doc.username, doc.username); | |
} else if (doc.type == "task-list") { | |
/* Control Write Access */ | |
if (isCreate()) { | |
try { | |
// Users can create/update lists for themselves. | |
requireUser(doc.owner); | |
} catch (e) { | |
// Moderators can create/update lists for other users. | |
requireRole("moderator"); | |
} | |
} | |
/* Validate */ | |
if (!isDelete()) { | |
// Validate required fields. | |
validateNotEmpty("name", doc.name); | |
validateNotEmpty("owner", doc.owner); | |
if (isCreate()) { | |
// Validate that the _id is prefixed by owner. | |
// validatePrefix("_id", doc._id, "owner", doc.owner + "."); | |
} else { | |
// Don’t allow task-list ownership to be changed. | |
validateReadOnly("owner", doc.owner, oldDoc.owner); | |
} | |
} | |
/* Route */ | |
// Add doc to task-list's channel. | |
channel("task-list." + doc._id); | |
channel("moderators"); | |
/* Grant Read Access */ | |
// Grant task-list owner access to the task-list, its tasks, and its users. | |
access(doc.owner, "task-list." + doc._id); | |
access(doc.owner, "task-list." + doc._id + ".users"); | |
} else if (doc.type == "task") { | |
/* Write Access */ | |
try { | |
requireAccess("task-list." + doc.taskList.id); | |
} catch (e) { | |
requireUser(doc.taskList.owner); | |
} | |
/* Validate */ | |
if (!isDelete()) { | |
// Validate required fields. | |
validateNotEmpty("taskList.id", doc.taskList.id); | |
validateNotEmpty("taskList.owner", doc.taskList.owner); | |
validateNotEmpty("task", doc.task); | |
if (isCreate()) { | |
// Validate that the taskList.id is prefixed by taskList.owner. We only need to | |
// validate this during create because these fields are read-only after create. | |
// validatePrefix("taskList.id", doc.taskList.id, "taskList.owner", doc.taskList.owner + "."); | |
} else { | |
// Don’t allow tasks to be moved to another task-list. | |
validateReadOnly("taskList.id", doc.taskList.id, oldDoc.taskList.id); | |
validateReadOnly("taskList.owner", doc.taskList.owner, oldDoc.taskList.owner); | |
} | |
} | |
/* Route */ | |
// Add doc to task-list and moderators channel. | |
channel("task-list." + doc.taskList.id); | |
channel("moderators"); | |
} else if (doc.type == "task-list.user") { | |
/* Control Write Access */ | |
try { | |
requireUser(doc.taskList.owner); | |
} catch (e) { | |
requireRole("moderator"); | |
} | |
/* Validate */ | |
if (!isDelete()) { | |
// Validate required fields. | |
validateNotEmpty("taskList.id", doc.taskList.id); | |
validateNotEmpty("taskList.owner", doc.taskList.owner); | |
validateNotEmpty("username", doc.username); | |
if (isCreate()) { | |
// We use a key pattern to ensure unique users w/in a list, so we need to | |
// ensure that doc._id matches the pattern {taskList.id}.{username}. | |
if (doc._id != doc.taskList.id + "." + doc.username) { | |
throw({forbidden: "_id must match the pattern {taskList.id}.{username}."}); | |
} | |
// Validate that the taskList.id is prefixed by taskList.owner. | |
// validatePrefix("taskList.id", doc.taskList.id, "taskList.owner", doc.taskList.owner + "."); | |
} else { | |
// Don’t allow users to be moved to another task-list. Also, doc._id is tied to | |
// these values, validated during create, and must remain this way to ensure | |
// uniqueness within a list. | |
validateReadOnly("taskList.id", doc.taskList.id, oldDoc.taskList.id); | |
validateReadOnly("taskList.owner", doc.taskList.owner, oldDoc.taskList.owner); | |
} | |
} | |
/* Route */ | |
// Add doc to task-list users and moderators channel. | |
channel("task-list." + doc.taskList.id + ".users"); | |
channel("moderators"); | |
/* Grant Read Access */ | |
// Grant the user access to the task-list and its tasks. | |
access(doc.username, "task-list." + doc.taskList.id); | |
} else { | |
// Log invalid document type error. | |
log("Invalid document type: " + doc.type); | |
throw({forbidden: "Invalid document type: " + doc.type}); | |
} | |
function isCreate() { | |
return (oldDoc == null && doc._deleted != true); | |
} | |
function isUpdate() { | |
return (!isCreate(oldDoc) && !isDelete(doc)); | |
} | |
function isDelete() { | |
return (doc._deleted == true); | |
} | |
function validateNotEmpty(key, value) { | |
if (!value) { | |
throw({forbidden: key + " is not provided."}); | |
} | |
} | |
function validateReadOnly(name, value, oldValue) { | |
if (value != oldValue) { | |
throw({forbidden: name + " is read-only."}); | |
} | |
} | |
} | |
` | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment