001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2007-2013 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.olap;
011
012import org.apache.log4j.Logger;
013
014import java.util.ArrayList;
015import java.util.List;
016
017/**
018 * Implementation of {@link Role} which combines the privileges of several
019 * roles and has the superset of their privileges.
020 *
021 * @see mondrian.olap.RoleImpl#union(java.util.List)
022 *
023 * @author jhyde
024 * @since Nov 26, 2007
025 */
026class UnionRoleImpl implements Role {
027    private static final Logger LOGGER =
028        Logger.getLogger(UnionRoleImpl.class);
029    private final List<Role> roleList;
030
031    /**
032     * Creates a UnionRoleImpl.
033     *
034     * @param roleList List of constituent roles
035     */
036    UnionRoleImpl(List<Role> roleList) {
037        this.roleList = new ArrayList<Role>(roleList);
038    }
039
040    public Access getAccess(Schema schema) {
041        Access access = Access.NONE;
042        for (Role role : roleList) {
043            access = max(access, role.getAccess(schema));
044            if (access == Access.ALL) {
045                break;
046            }
047        }
048        LOGGER.debug(
049            "Access level " + access
050            + " granted to schema " + schema.getName()
051            + " because of a union of roles.");
052        return access;
053    }
054
055    /**
056     * Returns the larger of two enum values. Useful if the enums are sorted
057     * so that more permissive values come after less permissive values.
058     *
059     * @param t1 First value
060     * @param t2 Second value
061     * @return larger of the two values
062     */
063    private static <T extends Enum<T>> T max(T t1, T t2) {
064        if (t1.ordinal() > t2.ordinal()) {
065            return t1;
066        } else {
067            return t2;
068        }
069    }
070
071    public Access getAccess(Cube cube) {
072        Access access = Access.NONE;
073        for (Role role : roleList) {
074            access = max(access, role.getAccess(cube));
075            if (access == Access.ALL) {
076                break;
077            }
078        }
079        LOGGER.debug(
080            "Access level " + access
081            + " granted to cube " + cube.getName()
082            + " because of a union of roles.");
083        return access;
084    }
085
086    public Access getAccess(Dimension dimension) {
087        Access access = Access.NONE;
088        for (Role role : roleList) {
089            access = max(access, role.getAccess(dimension));
090            if (access == Access.ALL) {
091                break;
092            }
093        }
094        LOGGER.debug(
095            "Access level " + access
096            + " granted to dimension " + dimension.getUniqueName()
097            + " because of a union of roles.");
098        return access;
099    }
100
101    public Access getAccess(Hierarchy hierarchy) {
102        Access access = Access.NONE;
103        for (Role role : roleList) {
104            access = max(access, role.getAccess(hierarchy));
105            if (access == Access.ALL) {
106                break;
107            }
108        }
109        LOGGER.debug(
110            "Access level " + access
111            + " granted to hierarchy " + hierarchy.getUniqueName()
112            + " because of a union of roles.");
113        return access;
114    }
115
116    public HierarchyAccess getAccessDetails(final Hierarchy hierarchy) {
117        List<HierarchyAccess> list = new ArrayList<HierarchyAccess>();
118        for (Role role : roleList) {
119            final HierarchyAccess accessDetails =
120                role.getAccessDetails(hierarchy);
121            if (accessDetails != null) {
122                list.add(accessDetails);
123            }
124        }
125        // If none of the roles call out access details, we shouldn't either.
126        if (list.isEmpty()) {
127            return null;
128        }
129        HierarchyAccess hierarchyAccess =
130            new UnionHierarchyAccessImpl(hierarchy, list);
131        if (list.size() > 5) {
132            hierarchyAccess =
133                new RoleImpl.CachingHierarchyAccess(hierarchyAccess);
134        }
135        return hierarchyAccess;
136    }
137
138    public Access getAccess(Level level) {
139        Access access = Access.NONE;
140        for (Role role : roleList) {
141            access = max(access, role.getAccess(level));
142            if (access == Access.ALL) {
143                break;
144            }
145        }
146        LOGGER.debug(
147            "Access level " + access
148            + " granted to level " + level.getUniqueName()
149            + " because of a union of roles.");
150        return access;
151    }
152
153    public Access getAccess(Member member) {
154        assert member != null;
155        HierarchyAccess hierarchyAccess =
156            getAccessDetails(member.getHierarchy());
157        if (hierarchyAccess != null) {
158            return hierarchyAccess.getAccess(member);
159        }
160        final Access access = getAccess(member.getDimension());
161        LOGGER.debug(
162            "Access level " + access
163            + " granted to member " + member.getUniqueName()
164            + " because of a union of roles.");
165        return access;
166    }
167
168    public Access getAccess(NamedSet set) {
169        Access access = Access.NONE;
170        for (Role role : roleList) {
171            access = max(access, role.getAccess(set));
172            if (access == Access.ALL) {
173                break;
174            }
175        }
176        LOGGER.debug(
177            "Access level " + access
178            + " granted to set " + set.getUniqueName()
179            + " because of a union of roles.");
180        return access;
181    }
182
183    public boolean canAccess(OlapElement olapElement) {
184        for (Role role : roleList) {
185            if (role.canAccess(olapElement)) {
186                return true;
187            }
188        }
189        return false;
190    }
191
192    /**
193     * Implementation of {@link mondrian.olap.Role.HierarchyAccess} that
194     * gives access to an object if any one of the constituent hierarchy
195     * accesses has access to that object.
196     */
197    private class UnionHierarchyAccessImpl implements HierarchyAccess {
198        private final List<HierarchyAccess> list;
199        private final Hierarchy hierarchy;
200
201        /**
202         * Creates a UnionHierarchyAccessImpl.
203         *
204         * @param hierarchy Hierarchy
205         * @param list List of underlying hierarchy accesses
206         */
207        UnionHierarchyAccessImpl(
208            Hierarchy hierarchy,
209            List<HierarchyAccess> list)
210        {
211            this.hierarchy = hierarchy;
212            this.list = list;
213        }
214
215        public Access getAccess(Member member) {
216            Access access = Access.NONE;
217            final int roleCount = roleList.size();
218            for (int i = 0; i < roleCount; i++) {
219                Role role = roleList.get(i);
220                access = max(access, role.getAccess(member));
221                if (access == Access.ALL) {
222                    break;
223                }
224            }
225            LOGGER.debug(
226                "Access level " + access
227                + " granted to member " + member.getUniqueName()
228                + " because of a union of roles.");
229            return access;
230        }
231
232        public int getTopLevelDepth() {
233            if (!isTopLeveRestricted()) {
234                // We don't restrict the top level.
235                // Return 0 for root.
236                return 0;
237            }
238            int access = Integer.MAX_VALUE;
239            for (HierarchyAccess hierarchyAccess : list) {
240                if (hierarchyAccess.getTopLevelDepth() == 0) {
241                    // No restrictions. Skip.
242                    continue;
243                }
244                access =
245                    Math.min(
246                        access,
247                        hierarchyAccess.getTopLevelDepth());
248                if (access == 0) {
249                    break;
250                }
251            }
252            return access;
253        }
254
255        public int getBottomLevelDepth() {
256            if (!isBottomLeveRestricted()) {
257                // We don't restrict the bottom level.
258                return list.get(0).getBottomLevelDepth();
259            }
260            int access = -1;
261            for (HierarchyAccess hierarchyAccess : list) {
262                if (hierarchyAccess.getBottomLevelDepth()
263                    == hierarchy.getLevels().length)
264                {
265                    // No restrictions. Skip.
266                    continue;
267                }
268                access =
269                    Math.max(
270                        access,
271                        hierarchyAccess.getBottomLevelDepth());
272            }
273            return access;
274        }
275
276        public RollupPolicy getRollupPolicy() {
277            RollupPolicy rollupPolicy = RollupPolicy.HIDDEN;
278            for (HierarchyAccess hierarchyAccess : list) {
279                rollupPolicy =
280                    max(
281                        rollupPolicy,
282                        hierarchyAccess.getRollupPolicy());
283                if (rollupPolicy == RollupPolicy.FULL) {
284                    break;
285                }
286            }
287            return rollupPolicy;
288        }
289
290        public boolean hasInaccessibleDescendants(Member member) {
291            // If any of the roles return all the members,
292            // we assume that all descendants are accessible when
293            // we create a union of these roles.
294            final Access unionAccess = getAccess(member);
295            if (unionAccess == Access.ALL) {
296                return false;
297            }
298            if (unionAccess == Access.NONE) {
299                return true;
300            }
301            for (HierarchyAccess hierarchyAccess : list) {
302                if (hierarchyAccess.getAccess(member) == Access.CUSTOM
303                    && !hierarchyAccess.hasInaccessibleDescendants(member))
304                {
305                    return false;
306                }
307            }
308            // All of the roles have restricted the descendants in
309            // some way.
310            return true;
311        }
312
313        private boolean isTopLeveRestricted() {
314            for (HierarchyAccess hierarchyAccess : list) {
315                if (hierarchyAccess.getTopLevelDepth() > 0) {
316                    return true;
317                }
318            }
319            return false;
320        }
321
322        private boolean isBottomLeveRestricted() {
323            for (HierarchyAccess hierarchyAccess : list) {
324                if (hierarchyAccess.getBottomLevelDepth()
325                    == hierarchy.getLevels().length)
326                {
327                    return true;
328                }
329            }
330            return false;
331        }
332    }
333}
334
335// End UnionRoleImpl.java