001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019package org.apache.isis.extensions.secman.api.permission;
020
021import java.io.Serializable;
022import java.util.Collection;
023import java.util.Collections;
024import java.util.List;
025
026import org.apache.isis.applib.annotation.Programmatic;
027import org.apache.isis.commons.internal.collections._Lists;
028import org.apache.isis.commons.internal.collections._Multimaps;
029import org.apache.isis.extensions.secman.api.IsisModuleExtSecmanApi;
030import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeatureId;
031
032/**
033 * A serializable value object representing a set of (anonymized) 
034 * {@link ApplicationPermissionValue permission}s.
035 *
036 * <p>
037 *     Intended for value type arithmetic and also for caching.
038 * </p>
039 */
040public class ApplicationPermissionValueSet implements Serializable {
041
042    private static final long serialVersionUID = 1L;
043
044
045    public static abstract class PropertyDomainEvent<T> extends IsisModuleExtSecmanApi.PropertyDomainEvent<ApplicationPermissionValueSet, T> {}
046
047    public static abstract class CollectionDomainEvent<T> extends IsisModuleExtSecmanApi.CollectionDomainEvent<ApplicationPermissionValueSet, T> {}
048
049    public static abstract class ActionDomainEvent extends IsisModuleExtSecmanApi.ActionDomainEvent<ApplicationPermissionValueSet> {}
050
051
052
053    // -- values
054    private final List<ApplicationPermissionValue> values;
055    /**
056     * Partitions the {@link ApplicationPermissionValue permissions} by feature and within that orders according to their
057     * evaluation precedence.
058     *
059     * <p>
060     *     The following sketches out what is stored:
061     * </p>
062     * <pre>
063     *     com.foo.Bar#bip -> ALLOW, CHANGING
064     *                     -> ALLOW, VIEWING
065     *                     -> VETO, VIEWING
066     *                     -> VETO, CHANGING
067     *     com.foo.Bar     -> ALLOW, CHANGING
068     *                     -> ALLOW, VIEWING
069     *                     -> VETO, VIEWING
070     *                     -> VETO, CHANGING
071     *     com.foo         -> ALLOW, CHANGING
072     *                     -> ALLOW, VIEWING
073     *                     -> VETO, VIEWING
074     *                     -> VETO, CHANGING
075     *     com             -> ALLOW, CHANGING
076     *                     -> ALLOW, VIEWING
077     *                     -> VETO, VIEWING
078     *                     -> VETO, CHANGING
079     * </pre>
080     * 
081     * <p>
082     *     Note that {@link org.apache.isis.extensions.security.manager.jdo.dom.permission.ApplicationPermissionRule#ALLOW allow} rule
083     *     is ordered before {@link org.apache.isis.extensions.security.manager.jdo.dom.permission.ApplicationPermissionRule#VETO veto} rule
084     *     meaning that it is checked first and therefore also takes precedence.
085     * </p>
086     */
087    private final _Multimaps.SetMultimap<ApplicationFeatureId, ApplicationPermissionValue> permissionsByFeature = 
088            _Multimaps.newSortedSetMultimap(
089                    Collections.reverseOrder(ApplicationFeatureId.Comparators.natural()),
090                    null // natural element order
091                    );
092
093    /**
094     * Note that we require PermissionsEvaluationService to be serializable.
095     */
096    private PermissionsEvaluationService permissionsEvaluationService;
097
098
099    // -- constructor
100
101    public ApplicationPermissionValueSet(
102            final List<ApplicationPermissionValue> permissionValues, 
103            final PermissionsEvaluationService permissionsEvaluationService) {
104
105        this.values = Collections.unmodifiableList(_Lists.newArrayList(permissionValues));
106        for (final ApplicationPermissionValue permissionValue : permissionValues) {
107            final ApplicationFeatureId featureId = permissionValue.getFeatureId();
108            permissionsByFeature.putElement(featureId, permissionValue);
109        }
110        this.permissionsEvaluationService = permissionsEvaluationService;
111    }
112
113
114    // -- grants, evaluate
115
116    public static class Evaluation {
117        private final ApplicationPermissionValue permissionValue;
118        private final boolean granted;
119
120        public Evaluation(final ApplicationPermissionValue permissionValue, final boolean granted) {
121            this.permissionValue = permissionValue;
122            this.granted = granted;
123        }
124
125        public ApplicationPermissionValue getCause() {
126            return permissionValue;
127        }
128
129        public boolean isGranted() {
130            return granted;
131        }
132    }
133
134    @Programmatic
135    public boolean grants(final ApplicationFeatureId featureId, final ApplicationPermissionMode mode) {
136        return evaluate(featureId, mode).isGranted();
137    }
138
139    @Programmatic
140    public Evaluation evaluate(
141            final ApplicationFeatureId featureId,
142            final ApplicationPermissionMode mode) {
143        final List<ApplicationFeatureId> pathIds = featureId.getPathIds();
144        for (final ApplicationFeatureId pathId : pathIds) {
145            final Collection<ApplicationPermissionValue> permissionValues = permissionsByFeature.get(pathId);
146            final Evaluation evaluation = permissionsEvaluationService.evaluate(featureId, mode, permissionValues);
147            if(evaluation != null) {
148                return evaluation;
149            }
150        }
151        return new Evaluation(null, false);
152    }
153
154
155    // -- equals, hashCode, toString
156    @Override
157    public boolean equals(final Object o) {
158        if (this == o) return true;
159        if (o == null || getClass() != o.getClass()) return false;
160
161        final ApplicationPermissionValueSet that = (ApplicationPermissionValueSet) o;
162
163        return !(values != null ? !values.equals(that.values) : that.values != null);
164
165    }
166
167    @Override
168    public int hashCode() {
169        return values != null ? values.hashCode() : 0;
170    }
171
172
173    @Override
174    public String toString() {
175        return "ApplicationPermissionValueSet{" +
176                "values=" + values +
177                '}';
178    }
179
180
181
182
183}