001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2020, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.openid.connect.sdk.federation.policy;
019
020
021import java.util.LinkedList;
022import java.util.List;
023import java.util.Map;
024
025import net.minidev.json.JSONObject;
026
027import com.nimbusds.oauth2.sdk.ParseException;
028import com.nimbusds.oauth2.sdk.util.CollectionUtils;
029import com.nimbusds.oauth2.sdk.util.StringUtils;
030import com.nimbusds.openid.connect.sdk.federation.policy.language.*;
031import com.nimbusds.openid.connect.sdk.federation.policy.operations.DefaultPolicyOperationCombinationValidator;
032import com.nimbusds.openid.connect.sdk.federation.policy.operations.DefaultPolicyOperationFactory;
033import com.nimbusds.openid.connect.sdk.federation.policy.operations.PolicyOperationCombinationValidator;
034import com.nimbusds.openid.connect.sdk.federation.policy.operations.PolicyOperationFactory;
035
036
037/**
038 * Policy entry for a metadata parameter.
039 *
040 * @see MetadataPolicy
041 *
042 * <p>Related specifications:
043 *
044 * <ul>
045 *     <li>OpenID Connect Federation 1.0, section 4.1.
046 * </ul>
047 */
048public class MetadataPolicyEntry implements Map.Entry<String, List<PolicyOperation>> {
049        
050        
051        /**
052         * The default policy operation factory.
053         */
054        protected final static PolicyOperationFactory DEFAULT_POLICY_OPERATION_FACTORY = new DefaultPolicyOperationFactory();
055        
056        
057        /**
058         * The default policy operation combination validator.
059         */
060        protected final static PolicyOperationCombinationValidator DEFAULT_POLICY_COMBINATION_VALIDATOR = new DefaultPolicyOperationCombinationValidator();
061        
062        
063        /**
064         * The parameter name.
065         */
066        private final String parameterName;
067        
068        
069        /**
070         * The policy operations, empty list if none.
071         */
072        private List<PolicyOperation> policyOperations;
073        
074        
075        /**
076         * Creates a new policy entry for a metadata parameter.
077         *
078         * @param parameterName    The parameter name. Must not be
079         *                         {@code null}.
080         * @param policyOperations The policy operations, empty list or
081         *                         {@code null} if none.
082         */
083        public MetadataPolicyEntry(final String parameterName, final List<PolicyOperation> policyOperations) {
084                if (StringUtils.isBlank(parameterName)) {
085                        throw new IllegalArgumentException("The parameter name must not be null or empty");
086                }
087                this.parameterName = parameterName;
088                this.policyOperations = policyOperations;
089        }
090        
091        
092        /**
093         * Returns the parameter name.
094         * @see #getKey()
095         *
096         * @return The parameter name.
097         */
098        public String getParameterName() {
099                return getKey();
100        }
101        
102        
103        /**
104         * @see #getParameterName()
105         */
106        @Override
107        public String getKey() {
108                return parameterName;
109        }
110        
111        
112        /**
113         * Returns the policy operations.
114         * @see #getValue()
115         *
116         * @return The policy operations, empty list if none.
117         */
118        public List<PolicyOperation> getPolicyOperations() {
119                return getValue();
120        }
121        
122        
123        /**
124         * @see #getPolicyOperations()
125         */
126        @Override
127        public List<PolicyOperation> getValue() {
128                return policyOperations;
129        }
130        
131        
132        @Override
133        public List<PolicyOperation> setValue(final List<PolicyOperation> policyOperations) {
134                throw new UnsupportedOperationException();
135        }
136        
137        
138        /**
139         * Applies this policy entry for a metadata parameter to the specified
140         * value.
141         *
142         * @param value The parameter value, {@code null} if not specified.
143         *
144         * @return The resulting value, can be {@code null}.
145         *
146         * @throws PolicyViolationException On a policy violation.
147         */
148        public Object apply(final Object value)
149                throws PolicyViolationException {
150                
151                if (CollectionUtils.isEmpty(getValue())) {
152                        // no ops
153                        return value;
154                }
155                
156                // Apply policy operations in list
157                Object updatedValue = value;
158                for (PolicyOperation op: getValue()) {
159                        updatedValue = PolicyOperationApplication.apply(op, updatedValue);
160                }
161                return updatedValue;
162        }
163        
164        
165        /**
166         * Returns a JSON object representation of the policy operations for
167         * this entry.
168         *
169         * @return The JSON object keeping the ordering of the members.
170         */
171        public JSONObject toJSONObject() {
172                
173                if (CollectionUtils.isEmpty(getValue())) {
174                        return null;
175                }
176                
177                JSONObject jsonObject = new JSONObject();
178                for (PolicyOperation operation: getValue()) {
179                        // E.g. "subset_of": ["code", "code token", "code id_token"]}
180                        jsonObject.put(operation.getOperationName().getValue(), configToJSONEntity(operation));
181                }
182                
183                return jsonObject;
184        }
185        
186        
187        private static Object configToJSONEntity(final PolicyOperation op) {
188                
189                // The order matters
190                
191                if (op instanceof StringConfiguration) {
192                        Object value = ((StringConfiguration)op).getStringConfiguration();
193                        if (value != null) {
194                                return value;
195                        }
196                }
197                
198                if (op instanceof StringListConfiguration) {
199                        Object value = ((StringListConfiguration)op).getStringListConfiguration();
200                        if (value != null) {
201                                return value;
202                        }
203                }
204                
205                if (op instanceof BooleanConfiguration) {
206                        return ((BooleanConfiguration)op).getBooleanConfiguration();
207                }
208                
209                throw new IllegalArgumentException("Unsupported policy operation: " + op.getClass());
210                
211        }
212        
213        
214        /**
215         * Parses a policy entry for a metadata parameter. This method is
216         * intended for policies including non-standard
217         * {@link PolicyOperation}s.
218         *
219         * @param parameterName        The parameter name. Must not be
220         *                             {@code null}.
221         * @param entrySpec            The JSON object entry specification,
222         *                             must not be {@code null}.
223         * @param factory              The policy operation factory. Must not
224         *                             be {@code null}.
225         * @param combinationValidator The policy operation combination
226         *                             validator. Must not be {@code null}.
227         *
228         * @return The policy entry for the metadata parameter.
229         *
230         * @throws ParseException           On JSON parsing exception.
231         * @throws PolicyViolationException On a policy violation.
232         */
233        public static MetadataPolicyEntry parse(final String parameterName,
234                                                final JSONObject entrySpec,
235                                                final PolicyOperationFactory factory,
236                                                final PolicyOperationCombinationValidator combinationValidator)
237                throws ParseException, PolicyViolationException {
238                
239                List<PolicyOperation> policyOperations = new LinkedList<>();
240                
241                for (String opName: entrySpec.keySet()) {
242                        PolicyOperation op = factory.createForName(new OperationName(opName));
243                        op.parseConfiguration(entrySpec.get(opName));
244                        policyOperations.add(op);
245                }
246                
247                List<PolicyOperation> validatedPolicyOperations = combinationValidator.validate(policyOperations);
248                
249                return new MetadataPolicyEntry(parameterName, validatedPolicyOperations);
250        }
251        
252        
253        /**
254         * Parses a policy entry for a metadata parameter. This method is
255         * intended for policies with standard {@link PolicyOperation}s only.
256         * Uses the default {@link DefaultPolicyOperationFactory policy
257         * operation} and {@link DefaultPolicyOperationCombinationValidator
258         * policy combination validator} factories.
259         *
260         * @param parameterName The parameter name. Must not be {@code null}.
261         * @param entrySpec     The JSON object entry specification, must not
262         *                      be {@code null}.
263         *
264         * @return The policy entry for the metadata parameter.
265         *
266         * @throws ParseException           On JSON parsing exception.
267         * @throws PolicyViolationException On a policy violation.
268         */
269        public static MetadataPolicyEntry parse(final String parameterName,
270                                                final JSONObject entrySpec)
271                throws ParseException, PolicyViolationException {
272                
273                return parse(parameterName, entrySpec, DEFAULT_POLICY_OPERATION_FACTORY, DEFAULT_POLICY_COMBINATION_VALIDATOR);
274        }
275}