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.*; 022 023import net.minidev.json.JSONAware; 024import net.minidev.json.JSONObject; 025 026import com.nimbusds.oauth2.sdk.ParseException; 027import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 028import com.nimbusds.openid.connect.sdk.federation.policy.language.PolicyOperation; 029import com.nimbusds.openid.connect.sdk.federation.policy.language.PolicyViolationException; 030import com.nimbusds.openid.connect.sdk.federation.policy.operations.DefaultPolicyOperationCombinationValidator; 031import com.nimbusds.openid.connect.sdk.federation.policy.operations.DefaultPolicyOperationFactory; 032import com.nimbusds.openid.connect.sdk.federation.policy.operations.PolicyOperationCombinationValidator; 033import com.nimbusds.openid.connect.sdk.federation.policy.operations.PolicyOperationFactory; 034 035 036/** 037 * Policy for a federation entity metadata. 038 * 039 * <p>Example: 040 * 041 * <pre> 042 * { 043 * "scopes" : { 044 * "subset_of" : [ "openid", "eduperson", "phone" ], 045 * "superset_of" : [ "openid" ], 046 * "default" : [ "openid", "eduperson" ] 047 * }, 048 * "id_token_signed_response_alg" : { 049 * "one_of" : [ "ES256", "ES384", "ES512" ] 050 * }, 051 * "contacts" : { 052 * "add" : "helpdesk@federation.example.org" 053 * }, 054 * "application_type" : { "value": "web" 055 * } 056 * } 057 * </pre> 058 * 059 * <p>Related specifications: 060 * 061 * <ul> 062 * <li>OpenID Connect Federation 1.0, section 4.1. 063 * </ul> 064 */ 065public class MetadataPolicy implements JSONAware { 066 067 068 /** 069 * The policy entries, keyed by metadata parameter name. 070 */ 071 private final Map<String,List<PolicyOperation>> entries = new LinkedHashMap<>(); 072 073 074 /** 075 * Applies this policy to the specified metadata. 076 * 077 * @param metadata The metadata as JSON object. May be {@code null}. 078 * 079 * @return The resulting metadata, {@code null} if not specified. 080 * 081 * @throws PolicyViolationException On a policy violation. 082 */ 083 public JSONObject apply(final JSONObject metadata) 084 throws PolicyViolationException { 085 086 if (metadata == null) { 087 return null; 088 } 089 090 JSONObject out = new JSONObject(); 091 092 // Copy params not subject by policy 093 for (String key: metadata.keySet()) { 094 if (! entries.containsKey(key)) { 095 out.put(key, metadata.get(key)); 096 } 097 } 098 099 // Apply policy 100 for (String key: entries.keySet()) { 101 102 Object metadataValue = metadata.get(key); 103 MetadataPolicyEntry en = getEntry(key); 104 105 Object outputValue = en.apply(metadataValue); 106 107 if (outputValue != null) { 108 out.put(key, outputValue); 109 } 110 } 111 112 return out; 113 } 114 115 116 /** 117 * Puts a policy entry for a metadata parameter. 118 * 119 * @param parameterName The parameter name. Must not be {@code null}. 120 * @param policyOperation The policy operation for the parameter, 121 * {@code null} if none. 122 */ 123 public void put(final String parameterName, final PolicyOperation policyOperation) { 124 put(new MetadataPolicyEntry(parameterName, Collections.singletonList(policyOperation))); 125 } 126 127 128 /** 129 * Puts a policy entry for a metadata parameter. 130 * 131 * @param parameterName The parameter name. Must not be {@code null}. 132 * @param policyOperations The ordered policy operations for the 133 * parameter, {@code null} if none. 134 */ 135 public void put(final String parameterName, final List<PolicyOperation> policyOperations) { 136 put(new MetadataPolicyEntry(parameterName, policyOperations)); 137 } 138 139 140 /** 141 * Puts a policy entry for a metadata parameter. 142 * 143 * @param entry The policy entry. Must not be {@code null}. 144 */ 145 public void put(final MetadataPolicyEntry entry) { 146 entries.put(entry.getKey(), entry.getValue()); 147 } 148 149 150 /** 151 * Gets the policy operations for the specified metadata parameter 152 * name. 153 * 154 * @param parameterName The parameter name. Must not be {@code null}. 155 * 156 * @return The ordered policy operations for the parameter, 157 * {@code null} if none. 158 */ 159 public List<PolicyOperation> get(final String parameterName) { 160 161 return entries.get(parameterName); 162 } 163 164 165 /** 166 * Gets the policy entry for the specified metadata parameter name. 167 * 168 * @param parameterName The parameter name. Must not be {@code null}. 169 * 170 * @return The policy entry for the parameter, {@code null} if none. 171 */ 172 public MetadataPolicyEntry getEntry(final String parameterName) { 173 174 List<PolicyOperation> policyOperations = entries.get(parameterName); 175 176 if (policyOperations == null) { 177 return null; 178 } 179 180 return new MetadataPolicyEntry(parameterName, policyOperations); 181 } 182 183 184 /** 185 * Gets the policy entries set. 186 * 187 * @return The policy entries set. 188 */ 189 public Set<MetadataPolicyEntry> entrySet() { 190 191 Set<MetadataPolicyEntry> set = new LinkedHashSet<>(); 192 for (Map.Entry<String,List<PolicyOperation>> en: entries.entrySet()) { 193 set.add(new MetadataPolicyEntry(en.getKey(), en.getValue())); 194 } 195 return set; 196 } 197 198 199 /** 200 * Removes a policy entry. 201 * 202 * @param parameterName The parameter name. Must not be {@code null}. 203 * 204 * @return The ordered policy operations for the removed parameter, 205 * {@code null} if not found. 206 */ 207 public List<PolicyOperation> remove(final String parameterName) { 208 209 return entries.remove(parameterName); 210 } 211 212 213 /** 214 * Returns a JSON object representation of this metadata policy. 215 * 216 * @return The JSON object. 217 */ 218 public JSONObject toJSONObject() { 219 220 JSONObject jsonObject = new JSONObject(); 221 222 for (MetadataPolicyEntry en: entrySet()) { 223 jsonObject.put(en.getKey(), en.toJSONObject()); 224 } 225 226 return jsonObject; 227 } 228 229 230 @Override 231 public String toJSONString() { 232 return toJSONObject().toJSONString(); 233 } 234 235 236 /** 237 * Combines the specified list of metadata policies. Uses the 238 * {@link DefaultPolicyOperationCombinationValidator default policy 239 * combination validator}. 240 * 241 * @param policies The metadata policies. Must not be empty or 242 * {@code null}. 243 * 244 * @return The new combined metadata policy. 245 * 246 * @throws PolicyViolationException On a policy violation. 247 */ 248 public static MetadataPolicy combine(final List<MetadataPolicy> policies) 249 throws PolicyViolationException { 250 251 return combine(policies, MetadataPolicyEntry.DEFAULT_POLICY_COMBINATION_VALIDATOR); 252 } 253 254 255 /** 256 * Combines the specified list of metadata policies. 257 * 258 * @param policies The metadata policies. Must not be empty 259 * or {@code null}. 260 * @param combinationValidator The policy operation combination 261 * validator. Must not be {@code null}. 262 * 263 * @return The new combined metadata policy. 264 * 265 * @throws PolicyViolationException On a policy violation. 266 */ 267 public static MetadataPolicy combine(final List<MetadataPolicy> policies, 268 final PolicyOperationCombinationValidator combinationValidator) 269 throws PolicyViolationException { 270 271 MetadataPolicy out = new MetadataPolicy(); 272 273 for (MetadataPolicy p: policies) { 274 for (MetadataPolicyEntry entry: p.entrySet()) { 275 MetadataPolicyEntry existingEntry = out.getEntry(entry.getParameterName()); 276 if (existingEntry == null) { 277 // add 278 out.put(entry); 279 } else { 280 // merge 281 out.put(existingEntry.combine(entry, combinationValidator)); 282 } 283 } 284 } 285 286 return out; 287 } 288 289 290 /** 291 * Parses a policy for a federation entity metadata. This method is 292 * intended for policies with standard {@link PolicyOperation}s only. 293 * Uses the default {@link DefaultPolicyOperationFactory policy 294 * operation} and {@link DefaultPolicyOperationCombinationValidator 295 * policy combination validator} factories. 296 * 297 * @param policySpec The JSON object string for the policy 298 * specification. Must not be {@code null}. 299 * 300 * @return The metadata policy. 301 * 302 * @throws ParseException On JSON parsing exception. 303 * @throws PolicyViolationException On a policy violation. 304 */ 305 public static MetadataPolicy parse(final JSONObject policySpec) 306 throws ParseException, PolicyViolationException { 307 308 return parse(policySpec, 309 MetadataPolicyEntry.DEFAULT_POLICY_OPERATION_FACTORY, 310 MetadataPolicyEntry.DEFAULT_POLICY_COMBINATION_VALIDATOR); 311 } 312 313 314 /** 315 * Parses a policy for a federation entity metadata. This method is 316 * intended for policies including non-standard 317 * {@link PolicyOperation}s. 318 * 319 * @param policySpec The JSON object for the policy 320 * specification. Must not be {@code null}. 321 * @param factory The policy operation factory. Must not 322 * be {@code null}. 323 * @param combinationValidator The policy operation combination 324 * validator. Must not be {@code null}. 325 * 326 * @return The metadata policy. 327 * 328 * @throws ParseException On JSON parsing exception. 329 * @throws PolicyViolationException On a policy violation. 330 */ 331 public static MetadataPolicy parse(final JSONObject policySpec, 332 final PolicyOperationFactory factory, 333 final PolicyOperationCombinationValidator combinationValidator) 334 throws ParseException, PolicyViolationException { 335 336 MetadataPolicy metadataPolicy = new MetadataPolicy(); 337 338 for (String parameterName: policySpec.keySet()) { 339 JSONObject entrySpec = JSONObjectUtils.getJSONObject(policySpec, parameterName); 340 metadataPolicy.put(MetadataPolicyEntry.parse(parameterName, entrySpec, factory, combinationValidator)); 341 } 342 343 return metadataPolicy; 344 } 345 346 347 /** 348 * Parses a policy for a federation entity metadata. This method is 349 * intended for policies with standard {@link PolicyOperation}s only. 350 * Uses the default {@link DefaultPolicyOperationFactory policy 351 * operation} and {@link DefaultPolicyOperationCombinationValidator 352 * policy combination validator} factories. 353 * 354 * @param policySpec The JSON object string for the policy 355 * specification. Must not be {@code null}. 356 * 357 * @return The metadata policy. 358 * 359 * @throws ParseException On JSON parsing exception. 360 * @throws PolicyViolationException On a policy violation. 361 */ 362 public static MetadataPolicy parse(final String policySpec) 363 throws ParseException, PolicyViolationException { 364 365 return parse(policySpec, 366 MetadataPolicyEntry.DEFAULT_POLICY_OPERATION_FACTORY, 367 MetadataPolicyEntry.DEFAULT_POLICY_COMBINATION_VALIDATOR); 368 } 369 370 371 /** 372 * Parses a policy for a federation entity metadata. This method is 373 * intended for policies including non-standard 374 * {@link PolicyOperation}s. 375 * 376 * @param policySpec The JSON object for the policy 377 * specification. Must not be {@code null}. 378 * @param factory The policy operation factory. Must not 379 * be {@code null}. 380 * @param combinationValidator The policy operation combination 381 * validator. Must not be {@code null}. 382 * 383 * @return The metadata policy. 384 * 385 * @throws ParseException On JSON parsing exception. 386 * @throws PolicyViolationException On a policy violation. 387 */ 388 public static MetadataPolicy parse(final String policySpec, 389 final PolicyOperationFactory factory, 390 final PolicyOperationCombinationValidator combinationValidator) 391 throws ParseException, PolicyViolationException { 392 393 return parse(JSONObjectUtils.parse(policySpec), factory, combinationValidator); 394 } 395}