View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  package org.opencastproject.urlsigning.utils;
22  
23  import org.opencastproject.urlsigning.common.Policy;
24  
25  import org.apache.commons.codec.binary.Base64;
26  import org.joda.time.DateTime;
27  import org.joda.time.DateTimeZone;
28  import org.json.simple.JSONObject;
29  import org.json.simple.parser.JSONParser;
30  import org.json.simple.parser.ParseException;
31  
32  import java.nio.charset.StandardCharsets;
33  import java.util.Map;
34  import java.util.TreeMap;
35  
36  /**
37   * A Utility class to encode / decode Policy files from and to Base 64 and Json.
38   */
39  public final class PolicyUtils {
40    /** The JSON key for object that contains the date and ip conditions for the resource. */
41    private static final String CONDITION_KEY = "Condition";
42    /** The JSON key for the date and time the resource will become available. */
43    private static final String DATE_GREATER_THAN_KEY = "DateGreaterThan";
44    /** The JSON key for the date and time the resource will no longer be available. */
45    private static final String DATE_LESS_THAN_KEY = "DateLessThan";
46    /** The JSON key for the IP address of the acceptable client. */
47    private static final String IP_ADDRESS_KEY = "IpAddress";
48    /** The JSON key for the base url for the resource. */
49    private static final String RESOURCE_KEY = "Resource";
50    /** The JSON key for the main object of the policy. */
51    private static final String STATEMENT_KEY = "Statement";
52  
53    private PolicyUtils() {
54  
55    }
56  
57    /**
58     * Encode a {@link String} into Base 64 encoding
59     *
60     * @param value
61     *          The {@link String} to encode into base 64 encoding
62     * @return The {@link String} encoded into base 64.
63     */
64    public static String base64Encode(String value) {
65      return Base64.encodeBase64URLSafeString(value.getBytes());
66    }
67  
68    /**
69     * Decode a {@link String} from Base 64 encoding
70     *
71     * @param value
72     *          The {@link String} to encode into Base 64
73     * @return The {@link String} decoded from base 64.
74     */
75    public static String base64Decode(String value) {
76      return new String(Base64.decodeBase64(value), StandardCharsets.UTF_8);
77    }
78  
79    /**
80     * Get a {@link Policy} from JSON data.
81     *
82     * @param policyJson
83     *          The {@link String} representation of the json.
84     * @return A new {@link Policy} object populated from the JSON.
85     */
86    public static Policy fromJson(String policyJson) {
87      JSONObject jsonPolicy = null;
88      JSONParser jsonParser = new JSONParser();
89      try {
90        jsonPolicy = (JSONObject) jsonParser.parse(policyJson);
91      } catch (ParseException e) {
92        e.printStackTrace();
93      }
94      JSONObject statement = (JSONObject) jsonPolicy.get(STATEMENT_KEY);
95      String resource = statement.get(RESOURCE_KEY).toString();
96      JSONObject condition = (JSONObject) statement.get(CONDITION_KEY);
97  
98      final String lessThanString = condition.get(DATE_LESS_THAN_KEY).toString();
99      final DateTime dateLessThan = new DateTime(Long.parseLong(lessThanString), DateTimeZone.UTC);
100 
101     final DateTime dateGreaterThan;
102     Object greaterThanString = condition.get(DATE_GREATER_THAN_KEY);
103     if (greaterThanString != null) {
104       dateGreaterThan = new DateTime(Long.parseLong(greaterThanString.toString()), DateTimeZone.UTC);
105     } else {
106       dateGreaterThan = null;
107     }
108 
109     return Policy.mkPolicyValidFromWithIP(resource, dateLessThan, dateGreaterThan,
110             (String) condition.get(IP_ADDRESS_KEY));
111   }
112 
113   /**
114    * Render a {@link Policy} into JSON.
115    *
116    * @param policy
117    *          The {@link Policy} to render into JSON.
118    * @return The {@link JSONObject} representation of the {@link Policy}.
119    */
120   @SuppressWarnings("unchecked")
121   public static JSONObject toJson(Policy policy) {
122     JSONObject policyJSON = new JSONObject();
123 
124     Map<String, Object> conditions = new TreeMap<String, Object>();
125     conditions.put(DATE_LESS_THAN_KEY, new Long(policy.getValidUntil().getMillis()));
126     if (policy.getValidFrom().isPresent()) {
127       conditions.put(DATE_GREATER_THAN_KEY, new Long(policy.getValidFrom().get().getMillis()));
128     }
129     if (policy.getClientIpAddress().isPresent()) {
130       conditions.put(IP_ADDRESS_KEY, policy.getClientIpAddress().get().getHostAddress());
131     }
132     JSONObject conditionsJSON = new JSONObject();
133     conditionsJSON.putAll(conditions);
134 
135     JSONObject statement = new JSONObject();
136     statement.put(RESOURCE_KEY, policy.getResource());
137     statement.put(CONDITION_KEY, conditions);
138 
139     policyJSON.put(STATEMENT_KEY, statement);
140 
141     return policyJSON;
142   }
143 
144   /**
145    * Create a {@link Policy} in Json format and Base 64 encoded.
146    *
147    * @param encodedPolicy
148    *          The String representation of the {@link Policy} in Json format and encoded into Base 64
149    * @return The {@link Policy} data
150    */
151   public static Policy fromBase64EncodedPolicy(String encodedPolicy) {
152     String decodedPolicyString = base64Decode(encodedPolicy);
153     return fromJson(decodedPolicyString);
154   }
155 
156   /**
157    * Create a {@link Policy} in Json format and Base 64 encoded.
158    *
159    * @param policy
160    *          The String representation of the {@link Policy} in Json format and encoded into Base 64
161    * @return The {@link Policy} data
162    */
163   public static String toBase64EncodedPolicy(Policy policy) {
164     return base64Encode(PolicyUtils.toJson(policy).toJSONString());
165   }
166 
167   /**
168    * Get an encrypted version of a {@link Policy} to use as a signature.
169    *
170    * @param policy
171    *          {@link Policy} that needs to be encrypted.
172    * @param encryptionKey
173    *          The key to use to encrypt the {@link Policy}.
174    * @return An encrypted version of the {@link Policy} that is also Base64 encoded to make it safe to transmit as a
175    *         query parameter.
176    * @throws Exception
177    *           Thrown if there is a problem encrypting or encoding the {@link Policy}
178    */
179   public static String getPolicySignature(Policy policy, String encryptionKey) throws Exception {
180     return SHA256Util.digest(PolicyUtils.toJson(policy).toJSONString(), encryptionKey);
181   }
182 }