1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * @overview 26 * 27 * This file defines an access control list and associated classes. 28 * 29 */ 30 31 /** 32 * Creates an empty access control list (ACL). 33 * @class 34 * An access control list is a collection of access control entries (ACEs). Each entry contains 35 * information about a certain permission applied by the current user to another user or users 36 * for a particular type of action. So far, there are two types of rights that are managed in 37 * this way: 38 * 39 * <ul> 40 * <li><b>viewFreeBusy</b> - governs whether other users may view this user's free/busy information</li> 41 * <li><b>invite</b> - determines whether an invite from other users will automatically create a tentative appointment on this user's calendar</li> 42 * </ul> 43 * 44 * Note: that shared organizers ({@link ZmShare}) manage rights (read/write/manage) in their own way. 45 * 46 * @author Conrad Damon 47 * 48 * @param {Array} aces the list of {@link ZmAccessControlEntry} objects 49 */ 50 ZmAccessControlList = function(aces) { 51 this._aces = {}; 52 } 53 54 /** 55 * Returns a string representation of the object. 56 * 57 * @return {String} a string representation of the object 58 */ 59 ZmAccessControlList.prototype.toString = 60 function() { 61 return "ZmAccessControlList"; 62 }; 63 64 /** 65 * Loads the list. 66 * 67 * @param {AjxCallback} callback the function to callback after the loaded 68 * 69 * @private 70 */ 71 ZmAccessControlList.prototype.load = 72 function(callback) { 73 var jsonObj = {GetRightsRequest:{_jsns:"urn:zimbraAccount"}}; 74 var respCallback = new AjxCallback(this, this._handleResponseLoad, [callback]); 75 appCtxt.getAppController().sendRequest({jsonObj:jsonObj, asyncMode:true, callback:respCallback}); 76 }; 77 78 /** 79 * @private 80 */ 81 ZmAccessControlList.prototype._handleResponseLoad = 82 function(callback, result) { 83 var response = result.getResponse(); 84 var aces = response.GetRightsResponse.ace; 85 if (aces && aces.length) { 86 for (var i = 0; i < aces.length; i++) { 87 this.add(ZmAccessControlEntry.createFromDom(aces[i])); 88 } 89 } 90 if (callback) { 91 callback.run(); 92 } 93 }; 94 95 /** 96 * Gets the access control entry by right. 97 * 98 * @param {String} right the right 99 * @return {ZmAccessControlEntry} the entry 100 */ 101 ZmAccessControlList.prototype.getACLByRight = 102 function(right) { 103 return this._aces[right]; 104 }; 105 106 /** 107 * Gets the grantee type. 108 * 109 * @param {String} right the right 110 * @return {constant} the grantee type (see <code>ZmSetting.ACL_</code> constants) 111 * 112 * @see ZmSetting 113 */ 114 ZmAccessControlList.prototype.getGranteeType = 115 function(right) { 116 var aces = this._aces[right]; 117 var gt = ZmSetting.ACL_PUBLIC; 118 119 var gtMap = {}; 120 if(aces && aces.length) { 121 for (var i = 0; i < aces.length; i++) { 122 var ace = aces[i]; 123 DBG.println("<font color=red>ace:</font>" + (ace.negative?"-":"") + ace.granteeType +"," + ace.grantee ); 124 var aceGranteeType = (ace.granteeType == ZmSetting.ACL_USER || ace.granteeType == ZmSetting.ACL_GROUP) ? ZmSetting.ACL_USER : ace.granteeType; 125 gtMap[aceGranteeType] = ace.negative ? -1 : 1; 126 } 127 } 128 129 var allowPublic = (gtMap[ZmSetting.ACL_PUBLIC] == 1); 130 var denyPublic = (gtMap[ZmSetting.ACL_PUBLIC] == -1); 131 var allowLocal = (gtMap[ZmSetting.ACL_AUTH] == 1); 132 var denyLocal = (gtMap[ZmSetting.ACL_AUTH] == -1); 133 var allowDomainOnly = (gtMap[ZmSetting.ACL_DOMAIN] == 1); 134 135 var allowUser = (gtMap[ZmSetting.ACL_USER] == 1); 136 var allowNone = (denyPublic || denyLocal) && (gtMap[ZmSetting.ACL_USER] == null); 137 138 if(allowPublic) { 139 return ZmSetting.ACL_PUBLIC; 140 } 141 142 if(allowLocal) { 143 return ZmSetting.ACL_AUTH; 144 } 145 146 if(denyPublic) { 147 if(allowLocal) { 148 return ZmSetting.ACL_AUTH; 149 } 150 } 151 152 if(allowUser) { 153 return ZmSetting.ACL_USER; 154 } 155 156 if(allowDomainOnly) { 157 return ZmSetting.ACL_DOMAIN; 158 } 159 160 if(allowNone) { 161 return ZmSetting.ACL_NONE; 162 } 163 return gt; 164 }; 165 166 /** 167 * Gets the access control entry by grantee type. 168 * 169 * @param {String} right the right 170 * @param {constant} gt the grantee type (see <code>ZmSetting.ACL_</code> constants) 171 * @return {Array} an array of {@link ZmAccessControlEntry} objects 172 */ 173 ZmAccessControlList.prototype.getACLByGranteeType = 174 function(right, gt) { 175 var aces = this._aces[right]; 176 var list = []; 177 if (aces && aces.length) { 178 for (var i = 0; i < aces.length; i++) { 179 var ace = aces[i]; 180 if (ace.granteeType == gt) { 181 list.push(ace); 182 } 183 } 184 } 185 list.sort(); 186 return list; 187 }; 188 189 /** 190 * Gets the grantees. 191 * 192 * @param {String} right the right 193 * @return {Array} an array of grantees 194 */ 195 ZmAccessControlList.prototype.getGrantees = 196 function(right) { 197 var aces = this._aces[right]; 198 var list = []; 199 if (aces && aces.length) { 200 for (var i = 0; i < aces.length; i++) { 201 var ace = aces[i]; 202 if (ace.granteeType == ZmSetting.ACL_USER || ace.granteeType == ZmSetting.ACL_GROUP) { 203 list.push(ace.grantee); 204 } 205 } 206 } 207 list.sort(); 208 return list; 209 }; 210 211 /** 212 * Gets the grantees info. 213 * 214 * @param {String} right the right 215 * @return {Array} an array of grantree info objects (obj.grantee, obj.zid) 216 */ 217 ZmAccessControlList.prototype.getGranteesInfo = 218 function(right) { 219 var aces = this._aces[right]; 220 var list = []; 221 if (aces && aces.length) { 222 for (var i = 0; i < aces.length; i++) { 223 var ace = aces[i]; 224 if (ace.granteeType == ZmSetting.ACL_USER || ace.granteeType == ZmSetting.ACL_GROUP) { 225 list.push({grantee: ace.grantee, zid: ace.zid}); 226 } 227 } 228 } 229 list.sort(ZmAccessControlList.sortByGrantee); 230 return list; 231 }; 232 233 /** 234 * Grants permissions on the access control entries. 235 * 236 * @param {Array} aces an array of {@link ZmAccessControlEntry} objects 237 * @param {AjxCallback} callback the callback 238 * @param {Boolean} batchCmd <code>true</code> to submit as a batch command 239 */ 240 ZmAccessControlList.prototype.grant = 241 function(aces, callback, batchCmd) { 242 this._setPerms(aces, false, callback, batchCmd); 243 }; 244 245 /** 246 * Revokes and denies permissions the access control entries. 247 * 248 * @param {Array} aces an array of {@link ZmAccessControlEntry} objects 249 * @param {AjxCallback} callback the callback 250 * @param {Boolean} batchCmd <code>true</code> to submit as a batch command 251 */ 252 ZmAccessControlList.prototype.revoke = 253 function(aces, callback, batchCmd) { 254 this._setPerms(aces, true, callback, batchCmd); 255 }; 256 257 /** 258 * Sets the permissions. 259 * 260 * @param {Array} aces an array of {@link ZmAccessControlEntry} objects 261 * @param {Boolean} revoke <code>true</code> to deny; <code>false</code> to grant 262 * @param {AjxCallback} callback the callback 263 * @param {Boolean} batchCmd <code>true</code> to submit as a batch command 264 * 265 * @private 266 */ 267 ZmAccessControlList.prototype._setPerms = 268 function(aces, revoke, callback, batchCmd) { 269 var reqName = revoke ? "RevokeRightsRequest" : "GrantRightsRequest"; 270 var soapDoc = AjxSoapDoc.create(reqName, "urn:zimbraAccount"); 271 for (var i = 0; i < aces.length; i++) { 272 var ace = aces[i]; 273 var aceNode = soapDoc.set("ace"); 274 aceNode.setAttribute("right", ace.right); 275 aceNode.setAttribute("gt", ace.granteeType); 276 if(ace.grantee) { 277 aceNode.setAttribute("d", ace.grantee); 278 } 279 if (ace.zid) { 280 aceNode.setAttribute("zid", ace.zid); 281 } 282 if (ace.negative) { 283 aceNode.setAttribute("deny", 1); 284 } 285 } 286 var respCallback = new AjxCallback(this, this._handleResponseSetPerms, [revoke, callback]); 287 if (batchCmd) { 288 batchCmd.addNewRequestParams(soapDoc, respCallback); 289 } else { 290 appCtxt.getAppController().sendRequest({soapDoc:soapDoc, asyncMode:true, callback:respCallback}); 291 } 292 }; 293 294 /** 295 * @private 296 */ 297 ZmAccessControlList.prototype._handleResponseSetPerms = 298 function(revoke, callback, result) { 299 var response = result.getResponse(); 300 var resp = revoke ? response.RevokeRightsResponse : response.GrantRightsResponse; 301 var aces = resp && resp.ace; 302 var aceList = []; 303 if (aces && aces.length) { 304 for (var i = 0; i < aces.length; i++) { 305 var ace = ZmAccessControlEntry.createFromDom(aces[i]); 306 aceList.push(ace); 307 if (revoke) { 308 this.remove(ace); 309 } else { 310 this.update(ace); 311 } 312 } 313 } 314 315 if (callback) { 316 callback.run(aceList); 317 } 318 }; 319 320 /** 321 * Adds the entry to the ACL. 322 * 323 * @param {ZmAccessControlEntry} ace the entry to add 324 */ 325 ZmAccessControlList.prototype.add = 326 function(ace) { 327 if (!ace) { return; } 328 var right = ace.right; 329 if (!this._aces[right]) { 330 this._aces[right] = []; 331 } 332 this._aces[right].push(ace); 333 }; 334 335 /** 336 * Removes the entry to the ACL. 337 * 338 * @param {ZmAccessControlEntry} ace the entry to remove 339 */ 340 ZmAccessControlList.prototype.remove = 341 function(ace) { 342 if (!ace) { return; } 343 var list = this._aces[ace.right]; 344 var newList = []; 345 if (list && list.length) { 346 for (var i = 0; i < list.length; i++) { 347 if (list[i].grantee != ace.grantee) { 348 newList.push(list[i]); 349 } 350 } 351 } 352 this._aces[ace.right] = newList; 353 }; 354 355 /** 356 * Updates the entry to the ACL. 357 * 358 * @param {ZmAccessControlEntry} ace the entry to update 359 * @param {Boolean} removeEnty not used 360 */ 361 ZmAccessControlList.prototype.update = 362 function(ace, removeEntry) { 363 if (!ace || !ace.right) { return; } 364 var found = false; 365 366 if(!this._aces[ace.right]) { 367 this._aces[ace.right] = []; 368 } 369 370 var list = this._aces[ace.right]; 371 if (list.length) { 372 //search for ace to update 373 for (var i = 0; i < list.length; i++) { 374 if ((list[i].grantee == ace.grantee) && (list[i].granteeType == ace.granteeType)) { 375 this._aces[ace.right][i] = ace; 376 found = true; 377 } 378 } 379 } 380 if(!found) { 381 //adding new entry to ace list 382 this._aces[ace.right].push(ace); 383 } 384 }; 385 386 /** 387 * Cleans up the ACL. 388 * 389 */ 390 ZmAccessControlList.prototype.cleanup = 391 function() { 392 this._aces = {}; 393 }; 394 395 /** 396 * Sorts the ACL by grantee. 397 * 398 * @param {Hash} a grantee "a" 399 * @param {String} a.grantee the grantee 400 * @param {Hash} b grantee "b" 401 * @param {Hash} b.grantee grantee "b" 402 * @return {int} 0 if "a" and "b" are the same; 1 if "a" is before "b"; -1 if "b" is before "a" 403 */ 404 ZmAccessControlList.sortByGrantee = 405 function(a, b) { 406 407 var granteeA = a.grantee || ""; 408 var granteeB = b.grantee || ""; 409 410 if (granteeA.toLowerCase() > granteeB.toLowerCase()) { return 1; } 411 if (granteeA.toLowerCase() < granteeB.toLowerCase()) { return -1; } 412 413 return 0; 414 }; 415 416 417 /** 418 * Creates an access control entry. 419 * @class 420 * An access control entry encapsulates the permission information pertaining to a user or users 421 * regarding a certain right. 422 * 423 * @param {Hash} params a hash of parameters 424 * @param {String} params.right the action governed by this ACE 425 * @param {String} params.grantee the account name of user or group permission applies to 426 * @param {String} params.zid the ZID of grantee 427 * @param {constant} params.granteeType type of grantee (see <code>ZmSetting.ACL_</code> constants) 428 * @param {Boolean} params.negative if <code>true</code>, permission is denied by this ACE 429 * @see ZmSetting 430 */ 431 ZmAccessControlEntry = 432 function(params) { 433 this.grantee = params.grantee; 434 this.zid = params.zid; 435 this.granteeType = params.granteeType; 436 this.right = params.right; 437 this.negative = params.negative; 438 } 439 440 /** 441 * Returns a string representation of the object. 442 * 443 * @return {String} a string representation of the object 444 */ 445 ZmAccessControlEntry.prototype.toString = 446 function() { 447 return "ZmAccessControlEntry"; 448 }; 449 450 /** 451 * Creates an entry from the DOM object. 452 * 453 * @param {Hash} obj the DOM object 454 * @param {String} obj.right the action governed by this ACE 455 * @param {String} obj.d the account name of user or group permission applies to 456 * @param {String} obj.zid the ZID of grantee 457 * @param {constant} obj.gt the type of grantee (see <code>ZmSetting.ACL_</code> constants) 458 * @param {Boolean} obj.deny if <code>1</code>, permission is denied by this ACE 459 * @return {ZmAccessControlEntry} the newly created entry 460 */ 461 ZmAccessControlEntry.createFromDom = 462 function(obj) { 463 var params = {}; 464 params.grantee = obj.d; 465 params.granteeType = obj.gt; 466 params.zid = obj.zid; 467 params.right = obj.right; 468 params.negative = (obj.deny == "1"); 469 470 return new ZmAccessControlEntry(params); 471 }; 472