UNPKG

pouchdb-security

Version:

PouchDB database access restrictions using a security document.

1,730 lines (1,487 loc) 61.9 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Security = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ },{}],2:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. var objectCreate = Object.create || objectCreatePolyfill var objectKeys = Object.keys || objectKeysPolyfill var bind = Function.prototype.bind || functionBindPolyfill function EventEmitter() { if (!this._events || !Object.prototype.hasOwnProperty.call(this, '_events')) { this._events = objectCreate(null); this._eventsCount = 0; } this._maxListeners = this._maxListeners || undefined; } module.exports = EventEmitter; // Backwards-compat with node 0.10.x EventEmitter.EventEmitter = EventEmitter; EventEmitter.prototype._events = undefined; EventEmitter.prototype._maxListeners = undefined; // By default EventEmitters will print a warning if more than 10 listeners are // added to it. This is a useful default which helps finding memory leaks. var defaultMaxListeners = 10; var hasDefineProperty; try { var o = {}; if (Object.defineProperty) Object.defineProperty(o, 'x', { value: 0 }); hasDefineProperty = o.x === 0; } catch (err) { hasDefineProperty = false } if (hasDefineProperty) { Object.defineProperty(EventEmitter, 'defaultMaxListeners', { enumerable: true, get: function() { return defaultMaxListeners; }, set: function(arg) { // check whether the input is a positive number (whose value is zero or // greater and not a NaN). if (typeof arg !== 'number' || arg < 0 || arg !== arg) throw new TypeError('"defaultMaxListeners" must be a positive number'); defaultMaxListeners = arg; } }); } else { EventEmitter.defaultMaxListeners = defaultMaxListeners; } // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { if (typeof n !== 'number' || n < 0 || isNaN(n)) throw new TypeError('"n" argument must be a positive number'); this._maxListeners = n; return this; }; function $getMaxListeners(that) { if (that._maxListeners === undefined) return EventEmitter.defaultMaxListeners; return that._maxListeners; } EventEmitter.prototype.getMaxListeners = function getMaxListeners() { return $getMaxListeners(this); }; // These standalone emit* functions are used to optimize calling of event // handlers for fast cases because emit() itself often has a variable number of // arguments and can be deoptimized because of that. These functions always have // the same number of arguments and thus do not get deoptimized, so the code // inside them can execute faster. function emitNone(handler, isFn, self) { if (isFn) handler.call(self); else { var len = handler.length; var listeners = arrayClone(handler, len); for (var i = 0; i < len; ++i) listeners[i].call(self); } } function emitOne(handler, isFn, self, arg1) { if (isFn) handler.call(self, arg1); else { var len = handler.length; var listeners = arrayClone(handler, len); for (var i = 0; i < len; ++i) listeners[i].call(self, arg1); } } function emitTwo(handler, isFn, self, arg1, arg2) { if (isFn) handler.call(self, arg1, arg2); else { var len = handler.length; var listeners = arrayClone(handler, len); for (var i = 0; i < len; ++i) listeners[i].call(self, arg1, arg2); } } function emitThree(handler, isFn, self, arg1, arg2, arg3) { if (isFn) handler.call(self, arg1, arg2, arg3); else { var len = handler.length; var listeners = arrayClone(handler, len); for (var i = 0; i < len; ++i) listeners[i].call(self, arg1, arg2, arg3); } } function emitMany(handler, isFn, self, args) { if (isFn) handler.apply(self, args); else { var len = handler.length; var listeners = arrayClone(handler, len); for (var i = 0; i < len; ++i) listeners[i].apply(self, args); } } EventEmitter.prototype.emit = function emit(type) { var er, handler, len, args, i, events; var doError = (type === 'error'); events = this._events; if (events) doError = (doError && events.error == null); else if (!doError) return false; // If there is no 'error' event listener then throw. if (doError) { if (arguments.length > 1) er = arguments[1]; if (er instanceof Error) { throw er; // Unhandled 'error' event } else { // At least give some kind of context to the user var err = new Error('Unhandled "error" event. (' + er + ')'); err.context = er; throw err; } return false; } handler = events[type]; if (!handler) return false; var isFn = typeof handler === 'function'; len = arguments.length; switch (len) { // fast cases case 1: emitNone(handler, isFn, this); break; case 2: emitOne(handler, isFn, this, arguments[1]); break; case 3: emitTwo(handler, isFn, this, arguments[1], arguments[2]); break; case 4: emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]); break; // slower default: args = new Array(len - 1); for (i = 1; i < len; i++) args[i - 1] = arguments[i]; emitMany(handler, isFn, this, args); } return true; }; function _addListener(target, type, listener, prepend) { var m; var events; var existing; if (typeof listener !== 'function') throw new TypeError('"listener" argument must be a function'); events = target._events; if (!events) { events = target._events = objectCreate(null); target._eventsCount = 0; } else { // To avoid recursion in the case that type === "newListener"! Before // adding it to the listeners, first emit "newListener". if (events.newListener) { target.emit('newListener', type, listener.listener ? listener.listener : listener); // Re-assign `events` because a newListener handler could have caused the // this._events to be assigned to a new object events = target._events; } existing = events[type]; } if (!existing) { // Optimize the case of one listener. Don't need the extra array object. existing = events[type] = listener; ++target._eventsCount; } else { if (typeof existing === 'function') { // Adding the second element, need to change to array. existing = events[type] = prepend ? [listener, existing] : [existing, listener]; } else { // If we've already got an array, just append. if (prepend) { existing.unshift(listener); } else { existing.push(listener); } } // Check for listener leak if (!existing.warned) { m = $getMaxListeners(target); if (m && m > 0 && existing.length > m) { existing.warned = true; var w = new Error('Possible EventEmitter memory leak detected. ' + existing.length + ' "' + String(type) + '" listeners ' + 'added. Use emitter.setMaxListeners() to ' + 'increase limit.'); w.name = 'MaxListenersExceededWarning'; w.emitter = target; w.type = type; w.count = existing.length; if (typeof console === 'object' && console.warn) { console.warn('%s: %s', w.name, w.message); } } } } return target; } EventEmitter.prototype.addListener = function addListener(type, listener) { return _addListener(this, type, listener, false); }; EventEmitter.prototype.on = EventEmitter.prototype.addListener; EventEmitter.prototype.prependListener = function prependListener(type, listener) { return _addListener(this, type, listener, true); }; function onceWrapper() { if (!this.fired) { this.target.removeListener(this.type, this.wrapFn); this.fired = true; switch (arguments.length) { case 0: return this.listener.call(this.target); case 1: return this.listener.call(this.target, arguments[0]); case 2: return this.listener.call(this.target, arguments[0], arguments[1]); case 3: return this.listener.call(this.target, arguments[0], arguments[1], arguments[2]); default: var args = new Array(arguments.length); for (var i = 0; i < args.length; ++i) args[i] = arguments[i]; this.listener.apply(this.target, args); } } } function _onceWrap(target, type, listener) { var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; var wrapped = bind.call(onceWrapper, state); wrapped.listener = listener; state.wrapFn = wrapped; return wrapped; } EventEmitter.prototype.once = function once(type, listener) { if (typeof listener !== 'function') throw new TypeError('"listener" argument must be a function'); this.on(type, _onceWrap(this, type, listener)); return this; }; EventEmitter.prototype.prependOnceListener = function prependOnceListener(type, listener) { if (typeof listener !== 'function') throw new TypeError('"listener" argument must be a function'); this.prependListener(type, _onceWrap(this, type, listener)); return this; }; // Emits a 'removeListener' event if and only if the listener was removed. EventEmitter.prototype.removeListener = function removeListener(type, listener) { var list, events, position, i, originalListener; if (typeof listener !== 'function') throw new TypeError('"listener" argument must be a function'); events = this._events; if (!events) return this; list = events[type]; if (!list) return this; if (list === listener || list.listener === listener) { if (--this._eventsCount === 0) this._events = objectCreate(null); else { delete events[type]; if (events.removeListener) this.emit('removeListener', type, list.listener || listener); } } else if (typeof list !== 'function') { position = -1; for (i = list.length - 1; i >= 0; i--) { if (list[i] === listener || list[i].listener === listener) { originalListener = list[i].listener; position = i; break; } } if (position < 0) return this; if (position === 0) list.shift(); else spliceOne(list, position); if (list.length === 1) events[type] = list[0]; if (events.removeListener) this.emit('removeListener', type, originalListener || listener); } return this; }; EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) { var listeners, events, i; events = this._events; if (!events) return this; // not listening for removeListener, no need to emit if (!events.removeListener) { if (arguments.length === 0) { this._events = objectCreate(null); this._eventsCount = 0; } else if (events[type]) { if (--this._eventsCount === 0) this._events = objectCreate(null); else delete events[type]; } return this; } // emit removeListener for all listeners on all events if (arguments.length === 0) { var keys = objectKeys(events); var key; for (i = 0; i < keys.length; ++i) { key = keys[i]; if (key === 'removeListener') continue; this.removeAllListeners(key); } this.removeAllListeners('removeListener'); this._events = objectCreate(null); this._eventsCount = 0; return this; } listeners = events[type]; if (typeof listeners === 'function') { this.removeListener(type, listeners); } else if (listeners) { // LIFO order for (i = listeners.length - 1; i >= 0; i--) { this.removeListener(type, listeners[i]); } } return this; }; function _listeners(target, type, unwrap) { var events = target._events; if (!events) return []; var evlistener = events[type]; if (!evlistener) return []; if (typeof evlistener === 'function') return unwrap ? [evlistener.listener || evlistener] : [evlistener]; return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); } EventEmitter.prototype.listeners = function listeners(type) { return _listeners(this, type, true); }; EventEmitter.prototype.rawListeners = function rawListeners(type) { return _listeners(this, type, false); }; EventEmitter.listenerCount = function(emitter, type) { if (typeof emitter.listenerCount === 'function') { return emitter.listenerCount(type); } else { return listenerCount.call(emitter, type); } }; EventEmitter.prototype.listenerCount = listenerCount; function listenerCount(type) { var events = this._events; if (events) { var evlistener = events[type]; if (typeof evlistener === 'function') { return 1; } else if (evlistener) { return evlistener.length; } } return 0; } EventEmitter.prototype.eventNames = function eventNames() { return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : []; }; // About 1.5x faster than the two-arg version of Array#splice(). function spliceOne(list, index) { for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1) list[i] = list[k]; list.pop(); } function arrayClone(arr, n) { var copy = new Array(n); for (var i = 0; i < n; ++i) copy[i] = arr[i]; return copy; } function unwrapListeners(arr) { var ret = new Array(arr.length); for (var i = 0; i < ret.length; ++i) { ret[i] = arr[i].listener || arr[i]; } return ret; } function objectCreatePolyfill(proto) { var F = function() {}; F.prototype = proto; return new F; } function objectKeysPolyfill(obj) { var keys = []; for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) { keys.push(k); } return k; } function functionBindPolyfill(context) { var fn = this; return function () { return fn.apply(context, arguments); }; } },{}],3:[function(require,module,exports){ 'use strict'; var hasOwn = Object.prototype.hasOwnProperty; var toStr = Object.prototype.toString; var isArray = function isArray(arr) { if (typeof Array.isArray === 'function') { return Array.isArray(arr); } return toStr.call(arr) === '[object Array]'; }; var isPlainObject = function isPlainObject(obj) { if (!obj || toStr.call(obj) !== '[object Object]') { return false; } var hasOwnConstructor = hasOwn.call(obj, 'constructor'); var hasIsPrototypeOf = obj.constructor && obj.constructor.prototype && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf'); // Not own constructor property must be Object if (obj.constructor && !hasOwnConstructor && !hasIsPrototypeOf) { return false; } // Own properties are enumerated firstly, so to speed up, // if last one is own, then all properties are own. var key; for (key in obj) { /**/ } return typeof key === 'undefined' || hasOwn.call(obj, key); }; module.exports = function extend() { var options, name, src, copy, copyIsArray, clone; var target = arguments[0]; var i = 1; var length = arguments.length; var deep = false; // Handle a deep copy situation if (typeof target === 'boolean') { deep = target; target = arguments[1] || {}; // skip the boolean and the target i = 2; } if (target == null || (typeof target !== 'object' && typeof target !== 'function')) { target = {}; } for (; i < length; ++i) { options = arguments[i]; // Only deal with non-null/undefined values if (options != null) { // Extend the base object for (name in options) { src = target[name]; copy = options[name]; // Prevent never-ending loop if (target !== copy) { // Recurse if we're merging plain objects or arrays if (deep && copy && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) { if (copyIsArray) { copyIsArray = false; clone = src && isArray(src) ? src : []; } else { clone = src && isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[name] = extend(deep, clone, copy); // Don't bring in undefined values } else if (typeof copy !== 'undefined') { target[name] = copy; } } } } } // Return the modified object return target; }; },{}],4:[function(require,module,exports){ /* Copyright 2014, Marten de Vries Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ "use strict"; module.exports = function (header) { //the exceptions var result = { "content-md5": "Content-MD5", "dnt": "DNT", "etag": "ETag", "last-event-id": "Last-Event-ID", "tcn": "TCN", "te": "TE", "www-authenticate": "WWW-Authenticate", "x-dnsprefetch-control": "X-DNSPrefetch-Control" }[header.toLowerCase()]; if (result) { return result; } //the default return header .split("-") .map(function (text) { return text.charAt(0).toUpperCase() + text.substr(1).toLowerCase(); }) .join("-"); }; },{}],5:[function(require,module,exports){ 'use strict'; function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var lie = _interopDefault(require('lie')); /* istanbul ignore next */ var PouchPromise = typeof Promise === 'function' ? Promise : lie; module.exports = PouchPromise; },{"lie":7}],6:[function(require,module,exports){ (function (global){ 'use strict'; var Mutation = global.MutationObserver || global.WebKitMutationObserver; var scheduleDrain; { if (Mutation) { var called = 0; var observer = new Mutation(nextTick); var element = global.document.createTextNode(''); observer.observe(element, { characterData: true }); scheduleDrain = function () { element.data = (called = ++called % 2); }; } else if (!global.setImmediate && typeof global.MessageChannel !== 'undefined') { var channel = new global.MessageChannel(); channel.port1.onmessage = nextTick; scheduleDrain = function () { channel.port2.postMessage(0); }; } else if ('document' in global && 'onreadystatechange' in global.document.createElement('script')) { scheduleDrain = function () { // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called. var scriptEl = global.document.createElement('script'); scriptEl.onreadystatechange = function () { nextTick(); scriptEl.onreadystatechange = null; scriptEl.parentNode.removeChild(scriptEl); scriptEl = null; }; global.document.documentElement.appendChild(scriptEl); }; } else { scheduleDrain = function () { setTimeout(nextTick, 0); }; } } var draining; var queue = []; //named nextTick for less confusing stack traces function nextTick() { draining = true; var i, oldQueue; var len = queue.length; while (len) { oldQueue = queue; queue = []; i = -1; while (++i < len) { oldQueue[i](); } len = queue.length; } draining = false; } module.exports = immediate; function immediate(task) { if (queue.push(task) === 1 && !draining) { scheduleDrain(); } } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],7:[function(require,module,exports){ 'use strict'; var immediate = require('immediate'); /* istanbul ignore next */ function INTERNAL() {} var handlers = {}; var REJECTED = ['REJECTED']; var FULFILLED = ['FULFILLED']; var PENDING = ['PENDING']; module.exports = Promise; function Promise(resolver) { if (typeof resolver !== 'function') { throw new TypeError('resolver must be a function'); } this.state = PENDING; this.queue = []; this.outcome = void 0; if (resolver !== INTERNAL) { safelyResolveThenable(this, resolver); } } Promise.prototype["catch"] = function (onRejected) { return this.then(null, onRejected); }; Promise.prototype.then = function (onFulfilled, onRejected) { if (typeof onFulfilled !== 'function' && this.state === FULFILLED || typeof onRejected !== 'function' && this.state === REJECTED) { return this; } var promise = new this.constructor(INTERNAL); if (this.state !== PENDING) { var resolver = this.state === FULFILLED ? onFulfilled : onRejected; unwrap(promise, resolver, this.outcome); } else { this.queue.push(new QueueItem(promise, onFulfilled, onRejected)); } return promise; }; function QueueItem(promise, onFulfilled, onRejected) { this.promise = promise; if (typeof onFulfilled === 'function') { this.onFulfilled = onFulfilled; this.callFulfilled = this.otherCallFulfilled; } if (typeof onRejected === 'function') { this.onRejected = onRejected; this.callRejected = this.otherCallRejected; } } QueueItem.prototype.callFulfilled = function (value) { handlers.resolve(this.promise, value); }; QueueItem.prototype.otherCallFulfilled = function (value) { unwrap(this.promise, this.onFulfilled, value); }; QueueItem.prototype.callRejected = function (value) { handlers.reject(this.promise, value); }; QueueItem.prototype.otherCallRejected = function (value) { unwrap(this.promise, this.onRejected, value); }; function unwrap(promise, func, value) { immediate(function () { var returnValue; try { returnValue = func(value); } catch (e) { return handlers.reject(promise, e); } if (returnValue === promise) { handlers.reject(promise, new TypeError('Cannot resolve promise with itself')); } else { handlers.resolve(promise, returnValue); } }); } handlers.resolve = function (self, value) { var result = tryCatch(getThen, value); if (result.status === 'error') { return handlers.reject(self, result.value); } var thenable = result.value; if (thenable) { safelyResolveThenable(self, thenable); } else { self.state = FULFILLED; self.outcome = value; var i = -1; var len = self.queue.length; while (++i < len) { self.queue[i].callFulfilled(value); } } return self; }; handlers.reject = function (self, error) { self.state = REJECTED; self.outcome = error; var i = -1; var len = self.queue.length; while (++i < len) { self.queue[i].callRejected(error); } return self; }; function getThen(obj) { // Make sure we only access the accessor once as required by the spec var then = obj && obj.then; if (obj && (typeof obj === 'object' || typeof obj === 'function') && typeof then === 'function') { return function appyThen() { then.apply(obj, arguments); }; } } function safelyResolveThenable(self, thenable) { // Either fulfill, reject or reject with error var called = false; function onError(value) { if (called) { return; } called = true; handlers.reject(self, value); } function onSuccess(value) { if (called) { return; } called = true; handlers.resolve(self, value); } function tryToUnwrap() { thenable(onSuccess, onError); } var result = tryCatch(tryToUnwrap); if (result.status === 'error') { onError(result.value); } } function tryCatch(func, value) { var out = {}; try { out.value = func(value); out.status = 'success'; } catch (e) { out.status = 'error'; out.value = e; } return out; } Promise.resolve = resolve; function resolve(value) { if (value instanceof this) { return value; } return handlers.resolve(new this(INTERNAL), value); } Promise.reject = reject; function reject(reason) { var promise = new this(INTERNAL); return handlers.reject(promise, reason); } Promise.all = all; function all(iterable) { var self = this; if (Object.prototype.toString.call(iterable) !== '[object Array]') { return this.reject(new TypeError('must be an array')); } var len = iterable.length; var called = false; if (!len) { return this.resolve([]); } var values = new Array(len); var resolved = 0; var i = -1; var promise = new this(INTERNAL); while (++i < len) { allResolver(iterable[i], i); } return promise; function allResolver(value, i) { self.resolve(value).then(resolveFromAll, function (error) { if (!called) { called = true; handlers.reject(promise, error); } }); function resolveFromAll(outValue) { values[i] = outValue; if (++resolved === len && !called) { called = true; handlers.resolve(promise, values); } } } } Promise.race = race; function race(iterable) { var self = this; if (Object.prototype.toString.call(iterable) !== '[object Array]') { return this.reject(new TypeError('must be an array')); } var len = iterable.length; var called = false; if (!len) { return this.resolve([]); } var i = -1; var promise = new this(INTERNAL); while (++i < len) { resolver(iterable[i]); } return promise; function resolver(value) { self.resolve(value).then(function (response) { if (!called) { called = true; handlers.resolve(promise, response); } }, function (error) { if (!called) { called = true; handlers.reject(promise, error); } }); } } },{"immediate":6}],8:[function(require,module,exports){ /* Copyright 2013-2014, Marten de Vries Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ "use strict"; module.exports = function nodify(promise, callback) { if (typeof callback === "function") { promise.then(function (resp) { callback(null, resp); }, function (err) { callback(err, null); }); } }; },{}],9:[function(require,module,exports){ /* Copyright 2014, Marten de Vries Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ "use strict"; var Promise = require("pouchdb-promise"); module.exports = function createBulkDocsWrapper(handler) { return bulkDocsWrapper.bind(null, handler); }; function bulkDocsWrapper(handler, bulkDocs, args) { //the ``all_or_nothing`` attribute on ``bulkDocs`` is unsupported. //Also, the result array might not be in the same order as //``bulkDocs.docs`` argument. var notYetDone = []; var done = []; var promises = args.docs.map(function (doc) { return handler(doc, args) .then(function () { notYetDone.push(doc); }) .catch(function (err) { err.id = doc._id; done.push(err); }); }); return Promise.all(promises) .then(function () { args.docs = notYetDone; return bulkDocs(); }) .then(function (dbResponses) { return done.concat(dbResponses); }); } },{"pouchdb-promise":5}],10:[function(require,module,exports){ /* Copyright 2015, Marten de Vries Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ 'use strict'; var events = require('events'); var changesEvents = 'change complete error create update delete'.split(' '); module.exports = function createChangeslikeWrapper(handler) { return changesLikeWrapper.bind(null, handler); }; function changesLikeWrapper(handler, origChanges, args) { var newResult = new events.EventEmitter(); var isCancelled = false; var promise = handler(function () { var origResult = origChanges(); changesEvents.forEach(function (event) { origResult.on(event, newResult.emit.bind(newResult, event)); }); if (isCancelled) { origResult.cancel(); } else { newResult.on('cancel', function () { origResult.cancel(); }); } return origResult; }, args); newResult.then = promise.then.bind(promise); newResult.catch = promise.catch.bind(promise); newResult.cancel = function () { isCancelled = true; newResult.emit('cancel'); newResult.removeAllListeners(); }; return newResult; } },{"events":2}],11:[function(require,module,exports){ /* Copyright 2014, Marten de Vries Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ "use strict"; function PouchPluginError(opts) { this.status = opts.status; this.name = opts.name; this.message = opts.message; this.error = true; this.stack = (new Error()).stack; } PouchPluginError.prototype.toString = function () { return JSON.stringify({ status: this.status, name: this.name, message: this.message }); }; module.exports = PouchPluginError; },{}],12:[function(require,module,exports){ (function (global){ /* Copyright 2014, Marten de Vries Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /*global XMLHttpRequest */ "use strict"; var Promise = require("pouchdb-promise"); var PouchPluginError = require("pouchdb-plugin-error"); var normalizeHeaderCase = require("header-case-normalizer"); var extend = require("extend"); if (typeof global.XMLHttpRequest === "undefined") { global.XMLHttpRequest = require("xmlhttprequest-cookie").XMLHttpRequest; } module.exports = function httpQuery(db, req) { return new Promise(function (resolve, reject) { function callback() { if (xhr.readyState !== 4) { return; } if (xhr.status < 200 || xhr.status >= 300) { try { var err = JSON.parse(xhr.responseText); reject(new PouchPluginError({ "name": err.error, "message": err.reason, "status": xhr.status })); } catch (err) { //error isn't valid json. Probably some connection error //(which is hard to test without mocking XHR -> not worth it) reject(new PouchPluginError({ //coverage: ignore "name": "unknown_error", "message": xhr.responseText, "status": 500 })); } return; } var headers = {}; xhr.getAllResponseHeaders().split("\r\n").forEach(function (line) { if (line) { var splittedHeader = line.split(":"); headers[normalizeHeaderCase(splittedHeader[0]).trim()] = splittedHeader[1].trim(); } }); var result = { body: xhr.responseText, headers: headers, code: xhr.status }; if (headers["content-type"] === "application/json") { result.json = JSON.parse(result.body); } resolve(result); } //strips the database from the requested_path var url = db.name.replace(/\/[^\/]+\/?$/, "") + req.raw_path; var pouchHeaders = (db.getHeaders || fakeGetHeaders)(); var headers = extend({}, pouchHeaders, req.headers); var xhr = new XMLHttpRequest(); xhr.withCredentials = true; xhr.onreadystatechange = callback; xhr.open(req.method, url, true); for (var name in headers) { if (headers.hasOwnProperty(name)) { if (xhr.setDisableHeaderCheck) { xhr.setDisableHeaderCheck(true); } xhr.setRequestHeader(name, headers[name]); } } // Note: this is not missing a 'typeof'. "undefined" is literally part of // the request format. See for more info: // http://docs.couchdb.org/en/2.0.0/json-structure.html#request-object xhr.send(req.body === "undefined" ? null : req.body); }); }; function fakeGetHeaders() { return {}; } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"extend":3,"header-case-normalizer":4,"pouchdb-plugin-error":11,"pouchdb-promise":5,"xmlhttprequest-cookie":1}],13:[function(require,module,exports){ /* Copyright 2014-2015, Marten de Vries Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ "use strict"; var extend = require("extend"); var Promise = require("pouchdb-promise"); var nodify = require("promise-nodify"); var httpQuery = require("pouchdb-req-http-query"); var wrappers = require("pouchdb-wrappers"); var createBulkDocsWrapper = require("pouchdb-bulkdocs-wrapper"); var createChangeslikeWrapper = require("pouchdb-changeslike-wrapper"); var PouchDBPluginError = require("pouchdb-plugin-error"); var DOC_ID = "_local/_security"; exports.installSecurityMethods = function () { try { wrappers.installWrapperMethods(this, securityWrappers); } catch (err) { throw new Error("Security methods already installed."); } }; exports.installStaticSecurityMethods = function (PouchDB) { //'static' method. try { wrappers.installStaticWrapperMethods(PouchDB, staticSecurityWrappers); } catch (err) { throw new Error("Static security methods already installed."); } }; function securityWrapper(checkAllowed, original, args) { var userCtx = args.options.userCtx || { //Admin party! name: null, roles: ["_admin"] }; if (userCtx.roles.indexOf("_admin") !== -1) { return original(); } if (!checkAllowed) { return Promise.resolve().then(throw401); } return filledInSecurity(args) .then(function (security) { if (!checkAllowed(userCtx, security)) { throw401(); } }) .then(original); } function throw401() { throw new PouchDBPluginError({ status: 401, name: "unauthorized", message: "You are not authorized to access this db." }); } function filledInSecurity(args) { var getSecurity; if (typeof args.options.secObj === "undefined") { //needs the unwrapped getSecurity() to prevent recursion getSecurity = exports.getSecurity.bind(args.base); } else { getSecurity = Promise.resolve.bind(Promise, args.options.secObj); } return getSecurity() .then(function (security) { security.members = security.members || {}; security.admins = security.admins || {}; fillInSection(security.members); fillInSection(security.admins); return security; }); } function fillInSection(section) { section.names = section.names || []; section.roles = section.roles || []; } function isIn(userCtx, section) { return section.names.some(function (name) { return name === userCtx.name; }) || section.roles.some(function (role) { return userCtx.roles.indexOf(role) !== -1; }); } var securityWrappers = {}; //first the 'special' wrappers for functions that can be called //depending on their arguments. securityWrappers.query = function (original, args) { //query may only be called if //- a stored view & at least a db member or //- at least a db admin return securityWrapper(function (userCtx, security) { var isStoredView = typeof args.fun === "string"; return ( isIn(userCtx, security.admins) || (isStoredView && isMember(userCtx, security)) ); }, original, args); }; function documentModificationWrapper(original, args, docId) { //the document modification functions may only be called if //- a non-design document & at least a db member or //- at least a db admin return securityWrapper(function (userCtx, security) { var isNotDesignDoc = String(docId).indexOf("_design/") !== 0; return ( isIn(userCtx, security.admins) || (isNotDesignDoc && isMember(userCtx, security)) ); }, original, args); } function isMember(userCtx, security) { var thereAreMembers = ( security.members.names.length || security.members.roles.length ); return (!thereAreMembers) || isIn(userCtx, security.members); } securityWrappers.put = function (original, args) { return documentModificationWrapper(original, args, args.doc._id); }; securityWrappers.post = securityWrappers.put; securityWrappers.remove = securityWrappers.put; securityWrappers.putAttachment = function (original, args) { return documentModificationWrapper(original, args, args.docId); }; securityWrappers.removeAttachment = securityWrappers.putAttachment; securityWrappers.bulkDocs = createBulkDocsWrapper(function (doc, args) { var noop = Promise.resolve.bind(Promise); return documentModificationWrapper(noop, args, doc._id); }); //functions requiring a server admin var requiresServerAdmin = securityWrapper.bind(null, null); securityWrappers.destroy = requiresServerAdmin; //functions requiring a db admin var requiresAdminWrapper = securityWrapper.bind(null, function (userCtx, security) { return isIn(userCtx, security.admins); }); [ 'compact', 'putSecurity', 'viewCleanup', 'createIndex', 'deleteIndex' ].forEach(function (name) { securityWrappers[name] = requiresAdminWrapper; }); //functions requiring a db member var requiresMemberWrapper = securityWrapper.bind(null, function (userCtx, security) { return ( isIn(userCtx, security.admins) || isMember(userCtx, security) ); }); var requireMemberChangesWrapper = createChangeslikeWrapper(requiresMemberWrapper); [ 'get', 'allDocs', 'getAttachment', 'info', 'revsDiff', 'getSecurity', 'list', 'show', 'update', 'rewriteResultRequestObject', 'bulkGet', 'getIndexes', 'find', 'explain' ].forEach(function (name) { securityWrappers[name] = requiresMemberWrapper; }); [ 'changes', 'sync', 'replicate.to', 'replicate.from' ].forEach(function (name) { securityWrappers[name] = requireMemberChangesWrapper; }); var staticSecurityWrappers = {}; staticSecurityWrappers.new = requiresServerAdmin; staticSecurityWrappers.destroy = requiresServerAdmin; staticSecurityWrappers.replicate = function (original, args) { //emulate replicate.to/replicate.from var PouchDB = args.base; args.base = args.source instanceof PouchDB ? args.source : new PouchDB(args.source); //and call its handler var handler = securityWrappers['replicate.to']; return handler(original, args); }; exports.uninstallSecurityMethods = function () { try { wrappers.uninstallWrapperMethods(this, securityWrappers); } catch (err) { throw new Error("Security methods not installed."); } }; exports.uninstallStaticSecurityMethods = function (PouchDB) { //'static' method. try { wrappers.uninstallStaticWrapperMethods(PouchDB, staticSecurityWrappers); } catch (err) { throw new Error("Static security methods not installed."); } }; exports.putSecurity = function (secObj, callback) { var db = this; var promise; if (isHTTP(db)) { promise = httpRequest(db, { method: "PUT", body: JSON.stringify(secObj) }); } else { promise = db.get(DOC_ID) .catch(function () { return {_id: DOC_ID}; }) .then(function (doc) { doc.security = secObj; return db.put(doc); }) .then(function () { return {ok: true}; }); } nodify(promise, callback); return promise; }; function isHTTP(db) { return ["http", "https"].indexOf(db.type()) !== -1; } function httpRequest(db, reqStub) { return db.info() .then(function (info) { extend(reqStub, { raw_path: "/" + info.db_name + "/_security", headers: { "Content-Type": "application/json" } }); return httpQuery(db, reqStub) .then(function (resp) { return JSON.parse(resp.body); }); }); } exports.getSecurity = function (callback) { var db = this; var promise; if (isHTTP(db)) { promise = httpRequest(db, { method: "GET" }); } else { promise = db.get(DOC_ID) .catch(function () { return {security: {}}; }) .then(function (doc) { return doc.security; }); } nodify(promise, callback); return promise; }; },{"extend":3,"pouchdb-bulkdocs-wrapper":9,"pouchdb-changeslike-wrapper":10,"pouchdb-plugin-error":11,"pouchdb-promise":5,"pouchdb-req-http-query":12,"pouchdb-wrappers":14,"promise-nodify":8}],14:[function(require,module,exports){ /* Copyright 2014-2015, Marten de Vries Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ "use strict"; var nodify = require("promise-nodify"); exports.installStaticWrapperMethods = function (PouchDB, handlers) { //set an 'alternative constructor' so the constructor can be easily //wrapped, since wrapping 'real' constructors is hard. PouchDB.new = PouchDB.new || function (name, options, callback) { return new PouchDB(name, options, callback); }; PouchDB.destroy = PouchDB.destroy || function (name, options, callback) { var args = parseBaseArgs(PouchDB, this, options, callback); var db = new PouchDB(name, args.options); var promise = db.destroy(); nodify(promise, args.callback); return promise; }; installWrappers(PouchDB, handlers, exports.createStaticWrapperMethod); }; exports.installWrapperMethods = function (db, handlers) { installWrappers(db, handlers, exports.createWrapperMethod); }; function installWrappers(base, handlers, createWrapperMethod) { for (var name in handlers) { if (!handlers.hasOwnProperty(name)) { continue; } var info = getBaseAndName(base, name); var original = info.base[info.name]; if (!original) { //no method to wrap continue; } if (original.hasOwnProperty("_handlers")) { if (original._handlers.indexOf(handlers[name]) !== -1) { throw new Error("Wrapper method for '" + name + "' already installed: " + handlers[name]); } original._handlers.push(handlers[name]); } else { info.base[info.name] = createWrapperMethod(name, original, handlers[name], base); } } } function getBaseAndName(base, name) { name = name.split("."); while (name.length > 1) { base = base[name.shift(0)]; } return { base: base, name: name[0] }; } exports.createStaticWrapperMethod = function (name, original, handler, PouchDB) { //PouchDB is optional return createWrapper(name, original, handler, staticWrapperBuilders, PouchDB); }; exports.createWrapperMethod = function (name, original, handler, db) { //db is optional return createWrapper(name, original, handler, wrapperBuilders, db); }; function createWrapper(name, original, handler, theWrapperBuilders, thisVal) { //thisVal is optional var buildWrapper = theWrapperBuilders[name]; if (typeof buildWrapper === "undefined") { throw new Error("No known wrapper for method name: " + name); //coverage: ignore } var handlers = [handler]; var wrapper = buildWrapper(thisVal, original, handlers); wrapper._original = original; wrapper._handlers = handlers; return wrapper; } var wrapperBuilders = {}; wrapperBuilders.destroy = function (db, destroy, handlers) { return function (options, callback) { var args = parseBaseArgs(db, this, options, callback); return callHandlers(handlers, args, makeCall(destroy)); }; }; wrapperBuilders.put = function (db, put, handlers) { return function (/*doc, docId, docRev, options, callback*/) { var args = {}; args.base = db || this; var argsList = Array.prototype.slice.call(arguments); //parsing code borrowed from PouchDB (adapted). args.doc = argsList.shift(); var id = '_id' in args.doc; do { var temp = argsList.shift(); var temptype = typeof temp; if (temptype === "string" && !id) { args.doc._id = temp; id = true; } else if (temptype === "string" && id && !('_rev' in args.doc)) { args.doc._rev = temp; } else if (temptype === "object") { args.options = temp; } else if (temptype === "function") { args.callback = temp; } } while (argsList.length); args.options = args.options || {}; return callHandlers(handlers, args, function () { return put.call(this, args.doc, args.options); }); }; }; wrapperBuilders.post = function (db, post, handlers) { return function (doc, options, callback) { var args = parseBaseArgs(db, this, options, callback); args.doc = doc; return callHandlers(handlers, args, function () { return post.call(this, args.doc, args.options); }); }; }; wrapperBuilders.get = function (db, get, handlers)