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