@@ -181,10 +181,10 @@ public GetApiKeyResponse waitForApiKey(
181
181
}
182
182
183
183
/**
184
- * Helper: Wait for an API key to be added, updated or deleted based on a given `operation`.
184
+ * Helper: Wait for an API key to be added or deleted based on a given `operation`.
185
185
*
186
186
* @param operation The `operation` that was done on a `key`. (ADD or DELETE only)
187
- * @param key The `key` that has been added, deleted or updated .
187
+ * @param key The `key` that has been added or deleted .
188
188
* @param maxRetries The maximum number of retry. 50 by default. (optional)
189
189
* @param timeout The function to decide how long to wait between retries. min(retries * 200, 5000) by default. (optional)
190
190
* @param requestOptions The requestOptions to send along with the query, they will be merged with the transporter requestOptions. (optional)
@@ -204,10 +204,10 @@ public GetApiKeyResponse waitForApiKey(ApiKeyOperation operation, String key, Ap
204
204
return this.waitForApiKey(operation, key, apiKey, TaskUtils.DEFAULT_MAX_RETRIES, TaskUtils.DEFAULT_TIMEOUT, requestOptions);
205
205
}
206
206
/**
207
- * Helper: Wait for an API key to be added, updated or deleted based on a given `operation`.
207
+ * Helper: Wait for an API key to be added or deleted based on a given `operation`.
208
208
*
209
209
* @param operation The `operation` that was done on a `key`. (ADD or DELETE only)
210
- * @param key The `key` that has been added, deleted or updated .
210
+ * @param key The `key` that has been added or deleted .
211
211
* @param requestOptions The requestOptions to send along with the query, they will be merged with the transporter requestOptions. (optional)
212
212
*/
213
213
public GetApiKeyResponse waitForApiKey(ApiKeyOperation operation, String key, RequestOptions requestOptions) {
@@ -226,10 +226,10 @@ public GetApiKeyResponse waitForApiKey(ApiKeyOperation operation, String key, Ap
226
226
return this.waitForApiKey(operation, key, apiKey, maxRetries, timeout, null);
227
227
}
228
228
/**
229
- * Helper: Wait for an API key to be added, updated or deleted based on a given `operation`.
229
+ * Helper: Wait for an API key to be added or deleted based on a given `operation`.
230
230
*
231
231
* @param operation The `operation` that was done on a `key`. (ADD or DELETE only)
232
- * @param key The `key` that has been added, deleted or updated .
232
+ * @param key The `key` that has been added or deleted .
233
233
* @param maxRetries The maximum number of retry. 50 by default. (optional)
234
234
* @param timeout The function to decide how long to wait between retries. min(retries * 200, 5000) by default. (optional)
235
235
*/
@@ -247,10 +247,10 @@ public GetApiKeyResponse waitForApiKey(ApiKeyOperation operation, String key, Ap
247
247
return this.waitForApiKey(operation, key, apiKey, TaskUtils.DEFAULT_MAX_RETRIES, TaskUtils.DEFAULT_TIMEOUT, null);
248
248
}
249
249
/**
250
- * Helper: Wait for an API key to be added, updated or deleted based on a given `operation`.
250
+ * Helper: Wait for an API key to be added or deleted based on a given `operation`.
251
251
*
252
252
* @param operation The `operation` that was done on a `key`. (ADD or DELETE only)
253
- * @param key The `key` that has been added, deleted or updated .
253
+ * @param key The `key` that has been added or deleted .
254
254
*/
255
255
public GetApiKeyResponse waitForApiKey(ApiKeyOperation operation, String key) {
256
256
return this.waitForApiKey(operation, key, null, TaskUtils.DEFAULT_MAX_RETRIES, TaskUtils.DEFAULT_TIMEOUT, null);
@@ -640,4 +640,89 @@ return new ReplaceAllObjectsResponse()
640
640
.setBatchResponses(batchResponses)
641
641
.setMoveOperationResponse(moveOperationResponse);
642
642
}
643
+
644
+ /**
645
+ * Helper: Generates a secured API key based on the given `parent_api_key` and given
646
+ * `restrictions`.
647
+ *
648
+ * @param parentApiKey API key to generate from.
649
+ * @param restrictions Restrictions to add the key
650
+ * @throws Exception if an error occurs during the encoding
651
+ * @throws AlgoliaRetryException When the retry has failed on all hosts
652
+ * @throws AlgoliaApiException When the API sends an http error code
653
+ * @throws AlgoliaRuntimeException When an error occurred during the serialization
654
+ */
655
+ public String generateSecuredApiKey(@Nonnull String parentApiKey, @Nonnull SecuredAPIKeyRestrictions restrictions) throws Exception {
656
+ Map<String, String> restrictionsMap = new HashMap<>();
657
+ if (restrictions.getFilters() != null) restrictionsMap.put("filters", StringUtils.paramToString(restrictions.getFilters()));
658
+ if (restrictions.getValidUntil() != 0) restrictionsMap.put("validUntil", StringUtils.paramToString(restrictions.getValidUntil()));
659
+ if (restrictions.getRestrictIndices() != null) restrictionsMap.put(
660
+ "restrictIndices",
661
+ StringUtils.paramToString(restrictions.getRestrictIndices())
662
+ );
663
+ if (restrictions.getRestrictSources() != null) restrictionsMap.put(
664
+ "restrictSources",
665
+ StringUtils.paramToString(restrictions.getRestrictSources())
666
+ );
667
+ if (restrictions.getUserToken() != null) restrictionsMap.put("userToken", StringUtils.paramToString(restrictions.getUserToken()));
668
+
669
+ if (restrictions.getSearchParams() != null) {
670
+ Map<String, Object> searchParamsMap = JsonSerializer
671
+ .getObjectMapper()
672
+ .convertValue(restrictions.getSearchParams(), new TypeReference<Map<String, Object>>() {});
673
+ searchParamsMap.forEach((key, value) -> restrictionsMap.put(key, StringUtils.paramToString(value)));
674
+ }
675
+
676
+ String queryStr = restrictionsMap
677
+ .entrySet()
678
+ .stream()
679
+ .sorted(Map.Entry.comparingByKey())
680
+ .map(entry -> String.format("%s=%s", entry.getKey(), entry.getValue()))
681
+ .collect(Collectors.joining("&"));
682
+
683
+ String key = hmac(parentApiKey, queryStr);
684
+
685
+ return new String(Base64.getEncoder().encode(String.format("%s%s", key, queryStr).getBytes(Charset.forName("UTF8"))));
686
+ }
687
+
688
+ private String hmac(String key, String msg) throws NoSuchAlgorithmException, InvalidKeyException {
689
+ Mac hmac = Mac.getInstance("HmacSHA256");
690
+ hmac.init(new SecretKeySpec(key.getBytes(), "HmacSHA256"));
691
+ byte[] rawHmac = hmac.doFinal(msg.getBytes());
692
+ StringBuilder sb = new StringBuilder(rawHmac.length * 2);
693
+ for (byte b : rawHmac) {
694
+ sb.append(String.format("%02x", b & 0xff));
695
+ }
696
+ return sb.toString();
697
+ }
698
+
699
+ /**
700
+ * Helper: Retrieves the remaining validity of the previous generated `secured_api_key`, the
701
+ * `validUntil` parameter must have been provided.
702
+ *
703
+ * @param securedApiKey The secured API Key to check
704
+ * @throws AlgoliaRuntimeException if <code>securedApiKey</code> is null, empty or whitespaces.
705
+ * @throws AlgoliaRuntimeException if <code>securedApiKey</code> doesn' t have a < code> validUntil
706
+ * < /code> parameter.
707
+ */
708
+ public Duration getSecuredApiKeyRemainingValidity(@Nonnull String securedApiKey) {
709
+ if (securedApiKey == null || securedApiKey.trim().isEmpty()) {
710
+ throw new AlgoliaRuntimeException(" securedAPIKey must not be empty, null or whitespaces" );
711
+ }
712
+
713
+ byte[] decodedBytes = Base64.getDecoder().decode(securedApiKey);
714
+ String decodedString = new String(decodedBytes);
715
+
716
+ Pattern pattern = Pattern.compile("validUntil=\\d+");
717
+ Matcher matcher = pattern.matcher(decodedString);
718
+
719
+ if (!matcher.find()) {
720
+ throw new AlgoliaRuntimeException(" The Secured API Key doesn't have a validUntil parameter." );
721
+ }
722
+
723
+ String validUntilMatch = matcher.group(0);
724
+ long timeStamp = Long.parseLong(validUntilMatch.replace("validUntil=", ""));
725
+
726
+ return Duration.ofSeconds(timeStamp - Instant.now().getEpochSecond());
727
+ }
643
728
{ {/isSearchClient} }
0 commit comments