1 /** 2 * @fileOverview 3 * @author <a href="https://www.labkey.org">LabKey</a> (<a href="mailto:info@labkey.com">info@labkey.com</a>) 4 * @license Copyright (c) 2009-2018 LabKey Corporation 5 * <p/> 6 * Licensed under the Apache License, Version 2.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 9 * <p/> 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * <p/> 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * <p/> 18 */ 19 20 21 /** 22 * @class Represents a security policy for a particular securable resource on the server. In general, you 23 * should obtain an instance of this class from the LABKEY.Security.getPolicy() method. You may use the methods 24 * of this class to alter the policy and save it back to the server using the LABKEY.Security.savePolicy() method. 25 * <p> 26 * The following definitions should be helpful in understanding the methods of this class: 27 * <ul> 28 * <li><b>Principal:</b> A user principal, which can be either a user or a group. Users and groups are both 29 * user principals, and in a security policy, a user principal is assigned to a given role.</li> 30 * <li><b>Role:</b> A role grants a specific set of permissions. For example, the 'Reader' role grants the read permission. 31 * Roles are identified by unique names (usually a fully-qualified Java class name). A full set of roles is obtainable 32 * from the LABKEY.Security.getRoles() method.</li> 33 * <li><b>Direct vs Effective Assignment:</b> In a policy, principals are assigned to one or more roles. However, because a 34 * principal might be a group, the users that belong to that group are effectively in whatever role the group is 35 * assigned to. In this situation, the user is 'effectively' assigned to the role, while the group is 'directly' 36 * assigned to the role. Asking for a user's effective roles will return all roles the user is directly assigned to 37 * plus all roles the groups the user belongs to are assigned to.</li> 38 * </ul> 39 * <p>Additional Documentation: 40 * <ul> 41 * <li><a href="https://www.labkey.org/Documentation/wiki-page.view?name=security">LabKey Security and Accounts</a></li> 42 * </ul> 43 * </p> 44 * @example 45 <script type="text/javascript"> 46 LABKEY.Security.getPolicy({ 47 resourceId: .... 48 successCallback: onGetPolicy 49 }); 50 51 function onGetPolicy(policy, relevantRoles) 52 { 53 //policy is an instance of this class 54 //relevantRoles is an array of role unique names that are relevant to the resource 55 } 56 </script> 57 */ 58 LABKEY.SecurityPolicy = Ext.extend(Ext.util.Observable, { 59 60 guestsPrincipal:-3, 61 noPermissionsRole: "org.labkey.api.security.roles.NoPermissionsRole", 62 63 constructor : function(config) 64 { 65 LABKEY.SecurityPolicy.superclass.constructor.apply(this, arguments); 66 67 this.policy = config; 68 this._dirty = false; 69 70 /** 71 * @memberOf LABKEY.SecurityPolicy# 72 * @name change 73 * @event 74 * @description Fired after the policy has been changed in some way. 75 */ 76 this.addEvents({ 77 "change": true 78 }); 79 80 }, 81 82 /** 83 * Returns the resource ID this policy applies to. Note that this may not be same ID that was requested. 84 * If the requested resource inherits its permissions from an ancestor resource, this method will return 85 * the ID of the nearest resource that has an policy associated with it. 86 * @name getResourceId 87 * @function 88 * @memberOf LABKEY.SecurityPolicy# 89 * @returns The resource ID for this policy. 90 */ 91 getResourceId : function() 92 { 93 return this.policy.resourceId; 94 }, 95 96 /** 97 * Returns true if this policy is empty (i.e., has no role assignments). 98 * @name isEmpty 99 * @function 100 * @memberOf LABKEY.SecurityPolicy# 101 * @returns true if this policy is empty, false otherwise. 102 */ 103 isEmpty : function() 104 { 105 return this.policy.assignments.length == 0; 106 }, 107 108 /** 109 * Returns true if this policy was inherited from an ancestor resource (see getResourceId()) 110 * @name isInherited 111 * @function 112 * @memberOf LABKEY.SecurityPolicy# 113 * @returns true if this policy was inherited, false otherwise. 114 */ 115 isInherited : function() 116 { 117 return this.policy.requestedResourceId != this.policy.resourceId; 118 }, 119 120 /** 121 * Returns the array of roles to which the given principal is directly assigned. 122 * @name getAssignedRoles 123 * @function 124 * @memberOf LABKEY.SecurityPolicy# 125 * @param principalId The ID of the principal. 126 * @returns An array of role unique names. 127 */ 128 getAssignedRoles : function(principalId) 129 { 130 var idx, assgn; 131 var roles = []; 132 for (idx = 0; idx < this.policy.assignments.length; ++idx) 133 { 134 assgn = this.policy.assignments[idx]; 135 if(assgn.userId == principalId) 136 roles.push(assgn.role); 137 } 138 return roles; 139 }, 140 141 /** 142 * Returns an array of principal IDs that are directly assigned to a given role. 143 * @name getAssignedPrincipals 144 * @function 145 * @memberOf LABKEY.SecurityPolicy# 146 * @param role The unique name of the role 147 * @returns An array of principal IDs 148 */ 149 getAssignedPrincipals : function(role) 150 { 151 var idx, len, assgn; 152 var principals = []; 153 for (idx = 0, len=this.policy.assignments.length ; idx < len ; ++idx) 154 { 155 assgn = this.policy.assignments[idx]; 156 if (assgn.role == role) 157 principals.push(assgn.userId); 158 } 159 return principals; 160 }, 161 162 /** 163 * Adds a direct role assignment to the policy. 164 * @name addRoleAssignment 165 * @function 166 * @memberOf LABKEY.SecurityPolicy# 167 * @param principalId The principal ID 168 * @param role The role unique name 169 */ 170 addRoleAssignment : function(principalId, role) 171 { 172 this.removeRoleAssignment(principalId, this.noPermissionsRole); 173 174 var idx, len, assgn; 175 for (idx = 0, len = this.policy.assignments.length; idx < len ; ++idx) 176 { 177 assgn = this.policy.assignments[idx]; 178 if (assgn.userId == principalId && assgn.role == role) 179 return; 180 } 181 this.policy.assignments.push({ 182 userId: principalId, 183 role: role 184 }); 185 this.fireEvent("change"); 186 this._dirty = true; 187 }, 188 189 /** 190 * Removes a direct role assignment from the policy. 191 * @name removeRoleAssignment 192 * @function 193 * @memberOf LABKEY.SecurityPolicy# 194 * @param principalId The principal ID 195 * @param role The role unique name 196 */ 197 removeRoleAssignment : function(principalId, role) 198 { 199 var idx, assgn; 200 for (idx = 0; idx < this.policy.assignments.length; ++idx) 201 { 202 assgn = this.policy.assignments[idx]; 203 if (assgn.userId == principalId && assgn.role == role) 204 break; 205 } 206 if(idx < this.policy.assignments.length) 207 { 208 this.policy.assignments.splice(idx, 1); 209 this.fireEvent("change"); 210 this._dirty = true; 211 } 212 }, 213 214 /** 215 * Removes all direct role assignments for the given principal 216 * @name clearRoleAssignments 217 * @function 218 * @memberOf LABKEY.SecurityPolicy# 219 * @param principalId The principal ID 220 */ 221 clearRoleAssignments : function(principalId) 222 { 223 if (undefined === principalId) 224 { 225 this.policy.assignments = []; 226 this.fireEvent("change"); 227 this._dirty = true; 228 return; 229 } 230 231 var idx, assgn, len = this.policy.assignments.length; 232 for (idx = len-1 ; idx >= 0 ; --idx) 233 { 234 assgn = this.policy.assignments[idx]; 235 if (assgn.userId == principalId) 236 this.policy.assignments.splice(idx, 1); 237 } 238 if (len != this.policy.assignments.length) 239 { 240 this.fireEvent("change"); 241 this._dirty = true; 242 } 243 }, 244 245 /** 246 * Returns all the roles the principal is effectively assigned to in this policy. See the definitions 247 * in the class description for the distinction between effective and direct assignment. 248 * @name getEffectiveRoles 249 * @function 250 * @memberOf LABKEY.SecurityPolicy# 251 * @param principalId The principal ID 252 * @param membershipsTable The group memberships table. This is required to determine the groups 253 * the principal belongs to. You can obtain this table by requesting the 'Members' table from the 'Core' 254 * schema using LABKEY.Query.selectRows(). 255 * @returns An array of roles the principal is effectively playing. 256 */ 257 getEffectiveRoles : function(principalId, membershipsTable) 258 { 259 var ids = this.getGroupsForPrincipal(principalId, membershipsTable); 260 ids.push(principalId); 261 return this.getEffectiveRolesForIds(ids); 262 }, 263 264 /** 265 * Returns an object containing a property per role the given principals are effectively playing. 266 * The name of each property is the role unique name, and the value of each property is simply 'true'. 267 * Thus, the returned object is essentially a Set. 268 * @name getEffectiveRolesForIds 269 * @function 270 * @memberOf LABKEY.SecurityPolicy# 271 * @param ids An array of principal IDs 272 * @returns An object with a property per unique role name the users are effectively playing. 273 */ 274 getEffectiveRolesForIds : function(ids) 275 { 276 var idxAssgn, assgn, idxIds; 277 var set = {}; 278 for (idxAssgn = 0; idxAssgn < this.policy.assignments.length; ++idxAssgn) 279 { 280 assgn = this.policy.assignments[idxAssgn]; 281 for (idxIds = 0; idxIds < ids.length; ++idxIds) 282 { 283 if(ids[idxIds] == assgn.userId) 284 set[assgn.role]=true; 285 } 286 } 287 return set; 288 // var roles = []; 289 // for (var role in set) 290 // roles.push(role); 291 // return roles; 292 }, 293 294 295 /** 296 * Returns all groups this principal belongs to. This function allows for the possibility 297 * that groups may contain other groups. 298 * @name getGroupsForPrincipal 299 * @function 300 * @memberOf LABKEY.SecurityPolicy# 301 * @param principalId The principal 302 * @param membershipsTable The group memberships table. This is required to determine the groups 303 * the principal belongs to. You can obtain this table by requesting the 'Members' table from the 'Core' 304 * schema using LABKEY.Query.selectRows(). 305 * @returns An array of group IDs this user belongs to. 306 */ 307 getGroupsForPrincipal : function(principalId, membershipsTable) 308 { 309 //recurses to determine all relevant groups for a given principal id 310 var rows = membershipsTable.rows || membershipsTable; 311 var idx, row; 312 var groups = []; 313 314 for(idx = 0; idx < rows.length; ++idx) 315 { 316 row = rows[idx]; 317 if(row.UserId == principalId) 318 groups = groups.concat(row.GroupId, this.getGroupsForPrincipal(row.GroupId, membershipsTable)); 319 } 320 return groups; 321 }, 322 323 /** 324 * Sets the modified property to a new value. The modified property is used during save to determine if the policy has 325 * been modified since it was selected. You may pass null to this method to disable this optimistic concurrency 326 * check and force the policy to save, even if another user modified it since it was selected. 327 * @name setModified 328 * @function 329 * @memberOf LABKEY.SecurityPolicy# 330 * @param modified New modified value, or null to override optimistic concurrency check. 331 */ 332 setModified : function(modified) 333 { 334 this.policy.modified = modified; 335 this.fireEvent("change"); 336 this._dirty = true; 337 }, 338 339 /** 340 * Returns true if this policy has been modified. 341 * @name isDirty 342 * @function 343 * @memberOf LABKEY.SecurityPolicy# 344 * @returns true if modified, false otherwise. 345 */ 346 isDirty : function() 347 { 348 return this._dirty; 349 }, 350 351 /** 352 * Creates a new copy of this policy, optionally resetting the resource ID. 353 * @name copy 354 * @function 355 * @memberOf LABKEY.SecurityPolicy# 356 * @param resourceid A different resource ID to use. This is typically used when you 357 * want to create a new policy for a resource using the policy from another resource as a template. 358 * @returns A new instance of this class which is a deep copy of the current instance. 359 */ 360 copy : function(resourceid) 361 { 362 var config = Ext.apply(this.policy); 363 if (resourceid) 364 config.requestedResourceId = config.resourceId = resourceid; 365 config.assignments = this.copyArray(config.assignments); 366 return new LABKEY.SecurityPolicy(this.policy); 367 }, 368 369 /* private shallow copy*/ 370 copyArray : function(a) 371 { 372 var copy = []; 373 for (var i=0 ; i<a.length ; i++) 374 copy.push(a[i]); 375 return copy; 376 } 377 }); 378