View Javadoc
1   /*
2   * The contents of this file are subject to the terms of the Common Development and
3   * Distribution License (the License). You may not use this file except in compliance with the
4   * License.
5   *
6   * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7   * specific language governing permission and limitations under the License.
8   *
9   * When distributing Covered Software, include this CDDL Header Notice in each file and include
10  * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11  * Header, with the fields enclosed by brackets [] replaced by your own identifying
12  * information: "Portions copyright [year] [name of copyright owner]".
13  *
14  * Copyright 2014-2016 ForgeRock AS.
15  */
16  
17  package org.forgerock.json.resource;
18  
19  import static org.forgerock.json.JsonValueFunctions.setOf;
20  
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.Collection;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.TreeMap;
28  import java.util.regex.Pattern;
29  
30  import org.forgerock.services.context.AbstractContext;
31  import org.forgerock.services.context.Context;
32  import org.forgerock.json.JsonValue;
33  import org.forgerock.util.Reject;
34  
35  /**
36   * A {@link Context} containing information which should be returned to the user in some
37   * appropriate form to the user. For example, it could be contained within the body of the response
38   * or otherwise added to the headers returned.
39   *
40   * @since 2.4.0
41   */
42  public class AdviceContext extends AbstractContext {
43  
44      /** the persisted attribute name for the advices. */
45      private static final String ADVICE_ATTR = "advice";
46  
47      /** The persisted attribute name for the restricted advice names. */
48      private static final String RESTRICTED_ADVICE_NAMES_ATTR = "restrictedAdviceNames";
49  
50      private static final Pattern ALLOWED_RFC_CHARACTERS = Pattern.compile("^[\\x20-\\x7E]*$");
51  
52      private final Collection<String> restrictedAdviceNames = new HashSet<>();
53  
54      /** Advice currently stored for this context is help in this map. **/
55      private final Map<String, List<String>> advice = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
56  
57      /**
58       * Creates a new AdviceContext with the provided parent.
59       *
60       * @param parent The parent context.
61       * @param restrictedAdviceNames The restricted advice names.
62       */
63      public AdviceContext(Context parent, Collection<String> restrictedAdviceNames) {
64          super(parent, "advice");
65          this.restrictedAdviceNames.addAll(restrictedAdviceNames);
66          data.put(RESTRICTED_ADVICE_NAMES_ATTR, restrictedAdviceNames);
67          data.put(ADVICE_ATTR, advice);
68      }
69  
70      /**
71       * Restore from JSON representation.
72       *
73       * @param savedContext
74       *            The JSON representation from which this context's attributes
75       *            should be parsed.
76       * @param classLoader
77       *            The ClassLoader which can properly resolve the persisted class-name.
78       */
79      public AdviceContext(final JsonValue savedContext, final ClassLoader classLoader) {
80          super(savedContext, classLoader);
81          restrictedAdviceNames.addAll(data.get(RESTRICTED_ADVICE_NAMES_ATTR).as(setOf(String.class)));
82          advice.putAll(data.get(ADVICE_ATTR).asMapOfList(String.class));
83      }
84  
85      /**
86       * Returns the advices contained within this context.
87       *
88       * @return the advices contained within this context.
89       */
90      public Map<String, List<String>> getAdvices() {
91          return advice;
92      }
93  
94      /**
95       * Adds advice to the context, which can be retrieved and later returned to the user.
96       *
97       * @param adviceName Name of the advice to return to the user. Not null.
98       * @param advices Human-readable advice to return to the user. Not null.
99       */
100     public void putAdvice(String adviceName, String... advices) {
101         Reject.ifNull(adviceName, advices);
102         Reject.ifTrue(isRestrictedAdvice(adviceName), "Illegal use of restricted advice name, " + adviceName);
103         for (String adviceEntry : advices) {
104             Reject.ifTrue(!isRfcCompliant(adviceEntry), "Advice contains illegal characters in, " + adviceEntry);
105         }
106         List<String> adviceEntry = advice.get(adviceName);
107         if (adviceEntry == null) {
108             adviceEntry = new ArrayList<>();
109             advice.put(adviceName, adviceEntry);
110         }
111         adviceEntry.addAll(Arrays.asList(advices));
112     }
113 
114     private boolean isRfcCompliant(String advice) {
115         return ALLOWED_RFC_CHARACTERS.matcher(advice).matches();
116     }
117 
118     private boolean isRestrictedAdvice(String adviceName) {
119         return restrictedAdviceNames.contains(adviceName);
120     }
121 }