001 /* 002 // $Id: //open/mondrian/src/main/mondrian/olap/RoleImpl.java#12 $ 003 // This software is subject to the terms of the Common Public License 004 // Agreement, available at the following URL: 005 // http://www.opensource.org/licenses/cpl.html. 006 // Copyright (C) 2002-2002 Kana Software, Inc. 007 // Copyright (C) 2002-2008 Julian Hyde and others 008 // All Rights Reserved. 009 // You must accept the terms of that agreement to use this software. 010 // 011 // jhyde, Oct 5, 2002 012 */ 013 014 package mondrian.olap; 015 016 import java.util.*; 017 018 /** 019 * <code>RoleImpl</code> is Mondrian's default implementation for the 020 * <code>Role</code> interface. 021 * 022 * @author jhyde 023 * @since Oct 5, 2002 024 * @version $Id: //open/mondrian/src/main/mondrian/olap/RoleImpl.java#12 $ 025 */ 026 public class RoleImpl implements Role { 027 private boolean mutable = true; 028 private final Map<Schema, Access> schemaGrants = 029 new HashMap<Schema, Access>(); 030 private final Map<Cube, Access> cubeGrants = 031 new HashMap<Cube, Access>(); 032 private final Map<Dimension, Access> dimensionGrants = 033 new HashMap<Dimension, Access>(); 034 private final Map<Hierarchy, HierarchyAccessImpl> hierarchyGrants = 035 new HashMap<Hierarchy, HierarchyAccessImpl>(); 036 037 /** 038 * Creates a role with no permissions. 039 */ 040 public RoleImpl() { 041 } 042 043 protected RoleImpl clone() { 044 RoleImpl role = new RoleImpl(); 045 role.mutable = mutable; 046 role.schemaGrants.putAll(schemaGrants); 047 role.cubeGrants.putAll(cubeGrants); 048 role.dimensionGrants.putAll(dimensionGrants); 049 for (Map.Entry<Hierarchy, HierarchyAccessImpl> entry : 050 hierarchyGrants.entrySet()) { 051 role.hierarchyGrants.put( 052 entry.getKey(), 053 (HierarchyAccessImpl) entry.getValue().clone()); 054 } 055 return role; 056 } 057 058 /** 059 * Returns a copy of this <code>Role</code> which can be modified. 060 */ 061 public RoleImpl makeMutableClone() { 062 RoleImpl role = clone(); 063 role.mutable = true; 064 return role; 065 } 066 067 /** 068 * Prevents any further modifications. 069 * @post !isMutable() 070 */ 071 public void makeImmutable() { 072 mutable = false; 073 } 074 075 /** 076 * Returns whether modifications are possible. 077 */ 078 public boolean isMutable() { 079 return mutable; 080 } 081 082 /** 083 * Defines access to all cubes and dimensions in a schema. 084 * 085 * @param schema Schema whose access to grant/deny. 086 * @param access An {@link Access access code} 087 * 088 * @pre schema != null 089 * @pre access == Access.ALL || access == Access.NONE || access == Access.ALL_DIMENSIONS 090 * @pre isMutable() 091 */ 092 public void grant(Schema schema, Access access) { 093 assert schema != null; 094 assert access == Access.ALL || access == Access.NONE || access == Access.ALL_DIMENSIONS; 095 assert isMutable(); 096 schemaGrants.put(schema, access); 097 } 098 099 public Access getAccess(Schema schema) { 100 assert schema != null; 101 return toAccess(schemaGrants.get(schema)); 102 } 103 104 private static Access toAccess(Access access) { 105 return access == null ? Access.NONE : access; 106 } 107 108 /** 109 * Defines access to a cube. 110 * 111 * @param cube Cube whose access to grant/deny. 112 * @param access An {@link Access access code} 113 * 114 * @pre cube != null 115 * @pre access == Access.ALL || access == Access.NONE 116 * @pre isMutable() 117 */ 118 public void grant(Cube cube, Access access) { 119 Util.assertPrecondition(cube != null, "cube != null"); 120 assert access == Access.ALL || access == Access.NONE; 121 Util.assertPrecondition(isMutable(), "isMutable()"); 122 cubeGrants.put(cube, access); 123 } 124 125 public Access getAccess(Cube cube) { 126 assert cube != null; 127 Access access = cubeGrants.get(cube); 128 if (access == null) { 129 access = schemaGrants.get(cube.getSchema()); 130 } 131 return toAccess(access); 132 } 133 134 /** 135 * Removes the upper level restriction of each hierarchy in this role. 136 * This will allow member names to be resolved even if the upper levels 137 * are not visible. 138 * 139 * <p>For example, it should be possible to resolve 140 * [Store].[USA].[CA].[San Francisco] even if the role cannot see the 141 * nation level. 142 */ 143 public void removeTopLevels() { 144 for (Map.Entry<Hierarchy,HierarchyAccessImpl> entry 145 : hierarchyGrants.entrySet()) 146 { 147 final HierarchyAccessImpl hierarchyAccess = entry.getValue(); 148 if (hierarchyAccess.topLevel != null) { 149 final HierarchyAccessImpl hierarchyAccessClone = 150 new HierarchyAccessImpl( 151 hierarchyAccess.hierarchy, 152 hierarchyAccess.access, 153 null, 154 hierarchyAccess.bottomLevel, 155 hierarchyAccess.rollupPolicy); 156 hierarchyAccessClone.memberGrants.putAll( 157 hierarchyAccess.memberGrants); 158 entry.setValue(hierarchyAccessClone); 159 } 160 } 161 } 162 163 /** 164 * Defines access to a dimension. 165 * 166 * @param dimension Hierarchy whose access to grant/deny. 167 * @param access An {@link Access access code} 168 * 169 * @pre dimension != null 170 * @pre access == Access.ALL || access == Access.NONE 171 * @pre isMutable() 172 */ 173 public void grant(Dimension dimension, Access access) { 174 assert dimension != null; 175 assert access == Access.ALL || access == Access.NONE; 176 Util.assertPrecondition(isMutable(), "isMutable()"); 177 dimensionGrants.put(dimension, access); 178 } 179 180 public Access getAccess(Dimension dimension) { 181 assert dimension != null; 182 Access access = dimensionGrants.get(dimension); 183 if (access != null) { 184 return toAccess(access); 185 } 186 // If the role has access to a cube this dimension is part of, that's 187 // good enough. 188 for (Map.Entry<Cube,Access> cubeGrant : cubeGrants.entrySet()) { 189 access = toAccess(cubeGrant.getValue()); 190 if (access == Access.NONE) { 191 continue; 192 } 193 final Dimension[] dimensions = cubeGrant.getKey().getDimensions(); 194 for (Dimension dimension1 : dimensions) { 195 if (dimension1 == dimension) { 196 return access; 197 } 198 } 199 } 200 // Check access at the schema level. 201 switch (getAccess(dimension.getSchema())) { 202 case ALL: 203 case ALL_DIMENSIONS: 204 return Access.ALL; 205 default: 206 return Access.NONE; 207 } 208 } 209 210 /** 211 * Defines access to a hierarchy. 212 * 213 * @param hierarchy Hierarchy whose access to grant/deny. 214 * @param access An {@link Access access code} 215 * @param topLevel Top-most level which can be accessed, or null if the 216 * highest level. May only be specified if <code>access</code> is 217 * {@link mondrian.olap.Access#CUSTOM}. 218 * @param bottomLevel Bottom-most level which can be accessed, or null if 219 * the lowest level. May only be specified if <code>access</code> is 220 * {@link mondrian.olap.Access#CUSTOM}. 221 * 222 * @param rollupPolicy 223 * @pre hierarchy != null 224 * @pre Access.instance().isValid(access) 225 * @pre (access == Access.CUSTOM) || (topLevel == null && bottomLevel == null) 226 * @pre topLevel == null || topLevel.getHierarchy() == hierarchy 227 * @pre bottomLevel == null || bottomLevel.getHierarchy() == hierarchy 228 * @pre isMutable() 229 */ 230 public void grant( 231 Hierarchy hierarchy, 232 Access access, 233 Level topLevel, 234 Level bottomLevel, 235 RollupPolicy rollupPolicy) 236 { 237 assert hierarchy != null; 238 assert access != null; 239 assert (access == Access.CUSTOM) 240 || (topLevel == null && bottomLevel == null); 241 assert topLevel == null || topLevel.getHierarchy() == hierarchy; 242 assert bottomLevel == null || bottomLevel.getHierarchy() == hierarchy; 243 assert isMutable(); 244 assert rollupPolicy != null; 245 hierarchyGrants.put( 246 hierarchy, 247 new HierarchyAccessImpl( 248 hierarchy, access, topLevel, bottomLevel, rollupPolicy)); 249 } 250 251 public Access getAccess(Hierarchy hierarchy) { 252 assert hierarchy != null; 253 HierarchyAccessImpl hierarchyAccess = hierarchyGrants.get(hierarchy); 254 if (hierarchyAccess != null) { 255 return hierarchyAccess.access; 256 } 257 return getAccess(hierarchy.getDimension()); 258 } 259 260 public HierarchyAccess getAccessDetails(Hierarchy hierarchy) { 261 Util.assertPrecondition(hierarchy != null, "hierarchy != null"); 262 return hierarchyGrants.get(hierarchy); 263 } 264 265 public Access getAccess(Level level) { 266 assert level != null; 267 HierarchyAccessImpl hierarchyAccess = 268 hierarchyGrants.get(level.getHierarchy()); 269 if (hierarchyAccess != null) { 270 if (hierarchyAccess.topLevel != null && 271 level.getDepth() < hierarchyAccess.topLevel.getDepth()) { 272 return Access.NONE; 273 } 274 if (hierarchyAccess.bottomLevel != null && 275 level.getDepth() > hierarchyAccess.bottomLevel.getDepth()) { 276 return Access.NONE; 277 } 278 return hierarchyAccess.access; 279 } 280 return getAccess(level.getDimension()); 281 } 282 283 /** 284 * Defines access to a member in a hierarchy. 285 * 286 * <p>Notes:<ol> 287 * <li>The order of grants matters. If you grant/deny access to a 288 * member, previous grants/denials to its descendants are ignored.</li> 289 * <li>Member grants do not supersde top/bottom levels set using 290 * {@link #grant(Hierarchy, Access, Level, Level, mondrian.olap.Role.RollupPolicy)}. 291 * <li>If you have access to a member, then you can see its ancestors 292 * <em>even those explicitly denied</em>, up to the top level. 293 * </ol> 294 * 295 * @pre member != null 296 * @pre isMutable() 297 * @pre getAccess(member.getHierarchy()) == Access.CUSTOM 298 */ 299 public void grant(Member member, Access access) { 300 Util.assertPrecondition(member != null, "member != null"); 301 assert isMutable(); 302 assert getAccess(member.getHierarchy()) == Access.CUSTOM; 303 HierarchyAccessImpl hierarchyAccess = hierarchyGrants.get(member.getHierarchy()); 304 assert hierarchyAccess != null; 305 assert hierarchyAccess.access == Access.CUSTOM; 306 hierarchyAccess.grant(member, access); 307 } 308 309 public Access getAccess(Member member) { 310 assert member != null; 311 HierarchyAccessImpl hierarchyAccess = 312 hierarchyGrants.get(member.getHierarchy()); 313 if (hierarchyAccess != null) { 314 return hierarchyAccess.getAccess(member); 315 } 316 return getAccess(member.getDimension()); 317 } 318 319 public Access getAccess(NamedSet set) { 320 Util.assertPrecondition(set != null, "set != null"); 321 return Access.ALL; 322 } 323 324 public boolean canAccess(OlapElement olapElement) { 325 Util.assertPrecondition(olapElement != null, "olapElement != null"); 326 if (olapElement instanceof Member) { 327 return getAccess((Member) olapElement) != Access.NONE; 328 } else if (olapElement instanceof Level) { 329 return getAccess((Level) olapElement) != Access.NONE; 330 } else if (olapElement instanceof NamedSet) { 331 return getAccess((NamedSet) olapElement) != Access.NONE; 332 } else if (olapElement instanceof Hierarchy) { 333 return getAccess((Hierarchy) olapElement) != Access.NONE; 334 } else if (olapElement instanceof Cube) { 335 return getAccess((Cube) olapElement) != Access.NONE; 336 } else if (olapElement instanceof Dimension) { 337 return getAccess((Dimension) olapElement) != Access.NONE; 338 } else { 339 return false; 340 } 341 } 342 343 /** 344 * Creates an element which represents all access to a hierarchy. 345 * 346 * @param hierarchy Hierarchy 347 * @return element representing all access to a given hierarchy 348 */ 349 public static HierarchyAccess createAllAccess(Hierarchy hierarchy) { 350 final Level[] levels = hierarchy.getLevels(); 351 return new HierarchyAccessImpl( 352 hierarchy, Access.ALL, levels[0], 353 levels[levels.length - 1], Role.RollupPolicy.FULL); 354 } 355 356 public static Role union(final List<Role> roleList) { 357 assert roleList.size() > 0; 358 return new UnionRoleImpl(roleList); 359 } 360 361 // ~ Inner classes -------------------------------------------------------- 362 363 /** 364 * Represents the access that a role has to a particular hierarchy. 365 */ 366 private static class HierarchyAccessImpl implements Role.HierarchyAccess { 367 private final Hierarchy hierarchy; 368 private final Level topLevel; 369 private final Access access; 370 private final Level bottomLevel; 371 private final Map<Member, Access> memberGrants = 372 new HashMap<Member, Access>(); 373 private final RollupPolicy rollupPolicy; 374 375 /** 376 * Creates a <code>HierarchyAccessImpl</code> 377 */ 378 HierarchyAccessImpl( 379 Hierarchy hierarchy, 380 Access access, 381 Level topLevel, 382 Level bottomLevel, 383 RollupPolicy rollupPolicy) 384 { 385 assert access != null; 386 this.hierarchy = hierarchy; 387 this.access = access; 388 final Level[] levels = hierarchy.getLevels(); 389 this.topLevel = (topLevel == null) 390 ? levels[0] : topLevel; 391 this.bottomLevel = (bottomLevel == null) 392 ? levels[levels.length - 1] : bottomLevel; 393 assert rollupPolicy != null; 394 this.rollupPolicy = rollupPolicy; 395 } 396 397 public HierarchyAccess clone() { 398 HierarchyAccessImpl hierarchyAccess = 399 new HierarchyAccessImpl( 400 hierarchy, access, topLevel, bottomLevel, rollupPolicy); 401 hierarchyAccess.memberGrants.putAll(memberGrants); 402 return hierarchyAccess; 403 } 404 405 void grant(Member member, Access access) { 406 Util.assertTrue(member.getHierarchy() == hierarchy); 407 // Remove any existing grants to descendants of "member" 408 for (Iterator<Member> memberIter = 409 memberGrants.keySet().iterator(); memberIter.hasNext();) { 410 Member m = memberIter.next(); 411 if (m.isChildOrEqualTo(member)) { 412 memberIter.remove(); 413 } 414 } 415 416 memberGrants.put(member, access); 417 418 if (access == Access.NONE) { 419 // If an ancestor of this member has any children with 'All' 420 // access, set them to Custom. 421 loop: 422 for (Member m = member.getParentMember(); 423 m != null; 424 m = m.getParentMember()) { 425 final Access memberAccess = memberGrants.get(m); 426 if (memberAccess == null) { 427 if (childGrantsExist(m)) { 428 memberGrants.put(m, Access.CUSTOM); 429 } else { 430 break; 431 } 432 } else if (memberAccess == Access.CUSTOM) { 433 // Ancestor does not inherit access, but used to have 434 // at least one child with access. See if it still 435 // does... 436 if (childGrantsExist(m)) { 437 memberGrants.put(m, Access.CUSTOM); 438 } else { 439 break; 440 } 441 } else if (memberAccess == Access.NONE) { 442 // Ancestor is explicitly marked having no access. 443 // Leave it that way. 444 break; 445 } else if (memberAccess == Access.ALL) { 446 // Ancestor is explicitly marked having all access. 447 // Leave it that way. 448 break; 449 } 450 } 451 452 } else { 453 454 // Create 'custom' access for any ancestors of 'member' which 455 // do not have explicit access but which have at least one 456 // child visible. 457 for (Member m = member.getParentMember(); 458 m != null; 459 m = m.getParentMember()) { 460 switch (toAccess(memberGrants.get(m))) { 461 case NONE: 462 memberGrants.put(m, Access.CUSTOM); 463 break; 464 default: 465 // Existing access (All or Custom) is OK. 466 break; 467 } 468 } 469 } 470 } 471 472 private boolean childGrantsExist(Member parent) { 473 for (Map.Entry<Member, Access> entry : memberGrants.entrySet()) { 474 final Member member = entry.getKey(); 475 if (member.getParentMember() == parent) { 476 final Access access = toAccess(entry.getValue()); 477 if (access != Access.NONE) { 478 return true; 479 } 480 } 481 } 482 return false; 483 } 484 485 public Access getAccess(Member member) { 486 if (this.access != Access.CUSTOM) { 487 return this.access; 488 } 489 if (member.getLevel().getDepth() < getTopLevelDepth()) { 490 // no access 491 return Access.NONE; 492 } else if (member.getLevel().getDepth() > getBottomLevelDepth()) { 493 // no access 494 return Access.NONE; 495 } else { 496 // Check whether there is an explicit grant for the member or 497 // an ancestor. 498 for (Member m = member; m != null; m = m.getParentMember()) { 499 final Access memberAccess = memberGrants.get(m); 500 if (memberAccess == null) { 501 continue; 502 } 503 if (memberAccess == Access.CUSTOM && 504 m != member) { 505 // If member's ancestor has custom access, that 506 // means that member has no access. 507 return Access.NONE; 508 } 509 return memberAccess; 510 } 511 // If there is no inherited access, check for implicit access. 512 // A member is implicitly visible if one of its descendants is 513 // visible. 514 for (Map.Entry<Member, Access> entry : memberGrants.entrySet()) { 515 final Member grantedMember = entry.getKey(); 516 switch (entry.getValue()) { 517 case NONE: 518 continue; 519 } 520 for (Member m = grantedMember; m != null; m = m.getParentMember()) { 521 if (m == member) { 522 return Access.CUSTOM; 523 } 524 if (m != grantedMember && memberGrants.get(m) != null) { 525 break; 526 } 527 } 528 } 529 return Access.NONE; 530 } 531 } 532 533 public final int getTopLevelDepth() { 534 return topLevel.getDepth(); 535 } 536 537 public final int getBottomLevelDepth() { 538 return bottomLevel.getDepth(); 539 } 540 541 public RollupPolicy getRollupPolicy() { 542 return rollupPolicy; 543 } 544 545 public boolean hasInaccessibleDescendants(Member member) { 546 for (Map.Entry<Member,Access> entry : memberGrants.entrySet()) { 547 switch (entry.getValue()) { 548 case NONE: 549 Member grantedMember = entry.getKey(); 550 for (Member m = grantedMember; 551 m != null; m = m.getParentMember()) 552 { 553 if (m.equals(member)) { 554 // We have proved that this granted member is a 555 // descendant of 'member'. 556 return true; 557 } 558 } 559 } 560 } 561 // All descendants are accessible. 562 return false; 563 } 564 } 565 566 567 } 568 569 // End RoleImpl.java