diff --git a/docs/script-catalog/update_token/update-token.md b/docs/script-catalog/update_token/update-token.md
index 3edf645bcd5..3a798092702 100644
--- a/docs/script-catalog/update_token/update-token.md
+++ b/docs/script-catalog/update_token/update-token.md
@@ -104,7 +104,7 @@ Jans AS->>RP: return token(s) (Access token, ID token or Refresh Token) reflecti
## Associate an Update Token script to a client (RP) [optional step]
-📝 Note: If the Update token script is not associated with a client, then it will be applicable to all clients registered in the Jans Server. Which implies that all tokens obtained using the Jans server will reflect modifications as per the script.
+📝 Note: If the Update token script is not associated with a client, then it will not be executed. To run all scripts globally (independent from what is assigned to client) please set global AS configuration property `runAllUpdateTokenScripts` is set to `true`. Which implies that all tokens obtained using the Jans server will reflect modifications as per the script.
To Associate an Update Token script to a client (RP), execute the command below with appropriate values for:
- inum of the client
diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java b/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java
index 8bffdc441d1..e66b95a4f58 100644
--- a/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java
+++ b/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java
@@ -571,6 +571,9 @@ public class AppConfiguration implements Configuration {
@DocProperty(description = "Boolean value specifying whether to disable prompt=consent", defaultValue = "false")
private Boolean disablePromptConsent = false;
+ @DocProperty(description = "Boolean value specifying whether to run all Update Token scripts", defaultValue = "false")
+ private Boolean runAllUpdateTokenScripts = false;
+
@DocProperty(description = "The lifetime of Logout Status JWT. If not set falls back to 1 day", defaultValue = "86400")
private Integer logoutStatusJwtLifetime = DEFAULT_LOGOUT_STATUS_JWT_LIFETIME;
@@ -1442,6 +1445,15 @@ public void setDisablePromptConsent(Boolean disablePromptConsent) {
this.disablePromptConsent = disablePromptConsent;
}
+ public Boolean getRunAllUpdateTokenScripts() {
+ if (runAllUpdateTokenScripts == null) runAllUpdateTokenScripts = false;
+ return runAllUpdateTokenScripts;
+ }
+
+ public void setRunAllUpdateTokenScripts(Boolean runAllUpdateTokenScripts) {
+ this.runAllUpdateTokenScripts = runAllUpdateTokenScripts;
+ }
+
public Boolean getIncludeSidInResponse() {
if (includeSidInResponse == null) includeSidInResponse = false;
return includeSidInResponse;
diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalUpdateTokenService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalUpdateTokenService.java
index 685dfc2ada3..76944c71a06 100644
--- a/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalUpdateTokenService.java
+++ b/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ExternalUpdateTokenService.java
@@ -7,6 +7,7 @@
package io.jans.as.server.service.external;
import com.google.common.collect.Lists;
+import io.jans.as.model.configuration.AppConfiguration;
import io.jans.as.model.token.JsonWebResponse;
import io.jans.as.server.model.common.AccessToken;
import io.jans.as.server.model.common.RefreshToken;
@@ -16,12 +17,15 @@
import io.jans.model.custom.script.type.token.UpdateTokenType;
import io.jans.service.custom.script.ExternalScriptService;
import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
import jakarta.ws.rs.WebApplicationException;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.function.Function;
+import static org.apache.commons.lang3.BooleanUtils.isTrue;
+
/**
* @author Yuriy Movchan
*/
@@ -29,6 +33,8 @@
public class ExternalUpdateTokenService extends ExternalScriptService {
private static final long serialVersionUID = -1033475075863270259L;
+ @Inject
+ private AppConfiguration appConfiguration;
public ExternalUpdateTokenService() {
super(CustomScriptType.UPDATE_TOKEN);
@@ -117,9 +123,20 @@ public int getRefreshTokenLifetimeInSeconds(ExternalUpdateTokenContext context)
}
@NotNull
- private List getScripts(@NotNull ExternalUpdateTokenContext context) {
- if (customScriptConfigurations == null || customScriptConfigurations.isEmpty() || context.getClient() == null) {
- log.trace("No UpdateToken scripts or client is null.");
+ protected List getScripts(@NotNull ExternalUpdateTokenContext context) {
+ List customScriptConfigurations = getCustomScriptConfigurations();
+ if (customScriptConfigurations == null || customScriptConfigurations.isEmpty()) {
+ log.trace("No UpdateToken scripts.");
+ return Lists.newArrayList();
+ }
+
+ if (isTrue(appConfiguration.getRunAllUpdateTokenScripts())) {
+ log.trace("Run all UpdateToken scripts.");
+ return customScriptConfigurations;
+ }
+
+ if (context.getClient() == null) {
+ log.trace("No client.");
return Lists.newArrayList();
}
diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/context/ExternalScriptContext.java b/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/context/ExternalScriptContext.java
index e2410f8ddc4..f6d8037750d 100644
--- a/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/context/ExternalScriptContext.java
+++ b/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/context/ExternalScriptContext.java
@@ -34,7 +34,7 @@ public class ExternalScriptContext extends io.jans.service.external.context.Exte
private static final Logger log = LoggerFactory.getLogger(ExternalScriptContext.class);
- private final PersistenceEntryManager persistenceEntryManager;
+ private PersistenceEntryManager persistenceEntryManager;
private ExecutionContext executionContext;
@@ -51,7 +51,6 @@ public ExternalScriptContext(HttpServletRequest httpRequest) {
public ExternalScriptContext(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
super(httpRequest, httpResponse);
- this.persistenceEntryManager = ServerUtil.getLdapManager();
}
public static ExternalScriptContext of(ExecutionContext executionContext) {
@@ -75,6 +74,13 @@ public AuthzDetail getAuthzDetail() {
}
public PersistenceEntryManager getPersistenceEntryManager() {
+ if (persistenceEntryManager == null) {
+ synchronized (this) {
+ if (persistenceEntryManager == null) {
+ persistenceEntryManager = ServerUtil.getLdapManager();
+ }
+ }
+ }
return persistenceEntryManager;
}
@@ -89,7 +95,7 @@ public boolean isInNetwork(String cidrNotation) {
protected CustomEntry getEntryByDn(String dn, String... ldapReturnAttributes) {
try {
- return persistenceEntryManager.find(dn, CustomEntry.class, ldapReturnAttributes);
+ return getPersistenceEntryManager().find(dn, CustomEntry.class, ldapReturnAttributes);
} catch (EntryPersistenceException epe) {
log.error("Failed to find entry '{}'", dn);
}
diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/context/ExternalUpdateTokenContext.java b/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/context/ExternalUpdateTokenContext.java
index 4d59b6d88d1..5415d521260 100644
--- a/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/context/ExternalUpdateTokenContext.java
+++ b/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/context/ExternalUpdateTokenContext.java
@@ -33,17 +33,21 @@ public class ExternalUpdateTokenContext extends ExternalScriptContext {
private static final Logger log = LoggerFactory.getLogger(ExternalUpdateTokenContext.class);
- private final Client client;
+ private Client client;
private AuthorizationGrant grant;
- private final AppConfiguration appConfiguration;
- private final AttributeService attributeService;
+ private AppConfiguration appConfiguration;
+ private AttributeService attributeService;
private CustomScriptConfiguration script;
@Nullable
private ExecutionContext executionContext;
private JwtSigner jwtSigner;
+ public ExternalUpdateTokenContext() {
+ super((HttpServletRequest) null);
+ }
+
public ExternalUpdateTokenContext(HttpServletRequest httpRequest, AuthorizationGrant grant,
Client client, AppConfiguration appConfiguration, AttributeService attributeService) {
super(httpRequest);
@@ -116,6 +120,10 @@ public Client getClient() {
return client;
}
+ public void setClient(Client client) {
+ this.client = client;
+ }
+
public AuthorizationGrant getGrant() {
return grant;
}
diff --git a/jans-auth-server/server/src/test/java/io/jans/as/server/service/external/ExternalUpdateTokenServiceTest.java b/jans-auth-server/server/src/test/java/io/jans/as/server/service/external/ExternalUpdateTokenServiceTest.java
new file mode 100644
index 00000000000..19d4cadcd32
--- /dev/null
+++ b/jans-auth-server/server/src/test/java/io/jans/as/server/service/external/ExternalUpdateTokenServiceTest.java
@@ -0,0 +1,101 @@
+package io.jans.as.server.service.external;
+
+import io.jans.as.common.model.registration.Client;
+import io.jans.as.model.configuration.AppConfiguration;
+import io.jans.as.model.error.ErrorResponseFactory;
+import io.jans.as.server.service.external.context.ExternalUpdateTokenContext;
+import io.jans.model.custom.script.conf.CustomScriptConfiguration;
+import io.jans.model.custom.script.model.CustomScript;
+import io.jans.model.custom.script.type.token.DummyUpdateTokenType;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.testng.MockitoTestNGListener;
+import org.slf4j.Logger;
+import org.testng.annotations.Listeners;
+import org.testng.annotations.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.Mockito.when;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertTrue;
+
+@Listeners(MockitoTestNGListener.class)
+public class ExternalUpdateTokenServiceTest {
+
+ @Spy
+ @InjectMocks
+ private ExternalUpdateTokenService externalUpdateTokenService;
+
+ @Mock
+ private Logger log;
+
+ @Mock
+ private AppConfiguration appConfiguration;
+
+ @Mock
+ private ErrorResponseFactory errorResponseFactory;
+
+ @Test
+ public void getScripts_whenNoScripts_shouldReturnEmptyList() {
+ assertTrue(externalUpdateTokenService.getScripts(new ExternalUpdateTokenContext()).isEmpty());
+ }
+
+ @Test
+ public void getScripts_whenScriptsPresentAndRunAllIsTrue_shouldReturnNonEmptyList() {
+ List scripts = new ArrayList<>();
+ scripts.add(new CustomScriptConfiguration(new CustomScript("", "", ""), new DummyUpdateTokenType(), new HashMap<>()));
+
+ when(appConfiguration.getRunAllUpdateTokenScripts()).thenReturn(true);
+ when(externalUpdateTokenService.getCustomScriptConfigurations()).thenReturn(scripts);
+
+ assertFalse(externalUpdateTokenService.getScripts(new ExternalUpdateTokenContext()).isEmpty());
+ }
+
+ @Test
+ public void getScripts_whenScriptsPresentAndRunAllIsFalse_shouldReturnEmptyList() {
+ List scripts = new ArrayList<>();
+ scripts.add(new CustomScriptConfiguration(new CustomScript("", "", ""), new DummyUpdateTokenType(), new HashMap<>()));
+
+ when(appConfiguration.getRunAllUpdateTokenScripts()).thenReturn(false);
+ when(externalUpdateTokenService.getCustomScriptConfigurations()).thenReturn(scripts);
+
+ assertTrue(externalUpdateTokenService.getScripts(new ExternalUpdateTokenContext()).isEmpty());
+ }
+
+ @Test
+ public void getScripts_whenScriptsPresentAndRunAllIsFalseAndClientHasNoScripts_shouldReturnEmptyList() {
+ List scripts = new ArrayList<>();
+ scripts.add(new CustomScriptConfiguration(new CustomScript("", "", ""), new DummyUpdateTokenType(), new HashMap<>()));
+
+ when(appConfiguration.getRunAllUpdateTokenScripts()).thenReturn(false);
+ when(externalUpdateTokenService.getCustomScriptConfigurations()).thenReturn(scripts);
+
+ ExternalUpdateTokenContext context = new ExternalUpdateTokenContext();
+ context.setClient(new Client());
+
+ assertTrue(externalUpdateTokenService.getScripts(context).isEmpty());
+ }
+
+ @Test
+ public void getScripts_whenScriptsPresentAndRunAllIsFalseAndClientHasScripts_shouldReturnNonEmptyList() {
+ List scripts = new ArrayList<>();
+ scripts.add(new CustomScriptConfiguration(new CustomScript("dummy", "", ""), new DummyUpdateTokenType(), new HashMap<>()));
+
+ when(appConfiguration.getRunAllUpdateTokenScripts()).thenReturn(false);
+ when(externalUpdateTokenService.getCustomScriptConfigurations()).thenReturn(scripts);
+ when(externalUpdateTokenService.getCustomScriptConfigurationsByDns(anyList())).thenReturn(scripts);
+
+ Client client = new Client();
+ client.getAttributes().setUpdateTokenScriptDns(List.of("dummy"));
+
+ ExternalUpdateTokenContext context = new ExternalUpdateTokenContext();
+ context.setClient(client);
+
+ assertFalse(externalUpdateTokenService.getScripts(context).isEmpty());
+ }
+}