├── lio ├── package.json └── lib │ └── lio.js └── dclabels ├── package.json └── lib └── dclabels.js /lio/package.json: -------------------------------------------------------------------------------- 1 | {"name" : "lio", 2 | "main" : "./lib/lio.js"} 3 | -------------------------------------------------------------------------------- /dclabels/package.json: -------------------------------------------------------------------------------- 1 | {"name" : "dclabels", 2 | "main" : "./lib/dclabels.js"} 3 | -------------------------------------------------------------------------------- /lio/lib/lio.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | "use strict"; 3 | var assert = require('assert'); 4 | var clone = require('clone'); 5 | 6 | // DC labels: 7 | var dclabel = require('../../dclabels'); 8 | var Group = dclabel.Group; 9 | var Label = dclabel.Label; 10 | var Privilege = dclabel.Privilege; 11 | 12 | // CURRENT LABEL ====================================================== 13 | 14 | // privacy: 15 | 16 | var privacyLabel = new Label(); 17 | 18 | Object.defineProperty(exports,"privacyLabel", 19 | { get: function() { return privacyLabel; }, 20 | set: setPrivacyLabel, 21 | enumerable: true, 22 | configurable: false 23 | }); 24 | 25 | function setPrivacyLabel(lnew, privs) { 26 | guardAllocPrivacy(lnew, privs); 27 | privacyLabel = lnew; 28 | } 29 | 30 | // trust: 31 | 32 | var trustLabel = new Label(); 33 | 34 | Object.defineProperty(exports,"trustLabel", 35 | { get: function() { return trustLabel; }, 36 | set: setTrustLabel, 37 | enumerable: true, 38 | configurable: false 39 | }); 40 | 41 | function setTrustLabel(lnew, privs) { 42 | guardAllocTrust(lnew, privs); 43 | trustLabel = lnew; 44 | } 45 | // ==================================================================== 46 | 47 | // CURRENT CLEARANCE ================================================== 48 | 49 | // privacy: 50 | 51 | var privacyClearance = null; 52 | 53 | Object.defineProperty(exports,"privacyClearance", 54 | { get: function() { return privacyClearance; }, 55 | set: setPrivacyClearance, 56 | enumerable: true, 57 | configurable: false 58 | }); 59 | 60 | function setPrivacyClearance(lnew, privs) { 61 | assert.ok(privacyClearance.subsumes(lnew, privs), 62 | "Existing clearance "+privacyClearance+ 63 | " does not subsume clearance "+lnew); 64 | 65 | assert.ok(lnew.subsumes(privacyLabel), 66 | "Label "+lnew+" does not subsume clearnace "+privacyLabel); 67 | 68 | privacyClearance = lnew; 69 | } 70 | 71 | // trust: 72 | 73 | var trustClearance = null; 74 | 75 | Object.defineProperty(exports,"trustClearance", 76 | { get: function() { return trustClearance; }, 77 | set: setTrustClearance, 78 | enumerable: true, 79 | configurable: false 80 | }); 81 | 82 | function setTrustClearance(lnew, privs) { 83 | assert.ok(lnew.subsumes(trustClearance, privs), 84 | "Existing clearance "+trustClearance+ 85 | " is not subsumed by clearance "+lnew); 86 | 87 | assert.ok(trustLabel.subsumes(lnew), 88 | "Label "+trustLabel+" does not subsume clearnace "+lnew); 89 | 90 | trustClearance = lnew; 91 | } 92 | 93 | // ==================================================================== 94 | 95 | // LABELED VALUES ===================================================== 96 | 97 | 98 | // Labeled Labeled(any, optional { privacy: Label, trust: Label}, Privilege) 99 | function Labeled(val, opts, privs) { 100 | if (!Labeled.isLabeled(this)) 101 | return new Labeled(val, opts); 102 | 103 | var value = clone(val); 104 | 105 | opts = opts || {}; 106 | 107 | // set the privacy label: 108 | this.privacy = opts.privacy || privacyLabel; 109 | assert.ok(Label.isLabel(this.privacy), "Expected Label"); 110 | guardAllocPrivacy(this.privacy, privs); 111 | 112 | // set the trust label: 113 | this.trust = opts.trust || trustLabel; 114 | assert.ok(Label.isLabel(this.trust), "Expected Label"); 115 | guardAllocTrust(this.trust, privs); 116 | 117 | // Call toString on the value, raising the current label 118 | this.toString = function() { 119 | setPrivacyLabel(this.privacy, privs); 120 | setTrustLabel(this.trust, privs); 121 | return value.toString(); 122 | } 123 | 124 | // Return the value, raising the current label 125 | this.valueOf = function() { 126 | setPrivacyLabel(this.privacy, privs); 127 | setTrustLabel(this.trust, privs); 128 | return value; 129 | }; 130 | 131 | deepFreeze(this); 132 | } 133 | 134 | Labeled.isLabeled = function(lobj) { 135 | return lobj instanceof Labeled; 136 | }; 137 | 138 | // ==================================================================== 139 | 140 | // GUARDS ============================================================= 141 | 142 | // Throws an exception if the object label is not between the current 143 | // label and clearance, taking privs into consideration. 144 | function guardAllocPrivacy(lobj, privs) { 145 | assert.ok(lobj.subsumes(privacyLabel, privs), 146 | "Existing label "+lobj+" is not subsumed by label "+lobj); 147 | 148 | if (privacyClearance) 149 | assert.ok(privacyClearance.subsumes(lobj), 150 | "Clearance "+privacyClearance+" does not subsume label "+lobj); 151 | } 152 | 153 | // Throws an exception if the object label is not between the current 154 | // label and clearance, taking privs into consideration. 155 | function guardAllocTrust(lobj, privs) { 156 | assert.ok(trustLabel.subsumes(lobj, privs), 157 | "Existing label "+trustLabel+ 158 | " does not subsume label "+lobj); 159 | if (trustClearance) 160 | assert.ok(lobj.subsumes(trustClearance), 161 | "Label "+lobj+" does not subsume clearance "+trustClearance); 162 | } 163 | 164 | 165 | // HELPER FUNCTIONS =================================================== 166 | 167 | // Deep freeze an object and return it; from Object.freeze on MDN 168 | function deepFreeze (o) { 169 | var prop, propKey; 170 | Object.freeze(o); 171 | for (propKey in o) { 172 | prop = o[propKey]; 173 | if ((!o.hasOwnProperty(propKey)) || 174 | typeof prop !== "object" || 175 | Object.isFrozen(prop)) { 176 | continue; 177 | } 178 | deepFreeze(prop); // Recursively call deepFreeze. 179 | } 180 | return o; 181 | } 182 | 183 | // ==================================================================== 184 | 185 | // EXPORTS ============================================================ 186 | 187 | exports.setPrivacyLabel = setPrivacyLabel; 188 | exports.setTrustLabel = setTrustLabel; 189 | 190 | exports.setPrivacyClearance = setPrivacyClearance; 191 | exports.setTrustClearance = setTrustClearance; 192 | 193 | exports.Group = Group; 194 | exports.Label = Label; 195 | exports.Privilege = Privilege; 196 | 197 | exports.Labeled = Labeled; 198 | 199 | })(); 200 | -------------------------------------------------------------------------------- /dclabels/lib/dclabels.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | "use strict"; 3 | var assert = require('assert'); 4 | 5 | // GROUPS ============================================================= 6 | 7 | // Group(String or [String] or Group) 8 | function Group(ps) { 9 | if (!Group.isGroup(this)) 10 | return new Group(ps); 11 | 12 | this._principals = []; 13 | this.or(ps); 14 | Object.seal(this); 15 | } 16 | 17 | // Returns true if the argument is a Group 18 | Group.isGroup = function(group) { 19 | return group instanceof Group; 20 | }; 21 | 22 | // Group or(String or [String] or Group) 23 | Group.prototype.or = function(ps) { 24 | function _or(principal) { 25 | assert.ok(isString(principal), "Expected non-empty String"); 26 | 27 | if (this._principals.indexOf(principal) === -1) 28 | this._principals.push(principal); 29 | } 30 | 31 | if (isString(ps)) { 32 | _or.call(this, ps); 33 | } else if (isArray(ps)) { 34 | assert.ok(isArray(ps), "Expected non-empty Array"); 35 | ps.forEach(function (p) { _or.call(this, p); }, this); 36 | } else { 37 | assert.ok(Group.isGroup(ps), 38 | "Expected non-empty Array of non-empty Strings, "+ 39 | "a non-empty String, or a Group"); 40 | ps._principals.forEach(_or, this); 41 | } 42 | return this; 43 | }; 44 | 45 | Group.prototype.toString = function() { 46 | var str = "Group("; 47 | var end = this._principals.length - 1; 48 | this._principals.forEach(function(element, idx) { 49 | str += '"'+element+'"'; 50 | if (idx !== end) { 51 | str += ").or("; 52 | } 53 | }); 54 | str += ")"; 55 | return str; 56 | }; 57 | 58 | // bool subsumes(Group other) 59 | Group.prototype.subsumes = function(other) { 60 | assert.ok(Group.isGroup(other), "Expected Group"); 61 | // We have more principals, hence cannot subsume 62 | if (other._principals.length < this._principals.length) 63 | return false; 64 | // Ensure that the other role contains all the principals of this role 65 | return this._principals.every(function(principal) { 66 | return (other._principals.indexOf(principal) !== -1); 67 | }); 68 | }; 69 | 70 | // bool equals(Group other) 71 | Group.prototype.equals = function(other) { 72 | return this === other || (this.subsumes(other) && other.subsumes(this)); 73 | }; 74 | 75 | // ==================================================================== 76 | 77 | 78 | // LABELS ============================================================= 79 | 80 | // Label(optional (String or Group or [Group] or Label)) 81 | function Label(gs) { 82 | if (!Label.isLabel(this)) 83 | return new Label(gs); 84 | 85 | this._groups = []; 86 | if (gs) 87 | this.and(gs); 88 | Object.seal(this); 89 | } 90 | 91 | // Returns true if the argument is a Label 92 | Label.isLabel = function(label) { 93 | return label instanceof Label; 94 | }; 95 | 96 | // Label and(String or Group or [Group] or Label) 97 | Label.prototype.and = function(gs) { 98 | function _and(group) { 99 | assert.ok(Group.isGroup(group), "Expected Group"); 100 | 101 | var isRedundant = this._groups.some(function (g) { 102 | return g.subsumes(group); 103 | }); 104 | if (isRedundant) return; 105 | this._groups.forEach(function (g, i) { 106 | if (group.subsumes(g)) 107 | this._groups.splice(i,1); 108 | }, this); 109 | this._groups.push(group); 110 | } 111 | 112 | if (isString(gs)) { 113 | _and.call(this, new Group(gs)); 114 | } else if (Group.isGroup(gs)) { 115 | _and.call(this, gs); 116 | } else if (isArray(gs)) { 117 | assert.ok(isArray(gs), "Expected non-empty Array"); 118 | gs.forEach(function(g) { _and.call(this, g); }, this); 119 | } else { 120 | assert.ok(Label.isLabel(gs), 121 | "Expected non-empty Array of Groups, "+ 122 | "a non-empty String, or a Label"); 123 | gs._groups.forEach(_and, this); 124 | } 125 | return this; 126 | }; 127 | 128 | // Label or(String or Group or [Group] or Label) 129 | Label.prototype.or = function(gs) { 130 | function _or(group) { 131 | assert.ok(Group.isGroup(group), "Expected Group"); 132 | var l = new Label(); 133 | this._groups.forEach(function (g, i) { 134 | l.and(g.or(group)); 135 | }); 136 | this._groups = l._groups; 137 | } 138 | 139 | if (isString(gs)) { 140 | _or.call(this, new Group(gs)); 141 | } else if (Group.isGroup(gs)) { 142 | _or.call(this, gs); 143 | } else if (isArray(gs)) { 144 | assert.ok(isArray(gs), "Expected non-empty Array"); 145 | gs.forEach(function(g) { _or.call(this, g); }, this); 146 | } else { 147 | assert.ok(Label.isLabel(gs), 148 | "Expected non-empty Array of Groups, "+ 149 | "a non-empty String, or a Label"); 150 | gs._groups.forEach(_or, this); 151 | } 152 | return this; 153 | }; 154 | 155 | Label.prototype.toString = function() { 156 | var str = "Label("; 157 | var end = this._groups.length - 1; 158 | this._groups.forEach(function(element, idx) { 159 | str += '"'+element+'"'; 160 | if (idx !== end) { 161 | str += ").and("; 162 | } 163 | }); 164 | str += ")"; 165 | return str; 166 | }; 167 | 168 | // bool subsumes(Label other, optional Privilege priv) 169 | Label.prototype.subsumes = function(other, priv) { 170 | assert.ok(Label.isLabel(other),"Expected Label"); 171 | 172 | if (priv) { 173 | assert.ok(Privilege.isPrivilege(priv),"Expected Privilege"); 174 | var this_ = new Label(this_); 175 | this_.and(priv._label); 176 | return this_.subsumes(other); 177 | } else { 178 | // there are more groups in the other formula, this label cannot subsumes it 179 | if (other._groups.length > this._groups.length) 180 | return false; 181 | // the other label has a group that no group in this label subsumes 182 | return other._groups.every(function(group) { 183 | return this._groups.some(function(tgroup) { 184 | return tgroup.subsumes(group); 185 | }); 186 | }, this); 187 | } 188 | }; 189 | 190 | // bool equals(Label other) 191 | Label.prototype.equals = function(other) { 192 | return this === other || (this.subsumes(other) && other.subsumes(this)); 193 | }; 194 | 195 | // ==================================================================== 196 | 197 | // PRIVILEGES ========================================================= 198 | 199 | // Privilege(Label)) 200 | function Privilege(l) { 201 | if (!Privilege.isPrivilege(this)) 202 | return new Privilege(l); 203 | 204 | l = l || new Label(); 205 | assert.ok(Label.isLabel(l),"Expected Label"); 206 | this._label = l; 207 | Object.seal(this); 208 | } 209 | 210 | // Privilege combine(Privilege) 211 | Privilege.prototype.combine = function(priv) { 212 | assert.ok(Privilege.isPrivilege(priv),"Expected Privilege"); 213 | this._label.and(priv._label); 214 | return this; 215 | }; 216 | 217 | // Returns true if the argument is a Privilege 218 | Privilege.isPrivilege = function(priv) { 219 | return priv instanceof Privilege; 220 | }; 221 | 222 | Privilege.prototype.toString = function() { 223 | return "Privilege("+this._label+")"; 224 | }; 225 | 226 | // bool subsumes(Privilege other) 227 | Privilege.prototype.subsumes = function(other) { 228 | return this._label.subsumes(other._label); 229 | }; 230 | 231 | // bool equals(Privilege other) 232 | Privilege.prototype.equals = function(other) { 233 | return this === other || (this.subsumes(other) && other.subsumes(this)); 234 | }; 235 | 236 | // Privilege freeze() 237 | Privilege.prototype.freeze = function() { 238 | deepFreeze(this); 239 | return this; 240 | }; 241 | 242 | // ==================================================================== 243 | 244 | 245 | // HELPER FUNCTIONS =================================================== 246 | 247 | // Returns true if the argument is a non-empty string 248 | function isString(str) { 249 | return str && typeof(str) === 'string'; 250 | } 251 | 252 | // Returns true if the argument is a non-empty array 253 | function isArray(ps) { 254 | return Array.isArray(ps) && ps.length > 0; 255 | } 256 | 257 | // Deep freeze an object and return it; from Object.freeze on MDN 258 | function deepFreeze (o) { 259 | var prop, propKey; 260 | Object.freeze(o); 261 | for (propKey in o) { 262 | prop = o[propKey]; 263 | if ((!o.hasOwnProperty(propKey)) || 264 | typeof prop !== "object" || 265 | Object.isFrozen(prop)) { 266 | continue; 267 | } 268 | deepFreeze(prop); // Recursively call deepFreeze. 269 | } 270 | return o; 271 | } 272 | 273 | // ==================================================================== 274 | 275 | // EXPORTS ============================================================ 276 | 277 | exports.Group = deepFreeze(Object.freeze(Group)); 278 | exports.Label = deepFreeze(Object.freeze(Label)); 279 | exports.Privilege = deepFreeze(Object.freeze(Privilege)); 280 | 281 | })(); 282 | --------------------------------------------------------------------------------