pax_global_header00006660000000000000000000000064124307474420014521gustar00rootroot0000000000000052 comment=8df9e34c8f66a5e634a1024dba3ea16704d5178d private-8df9e34c8f66a5e634a1024dba3ea16704d5178d/000077500000000000000000000000001243074744200201775ustar00rootroot00000000000000private-8df9e34c8f66a5e634a1024dba3ea16704d5178d/.travis.yml000066400000000000000000000001051243074744200223040ustar00rootroot00000000000000language: node_js node_js: - "0.11" - "0.10" - "0.8" - "0.6" private-8df9e34c8f66a5e634a1024dba3ea16704d5178d/LICENSE000066400000000000000000000020631243074744200212050ustar00rootroot00000000000000Copyright (c) 2014 Ben Newman 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. private-8df9e34c8f66a5e634a1024dba3ea16704d5178d/README.md000066400000000000000000000174261243074744200214700ustar00rootroot00000000000000private [![Build Status](https://travis-ci.org/benjamn/private.png?branch=master)](https://travis-ci.org/benjamn/private) === A general-purpose utility for associating truly private state with any JavaScript object. Installation --- From NPM: npm install private From GitHub: cd path/to/node_modules git clone git://github.com/benjamn/private.git cd private npm install . Usage --- **Get or create a secret object associated with any (non-frozen) object:** ```js var getSecret = require("private").makeAccessor(); var obj = Object.create(null); // any kind of object works getSecret(obj).totallySafeProperty = "p455w0rd"; console.log(Object.keys(obj)); // [] console.log(Object.getOwnPropertyNames(obj)); // [] console.log(getSecret(obj)); // { totallySafeProperty: "p455w0rd" } ``` Now, only code that has a reference to both `getSecret` and `obj` can possibly access `.totallySafeProperty`. *Importantly, no global references to the secret object are retained by the `private` package, so as soon as `obj` gets garbage collected, the secret will be reclaimed as well. In other words, you don't have to worry about memory leaks.* **Create a unique property name that cannot be enumerated or guessed:** ```js var secretKey = require("private").makeUniqueKey(); var obj = Object.create(null); // any kind of object works Object.defineProperty(obj, secretKey, { value: { totallySafeProperty: "p455w0rd" }, enumerable: false // optional; non-enumerability is the default }); Object.defineProperty(obj, "nonEnumerableProperty", { value: "anyone can guess my name", enumerable: false }); console.log(obj[secretKey].totallySafeProperty); // p455w0rd console.log(obj.nonEnumerableProperty); // "anyone can guess my name" console.log(Object.keys(obj)); // [] console.log(Object.getOwnPropertyNames(obj)); // ["nonEnumerableProperty"] for (var key in obj) { console.log(key); // never called } ``` Because these keys are non-enumerable, you can't discover them using a `for`-`in` loop. Because `secretKey` is a long string of random characters, you would have a lot of trouble guessing it. And because the `private` module wraps `Object.getOwnPropertyNames` to exclude the keys it generates, you can't even use that interface to discover it. Unless you have access to the value of the `secretKey` property name, there is no way to access the value associated with it. So your only responsibility as secret-keeper is to avoid handing out the value of `secretKey` to untrusted code. Think of this style as a home-grown version of the first style. Note, however, that it requires a full implementation of ES5's `Object.defineProperty` method in order to make any safety guarantees, whereas the first example will provide safety even in environments that do not support `Object.defineProperty`. Rationale --- In JavaScript, the only data that are truly private are local variables whose values do not *leak* from the scope in which they were defined. This notion of *closure privacy* is powerful, and it readily provides some of the benefits of traditional data privacy, a la Java or C++: ```js function MyClass(secret) { this.increment = function() { return ++secret; }; } var mc = new MyClass(3); console.log(mc.increment()); // 4 ``` You can learn something about `secret` by calling `.increment()`, and you can increase its value by one as many times as you like, but you can never decrease its value, because it is completely inaccessible except through the `.increment` method. And if the `.increment` method were not available, it would be as if no `secret` variable had ever been declared, as far as you could tell. This style breaks down as soon as you want to inherit methods from the prototype of a class: ```js function MyClass(secret) { this.secret = secret; } MyClass.prototype.increment = function() { return ++this.secret; }; ``` The only way to communicate between the `MyClass` constructor and the `.increment` method in this example is to manipulate shared properties of `this`. Unfortunately `this.secret` is now exposed to unlicensed modification: ```js var mc = new MyClass(6); console.log(mc.increment()); // 7 mc.secret -= Infinity; console.log(mc.increment()); // -Infinity mc.secret = "Go home JavaScript, you're drunk."; mc.increment(); // NaN ``` Another problem with closure privacy is that it only lends itself to per-instance privacy, whereas the `private` keyword in most object-oriented languages indicates that the data member in question is visible to all instances of the same class. Suppose you have a `Node` class with a notion of parents and children: ```js function Node() { var parent; var children = []; this.getParent = function() { return parent; }; this.appendChild = function(child) { children.push(child); child.parent = this; // Can this be made to work? }; } ``` The desire here is to allow other `Node` objects to manipulate the value returned by `.getParent()`, but otherwise disallow any modification of the `parent` variable. You could expose a `.setParent` function, but then anyone could call it, and you might as well give up on the getter/setter pattern. This module solves both of these problems. Usage --- Let's revisit the `Node` example from above: ```js var p = require("private").makeAccessor(); function Node() { var privates = p(this); var children = []; this.getParent = function() { return privates.parent; }; this.appendChild = function(child) { children.push(child); var cp = p(child); if (cp.parent) cp.parent.removeChild(child); cp.parent = this; return child; }; } ``` Now, in order to access the private data of a `Node` object, you need to have access to the unique `p` function that is being used here. This is already an improvement over the previous example, because it allows restricted access by other `Node` instances, but can it help with the `Node.prototype` problem too? Yes it can! ```js var p = require("private").makeAccessor(); function Node() { p(this).children = []; } var Np = Node.prototype; Np.getParent = function() { return p(this).parent; }; Np.appendChild = function(child) { p(this).children.push(child); var cp = p(child); if (cp.parent) cp.parent.removeChild(child); cp.parent = this; return child; }; ``` Because `p` is in scope not only within the `Node` constructor but also within `Node` methods, we can finally avoid redefining methods every time the `Node` constructor is called. Now, you might be wondering how you can restrict access to `p` so that no untrusted code is able to call it. The answer is to use your favorite module pattern, be it CommonJS, AMD `define`, or even the old Immediately-Invoked Function Expression: ```js var Node = (function() { var p = require("private").makeAccessor(); function Node() { p(this).children = []; } var Np = Node.prototype; Np.getParent = function() { return p(this).parent; }; Np.appendChild = function(child) { p(this).children.push(child); var cp = p(child); if (cp.parent) cp.parent.removeChild(child); cp.parent = this; return child; }; return Node; }()); var parent = new Node; var child = new Node; parent.appendChild(child); assert.strictEqual(child.getParent(), parent); ``` Because this version of `p` never leaks from the enclosing function scope, only `Node` objects have access to it. So, you see, the claim I made at the beginning of this README remains true: > In JavaScript, the only data that are truly private are local variables > whose values do not *leak* from the scope in which they were defined. It just so happens that closure privacy is sufficient to implement a privacy model similar to that provided by other languages. private-8df9e34c8f66a5e634a1024dba3ea16704d5178d/package.json000066400000000000000000000012311243074744200224620ustar00rootroot00000000000000{ "author": { "name": "Ben Newman", "email": "bn@cs.stanford.edu" }, "name": "private", "description": "Utility for associating truly private state with any JavaScript object", "keywords": [ "private", "access control", "access modifiers", "encapsulation", "secret", "state", "privilege", "scope", "es5" ], "version": "0.1.6", "homepage": "http://github.com/benjamn/private", "repository": { "type": "git", "url": "git://github.com/benjamn/private.git" }, "license": "MIT", "main": "private.js", "scripts": { "test": "node test/run.js" }, "engines": { "node": ">= 0.6" } } private-8df9e34c8f66a5e634a1024dba3ea16704d5178d/private.js000066400000000000000000000066771243074744200222270ustar00rootroot00000000000000"use strict"; var originalObject = Object; var originalDefProp = Object.defineProperty; var originalCreate = Object.create; function defProp(obj, name, value) { if (originalDefProp) try { originalDefProp.call(originalObject, obj, name, { value: value }); } catch (definePropertyIsBrokenInIE8) { obj[name] = value; } else { obj[name] = value; } } // For functions that will be invoked using .call or .apply, we need to // define those methods on the function objects themselves, rather than // inheriting them from Function.prototype, so that a malicious or clumsy // third party cannot interfere with the functionality of this module by // redefining Function.prototype.call or .apply. function makeSafeToCall(fun) { if (fun) { defProp(fun, "call", fun.call); defProp(fun, "apply", fun.apply); } return fun; } makeSafeToCall(originalDefProp); makeSafeToCall(originalCreate); var hasOwn = makeSafeToCall(Object.prototype.hasOwnProperty); var numToStr = makeSafeToCall(Number.prototype.toString); var strSlice = makeSafeToCall(String.prototype.slice); var cloner = function(){}; function create(prototype) { if (originalCreate) { return originalCreate.call(originalObject, prototype); } cloner.prototype = prototype || null; return new cloner; } var rand = Math.random; var uniqueKeys = create(null); function makeUniqueKey() { // Collisions are highly unlikely, but this module is in the business of // making guarantees rather than safe bets. do var uniqueKey = internString(strSlice.call(numToStr.call(rand(), 36), 2)); while (hasOwn.call(uniqueKeys, uniqueKey)); return uniqueKeys[uniqueKey] = uniqueKey; } function internString(str) { var obj = {}; obj[str] = true; return Object.keys(obj)[0]; } // External users might find this function useful, but it is not necessary // for the typical use of this module. defProp(exports, "makeUniqueKey", makeUniqueKey); // Object.getOwnPropertyNames is the only way to enumerate non-enumerable // properties, so if we wrap it to ignore our secret keys, there should be // no way (except guessing) to access those properties. var originalGetOPNs = Object.getOwnPropertyNames; Object.getOwnPropertyNames = function getOwnPropertyNames(object) { for (var names = originalGetOPNs(object), src = 0, dst = 0, len = names.length; src < len; ++src) { if (!hasOwn.call(uniqueKeys, names[src])) { if (src > dst) { names[dst] = names[src]; } ++dst; } } names.length = dst; return names; }; function defaultCreatorFn(object) { return create(null); } function makeAccessor(secretCreatorFn) { var brand = makeUniqueKey(); var passkey = create(null); secretCreatorFn = secretCreatorFn || defaultCreatorFn; function register(object) { var secret; // Created lazily. function vault(key, forget) { // Only code that has access to the passkey can retrieve (or forget) // the secret object. if (key === passkey) { return forget ? secret = null : secret || (secret = secretCreatorFn(object)); } } defProp(object, brand, vault); } function accessor(object) { if (!hasOwn.call(object, brand)) register(object); return object[brand](passkey); } accessor.forget = function(object) { if (hasOwn.call(object, brand)) object[brand](passkey, true); }; return accessor; } defProp(exports, "makeAccessor", makeAccessor); private-8df9e34c8f66a5e634a1024dba3ea16704d5178d/test/000077500000000000000000000000001243074744200211565ustar00rootroot00000000000000private-8df9e34c8f66a5e634a1024dba3ea16704d5178d/test/run.js000066400000000000000000000034341243074744200223240ustar00rootroot00000000000000var assert = require("assert"); var makeAccessor = require("../private").makeAccessor; var acc1 = makeAccessor(); var obj = {}; var hasOwn = obj.hasOwnProperty; acc1(obj).foo = 42; assert.deepEqual(obj, {}); assert.deepEqual(acc1(obj), { foo: 42 }); assert.deepEqual(acc1(acc1(obj)), {}); assert.deepEqual(acc1(obj), { foo: 42 }); assert.deepEqual(Object.keys(acc1(obj)), ["foo"]); assert.strictEqual(Object.getOwnPropertyNames(acc1(obj)).length, 1); assert.strictEqual(Object.getOwnPropertyNames(acc1(acc1(obj))).length, 0); acc1(obj).bar = "baz"; assert.deepEqual(acc1(obj), { foo: 42, bar: "baz" }); delete acc1(obj).foo; assert.deepEqual(acc1(obj), { bar: "baz" }); try { acc1(42); throw new Error("threw wrong error"); } catch (err) { assert.ok(err); } var acc2 = makeAccessor(); assert.notStrictEqual(acc1, acc2); assert.notStrictEqual(acc1(obj), acc2(obj)); assert.deepEqual(acc2(obj), {}); assert.strictEqual(Object.getOwnPropertyNames(obj).length, 0); assert.strictEqual(Object.keys(obj).length, 0); acc2(obj).bar = "asdf"; assert.deepEqual(acc2(obj), { bar: "asdf" }); acc2.forget(obj); acc2(obj).bar = "asdf"; var oldSecret = acc2(obj); assert.strictEqual(oldSecret.bar, "asdf"); acc2.forget(obj); var newSecret = acc2(obj); assert.notStrictEqual(oldSecret, newSecret); assert.ok(hasOwn.call(oldSecret, "bar")); assert.ok(!hasOwn.call(newSecret, "bar")); newSecret.bar = "zxcv"; assert.strictEqual(oldSecret.bar, "asdf"); assert.strictEqual(acc2(obj).bar, "zxcv"); function creatorFn(object) { return { self: object }; } var acc3 = makeAccessor(creatorFn); acc3(obj).xxx = "yyy"; assert.deepEqual(acc3(obj), { self: obj, xxx: "yyy" }); acc3.forget(obj); assert.deepEqual(acc3(obj), { self: obj }); var green = "\033[32m"; var reset = "\033[0m"; console.log(green + "ALL PASS" + reset);