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" : "[email protected]" 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 to 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 policyEntryJSONObject = en.toJSONObject(); 224 if (policyEntryJSONObject == null) { 225 continue; // skip 226 } 227 jsonObject.put(en.getKey(), en.toJSONObject()); 228 } 229 230 return jsonObject; 231 } 232 233 234 @Override 235 public String toJSONString() { 236 return toJSONObject().toJSONString(); 237 } 238 239 240 @Override 241 public String toString() { 242 return toJSONString(); 243 } 244 245 246 /** 247 * Combines the specified list of metadata policies. Uses the 248 * {@link DefaultPolicyOperationCombinationValidator default policy 249 * combination validator}. 250 * 251 * @param policies The metadata policies. Must not be empty or 252 * {@code null}. 253 * 254 * @return The new combined metadata policy. 255 * 256 * @throws PolicyViolationException On a policy violation. 257 */ 258 public static MetadataPolicy combine(final List<MetadataPolicy> policies) 259 throws PolicyViolationException { 260 261 return combine(policies, MetadataPolicyEntry.DEFAULT_POLICY_COMBINATION_VALIDATOR); 262 } 263 264 265 /** 266 * Combines the specified list of metadata policies. 267 * 268 * @param policies The metadata policies. Must not be empty 269 * or {@code null}. 270 * @param combinationValidator The policy operation combination 271 * validator. Must not be {@code null}. 272 * 273 * @return The new combined metadata policy. 274 * 275 * @throws PolicyViolationException On a policy violation. 276 */ 277 public static MetadataPolicy combine(final List<MetadataPolicy> policies, 278 final PolicyOperationCombinationValidator combinationValidator) 279 throws PolicyViolationException { 280 281 MetadataPolicy out = new MetadataPolicy(); 282 283 for (MetadataPolicy p: policies) { 284 for (MetadataPolicyEntry entry: p.entrySet()) { 285 MetadataPolicyEntry existingEntry = out.getEntry(entry.getParameterName()); 286 if (existingEntry == null) { 287 // add 288 out.put(entry); 289 } else { 290 // merge 291 out.put(existingEntry.combine(entry, combinationValidator)); 292 } 293 } 294 } 295 296 return out; 297 } 298 299 300 /** 301 * Parses a policy for a federation entity metadata. This method is 302 * intended for policies with standard {@link PolicyOperation}s only. 303 * Uses the default {@link DefaultPolicyOperationFactory policy 304 * operation} and {@link DefaultPolicyOperationCombinationValidator 305 * policy combination validator} factories. 306 * 307 * @param policySpec The JSON object string for the policy 308 * specification. Must not be {@code null}. 309 * 310 * @return The metadata policy. 311 * 312 * @throws ParseException On JSON parsing exception. 313 * @throws PolicyViolationException On a policy violation. 314 */ 315 public static MetadataPolicy parse(final JSONObject policySpec) 316 throws ParseException, PolicyViolationException { 317 318 return parse(policySpec, 319 MetadataPolicyEntry.DEFAULT_POLICY_OPERATION_FACTORY, 320 MetadataPolicyEntry.DEFAULT_POLICY_COMBINATION_VALIDATOR); 321 } 322 323 324 /** 325 * Parses a policy for a federation entity metadata. This method is 326 * intended for policies including non-standard 327 * {@link PolicyOperation}s. 328 * 329 * @param policySpec The JSON object for the policy 330 * specification. Must not be {@code null}. 331 * @param factory The policy operation factory. Must not 332 * be {@code null}. 333 * @param combinationValidator The policy operation combination 334 * validator. Must not be {@code null}. 335 * 336 * @return The metadata policy. 337 * 338 * @throws ParseException On JSON parsing exception. 339 * @throws PolicyViolationException On a policy violation. 340 */ 341 public static MetadataPolicy parse(final JSONObject policySpec, 342 final PolicyOperationFactory factory, 343 final PolicyOperationCombinationValidator combinationValidator) 344 throws ParseException, PolicyViolationException { 345 346 MetadataPolicy metadataPolicy = new MetadataPolicy(); 347 348 for (String parameterName: policySpec.keySet()) { 349 JSONObject entrySpec = JSONObjectUtils.getJSONObject(policySpec, parameterName); 350 metadataPolicy.put(MetadataPolicyEntry.parse(parameterName, entrySpec, factory, combinationValidator)); 351 } 352 353 return metadataPolicy; 354 } 355 356 357 /** 358 * Parses a policy for a federation entity metadata. This method is 359 * intended for policies with standard {@link PolicyOperation}s only. 360 * Uses the default {@link DefaultPolicyOperationFactory policy 361 * operation} and {@link DefaultPolicyOperationCombinationValidator 362 * policy combination validator} factories. 363 * 364 * @param policySpec The JSON object string for the policy 365 * specification. Must not be {@code null}. 366 * 367 * @return The metadata policy. 368 * 369 * @throws ParseException On JSON parsing exception. 370 * @throws PolicyViolationException On a policy violation. 371 */ 372 public static MetadataPolicy parse(final String policySpec) 373 throws ParseException, PolicyViolationException { 374 375 return parse(policySpec, 376 MetadataPolicyEntry.DEFAULT_POLICY_OPERATION_FACTORY, 377 MetadataPolicyEntry.DEFAULT_POLICY_COMBINATION_VALIDATOR); 378 } 379 380 381 /** 382 * Parses a policy for a federation entity metadata. This method is 383 * intended for policies including non-standard 384 * {@link PolicyOperation}s. 385 * 386 * @param policySpec The JSON object for the policy 387 * specification. Must not be {@code null}. 388 * @param factory The policy operation factory. Must not 389 * be {@code null}. 390 * @param combinationValidator The policy operation combination 391 * validator. Must not be {@code null}. 392 * 393 * @return The metadata policy. 394 * 395 * @throws ParseException On JSON parsing exception. 396 * @throws PolicyViolationException On a policy violation. 397 */ 398 public static MetadataPolicy parse(final String policySpec, 399 final PolicyOperationFactory factory, 400 final PolicyOperationCombinationValidator combinationValidator) 401 throws ParseException, PolicyViolationException { 402 403 return parse(JSONObjectUtils.parse(policySpec), factory, combinationValidator); 404 } 405}