Skip to content

Commit d28a027

Browse files
GregoryTravisfarmaazon
authored andcommitted
Implement reload cache clearing for AuthenticationProvider, EnsoSecretReader and AuditLog (#12541)
1 parent ed47729 commit d28a027

File tree

14 files changed

+169
-33
lines changed

14 files changed

+169
-33
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,8 @@
303303
- [Added `Table.geo_distance` to calculate the distance between two
304304
points.][12393]
305305
- [The reload button clears the Enso Cloud request cache.][12526]
306+
- [The reload button clears the AuthenticationProvider, EnsoSecretReader and
307+
AuditLog caches.][12541]
306308

307309
[11235]: https://github.com/enso-org/enso/pull/11235
308310
[11255]: https://github.com/enso-org/enso/pull/11255
@@ -317,6 +319,7 @@
317319
[12017]: https://github.com/enso-org/enso/pull/12017
318320
[12393]: https://github.com/enso-org/enso/pull/12393
319321
[12526]: https://github.com/enso-org/enso/pull/12526
322+
[12541]: https://github.com/enso-org/enso/pull/12526
320323

321324
#### Enso Language & Runtime
322325

distribution/lib/Standard/Base/0.0.0-dev/src/Enso_Cloud/Internal/Authentication.enso

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ polyglot java import org.enso.base.enso_cloud.AuthenticationProvider
4646
and a new one will be returned. Because of that, this method may make network
4747
requests.
4848
get_access_token : Text
49-
get_access_token = AuthenticationProvider.getAccessToken
49+
get_access_token = AuthenticationProvider.INSTANCE.getAccessToken
5050

5151
## PRIVATE
5252
Forcibly refreshes the access token.
5353
refresh_access_token : Nothing
5454
refresh_access_token =
55-
AuthenticationProvider.getAuthenticationServiceEnsoInstance.force_refresh
55+
AuthenticationProvider.INSTANCE.getAuthenticationServiceEnsoInstance.force_refresh
5656

5757
## PRIVATE
5858
credentials_file : File
Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,68 @@
11
package org.enso.base.enso_cloud;
22

3+
import org.enso.base.cache.ReloadDetector;
34
import org.enso.base.polyglot.EnsoMeta;
45
import org.graalvm.polyglot.Value;
56

6-
public class AuthenticationProvider {
7+
public class AuthenticationProvider implements ReloadDetector.HasClearableCache {
8+
public static AuthenticationProvider INSTANCE = new AuthenticationProvider();
9+
10+
private AuthenticationProvider() {
11+
ReloadDetector.register(this);
12+
}
713

814
public interface AuthenticationService {
915
String get_access_token();
1016

1117
void force_refresh();
1218
}
1319

14-
private static Value authenticationServiceAsEnso = null;
15-
private static AuthenticationService authenticationServiceAsJava = null;
20+
private Value authenticationServiceAsEnso = null;
21+
private AuthenticationService authenticationServiceAsJava = null;
1622

17-
public static void reset() {
23+
public void reset() {
1824
authenticationServiceAsEnso = null;
1925
authenticationServiceAsJava = null;
2026
}
2127

22-
private static Value createAuthenticationService() {
28+
private Value createAuthenticationService() {
2329
return EnsoMeta.callStaticModuleMethod(
2430
"Standard.Base.Enso_Cloud.Internal.Authentication", "instantiate_authentication_service");
2531
}
2632

27-
private static void ensureServicesSetup() {
33+
private void ensureServicesSetup() {
2834
var ensoInstance = createAuthenticationService();
2935
var javaInstance = ensoInstance.as(AuthenticationService.class);
3036
authenticationServiceAsEnso = ensoInstance;
3137
authenticationServiceAsJava = javaInstance;
3238
}
3339

34-
static AuthenticationService getAuthenticationService() {
40+
AuthenticationService getAuthenticationService() {
3541
if (authenticationServiceAsJava == null) {
3642
ensureServicesSetup();
3743
}
3844

3945
return authenticationServiceAsJava;
4046
}
4147

42-
public static Value getAuthenticationServiceEnsoInstance() {
48+
public Value getAuthenticationServiceEnsoInstance() {
49+
ReloadDetector.clearOnReload(this);
50+
4351
if (authenticationServiceAsEnso == null) {
4452
ensureServicesSetup();
4553
}
4654

4755
return authenticationServiceAsEnso;
4856
}
4957

50-
public static String getAccessToken() {
58+
public String getAccessToken() {
59+
ReloadDetector.clearOnReload(this);
60+
5161
return getAuthenticationService().get_access_token();
5262
}
63+
64+
@Override /* HasClearableCache */
65+
public void clearCache() {
66+
reset();
67+
}
5368
}

std-bits/base/src/main/java/org/enso/base/enso_cloud/CloudAPI.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ public static String getCloudSessionId() {
3737

3838
public static void flushCloudCaches() {
3939
CloudRequestCache.INSTANCE.clear();
40-
AuthenticationProvider.reset();
41-
EnsoSecretReader.flushCache();
40+
AuthenticationProvider.INSTANCE.reset();
41+
EnsoSecretReader.INSTANCE.flushCache();
4242
AuditLog.resetCache();
4343
}
4444
}

std-bits/base/src/main/java/org/enso/base/enso_cloud/EnsoSecretHelper.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.util.Comparator;
1717
import java.util.List;
1818
import java.util.Properties;
19+
import org.enso.base.cache.ReloadDetector;
1920
import org.enso.base.cache.ResponseTooLargeException;
2021
import org.enso.base.net.URISchematic;
2122
import org.enso.base.net.URIWithSecrets;
@@ -104,7 +105,7 @@ public static EnsoHttpResponse makeRequest(
104105
}
105106

106107
public static void deleteSecretFromCache(String secretId) {
107-
EnsoSecretReader.removeFromCache(secretId);
108+
EnsoSecretReader.INSTANCE.removeFromCache(secretId);
108109
}
109110

110111
private static class RequestMaker implements EnsoHTTPResponseCache.RequestMaker {
@@ -194,6 +195,16 @@ public static EnsoHTTPResponseCache getOrCreateCache() {
194195
return cache;
195196
}
196197

198+
/** Visible for testing */
199+
public static int getEnsoSecretReaderCacheSize() {
200+
return EnsoSecretReader.INSTANCE.getCacheSize();
201+
}
202+
203+
/** Visible for testing */
204+
public static void simulateEnsoSecretReaderReload() {
205+
ReloadDetector.simulateReloadTestOnly(EnsoSecretReader.INSTANCE);
206+
}
207+
197208
private static final Comparator<Pair<String, String>> headerNameComparator =
198209
Comparator.comparing((Pair<String, String> pair) -> pair.getLeft())
199210
.thenComparing(Comparator.comparing(pair -> pair.getRight()));

std-bits/base/src/main/java/org/enso/base/enso_cloud/EnsoSecretReader.java

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,25 @@
77
import java.net.http.HttpResponse;
88
import java.util.HashMap;
99
import java.util.Map;
10+
import org.enso.base.cache.ReloadDetector;
1011

1112
/** * Internal class to read secrets from the Enso Cloud. */
12-
class EnsoSecretReader {
13-
private static final Map<String, String> secrets = new HashMap<>();
13+
class EnsoSecretReader implements ReloadDetector.HasClearableCache {
14+
static final EnsoSecretReader INSTANCE = new EnsoSecretReader();
1415

15-
static void flushCache() {
16+
private final Map<String, String> secrets = new HashMap<>();
17+
18+
private EnsoSecretReader() {
19+
ReloadDetector.register(this);
20+
}
21+
22+
void flushCache() {
1623
secrets.clear();
1724
}
1825

19-
static void removeFromCache(String secretId) {
26+
void removeFromCache(String secretId) {
27+
ReloadDetector.clearOnReloadIfRegistered(this);
28+
2029
secrets.remove(secretId);
2130
}
2231

@@ -26,21 +35,23 @@ static void removeFromCache(String secretId) {
2635
* @param secretId the ID of the secret to read.
2736
* @return the secret value.
2837
*/
29-
static String readSecret(String secretId) {
38+
String readSecret(String secretId) {
39+
ReloadDetector.clearOnReloadIfRegistered(this);
40+
3041
if (secrets.containsKey(secretId)) {
3142
return secrets.get(secretId);
3243
}
3344

3445
return fetchSecretValue(secretId, 3);
3546
}
3647

37-
private static String fetchSecretValue(String secretId, int retryCount) {
48+
private String fetchSecretValue(String secretId, int retryCount) {
3849
var apiUri = CloudAPI.getAPIRootURI() + "s3cr3tz/" + secretId;
3950
var client = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.ALWAYS).build();
4051
var request =
4152
HttpRequest.newBuilder()
4253
.uri(URI.create(apiUri))
43-
.header("Authorization", "Bearer " + AuthenticationProvider.getAccessToken())
54+
.header("Authorization", "Bearer " + AuthenticationProvider.INSTANCE.getAccessToken())
4455
.GET()
4556
.build();
4657

@@ -64,7 +75,7 @@ private static String fetchSecretValue(String secretId, int retryCount) {
6475
"Unable to read secret - numerous " + kind + " failures (status code " + status + ").");
6576
} else {
6677
// We forcibly refresh the access token and try again.
67-
AuthenticationProvider.getAuthenticationService().force_refresh();
78+
AuthenticationProvider.INSTANCE.getAuthenticationService().force_refresh();
6879
return fetchSecretValue(secretId, retryCount - 1);
6980
}
7081
}
@@ -80,9 +91,19 @@ private static String fetchSecretValue(String secretId, int retryCount) {
8091
return secretValue;
8192
}
8293

83-
private static String readValueFromString(String json) {
94+
private String readValueFromString(String json) {
8495
var base64 = json.substring(1, json.length() - 1).translateEscapes();
8596
return new String(
8697
java.util.Base64.getDecoder().decode(base64), java.nio.charset.StandardCharsets.UTF_8);
8798
}
99+
100+
@Override /* HasClearableCache */
101+
public void clearCache() {
102+
flushCache();
103+
}
104+
105+
/** Visible for testing */
106+
public int getCacheSize() {
107+
return secrets.size();
108+
}
88109
}

std-bits/base/src/main/java/org/enso/base/enso_cloud/ExternalLibraryCredentialHelper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public static CredentialConfig readCredential(CredentialReference credentialRefe
6262
throws EnsoSecretAccessDenied {
6363
RestrictedAccess.checkAccess(allowParseCredential);
6464

65-
String secretPayload = EnsoSecretReader.readSecret(credentialReference.secretId());
65+
String secretPayload = EnsoSecretReader.INSTANCE.readSecret(credentialReference.secretId());
6666
ObjectMapper jsonMapper = new ObjectMapper();
6767
JsonNode json;
6868
try {
@@ -103,7 +103,7 @@ public static AccessToken requestAccessToken(CredentialReference credentialRefer
103103
var request =
104104
HttpRequest.newBuilder()
105105
.uri(URI.create(apiUri))
106-
.header("Authorization", "Bearer " + AuthenticationProvider.getAccessToken())
106+
.header("Authorization", "Bearer " + AuthenticationProvider.INSTANCE.getAccessToken())
107107
.POST(HttpRequest.BodyPublishers.noBody())
108108
.build();
109109
// TODO retries?

std-bits/base/src/main/java/org/enso/base/enso_cloud/SecretValueResolver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ sealed class SecretValueResolver permits EnsoSecretHelper, ExternalLibrarySecret
1010
protected static String resolveValue(HideableValue value) {
1111
return switch (value) {
1212
case HideableValue.PlainValue plainValue -> plainValue.value();
13-
case HideableValue.SecretValue secretValue -> EnsoSecretReader.readSecret(
13+
case HideableValue.SecretValue secretValue -> EnsoSecretReader.INSTANCE.readSecret(
1414
secretValue.secretId());
1515
case HideableValue.ConcatValues concatValues -> {
1616
String left = resolveValue(concatValues.left());

std-bits/base/src/main/java/org/enso/base/enso_cloud/audit/AuditLogApiAccess.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@
1616
import java.util.concurrent.ThreadPoolExecutor;
1717
import java.util.concurrent.TimeUnit;
1818
import java.util.logging.Logger;
19+
import org.enso.base.cache.ReloadDetector;
1920
import org.enso.base.enso_cloud.AuthenticationProvider;
2021
import org.enso.base.enso_cloud.CloudAPI;
2122

2223
/**
2324
* Gives access to the low-level log event API in the Cloud and manages asynchronously submitting
2425
* the logs.
2526
*/
26-
class AuditLogApiAccess {
27+
public final class AuditLogApiAccess implements ReloadDetector.HasClearableCache {
2728
private static final Logger logger = Logger.getLogger(AuditLogApiAccess.class.getName());
2829

2930
/**
@@ -45,16 +46,21 @@ private AuditLogApiAccess() {
4546
// If the thread is idle for 60 seconds, it will be shut down.
4647
backgroundThreadService =
4748
new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
49+
ReloadDetector.register(this);
4850
}
4951

5052
public Future<Void> logWithConfirmation(LogMessage message) {
53+
ReloadDetector.clearOnReload(this);
54+
5155
var currentRequestConfig = getRequestConfig();
5256
CompletableFuture<Void> completionNotification = new CompletableFuture<>();
5357
enqueueJob(new LogJob(message, completionNotification, currentRequestConfig));
5458
return completionNotification;
5559
}
5660

5761
public void logWithoutConfirmation(LogMessage message) {
62+
ReloadDetector.clearOnReload(this);
63+
5864
var currentRequestConfig = getRequestConfig();
5965
enqueueJob(new LogJob(message, null, currentRequestConfig));
6066
}
@@ -195,7 +201,7 @@ private RequestConfig getRequestConfig() {
195201
}
196202

197203
var uri = URI.create(CloudAPI.getAPIRootURI() + "logs");
198-
var config = new RequestConfig(uri, AuthenticationProvider.getAccessToken());
204+
var config = new RequestConfig(uri, AuthenticationProvider.INSTANCE.getAccessToken());
199205
cachedRequestConfig = config;
200206
return config;
201207
}
@@ -213,6 +219,14 @@ private RequestConfig getRequestConfig() {
213219
*/
214220
private record RequestConfig(URI apiUri, String accessToken) {}
215221

222+
public String getAccessTokenTestOnly() {
223+
if (cachedRequestConfig == null) {
224+
return null;
225+
} else {
226+
return cachedRequestConfig.accessToken();
227+
}
228+
}
229+
216230
private void sendLogRequest(HttpRequest request, int retryCount) throws RequestFailureException {
217231
try {
218232
try {
@@ -265,4 +279,9 @@ record LogJob(
265279
void resetCache() {
266280
cachedRequestConfig = null;
267281
}
282+
283+
@Override /* HasClearableCache */
284+
public void clearCache() {
285+
resetCache();
286+
}
268287
}

std-bits/table/src/main/java/org/enso/table/excel/ExcelConnectionPool.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,8 @@ public void clearCache() {
228228
LOGGER.error("Unable to close " + record, e);
229229
}
230230
}
231-
records.clear();
232231
}
232+
records.clear();
233233
}
234234

235235
/** Public for testing. */

0 commit comments

Comments
 (0)