Skip to content

Instantly share code, notes, and snippets.

@tleyden
Created October 31, 2016 17:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tleyden/f260b2d9b2ef828fadfad462f0014aed to your computer and use it in GitHub Desktop.
Save tleyden/f260b2d9b2ef828fadfad462f0014aed to your computer and use it in GitHub Desktop.
{
"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