From 35a2074168af1ea9961ba81e2a7368be248e2ad3 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Tue, 3 Mar 2026 17:15:44 +0100 Subject: [PATCH 01/26] Add dashboard properties --- .../it/infn/mw/iam/config/IamProperties.java | 31 +++++++++++++++++++ .../src/main/resources/application.yml | 4 +++ 2 files changed, 35 insertions(+) diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java b/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java index 9e5066c019..0435f3eefe 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java @@ -599,6 +599,27 @@ public void setTrackLastUsed(boolean trackLastUsed) { } } + public static class DashboardProperties { + private String dashboardClientId; + private String dashboardClientSecret; + + public String getDashboardClientId() { + return dashboardClientId; + } + + public void setDashboardClientId(String dashboardClientId) { + this.dashboardClientId = dashboardClientId; + } + + public String getDashboardClientSecret() { + return dashboardClientSecret; + } + + public void setDashboardClientSecret(String dashboardClientSecret) { + this.dashboardClientSecret = dashboardClientSecret; + } + } + public static class DefaultGroup { private String name; private String enrollment = "INSERT"; @@ -727,6 +748,8 @@ public void setUrnSubnamespaces(String urnSubnamespaces) { private AarcProfile aarcProfile = new AarcProfile(); + private DashboardProperties dashboard = new DashboardProperties(); + public String getBaseUrl() { return baseUrl; } @@ -969,6 +992,14 @@ public ClientProperties getClient() { return client; } + public DashboardProperties getDashboard() { + return dashboard; + } + + public void setDashboard(DashboardProperties dashboard) { + this.dashboard = dashboard; + } + public AarcProfile getAarcProfile() { return aarcProfile; } diff --git a/iam-login-service/src/main/resources/application.yml b/iam-login-service/src/main/resources/application.yml index 1f97d874fb..353312ec28 100644 --- a/iam-login-service/src/main/resources/application.yml +++ b/iam-login-service/src/main/resources/application.yml @@ -189,6 +189,10 @@ iam: client: track-last-used: ${IAM_CLIENT_TRACK_LAST_USED:false} + dashboard: + client-id: ${IAM_DASHBOARD_CLIENT_ID} + client-secret: ${IAM_DASHBOARD_CLIENT_SECRET} + cache: enabled: ${IAM_CACHE_ENABLED:true} redis: From 5c4a02cf73386d2e6ee85857d6ea92ad8b46cad9 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Tue, 3 Mar 2026 17:59:52 +0100 Subject: [PATCH 02/26] Refactor DashboardProperties --- .../it/infn/mw/iam/config/IamProperties.java | 33 +++++++++++++------ .../src/main/resources/application.yml | 1 + 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java b/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java index 0435f3eefe..85e05bccb2 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java @@ -600,23 +600,32 @@ public void setTrackLastUsed(boolean trackLastUsed) { } public static class DashboardProperties { - private String dashboardClientId; - private String dashboardClientSecret; + private String clientId; + private String clientSecret; + private String clientBaseUrl; - public String getDashboardClientId() { - return dashboardClientId; + public String getClientId() { + return clientId; } - public void setDashboardClientId(String dashboardClientId) { - this.dashboardClientId = dashboardClientId; + public void setClientId(String clientId) { + this.clientId = clientId; } - public String getDashboardClientSecret() { - return dashboardClientSecret; + public String getClientSecret() { + return clientSecret; } - public void setDashboardClientSecret(String dashboardClientSecret) { - this.dashboardClientSecret = dashboardClientSecret; + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + public String getClientBaseUrl() { + return clientBaseUrl; + } + + public void setClientBaseUrl(String clientBaseUrl) { + this.clientBaseUrl = clientBaseUrl; } } @@ -999,6 +1008,10 @@ public DashboardProperties getDashboard() { public void setDashboard(DashboardProperties dashboard) { this.dashboard = dashboard; } + + public Boolean isDashboardPropertiesEnable() { + return dashboard != null; + } public AarcProfile getAarcProfile() { return aarcProfile; diff --git a/iam-login-service/src/main/resources/application.yml b/iam-login-service/src/main/resources/application.yml index 353312ec28..071657a87a 100644 --- a/iam-login-service/src/main/resources/application.yml +++ b/iam-login-service/src/main/resources/application.yml @@ -192,6 +192,7 @@ iam: dashboard: client-id: ${IAM_DASHBOARD_CLIENT_ID} client-secret: ${IAM_DASHBOARD_CLIENT_SECRET} + client-base-url: ${IAM_DASHBOARD_CLIENT_BASE_URL} cache: enabled: ${IAM_CACHE_ENABLED:true} From e355c1268674c1f84987b8f0b097b41fe01c96de Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Fri, 6 Mar 2026 10:18:22 +0100 Subject: [PATCH 03/26] Add dashboard properties validator --- .../validation/DashboardConfigValidator.java | 49 +++++++++++++++++++ .../management/validation/ValidDashboard.java | 36 ++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/DashboardConfigValidator.java create mode 100644 iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/ValidDashboard.java diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/DashboardConfigValidator.java b/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/DashboardConfigValidator.java new file mode 100644 index 0000000000..ce558ffe05 --- /dev/null +++ b/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/DashboardConfigValidator.java @@ -0,0 +1,49 @@ + +/** + * Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package it.infn.mw.iam.api.client.management.validation; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import it.infn.mw.iam.config.IamProperties.DashboardProperties; +import it.infn.mw.iam.api.client.management.validation.ValidDashboard; + +@Component +@Scope("prototype") +public class DashboardConfigValidator implements ConstraintValidator { + + private static final String CLIENT_ID_REGEX = "^[a-zA-Z0-9\\-._~]{3,64}$"; + private static final String CLIENT_SECRET_REGEX = "^[a-zA-Z0-9\\-._~]{32,72}$"; + private static final String URL_REGEX = "^(https?://)?([\\w.-]+)(:[0-9]+)?(/.*)?$"; + + @Override + public boolean isValid(DashboardProperties dashboardProperties, ConstraintValidatorContext context) { + if (dashboardProperties == null || !dashboardProperties.isEnabled()) { + return true; + } + boolean validClientId = dashboardProperties.getClientId() != null + && dashboardProperties.getClientId().matches(CLIENT_ID_REGEX); + boolean validClientSecret = dashboardProperties.getClientSecret() != null + && dashboardProperties.getClientSecret().matches(CLIENT_SECRET_REGEX); + boolean validUrl = dashboardProperties.getClientBaseUrl() != null + && dashboardProperties.getClientBaseUrl().matches(URL_REGEX); + + return validClientId && validClientSecret && validUrl; + } +} \ No newline at end of file diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/ValidDashboard.java b/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/ValidDashboard.java new file mode 100644 index 0000000000..02b010c07e --- /dev/null +++ b/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/ValidDashboard.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package it.infn.mw.iam.api.client.management.validation; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +@Retention(RUNTIME) +@Target({FIELD}) +@Constraint(validatedBy = DashboardConfigValidator.class) +public @interface ValidDashboard { + String message() default "Invalid dashboard client configuration"; + + Class [] groups() default {}; + + Class[] payload() default {}; +} From 0f7a5561d6c948473be728e84f2e109c90cfdf76 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Mon, 9 Mar 2026 11:15:39 +0100 Subject: [PATCH 04/26] Adds dashboard enabled flag --- .../java/it/infn/mw/iam/config/IamConfig.java | 3 +++ .../it/infn/mw/iam/config/IamProperties.java | 16 ++++++++++++++++ .../src/main/resources/application.yml | 1 + 3 files changed, 20 insertions(+) diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/config/IamConfig.java b/iam-login-service/src/main/java/it/infn/mw/iam/config/IamConfig.java index 80b127a34f..930b75a0de 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/config/IamConfig.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/config/IamConfig.java @@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; @@ -43,6 +44,7 @@ import it.infn.mw.iam.api.account.AccountUtils; import it.infn.mw.iam.api.scim.converter.SshKeyConverter; +import it.infn.mw.iam.config.IamProperties.DashboardProperties; import it.infn.mw.iam.core.oauth.attributes.AttributeMapHelper; import it.infn.mw.iam.core.oauth.profile.JWTProfile; import it.infn.mw.iam.core.oauth.profile.JWTProfileResolver; @@ -98,6 +100,7 @@ @SuppressWarnings("deprecation") @Configuration +@EnableConfigurationProperties(DashboardProperties.class) public class IamConfig { public static final Logger LOG = LoggerFactory.getLogger(IamConfig.class); diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java b/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java index 85e05bccb2..7c43fe794b 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java @@ -21,17 +21,20 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; +import org.springframework.validation.annotation.Validated; import com.fasterxml.jackson.annotation.JsonInclude; import com.google.common.collect.Lists; import com.nimbusds.jose.JWEAlgorithm; import com.nimbusds.jose.JWSAlgorithm; +import it.infn.mw.iam.api.client.management.validation.ValidDashboard; import it.infn.mw.iam.authn.ExternalAuthenticationRegistrationInfo.ExternalAuthenticationType; import it.infn.mw.iam.config.login.LoginButtonProperties; import it.infn.mw.iam.config.multi_factor_authentication.VerifyButtonProperties; @Component +@Validated @ConfigurationProperties(prefix = "iam") public class IamProperties { @@ -599,11 +602,23 @@ public void setTrackLastUsed(boolean trackLastUsed) { } } + @Validated + @ConfigurationProperties(prefix = "app.dashboard") public static class DashboardProperties { + + private boolean enabled = false; private String clientId; private String clientSecret; private String clientBaseUrl; + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + public String getClientId() { return clientId; } @@ -757,6 +772,7 @@ public void setUrnSubnamespaces(String urnSubnamespaces) { private AarcProfile aarcProfile = new AarcProfile(); +@ValidDashboard private DashboardProperties dashboard = new DashboardProperties(); public String getBaseUrl() { diff --git a/iam-login-service/src/main/resources/application.yml b/iam-login-service/src/main/resources/application.yml index 071657a87a..0c07ed3304 100644 --- a/iam-login-service/src/main/resources/application.yml +++ b/iam-login-service/src/main/resources/application.yml @@ -190,6 +190,7 @@ iam: track-last-used: ${IAM_CLIENT_TRACK_LAST_USED:false} dashboard: + enabled: ${IAM_DASHBOARD_ENABLED:false} client-id: ${IAM_DASHBOARD_CLIENT_ID} client-secret: ${IAM_DASHBOARD_CLIENT_SECRET} client-base-url: ${IAM_DASHBOARD_CLIENT_BASE_URL} From bd9b92b13d591a3ed19b194962542e25e4061af9 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Mon, 9 Mar 2026 11:43:13 +0100 Subject: [PATCH 05/26] Fix typo --- .../src/main/java/it/infn/mw/iam/config/IamProperties.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java b/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java index 7c43fe794b..d3676dc4e9 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java @@ -772,7 +772,7 @@ public void setUrnSubnamespaces(String urnSubnamespaces) { private AarcProfile aarcProfile = new AarcProfile(); -@ValidDashboard + @ValidDashboard private DashboardProperties dashboard = new DashboardProperties(); public String getBaseUrl() { From 6f9f814387441fe43f5bf4b4a4dc3c54dc859386 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Mon, 9 Mar 2026 16:48:22 +0100 Subject: [PATCH 06/26] Check dashboard configuration record on DB --- .../infn/mw/iam/core/util/StartupRunner.java | 61 +++++++ .../iam/dashboard/DashboardConfigService.java | 153 ++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 iam-login-service/src/main/java/it/infn/mw/iam/core/util/StartupRunner.java create mode 100644 iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/core/util/StartupRunner.java b/iam-login-service/src/main/java/it/infn/mw/iam/core/util/StartupRunner.java new file mode 100644 index 0000000000..192c777380 --- /dev/null +++ b/iam-login-service/src/main/java/it/infn/mw/iam/core/util/StartupRunner.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package it.infn.mw.iam.core.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +import it.infn.mw.iam.config.IamProperties; +import it.infn.mw.iam.dashboard.DashboardConfigService; + +@Component +public class StartupRunner implements ApplicationRunner { + + private static final Logger LOG = LoggerFactory.getLogger(StartupRunner.class); + + private final DashboardConfigService dashboardConfigService; + private final IamProperties.DashboardProperties dashboardProperties; + + public StartupRunner(DashboardConfigService dashboardConfigService, IamProperties iamProperties) { + this.dashboardConfigService = dashboardConfigService; + this.dashboardProperties = iamProperties.getDashboard(); + } + + @Override + public void run(ApplicationArguments args) throws IllegalStateException { + if (!dashboardProperties.isEnabled()) { + LOG.info( + "Dashboard client is disabled, skipping checks for the dashboard client properties and the presence of the record for the dashboard client"); + return; + } + + try { + boolean recordExists = this.dashboardConfigService.initDashboardClient(dashboardProperties); + if (!recordExists) { + throw new IllegalStateException( + "Dashboard client record does not exist or is not valid. Please check the dashboard client properties and ensure that a record with the specified client id, client secret and redirect uri exists in the database"); + } + } catch (Exception e) { + throw new IllegalStateException( + "An error occurred while verifying the presence of the record for the dashboard client: " + e.getMessage(), + e); + } + } +} diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java b/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java new file mode 100644 index 0000000000..d50fc952b1 --- /dev/null +++ b/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java @@ -0,0 +1,153 @@ +/** + * Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package it.infn.mw.iam.dashboard; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import javax.annotation.PostConstruct; +import org.springframework.transaction.annotation.Transactional; +import org.mitre.oauth2.model.ClientDetailsEntity; +import org.mitre.oauth2.model.PKCEAlgorithm; +import org.mitre.oauth2.service.SystemScopeService; +import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.security.oauth2.core.oidc.StandardClaimNames; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +import it.infn.mw.iam.api.client.management.service.DefaultClientManagementService; +import it.infn.mw.iam.api.client.service.ClientService; +import it.infn.mw.iam.config.IamProperties.DashboardProperties; +import it.infn.mw.iam.core.oauth.scope.pdp.DefaultScopeFilter; +import it.infn.mw.iam.persistence.repository.client.IamClientRepository; +import it.infn.mw.iam.api.common.client.AuthorizationGrantType; +import it.infn.mw.iam.api.common.client.RegisteredClientDTO; +import it.infn.mw.iam.api.common.client.TokenEndpointAuthenticationMethod; + +import com.beust.jcommander.internal.Sets; + +@Service +public class DashboardConfigService { + + private static final Logger LOG = LoggerFactory.getLogger(DashboardConfigService.class); + + private static final String DASHBOARD_CALLBACK = "/api/auth/oauth2/callback/indigo-iam"; + Set dashboardScopes = Set.of(SystemScopeService.OPENID_SCOPE, SystemScopeService.OFFLINE_ACCESS, "email", + "profile", "iam:admin.read", "iam:admin.write", "scim:read", "scim:write"); + + private final IamClientRepository iamClientDetailsRepository; + private final DefaultClientManagementService clientService; + + public DashboardConfigService(IamClientRepository iamClientDetailsRepository, + DefaultClientManagementService clientService) { + this.clientService = clientService; + this.iamClientDetailsRepository = iamClientDetailsRepository; + } + + @Transactional + public boolean initDashboardClient(DashboardProperties dashboardProperties) throws java.text.ParseException { + String clientId = dashboardProperties.getClientId(); + String clientSecret = dashboardProperties.getClientSecret(); + String url = dashboardProperties.getClientBaseUrl() + DASHBOARD_CALLBACK; + + Optional test = iamClientDetailsRepository.findByClientId(clientId); + + if (test.isPresent()) { + ClientDetailsEntity client = test.get(); + + boolean isConfigured = checkRecordConfiguration(test.get(), clientSecret, url); + if (!isConfigured) { + LOG.warn("The record is not properly configured. Updating Dashboard client."); + client.setScope(dashboardScopes); + client.setGrantTypes(Set.of(AuthorizationGrantType.CODE.getGrantType(), AuthorizationGrantType.REFRESH_TOKEN.getGrantType())); + client.setCodeChallengeMethod(PKCEAlgorithm.S256); + client.setRedirectUris(Set.of(url)); + client.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC); + + iamClientDetailsRepository.save(client); + } + return true; + } else { + LOG.info("The client record for dashboard does not exist. Creating record with default configuration..."); + RegisteredClientDTO client = new RegisteredClientDTO(); + Set clientScopes = Sets.newHashSet(); + clientScopes.add(StandardClaimNames.PROFILE); + clientScopes.add(StandardClaimNames.EMAIL); + clientScopes.add(DefaultScopeFilter.ADMIN_SCOPES.toString()); + + client.setScope(clientScopes); + client.setClientId(clientId); + client.setClientName("dashboard"); + client.setClientSecret(clientSecret); + client.setTokenEndpointAuthMethod(TokenEndpointAuthenticationMethod.client_secret_basic); + client.setAccessTokenValiditySeconds(3600); + client.setCodeChallengeMethod(PKCEAlgorithm.S256.toString()); + client.setActive(true); + client.setRedirectUris(Set.of(url)); + client.setGrantTypes(Set.of(AuthorizationGrantType.CODE, AuthorizationGrantType.REFRESH_TOKEN)); + + try { + clientService.saveNewClient(client); + } catch (Exception e) { + LOG.error("Error saving dashboard client: " + e.getMessage()); + return false; + } + return true; + } + } + + public boolean checkRecordConfiguration(ClientDetailsEntity client, String clientSecret, String url) { + return hasAllRequiredScopes(client) + && hasValidClientSecret(client, clientSecret) + && hasValidRedirectUris(client, url) + && supportsAuthorizationCodeGrant(client) + && usesClientSecretBasicAuth(client) + && usesPKCES256(client); + } + + private boolean hasAllRequiredScopes(ClientDetailsEntity client) { + return client.getScope().containsAll(dashboardScopes); + } + + private boolean hasValidClientSecret(ClientDetailsEntity client, String clientSecret) { + return client.getClientSecret().equals(clientSecret); + } + + private boolean hasValidRedirectUris(ClientDetailsEntity client, String url) { + return client.getRedirectUris().containsAll(Set.of(url)); + } + + private boolean supportsAuthorizationCodeGrant(ClientDetailsEntity client) { + return client.getGrantTypes().containsAll( + Set.of(AuthorizationGrantType.CODE.getGrantType(), AuthorizationGrantType.REFRESH_TOKEN.getGrantType())); + } + + private boolean usesClientSecretBasicAuth(ClientDetailsEntity client) { + return client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_BASIC); + } + + private boolean usesPKCES256(ClientDetailsEntity client) { + return client.getCodeChallengeMethod().getName().equals(PKCEAlgorithm.S256.toString()); + } +} From 509bfe0013c5d80b3fbf2e6008b13ff7125894c1 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Mon, 9 Mar 2026 16:49:50 +0100 Subject: [PATCH 07/26] Limit max client secret length --- .../iam/api/client/service/DefaultClientDefaultsService.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/api/client/service/DefaultClientDefaultsService.java b/iam-login-service/src/main/java/it/infn/mw/iam/api/client/service/DefaultClientDefaultsService.java index a88c27ab3c..2ce7b9d363 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/api/client/service/DefaultClientDefaultsService.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/api/client/service/DefaultClientDefaultsService.java @@ -40,6 +40,7 @@ public class DefaultClientDefaultsService implements ClientDefaultsService { EnumSet.of(AuthMethod.SECRET_BASIC, AuthMethod.SECRET_POST, AuthMethod.SECRET_JWT); private static final int SECRET_SIZE = 512; + private static final int BCRYPT_MAX_SIZE = 72; private static final SecureRandom RNG = new SecureRandom(); private final ClientRegistrationProperties properties; @@ -95,9 +96,7 @@ public ClientDetailsEntity setupClientDefaults(ClientDetailsEntity client) { @Override public String generateClientSecret() { - return - Base64.encodeBase64URLSafeString(new BigInteger(SECRET_SIZE, RNG).toByteArray()) - .replace("=", ""); + return Base64.encodeBase64URLSafeString(new BigInteger(SECRET_SIZE, RNG).toByteArray()).substring(0, BCRYPT_MAX_SIZE); } } From e0f5a615a1ac05655197c88f2cfbe660f9ccf781 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Tue, 10 Mar 2026 22:16:39 +0100 Subject: [PATCH 08/26] Removes dashboard client base URL from config --- .../management/validation/DashboardConfigValidator.java | 5 +---- .../main/java/it/infn/mw/iam/config/IamProperties.java | 9 --------- .../java/it/infn/mw/iam/core/util/StartupRunner.java | 4 +++- .../it/infn/mw/iam/dashboard/DashboardConfigService.java | 6 +++--- iam-login-service/src/main/resources/application.yml | 1 - 5 files changed, 7 insertions(+), 18 deletions(-) diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/DashboardConfigValidator.java b/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/DashboardConfigValidator.java index ce558ffe05..a2bffc1a72 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/DashboardConfigValidator.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/DashboardConfigValidator.java @@ -30,7 +30,6 @@ public class DashboardConfigValidator implements ConstraintValidator dashboardScopes = Set.of(SystemScopeService.OPENID_SCOPE, SystemScopeService.OFFLINE_ACCESS, "email", "profile", "iam:admin.read", "iam:admin.write", "scim:read", "scim:write"); @@ -66,10 +66,10 @@ public DashboardConfigService(IamClientRepository iamClientDetailsRepository, } @Transactional - public boolean initDashboardClient(DashboardProperties dashboardProperties) throws java.text.ParseException { + public boolean initDashboardClient(DashboardProperties dashboardProperties, String iamUrl) throws java.text.ParseException { String clientId = dashboardProperties.getClientId(); String clientSecret = dashboardProperties.getClientSecret(); - String url = dashboardProperties.getClientBaseUrl() + DASHBOARD_CALLBACK; + String url = iamUrl + DASHBOARD_CALLBACK; Optional test = iamClientDetailsRepository.findByClientId(clientId); diff --git a/iam-login-service/src/main/resources/application.yml b/iam-login-service/src/main/resources/application.yml index 0c07ed3304..b2882793a6 100644 --- a/iam-login-service/src/main/resources/application.yml +++ b/iam-login-service/src/main/resources/application.yml @@ -193,7 +193,6 @@ iam: enabled: ${IAM_DASHBOARD_ENABLED:false} client-id: ${IAM_DASHBOARD_CLIENT_ID} client-secret: ${IAM_DASHBOARD_CLIENT_SECRET} - client-base-url: ${IAM_DASHBOARD_CLIENT_BASE_URL} cache: enabled: ${IAM_CACHE_ENABLED:true} From 221e3806dbb85eec6e4a5ac2bc525d9a6ceb36b8 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Tue, 10 Mar 2026 22:40:47 +0100 Subject: [PATCH 09/26] Add unit tests for DashboardConfigService --- .../dashboard/DashboardConfigServiceTest.java | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java new file mode 100644 index 0000000000..53b1b60523 --- /dev/null +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java @@ -0,0 +1,110 @@ +package it.infn.mw.iam.test.dashboard; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mitre.oauth2.model.ClientDetailsEntity; +import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod; +import org.mitre.oauth2.model.PKCEAlgorithm; + +import java.text.ParseException; +import java.util.Set; +import com.google.common.collect.Sets; + +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import it.infn.mw.iam.dashboard.DashboardConfigService; +import it.infn.mw.iam.persistence.repository.client.IamClientRepository; +import it.infn.mw.iam.api.client.management.service.DefaultClientManagementService; +import it.infn.mw.iam.api.common.client.AuthorizationGrantType; +import it.infn.mw.iam.config.IamProperties.DashboardProperties; + +@ExtendWith(MockitoExtension.class) +public class DashboardConfigServiceTest { + + private static final String CLIENT_ID = "dashboard-client"; + private static final String CLIENT_SECRET = "secret"; + private static final String BASE_URL = "http://localhost:8080"; + private static final Set SCOPES = Sets.newHashSet("openid", "profile", "email", "iam:admin.read", + "iam:admin.write", "scim:read", "scim:write", "offline_access"); + private static final Set AUTH_GRAND_TYPE = Set.of(AuthorizationGrantType.CODE.getGrantType(), + AuthorizationGrantType.REFRESH_TOKEN.getGrantType()); + + private ClientDetailsEntity client; + + @InjectMocks + private DashboardConfigService dashboardConfigService; + + @Mock + private IamClientRepository iamClientDetailsRepository; + + @Mock + private DefaultClientManagementService clientService; + + @BeforeEach + void setUp() { + this.client = new ClientDetailsEntity(); + client.setClientId(CLIENT_ID); + client.setClientSecret(CLIENT_SECRET); + client.setScope(SCOPES); + client.setGrantTypes(AUTH_GRAND_TYPE); + client.setRedirectUris(Set.of("http://localhost:8080/api/auth/oauth2/callback/indigo-iam")); + client.setCodeChallengeMethod(PKCEAlgorithm.S256); + client.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC); + iamClientDetailsRepository.save(client); + } + + @AfterEach + void tearDown() { + iamClientDetailsRepository.delete(client); + } + + @Test + void testCheckRecordConfiguration() { + ClientDetailsEntity client = createClientDashboard(CLIENT_ID, CLIENT_SECRET, BASE_URL, AUTH_GRAND_TYPE, SCOPES); + + assertEquals(dashboardConfigService.checkRecordConfiguration(client, CLIENT_SECRET, BASE_URL), true); + } + + @Test + void testFailCheckRecordScopeConfiguration() { + ClientDetailsEntity client = createClientDashboard(CLIENT_ID, CLIENT_SECRET, BASE_URL, AUTH_GRAND_TYPE, + Sets.newHashSet("openid")); + + assertEquals(dashboardConfigService.checkRecordConfiguration(client, CLIENT_SECRET, BASE_URL), false); + } + + @Test + void testFailCheckRecordClientSecretConfiguration() { + ClientDetailsEntity client = createClientDashboard(CLIENT_ID, CLIENT_SECRET, BASE_URL, AUTH_GRAND_TYPE, SCOPES); + + assertEquals(dashboardConfigService.checkRecordConfiguration(client, "test_secret", BASE_URL), false); + } + + @Test + void testInitDashboardClient() throws ParseException { + DashboardProperties properties = new DashboardProperties(); + properties.setClientId(CLIENT_ID); + properties.setClientSecret(CLIENT_SECRET); + + assertEquals(dashboardConfigService.initDashboardClient(properties, BASE_URL), true); + } + + private ClientDetailsEntity createClientDashboard(String clientId, String clientSecret, + String redirectUris, Set grantTypes, Set scopes) { + ClientDetailsEntity client = new ClientDetailsEntity(); + client.setClientId(clientId); + client.setClientSecret(clientSecret); + client.setScope(scopes); + client.setGrantTypes(grantTypes); + client.setRedirectUris(Set.of(redirectUris)); + client.setCodeChallengeMethod(PKCEAlgorithm.S256); + client.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC); + return client; + } +} From 2368b2e98584d17836fb634d8a60d105c3f1c104 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Wed, 11 Mar 2026 13:01:57 +0100 Subject: [PATCH 10/26] Add unit tests for DashboardConfigValidator --- .../config/DashboardConfigValidatorTests.java | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 iam-login-service/src/test/java/it/infn/mw/iam/test/config/DashboardConfigValidatorTests.java diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/config/DashboardConfigValidatorTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/config/DashboardConfigValidatorTests.java new file mode 100644 index 0000000000..a78ad3a885 --- /dev/null +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/config/DashboardConfigValidatorTests.java @@ -0,0 +1,94 @@ +package it.infn.mw.iam.test.config; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; + +import javax.validation.ConstraintValidatorContext; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import it.infn.mw.iam.api.client.management.validation.DashboardConfigValidator; +import it.infn.mw.iam.config.IamProperties.DashboardProperties; + +public class DashboardConfigValidatorTests { + + private final DashboardConfigValidator validator = new DashboardConfigValidator(); + private final ConstraintValidatorContext context = mock(ConstraintValidatorContext.class); + private static DashboardProperties dashboardProperties; + + @BeforeEach + void setup() { + dashboardProperties = new DashboardProperties(); + dashboardProperties.setEnabled(true); + dashboardProperties.setClientId("123e4567-e89b-12d3-a456-426655440000"); + dashboardProperties.setClientSecret("0tlkqGPJD2vWN1dgTqi3xn-PAJ7EMgNKFFUOydZPsTLkIouqQFmfioJcvfk0V2Xt"); + } + + @Test + void testSkipValidation() { + dashboardProperties = new DashboardProperties(); + dashboardProperties.setEnabled(false); + dashboardProperties.setClientId(null); + dashboardProperties.setClientSecret(null); + boolean isValid = validator.isValid(dashboardProperties, context); + + assertTrue(isValid); + } + + @Test + void testDashboardPropertiesValid() { + boolean isValid = validator.isValid(dashboardProperties, context); + + assertTrue(isValid); + } + + @Test + void testIsNotValidDashboardSecret() { + dashboardProperties.setClientSecret("too-short-client-secret"); + boolean isValid = validator.isValid(dashboardProperties, context); + + assertFalse(isValid); + } + + @Test + void testIsNotValidDashboardSecretTooLong() { + dashboardProperties.setClientSecret("too-long-client-secret-82gbV6OEwGBCPMmcFPXg5-4wRJXnKc-4wds5odwrFiY4wds5odwrF"); + boolean isValid = validator.isValid(dashboardProperties, context); + + assertFalse(isValid); + } + + @Test + void testIsNotValidDashboardSecretInvalidChars() { + dashboardProperties.setClientSecret("0tlkqGPJD2vWN1/dgTqi3xn-PAJ7EMgNKFFUOydZPsTLkIouqQFmfioJcvfk0V2Xt"); + boolean isValid = validator.isValid(dashboardProperties, context); + + assertFalse(isValid); + } + + @Test + void testIsNotValidDashboardSecretInvalidNull() { + dashboardProperties.setClientSecret(null); + boolean isValid = validator.isValid(dashboardProperties, context); + + assertFalse(isValid); + } + + @Test + void testIsNotValidDashboardClientIdInvalidUUID() { + dashboardProperties.setClientId("3xn-PAJ7EMgNKFFUOydZPsTLkIouqQFmfioJcvfk0V2Xt"); + boolean isValid = validator.isValid(dashboardProperties, context); + + assertFalse(isValid); + } + + @Test + void testIsNotValidDashboardClientIdNull() { + dashboardProperties.setClientId(null); + boolean isValid = validator.isValid(dashboardProperties, context); + + assertFalse(isValid); + } +} From 9a46b131461bb116150bece6ad7dbc91559240d3 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Wed, 11 Mar 2026 13:03:00 +0100 Subject: [PATCH 11/26] Update CLIENT_ID_REGEX to match UUID --- .../management/validation/DashboardConfigValidator.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/DashboardConfigValidator.java b/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/DashboardConfigValidator.java index a2bffc1a72..caa760e805 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/DashboardConfigValidator.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/DashboardConfigValidator.java @@ -28,7 +28,7 @@ @Scope("prototype") public class DashboardConfigValidator implements ConstraintValidator { - private static final String CLIENT_ID_REGEX = "^[a-zA-Z0-9\\-._~]{3,64}$"; + private static final String CLIENT_UUID_REGEX = "^[0-9a-fA-F]{8}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{12}$"; private static final String CLIENT_SECRET_REGEX = "^[a-zA-Z0-9\\-._~]{32,72}$"; @Override @@ -37,10 +37,10 @@ public boolean isValid(DashboardProperties dashboardProperties, ConstraintValida return true; } boolean validClientId = dashboardProperties.getClientId() != null - && dashboardProperties.getClientId().matches(CLIENT_ID_REGEX); + && dashboardProperties.getClientId().matches(CLIENT_UUID_REGEX); boolean validClientSecret = dashboardProperties.getClientSecret() != null && dashboardProperties.getClientSecret().matches(CLIENT_SECRET_REGEX); return validClientId && validClientSecret; } -} \ No newline at end of file +} From 707936d283b8f6d040f03b9a3198786e29e3a931 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Wed, 11 Mar 2026 16:05:57 +0100 Subject: [PATCH 12/26] Update DashboardConfigValidator --- .../validation/DashboardConfigValidator.java | 6 +-- .../config/DashboardConfigValidatorTests.java | 40 +++++++------------ 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/DashboardConfigValidator.java b/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/DashboardConfigValidator.java index caa760e805..e20a35f7b1 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/DashboardConfigValidator.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/validation/DashboardConfigValidator.java @@ -15,6 +15,7 @@ * limitations under the License. */ package it.infn.mw.iam.api.client.management.validation; + import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; @@ -22,13 +23,12 @@ import org.springframework.stereotype.Component; import it.infn.mw.iam.config.IamProperties.DashboardProperties; -import it.infn.mw.iam.api.client.management.validation.ValidDashboard; @Component @Scope("prototype") public class DashboardConfigValidator implements ConstraintValidator { - private static final String CLIENT_UUID_REGEX = "^[0-9a-fA-F]{8}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{4}\\b-[0-9a-fA-F]{12}$"; + private static final String CLIENT_ID_REGEX = "^[a-zA-Z0-9\\-._~]{4,256}$"; private static final String CLIENT_SECRET_REGEX = "^[a-zA-Z0-9\\-._~]{32,72}$"; @Override @@ -37,7 +37,7 @@ public boolean isValid(DashboardProperties dashboardProperties, ConstraintValida return true; } boolean validClientId = dashboardProperties.getClientId() != null - && dashboardProperties.getClientId().matches(CLIENT_UUID_REGEX); + && dashboardProperties.getClientId().matches(CLIENT_ID_REGEX); boolean validClientSecret = dashboardProperties.getClientSecret() != null && dashboardProperties.getClientSecret().matches(CLIENT_SECRET_REGEX); diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/config/DashboardConfigValidatorTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/config/DashboardConfigValidatorTests.java index a78ad3a885..12db2d7da5 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/config/DashboardConfigValidatorTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/config/DashboardConfigValidatorTests.java @@ -22,7 +22,7 @@ public class DashboardConfigValidatorTests { void setup() { dashboardProperties = new DashboardProperties(); dashboardProperties.setEnabled(true); - dashboardProperties.setClientId("123e4567-e89b-12d3-a456-426655440000"); + dashboardProperties.setClientId("client-dashboard"); dashboardProperties.setClientSecret("0tlkqGPJD2vWN1dgTqi3xn-PAJ7EMgNKFFUOydZPsTLkIouqQFmfioJcvfk0V2Xt"); } @@ -32,63 +32,53 @@ void testSkipValidation() { dashboardProperties.setEnabled(false); dashboardProperties.setClientId(null); dashboardProperties.setClientSecret(null); - boolean isValid = validator.isValid(dashboardProperties, context); - - assertTrue(isValid); + assertTrue(validator.isValid(dashboardProperties, context)); } @Test void testDashboardPropertiesValid() { - boolean isValid = validator.isValid(dashboardProperties, context); - - assertTrue(isValid); + assertTrue(validator.isValid(dashboardProperties, context)); } @Test void testIsNotValidDashboardSecret() { dashboardProperties.setClientSecret("too-short-client-secret"); - boolean isValid = validator.isValid(dashboardProperties, context); - - assertFalse(isValid); + assertFalse(validator.isValid(dashboardProperties, context)); } @Test void testIsNotValidDashboardSecretTooLong() { dashboardProperties.setClientSecret("too-long-client-secret-82gbV6OEwGBCPMmcFPXg5-4wRJXnKc-4wds5odwrFiY4wds5odwrF"); - boolean isValid = validator.isValid(dashboardProperties, context); - - assertFalse(isValid); + assertFalse(validator.isValid(dashboardProperties, context)); } @Test void testIsNotValidDashboardSecretInvalidChars() { dashboardProperties.setClientSecret("0tlkqGPJD2vWN1/dgTqi3xn-PAJ7EMgNKFFUOydZPsTLkIouqQFmfioJcvfk0V2Xt"); - boolean isValid = validator.isValid(dashboardProperties, context); - - assertFalse(isValid); + assertFalse(validator.isValid(dashboardProperties, context)); } @Test void testIsNotValidDashboardSecretInvalidNull() { dashboardProperties.setClientSecret(null); - boolean isValid = validator.isValid(dashboardProperties, context); + assertFalse(validator.isValid(dashboardProperties, context)); + } - assertFalse(isValid); + @Test + void testIsNotValidDashboardSecretInvalidString() { + dashboardProperties.setClientSecret(""); + assertFalse(validator.isValid(dashboardProperties, context)); } @Test void testIsNotValidDashboardClientIdInvalidUUID() { - dashboardProperties.setClientId("3xn-PAJ7EMgNKFFUOydZPsTLkIouqQFmfioJcvfk0V2Xt"); - boolean isValid = validator.isValid(dashboardProperties, context); - - assertFalse(isValid); + dashboardProperties.setClientId("client-dashboard!"); + assertFalse(validator.isValid(dashboardProperties, context)); } @Test void testIsNotValidDashboardClientIdNull() { dashboardProperties.setClientId(null); - boolean isValid = validator.isValid(dashboardProperties, context); - - assertFalse(isValid); + assertFalse(validator.isValid(dashboardProperties, context)); } } From dfd8ec522c071fb9ade60281978997b2b65d8252 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Wed, 11 Mar 2026 17:14:16 +0100 Subject: [PATCH 13/26] Fix scope for new DashboardClient --- .../it/infn/mw/iam/dashboard/DashboardConfigService.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java b/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java index b63567239d..b9e020e9f5 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java @@ -91,12 +91,7 @@ public boolean initDashboardClient(DashboardProperties dashboardProperties, Stri } else { LOG.info("The client record for dashboard does not exist. Creating record with default configuration..."); RegisteredClientDTO client = new RegisteredClientDTO(); - Set clientScopes = Sets.newHashSet(); - clientScopes.add(StandardClaimNames.PROFILE); - clientScopes.add(StandardClaimNames.EMAIL); - clientScopes.add(DefaultScopeFilter.ADMIN_SCOPES.toString()); - - client.setScope(clientScopes); + client.setScope(dashboardScopes); client.setClientId(clientId); client.setClientName("dashboard"); client.setClientSecret(clientSecret); From ce43a48035eb1ec957d014faf55df88cfbadef22 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Thu, 12 Mar 2026 11:21:14 +0100 Subject: [PATCH 14/26] Add tests in DashboardConfigServiceTest --- .../iam/dashboard/DashboardConfigService.java | 2 - .../config/DashboardConfigValidatorTests.java | 15 ++++ .../dashboard/DashboardConfigServiceTest.java | 82 ++++++++++++++++--- 3 files changed, 86 insertions(+), 13 deletions(-) diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java b/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java index b9e020e9f5..e469a49994 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java @@ -22,7 +22,6 @@ import java.util.Set; import javax.annotation.PostConstruct; -import org.springframework.transaction.annotation.Transactional; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.PKCEAlgorithm; import org.mitre.oauth2.service.SystemScopeService; @@ -65,7 +64,6 @@ public DashboardConfigService(IamClientRepository iamClientDetailsRepository, this.iamClientDetailsRepository = iamClientDetailsRepository; } - @Transactional public boolean initDashboardClient(DashboardProperties dashboardProperties, String iamUrl) throws java.text.ParseException { String clientId = dashboardProperties.getClientId(); String clientSecret = dashboardProperties.getClientSecret(); diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/config/DashboardConfigValidatorTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/config/DashboardConfigValidatorTests.java index 12db2d7da5..1fa62bdf78 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/config/DashboardConfigValidatorTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/config/DashboardConfigValidatorTests.java @@ -1,3 +1,18 @@ +/** + * Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package it.infn.mw.iam.test.config; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java index 53b1b60523..04486c9f7a 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java @@ -1,30 +1,49 @@ +/** + * Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package it.infn.mw.iam.test.dashboard; +import static org.hamcrest.CoreMatchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.hamcrest.MatcherAssert.assertThat; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod; import org.mitre.oauth2.model.PKCEAlgorithm; import java.text.ParseException; import java.util.Set; + +import javax.transaction.Transactional; + import com.google.common.collect.Sets; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; import it.infn.mw.iam.dashboard.DashboardConfigService; import it.infn.mw.iam.persistence.repository.client.IamClientRepository; -import it.infn.mw.iam.api.client.management.service.DefaultClientManagementService; +import it.infn.mw.iam.IamLoginService; import it.infn.mw.iam.api.common.client.AuthorizationGrantType; import it.infn.mw.iam.config.IamProperties.DashboardProperties; -@ExtendWith(MockitoExtension.class) +@SpringBootTest(classes = { IamLoginService.class }) +@Transactional public class DashboardConfigServiceTest { private static final String CLIENT_ID = "dashboard-client"; @@ -37,15 +56,12 @@ public class DashboardConfigServiceTest { private ClientDetailsEntity client; - @InjectMocks + @Autowired private DashboardConfigService dashboardConfigService; - @Mock + @Autowired private IamClientRepository iamClientDetailsRepository; - @Mock - private DefaultClientManagementService clientService; - @BeforeEach void setUp() { this.client = new ClientDetailsEntity(); @@ -95,6 +111,50 @@ void testInitDashboardClient() throws ParseException { assertEquals(dashboardConfigService.initDashboardClient(properties, BASE_URL), true); } + @Test + void testInitDashboardClientFail() throws ParseException { + DashboardProperties properties = new DashboardProperties(); + properties.setClientId(CLIENT_ID); + properties.setClientSecret(CLIENT_SECRET); + assertEquals(dashboardConfigService.initDashboardClient(properties, BASE_URL), true); + } + + @Test + void testInitDashboardClientInsertDashboard() throws ParseException { + assertThat(iamClientDetailsRepository.findByClientId(CLIENT_ID + "-new").isPresent(), is(false)); + + DashboardProperties properties = new DashboardProperties(); + properties.setClientId(CLIENT_ID + "-new"); + properties.setClientSecret(CLIENT_SECRET); + + assertEquals(dashboardConfigService.initDashboardClient(properties, BASE_URL), true); + + iamClientDetailsRepository.findByClientId(CLIENT_ID + "-new").ifPresentOrElse(c -> { + assertEquals(c.getClientId(), CLIENT_ID + "-new"); + assertEquals(c.getScope(), client.getScope()); + }, () -> { + throw new AssertionError("Client not found"); + }); + } + + @Test + void testInitDashboardClientUpdateDashboard() throws ParseException { + assertThat(iamClientDetailsRepository.findByClientId(CLIENT_ID + "-new").isPresent(), is(false)); + + DashboardProperties properties = new DashboardProperties(); + properties.setClientId(CLIENT_ID); + properties.setClientSecret(CLIENT_SECRET); + + assertEquals(dashboardConfigService.initDashboardClient(properties, BASE_URL), true); + + iamClientDetailsRepository.findByClientId(CLIENT_ID).ifPresentOrElse(c -> { + assertEquals(c.getClientId(), CLIENT_ID); + assertEquals(c.getScope(), client.getScope()); + }, () -> { + throw new AssertionError("Client not found"); + }); + } + private ClientDetailsEntity createClientDashboard(String clientId, String clientSecret, String redirectUris, Set grantTypes, Set scopes) { ClientDetailsEntity client = new ClientDetailsEntity(); From 212ae551351aacad8f2354095160978a917e4731 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Thu, 12 Mar 2026 12:32:31 +0100 Subject: [PATCH 15/26] Remove unused imports --- .../mw/iam/dashboard/DashboardConfigService.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java b/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java index e469a49994..812f1f5790 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java @@ -16,36 +16,24 @@ package it.infn.mw.iam.dashboard; -import java.util.Arrays; -import java.util.HashSet; import java.util.Optional; import java.util.Set; -import javax.annotation.PostConstruct; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.PKCEAlgorithm; import org.mitre.oauth2.service.SystemScopeService; import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.CommandLineRunner; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.security.oauth2.core.oidc.StandardClaimNames; -import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import it.infn.mw.iam.api.client.management.service.DefaultClientManagementService; -import it.infn.mw.iam.api.client.service.ClientService; import it.infn.mw.iam.config.IamProperties.DashboardProperties; -import it.infn.mw.iam.core.oauth.scope.pdp.DefaultScopeFilter; import it.infn.mw.iam.persistence.repository.client.IamClientRepository; import it.infn.mw.iam.api.common.client.AuthorizationGrantType; import it.infn.mw.iam.api.common.client.RegisteredClientDTO; import it.infn.mw.iam.api.common.client.TokenEndpointAuthenticationMethod; -import com.beust.jcommander.internal.Sets; - @Service public class DashboardConfigService { From 68af9af90b7e4e256f38d23244b4283e79e952ea Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Fri, 13 Mar 2026 11:20:53 +0100 Subject: [PATCH 16/26] Add StartupRunnerTests to verify dashboard initialization --- .../test/core/util/StartupRunnerTests.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java new file mode 100644 index 0000000000..1a5de2c801 --- /dev/null +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package it.infn.mw.iam.test.core.util; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.text.ParseException; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.SpyBean; + +import it.infn.mw.iam.IamLoginService; +import it.infn.mw.iam.config.IamProperties.DashboardProperties; +import it.infn.mw.iam.core.util.StartupRunner; +import it.infn.mw.iam.dashboard.DashboardConfigService; + +@SpringBootTest(classes = { IamLoginService.class } +// @formatter:off + , properties = { + "iam.dashboard.client-id=dashboard-client-id", + "iam.dashboard.client-secret=AS69GU9PWvXw3te2RtYqhRYLlNYOhY03IjCnTjeRA69nFXK", + "iam.dashboard.enabled=true" +// @formatter:on + }) +public class StartupRunnerTests { + + @SpyBean + private StartupRunner runner; + + @SpyBean + private DashboardConfigService service; + + @Test + void shouldStartRunner() throws ParseException { + verify(runner).run(any(ApplicationArguments.class)); + + verify(service, times(1)).initDashboardClient(any(DashboardProperties.class), any(String.class)); + + } +} From a76e6eceedf42fa263a199f21ae6636d9d762ef0 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Fri, 13 Mar 2026 11:21:46 +0100 Subject: [PATCH 17/26] Refactor StartupRunner --- .../main/java/it/infn/mw/iam/core/util/StartupRunner.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/core/util/StartupRunner.java b/iam-login-service/src/main/java/it/infn/mw/iam/core/util/StartupRunner.java index 370d4e1c7a..1f94af7b56 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/core/util/StartupRunner.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/core/util/StartupRunner.java @@ -23,6 +23,7 @@ import org.springframework.stereotype.Component; import it.infn.mw.iam.config.IamProperties; +import it.infn.mw.iam.config.IamProperties.DashboardProperties; import it.infn.mw.iam.dashboard.DashboardConfigService; @Component @@ -31,7 +32,7 @@ public class StartupRunner implements ApplicationRunner { private static final Logger LOG = LoggerFactory.getLogger(StartupRunner.class); private final DashboardConfigService dashboardConfigService; - private final IamProperties.DashboardProperties dashboardProperties; + private final DashboardProperties dashboardProperties; private final String iamBaseUrl; public StartupRunner(DashboardConfigService dashboardConfigService, IamProperties iamProperties) { @@ -49,7 +50,7 @@ public void run(ApplicationArguments args) throws IllegalStateException { } try { - boolean recordExists = this.dashboardConfigService.initDashboardClient(dashboardProperties, iamBaseUrl); + boolean recordExists = dashboardConfigService.initDashboardClient(dashboardProperties, iamBaseUrl); if (!recordExists) { throw new IllegalStateException( "Dashboard client record does not exist or is not valid. Please check the dashboard client properties and ensure that a record with the specified client id, client secret and redirect uri exists in the database"); From c78c60853964474b1cab19ab146fba1fc9d16e03 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Fri, 13 Mar 2026 11:44:51 +0100 Subject: [PATCH 18/26] Add @Transactional annotation --- .../java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java index 1a5de2c801..d1aed7381a 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java @@ -20,6 +20,7 @@ import static org.mockito.Mockito.verify; import java.text.ParseException; +import javax.transaction.Transactional; import org.junit.jupiter.api.Test; import org.springframework.boot.ApplicationArguments; @@ -39,6 +40,7 @@ "iam.dashboard.enabled=true" // @formatter:on }) +@Transactional public class StartupRunnerTests { @SpyBean From 287f2554db73b6f4a78b5cf229da3e996d3f7075 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Fri, 13 Mar 2026 12:50:20 +0100 Subject: [PATCH 19/26] Refactor exception --- .../it/infn/mw/iam/core/util/StartupRunner.java | 14 ++++---------- .../mw/iam/dashboard/DashboardConfigService.java | 12 ++++++------ .../mw/iam/test/core/util/StartupRunnerTests.java | 3 +-- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/core/util/StartupRunner.java b/iam-login-service/src/main/java/it/infn/mw/iam/core/util/StartupRunner.java index 1f94af7b56..4dee2d4255 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/core/util/StartupRunner.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/core/util/StartupRunner.java @@ -42,23 +42,17 @@ public StartupRunner(DashboardConfigService dashboardConfigService, IamPropertie } @Override - public void run(ApplicationArguments args) throws IllegalStateException { + public void run(ApplicationArguments args) { if (!dashboardProperties.isEnabled()) { LOG.info( "Dashboard client is disabled, skipping checks for the dashboard client properties and the presence of the record for the dashboard client"); return; } - try { - boolean recordExists = dashboardConfigService.initDashboardClient(dashboardProperties, iamBaseUrl); - if (!recordExists) { - throw new IllegalStateException( - "Dashboard client record does not exist or is not valid. Please check the dashboard client properties and ensure that a record with the specified client id, client secret and redirect uri exists in the database"); - } - } catch (Exception e) { + boolean recordExists = dashboardConfigService.initDashboardClient(dashboardProperties, iamBaseUrl); + if (!recordExists) { throw new IllegalStateException( - "An error occurred while verifying the presence of the record for the dashboard client: " + e.getMessage(), - e); + "Dashboard client record does not exist or is not valid. Please check the dashboard client properties and ensure that a record with the specified client id, client secret and redirect uri exists in the database"); } } } diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java b/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java index 812f1f5790..2f11e1732a 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java @@ -43,21 +43,21 @@ public class DashboardConfigService { Set dashboardScopes = Set.of(SystemScopeService.OPENID_SCOPE, SystemScopeService.OFFLINE_ACCESS, "email", "profile", "iam:admin.read", "iam:admin.write", "scim:read", "scim:write"); - private final IamClientRepository iamClientDetailsRepository; + private final IamClientRepository clientRepository; private final DefaultClientManagementService clientService; - public DashboardConfigService(IamClientRepository iamClientDetailsRepository, + public DashboardConfigService(IamClientRepository clientRepository, DefaultClientManagementService clientService) { this.clientService = clientService; - this.iamClientDetailsRepository = iamClientDetailsRepository; + this.clientRepository = clientRepository; } - public boolean initDashboardClient(DashboardProperties dashboardProperties, String iamUrl) throws java.text.ParseException { + public boolean initDashboardClient(DashboardProperties dashboardProperties, String iamUrl) { String clientId = dashboardProperties.getClientId(); String clientSecret = dashboardProperties.getClientSecret(); String url = iamUrl + DASHBOARD_CALLBACK; - Optional test = iamClientDetailsRepository.findByClientId(clientId); + Optional test = clientRepository.findByClientId(clientId); if (test.isPresent()) { ClientDetailsEntity client = test.get(); @@ -71,7 +71,7 @@ public boolean initDashboardClient(DashboardProperties dashboardProperties, Stri client.setRedirectUris(Set.of(url)); client.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC); - iamClientDetailsRepository.save(client); + clientRepository.save(client); } return true; } else { diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java index d1aed7381a..dd992200ed 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java @@ -50,10 +50,9 @@ public class StartupRunnerTests { private DashboardConfigService service; @Test - void shouldStartRunner() throws ParseException { + void shouldStartRunner() { verify(runner).run(any(ApplicationArguments.class)); verify(service, times(1)).initDashboardClient(any(DashboardProperties.class), any(String.class)); - } } From 2e58c587af2d166735f95595a8f5b01e2be7cc42 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Fri, 13 Mar 2026 17:17:19 +0100 Subject: [PATCH 20/26] Add StartupRunnerWithoutConfigurationTests --- ...artupRunnerWithoutConfigurationTests.java} | 36 ++++++------------- 1 file changed, 11 insertions(+), 25 deletions(-) rename iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/{StartupRunnerTests.java => StartupRunnerWithoutConfigurationTests.java} (56%) diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerWithoutConfigurationTests.java similarity index 56% rename from iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java rename to iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerWithoutConfigurationTests.java index dd992200ed..25c79b733c 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerWithoutConfigurationTests.java @@ -16,43 +16,29 @@ package it.infn.mw.iam.test.core.util; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import java.text.ParseException; -import javax.transaction.Transactional; - import org.junit.jupiter.api.Test; -import org.springframework.boot.ApplicationArguments; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.TestPropertySource; +import org.springframework.transaction.annotation.Transactional; import it.infn.mw.iam.IamLoginService; import it.infn.mw.iam.config.IamProperties.DashboardProperties; -import it.infn.mw.iam.core.util.StartupRunner; import it.infn.mw.iam.dashboard.DashboardConfigService; -@SpringBootTest(classes = { IamLoginService.class } -// @formatter:off - , properties = { - "iam.dashboard.client-id=dashboard-client-id", - "iam.dashboard.client-secret=AS69GU9PWvXw3te2RtYqhRYLlNYOhY03IjCnTjeRA69nFXK", - "iam.dashboard.enabled=true" -// @formatter:on - }) -@Transactional -public class StartupRunnerTests { - - @SpyBean - private StartupRunner runner; +@SpringBootTest(classes = { IamLoginService.class }) +@TestPropertySource(properties = "iam.dashboard.enabled=false") +public class StartupRunnerWithoutConfigurationTests { - @SpyBean - private DashboardConfigService service; + @MockBean + private DashboardConfigService myService; @Test - void shouldStartRunner() { - verify(runner).run(any(ApplicationArguments.class)); - - verify(service, times(1)).initDashboardClient(any(DashboardProperties.class), any(String.class)); + void testRunnerDoesNotInitializeDashboard() throws IllegalStateException { + verify(myService, times(0)).initDashboardClient(any(DashboardProperties.class), any(String.class)); } } From 9824e29ce8bac8ebc580c2861e228dc2ba34dd3b Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Fri, 13 Mar 2026 17:59:58 +0100 Subject: [PATCH 21/26] Add tests for StartupRunner and refactor --- .../test/core/util/StartupRunnerTests.java | 47 +++++++++++++++++++ ...tartupRunnerWithoutConfigurationTests.java | 6 +-- 2 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java new file mode 100644 index 0000000000..2a331d10ca --- /dev/null +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package it.infn.mw.iam.test.core.util; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.test.context.TestPropertySource; +import org.springframework.transaction.annotation.Transactional; + +import it.infn.mw.iam.IamLoginService; +import it.infn.mw.iam.config.IamProperties.DashboardProperties; +import it.infn.mw.iam.dashboard.DashboardConfigService; + +@SpringBootTest(classes = { IamLoginService.class }) +@TestPropertySource(properties = { + "iam.dashboard.enabled=true", + "iam.dashboard.client-id=dashboard-id", + "iam.dashboard.client-secret=10000000-1234-1234-1234-123456789012" }) +@Transactional +public class StartupRunnerTests { + + @SpyBean + private DashboardConfigService dashboardConfigService; + + @Test + void testRunnerInitializesDashboard() throws Exception { + verify(dashboardConfigService, times(1)).initDashboardClient(any(DashboardProperties.class), any(String.class)); + } +} diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerWithoutConfigurationTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerWithoutConfigurationTests.java index 25c79b733c..400dc9142e 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerWithoutConfigurationTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerWithoutConfigurationTests.java @@ -16,7 +16,6 @@ package it.infn.mw.iam.test.core.util; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -24,7 +23,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.TestPropertySource; -import org.springframework.transaction.annotation.Transactional; import it.infn.mw.iam.IamLoginService; import it.infn.mw.iam.config.IamProperties.DashboardProperties; @@ -35,10 +33,10 @@ public class StartupRunnerWithoutConfigurationTests { @MockBean - private DashboardConfigService myService; + private DashboardConfigService dashboardConfigService; @Test void testRunnerDoesNotInitializeDashboard() throws IllegalStateException { - verify(myService, times(0)).initDashboardClient(any(DashboardProperties.class), any(String.class)); + verify(dashboardConfigService, times(0)).initDashboardClient(any(DashboardProperties.class), any(String.class)); } } From aa9e2328a3f74583244f6991ba828b8865f11df0 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Fri, 13 Mar 2026 19:06:13 +0100 Subject: [PATCH 22/26] Add mock behavior for clientService in StartupRunnerTests --- .../it/infn/mw/iam/test/core/util/StartupRunnerTests.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java index 2a331d10ca..b2a4aa0153 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java @@ -18,14 +18,18 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.context.TestPropertySource; import org.springframework.transaction.annotation.Transactional; import it.infn.mw.iam.IamLoginService; +import it.infn.mw.iam.api.client.management.service.DefaultClientManagementService; +import it.infn.mw.iam.api.common.client.RegisteredClientDTO; import it.infn.mw.iam.config.IamProperties.DashboardProperties; import it.infn.mw.iam.dashboard.DashboardConfigService; @@ -40,8 +44,12 @@ public class StartupRunnerTests { @SpyBean private DashboardConfigService dashboardConfigService; + @MockBean + DefaultClientManagementService clientService; + @Test void testRunnerInitializesDashboard() throws Exception { + when(clientService.saveNewClient(any(RegisteredClientDTO.class))).thenReturn(null); verify(dashboardConfigService, times(1)).initDashboardClient(any(DashboardProperties.class), any(String.class)); } } From 2490e6bc49991de14f54b5b2cc2f8a14f1995e70 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Mon, 16 Mar 2026 11:53:01 +0100 Subject: [PATCH 23/26] Refactor and improve tests --- .../iam/dashboard/DashboardConfigService.java | 2 +- .../config/DashboardConfigValidatorTests.java | 47 ++++++--------- .../test/core/util/StartupRunnerTests.java | 2 +- ...tartupRunnerWithoutConfigurationTests.java | 2 +- .../dashboard/DashboardConfigServiceTest.java | 59 ++++++++----------- 5 files changed, 45 insertions(+), 67 deletions(-) diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java b/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java index 2f11e1732a..85baf488e7 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java @@ -91,7 +91,7 @@ public boolean initDashboardClient(DashboardProperties dashboardProperties, Stri try { clientService.saveNewClient(client); } catch (Exception e) { - LOG.error("Error saving dashboard client: " + e.getMessage()); + LOG.error("Error saving dashboard client: {}", e.getMessage()); return false; } return true; diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/config/DashboardConfigValidatorTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/config/DashboardConfigValidatorTests.java index 1fa62bdf78..0111dfc768 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/config/DashboardConfigValidatorTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/config/DashboardConfigValidatorTests.java @@ -27,7 +27,7 @@ import it.infn.mw.iam.api.client.management.validation.DashboardConfigValidator; import it.infn.mw.iam.config.IamProperties.DashboardProperties; -public class DashboardConfigValidatorTests { +class DashboardConfigValidatorTests { private final DashboardConfigValidator validator = new DashboardConfigValidator(); private final ConstraintValidatorContext context = mock(ConstraintValidatorContext.class); @@ -57,43 +57,30 @@ void testDashboardPropertiesValid() { @Test void testIsNotValidDashboardSecret() { - dashboardProperties.setClientSecret("too-short-client-secret"); - assertFalse(validator.isValid(dashboardProperties, context)); - } - - @Test - void testIsNotValidDashboardSecretTooLong() { - dashboardProperties.setClientSecret("too-long-client-secret-82gbV6OEwGBCPMmcFPXg5-4wRJXnKc-4wds5odwrFiY4wds5odwrF"); - assertFalse(validator.isValid(dashboardProperties, context)); - } - - @Test - void testIsNotValidDashboardSecretInvalidChars() { - dashboardProperties.setClientSecret("0tlkqGPJD2vWN1/dgTqi3xn-PAJ7EMgNKFFUOydZPsTLkIouqQFmfioJcvfk0V2Xt"); - assertFalse(validator.isValid(dashboardProperties, context)); - } - - @Test - void testIsNotValidDashboardSecretInvalidNull() { - dashboardProperties.setClientSecret(null); - assertFalse(validator.isValid(dashboardProperties, context)); + invalidSecret("too-short-client-secret"); + invalidSecret("too-long-client-secret-82gbV6OEwGBCPMmcFPXg5-4wRJXnKc-4wds5odwrFiY4wds5odwrF"); + invalidSecret("0tlkqGPJD2vWN1/dgTqi3xn-PAJ7EMgNKFFUOydZPsTLkIouqQFmfioJcvfk0V2Xt"); + invalidSecret(null); + invalidSecret(""); } @Test - void testIsNotValidDashboardSecretInvalidString() { - dashboardProperties.setClientSecret(""); - assertFalse(validator.isValid(dashboardProperties, context)); + void testIsNotValidDashboardClientIdNull() { + invalidClientId(null); + invalidClientId("id"); + invalidClientId("client-with-special-chars/"); + invalidClientId("client with spaces"); + invalidClientId("too long client-id over 255 characters " + "a".repeat(256)); + invalidClientId(""); } - @Test - void testIsNotValidDashboardClientIdInvalidUUID() { - dashboardProperties.setClientId("client-dashboard!"); + private void invalidSecret(String secret) { + dashboardProperties.setClientSecret(secret); assertFalse(validator.isValid(dashboardProperties, context)); } - @Test - void testIsNotValidDashboardClientIdNull() { - dashboardProperties.setClientId(null); + private void invalidClientId(String clientId) { + dashboardProperties.setClientId(clientId); assertFalse(validator.isValid(dashboardProperties, context)); } } diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java index b2a4aa0153..7d9d0a51f0 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java @@ -39,7 +39,7 @@ "iam.dashboard.client-id=dashboard-id", "iam.dashboard.client-secret=10000000-1234-1234-1234-123456789012" }) @Transactional -public class StartupRunnerTests { +class StartupRunnerTests { @SpyBean private DashboardConfigService dashboardConfigService; diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerWithoutConfigurationTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerWithoutConfigurationTests.java index 400dc9142e..be584a3026 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerWithoutConfigurationTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerWithoutConfigurationTests.java @@ -30,7 +30,7 @@ @SpringBootTest(classes = { IamLoginService.class }) @TestPropertySource(properties = "iam.dashboard.enabled=false") -public class StartupRunnerWithoutConfigurationTests { +class StartupRunnerWithoutConfigurationTests { @MockBean private DashboardConfigService dashboardConfigService; diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java index 04486c9f7a..2ce8cdab74 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java @@ -26,7 +26,6 @@ import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod; import org.mitre.oauth2.model.PKCEAlgorithm; -import java.text.ParseException; import java.util.Set; import javax.transaction.Transactional; @@ -44,7 +43,7 @@ @SpringBootTest(classes = { IamLoginService.class }) @Transactional -public class DashboardConfigServiceTest { +class DashboardConfigServiceTest { private static final String CLIENT_ID = "dashboard-client"; private static final String CLIENT_SECRET = "secret"; @@ -54,7 +53,7 @@ public class DashboardConfigServiceTest { private static final Set AUTH_GRAND_TYPE = Set.of(AuthorizationGrantType.CODE.getGrantType(), AuthorizationGrantType.REFRESH_TOKEN.getGrantType()); - private ClientDetailsEntity client; + private ClientDetailsEntity clientDashboard; @Autowired private DashboardConfigService dashboardConfigService; @@ -64,27 +63,27 @@ public class DashboardConfigServiceTest { @BeforeEach void setUp() { - this.client = new ClientDetailsEntity(); - client.setClientId(CLIENT_ID); - client.setClientSecret(CLIENT_SECRET); - client.setScope(SCOPES); - client.setGrantTypes(AUTH_GRAND_TYPE); - client.setRedirectUris(Set.of("http://localhost:8080/api/auth/oauth2/callback/indigo-iam")); - client.setCodeChallengeMethod(PKCEAlgorithm.S256); - client.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC); - iamClientDetailsRepository.save(client); + clientDashboard = new ClientDetailsEntity(); + clientDashboard.setClientId(CLIENT_ID); + clientDashboard.setClientSecret(CLIENT_SECRET); + clientDashboard.setScope(SCOPES); + clientDashboard.setGrantTypes(AUTH_GRAND_TYPE); + clientDashboard.setRedirectUris(Set.of("http://localhost:8080/api/auth/oauth2/callback/indigo-iam")); + clientDashboard.setCodeChallengeMethod(PKCEAlgorithm.S256); + clientDashboard.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC); + iamClientDetailsRepository.save(clientDashboard); } @AfterEach void tearDown() { - iamClientDetailsRepository.delete(client); + iamClientDetailsRepository.delete(clientDashboard); } @Test void testCheckRecordConfiguration() { ClientDetailsEntity client = createClientDashboard(CLIENT_ID, CLIENT_SECRET, BASE_URL, AUTH_GRAND_TYPE, SCOPES); - assertEquals(dashboardConfigService.checkRecordConfiguration(client, CLIENT_SECRET, BASE_URL), true); + assertEquals(true, dashboardConfigService.checkRecordConfiguration(client, CLIENT_SECRET, BASE_URL)); } @Test @@ -92,64 +91,56 @@ void testFailCheckRecordScopeConfiguration() { ClientDetailsEntity client = createClientDashboard(CLIENT_ID, CLIENT_SECRET, BASE_URL, AUTH_GRAND_TYPE, Sets.newHashSet("openid")); - assertEquals(dashboardConfigService.checkRecordConfiguration(client, CLIENT_SECRET, BASE_URL), false); + assertEquals(false, dashboardConfigService.checkRecordConfiguration(client, CLIENT_SECRET, BASE_URL)); } @Test void testFailCheckRecordClientSecretConfiguration() { ClientDetailsEntity client = createClientDashboard(CLIENT_ID, CLIENT_SECRET, BASE_URL, AUTH_GRAND_TYPE, SCOPES); - assertEquals(dashboardConfigService.checkRecordConfiguration(client, "test_secret", BASE_URL), false); + assertEquals(false, dashboardConfigService.checkRecordConfiguration(client, "test_secret", BASE_URL)); } @Test - void testInitDashboardClient() throws ParseException { + void testInitDashboardClient() { DashboardProperties properties = new DashboardProperties(); properties.setClientId(CLIENT_ID); properties.setClientSecret(CLIENT_SECRET); - assertEquals(dashboardConfigService.initDashboardClient(properties, BASE_URL), true); - } - - @Test - void testInitDashboardClientFail() throws ParseException { - DashboardProperties properties = new DashboardProperties(); - properties.setClientId(CLIENT_ID); - properties.setClientSecret(CLIENT_SECRET); - assertEquals(dashboardConfigService.initDashboardClient(properties, BASE_URL), true); + assertEquals(true, dashboardConfigService.initDashboardClient(properties, BASE_URL)); } @Test - void testInitDashboardClientInsertDashboard() throws ParseException { + void testInitDashboardClientInsertDashboard() { assertThat(iamClientDetailsRepository.findByClientId(CLIENT_ID + "-new").isPresent(), is(false)); DashboardProperties properties = new DashboardProperties(); properties.setClientId(CLIENT_ID + "-new"); properties.setClientSecret(CLIENT_SECRET); - assertEquals(dashboardConfigService.initDashboardClient(properties, BASE_URL), true); + assertEquals(true, dashboardConfigService.initDashboardClient(properties, BASE_URL)); iamClientDetailsRepository.findByClientId(CLIENT_ID + "-new").ifPresentOrElse(c -> { - assertEquals(c.getClientId(), CLIENT_ID + "-new"); - assertEquals(c.getScope(), client.getScope()); + assertEquals(CLIENT_ID + "-new", c.getClientId()); + assertEquals(clientDashboard.getScope(), c.getScope()); }, () -> { throw new AssertionError("Client not found"); }); } @Test - void testInitDashboardClientUpdateDashboard() throws ParseException { + void testInitDashboardClientUpdateDashboard() { assertThat(iamClientDetailsRepository.findByClientId(CLIENT_ID + "-new").isPresent(), is(false)); DashboardProperties properties = new DashboardProperties(); properties.setClientId(CLIENT_ID); properties.setClientSecret(CLIENT_SECRET); - assertEquals(dashboardConfigService.initDashboardClient(properties, BASE_URL), true); + assertEquals(true, dashboardConfigService.initDashboardClient(properties, BASE_URL)); iamClientDetailsRepository.findByClientId(CLIENT_ID).ifPresentOrElse(c -> { - assertEquals(c.getClientId(), CLIENT_ID); - assertEquals(c.getScope(), client.getScope()); + assertEquals(CLIENT_ID, c.getClientId()); + assertEquals(clientDashboard.getScope(), c.getScope()); }, () -> { throw new AssertionError("Client not found"); }); From c158b3b8be49ef538fc784a1eaa2da76b96ef6dd Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Fri, 20 Mar 2026 17:52:21 +0100 Subject: [PATCH 24/26] Refactor dashboard configuration and update tests --- .../java/it/infn/mw/iam/config/IamConfig.java | 1 - .../it/infn/mw/iam/config/IamProperties.java | 1 - .../infn/mw/iam/core/util/StartupRunner.java | 14 +- .../iam/dashboard/DashboardConfigService.java | 99 +++++++----- .../src/main/resources/application.yml | 4 +- .../test/core/util/StartupRunnerTests.java | 61 ++++--- ...tartupRunnerWithoutConfigurationTests.java | 42 ----- .../dashboard/DashboardConfigServiceTest.java | 153 +++++++++--------- 8 files changed, 179 insertions(+), 196 deletions(-) delete mode 100644 iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerWithoutConfigurationTests.java diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/config/IamConfig.java b/iam-login-service/src/main/java/it/infn/mw/iam/config/IamConfig.java index 05df5b587c..df43a9f875 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/config/IamConfig.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/config/IamConfig.java @@ -103,7 +103,6 @@ @SuppressWarnings("deprecation") @Configuration -@EnableConfigurationProperties(DashboardProperties.class) public class IamConfig { public static final Logger LOG = LoggerFactory.getLogger(IamConfig.class); diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java b/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java index 3679ce1578..5bd41371dd 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/config/IamProperties.java @@ -603,7 +603,6 @@ public void setTrackLastUsed(boolean trackLastUsed) { } @Validated - @ConfigurationProperties(prefix = "app.dashboard") public static class DashboardProperties { private boolean enabled = false; diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/core/util/StartupRunner.java b/iam-login-service/src/main/java/it/infn/mw/iam/core/util/StartupRunner.java index 4dee2d4255..a318f180b1 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/core/util/StartupRunner.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/core/util/StartupRunner.java @@ -22,8 +22,6 @@ import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; -import it.infn.mw.iam.config.IamProperties; -import it.infn.mw.iam.config.IamProperties.DashboardProperties; import it.infn.mw.iam.dashboard.DashboardConfigService; @Component @@ -32,25 +30,21 @@ public class StartupRunner implements ApplicationRunner { private static final Logger LOG = LoggerFactory.getLogger(StartupRunner.class); private final DashboardConfigService dashboardConfigService; - private final DashboardProperties dashboardProperties; - private final String iamBaseUrl; - public StartupRunner(DashboardConfigService dashboardConfigService, IamProperties iamProperties) { + public StartupRunner(DashboardConfigService dashboardConfigService) { this.dashboardConfigService = dashboardConfigService; - this.dashboardProperties = iamProperties.getDashboard(); - this.iamBaseUrl = iamProperties.getBaseUrl(); } @Override public void run(ApplicationArguments args) { - if (!dashboardProperties.isEnabled()) { + if (!dashboardConfigService.isEnabled()) { LOG.info( "Dashboard client is disabled, skipping checks for the dashboard client properties and the presence of the record for the dashboard client"); return; } - boolean recordExists = dashboardConfigService.initDashboardClient(dashboardProperties, iamBaseUrl); - if (!recordExists) { + boolean isValid = dashboardConfigService.init(); + if (!isValid) { throw new IllegalStateException( "Dashboard client record does not exist or is not valid. Please check the dashboard client properties and ensure that a record with the specified client id, client secret and redirect uri exists in the database"); } diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java b/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java index 85baf488e7..23f83e56fa 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java @@ -16,6 +16,7 @@ package it.infn.mw.iam.dashboard; +import java.text.ParseException; import java.util.Optional; import java.util.Set; @@ -28,6 +29,7 @@ import org.springframework.stereotype.Service; import it.infn.mw.iam.api.client.management.service.DefaultClientManagementService; +import it.infn.mw.iam.config.IamProperties; import it.infn.mw.iam.config.IamProperties.DashboardProperties; import it.infn.mw.iam.persistence.repository.client.IamClientRepository; import it.infn.mw.iam.api.common.client.AuthorizationGrantType; @@ -40,62 +42,53 @@ public class DashboardConfigService { private static final Logger LOG = LoggerFactory.getLogger(DashboardConfigService.class); private static final String DASHBOARD_CALLBACK = "/ui/api/auth/oauth2/callback/indigo-iam"; - Set dashboardScopes = Set.of(SystemScopeService.OPENID_SCOPE, SystemScopeService.OFFLINE_ACCESS, "email", + private static final Set DASHBOARD_SCOPES = Set.of(SystemScopeService.OPENID_SCOPE, + SystemScopeService.OFFLINE_ACCESS, "email", "profile", "iam:admin.read", "iam:admin.write", "scim:read", "scim:write"); private final IamClientRepository clientRepository; private final DefaultClientManagementService clientService; + private final IamProperties iamProperties; - public DashboardConfigService(IamClientRepository clientRepository, - DefaultClientManagementService clientService) { + public DashboardConfigService( + IamClientRepository clientRepository, + DefaultClientManagementService clientService, + IamProperties iamProperties) { this.clientService = clientService; this.clientRepository = clientRepository; + this.iamProperties = iamProperties; } - public boolean initDashboardClient(DashboardProperties dashboardProperties, String iamUrl) { + public boolean isEnabled() { + return iamProperties.getDashboard().isEnabled(); + } + + public boolean init() { + DashboardProperties dashboardProperties = iamProperties.getDashboard(); + String iamUrl = iamProperties.getBaseUrl(); String clientId = dashboardProperties.getClientId(); String clientSecret = dashboardProperties.getClientSecret(); String url = iamUrl + DASHBOARD_CALLBACK; + Optional dashboardRecord = clientRepository.findByClientId(clientId); - Optional test = clientRepository.findByClientId(clientId); - - if (test.isPresent()) { - ClientDetailsEntity client = test.get(); - - boolean isConfigured = checkRecordConfiguration(test.get(), clientSecret, url); - if (!isConfigured) { - LOG.warn("The record is not properly configured. Updating Dashboard client."); - client.setScope(dashboardScopes); - client.setGrantTypes(Set.of(AuthorizationGrantType.CODE.getGrantType(), AuthorizationGrantType.REFRESH_TOKEN.getGrantType())); - client.setCodeChallengeMethod(PKCEAlgorithm.S256); - client.setRedirectUris(Set.of(url)); - client.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC); - - clientRepository.save(client); - } - return true; - } else { + if (!dashboardRecord.isPresent()) { LOG.info("The client record for dashboard does not exist. Creating record with default configuration..."); - RegisteredClientDTO client = new RegisteredClientDTO(); - client.setScope(dashboardScopes); - client.setClientId(clientId); - client.setClientName("dashboard"); - client.setClientSecret(clientSecret); - client.setTokenEndpointAuthMethod(TokenEndpointAuthenticationMethod.client_secret_basic); - client.setAccessTokenValiditySeconds(3600); - client.setCodeChallengeMethod(PKCEAlgorithm.S256.toString()); - client.setActive(true); - client.setRedirectUris(Set.of(url)); - client.setGrantTypes(Set.of(AuthorizationGrantType.CODE, AuthorizationGrantType.REFRESH_TOKEN)); - try { - clientService.saveNewClient(client); + createRecordDashboard(clientId, clientSecret, url); + return true; } catch (Exception e) { LOG.error("Error saving dashboard client: {}", e.getMessage()); return false; } - return true; } + + ClientDetailsEntity client = dashboardRecord.get(); + boolean isConfigured = checkRecordConfiguration(client, clientSecret, url); + if (!isConfigured) { + LOG.warn("The record is not properly configured. Updating Dashboard client."); + updateRecordDashboard(client, clientSecret, url); + } + return true; } public boolean checkRecordConfiguration(ClientDetailsEntity client, String clientSecret, String url) { @@ -107,8 +100,36 @@ && usesClientSecretBasicAuth(client) && usesPKCES256(client); } + private void createRecordDashboard(String clientId, String secret, String url) throws ParseException { + RegisteredClientDTO client = new RegisteredClientDTO(); + client.setScope(DASHBOARD_SCOPES); + client.setClientId(clientId); + client.setClientName("dashboard"); + client.setClientSecret(secret); + client.setTokenEndpointAuthMethod(TokenEndpointAuthenticationMethod.client_secret_basic); + client.setAccessTokenValiditySeconds(3600); + client.setCodeChallengeMethod(PKCEAlgorithm.S256.toString()); + client.setActive(true); + client.setRedirectUris(Set.of(url)); + client.setGrantTypes(Set.of(AuthorizationGrantType.CODE, AuthorizationGrantType.REFRESH_TOKEN)); + + clientService.saveNewClient(client); + } + + private void updateRecordDashboard(ClientDetailsEntity client, String secret, String url) { + client.setScope(DASHBOARD_SCOPES); + client.setGrantTypes( + Set.of(AuthorizationGrantType.CODE.getGrantType(), AuthorizationGrantType.REFRESH_TOKEN.getGrantType())); + client.setCodeChallengeMethod(PKCEAlgorithm.S256); + client.setClientSecret(secret); + client.setRedirectUris(Set.of(url)); + client.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC); + + clientRepository.save(client); + } + private boolean hasAllRequiredScopes(ClientDetailsEntity client) { - return client.getScope().containsAll(dashboardScopes); + return client.getScope().containsAll(DASHBOARD_SCOPES); } private boolean hasValidClientSecret(ClientDetailsEntity client, String clientSecret) { @@ -116,11 +137,11 @@ private boolean hasValidClientSecret(ClientDetailsEntity client, String clientSe } private boolean hasValidRedirectUris(ClientDetailsEntity client, String url) { - return client.getRedirectUris().containsAll(Set.of(url)); + return client.getRedirectUris().equals(Set.of(url)); } private boolean supportsAuthorizationCodeGrant(ClientDetailsEntity client) { - return client.getGrantTypes().containsAll( + return client.getGrantTypes().equals( Set.of(AuthorizationGrantType.CODE.getGrantType(), AuthorizationGrantType.REFRESH_TOKEN.getGrantType())); } diff --git a/iam-login-service/src/main/resources/application.yml b/iam-login-service/src/main/resources/application.yml index 03b3148e71..121768207b 100644 --- a/iam-login-service/src/main/resources/application.yml +++ b/iam-login-service/src/main/resources/application.yml @@ -191,8 +191,8 @@ iam: dashboard: enabled: ${IAM_DASHBOARD_ENABLED:false} - client-id: ${IAM_DASHBOARD_CLIENT_ID} - client-secret: ${IAM_DASHBOARD_CLIENT_SECRET} + client-id: ${IAM_DASHBOARD_CLIENT_ID:} + client-secret: ${IAM_DASHBOARD_CLIENT_SECRET:} cache: enabled: ${IAM_CACHE_ENABLED:true} diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java index 7d9d0a51f0..bed913bd13 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerTests.java @@ -15,41 +15,54 @@ */ package it.infn.mw.iam.test.core.util; -import static org.mockito.ArgumentMatchers.any; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.boot.test.mock.mockito.SpyBean; -import org.springframework.test.context.TestPropertySource; -import org.springframework.transaction.annotation.Transactional; - -import it.infn.mw.iam.IamLoginService; -import it.infn.mw.iam.api.client.management.service.DefaultClientManagementService; -import it.infn.mw.iam.api.common.client.RegisteredClientDTO; -import it.infn.mw.iam.config.IamProperties.DashboardProperties; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.boot.ApplicationArguments; +import it.infn.mw.iam.core.util.StartupRunner; import it.infn.mw.iam.dashboard.DashboardConfigService; -@SpringBootTest(classes = { IamLoginService.class }) -@TestPropertySource(properties = { - "iam.dashboard.enabled=true", - "iam.dashboard.client-id=dashboard-id", - "iam.dashboard.client-secret=10000000-1234-1234-1234-123456789012" }) -@Transactional +@ExtendWith(MockitoExtension.class) class StartupRunnerTests { - @SpyBean - private DashboardConfigService dashboardConfigService; + @Mock + private DashboardConfigService service; - @MockBean - DefaultClientManagementService clientService; + private StartupRunner runner; + + @BeforeEach + void initRunner() { + runner = new StartupRunner(service); + } + + @Test + void testRunnerInitializesDashboard() { + when(service.isEnabled()).thenReturn(true); + when(service.init()).thenReturn(true); + runner.run(mock(ApplicationArguments.class)); + verify(service, times(1)).init(); + } + + @Test + void testRunnerDoesNotInitializeDashboard() { + when(service.isEnabled()).thenReturn(false); + runner.run(mock(ApplicationArguments.class)); + verify(service, times(0)).init(); + } @Test - void testRunnerInitializesDashboard() throws Exception { - when(clientService.saveNewClient(any(RegisteredClientDTO.class))).thenReturn(null); - verify(dashboardConfigService, times(1)).initDashboardClient(any(DashboardProperties.class), any(String.class)); + void testRunnerThrowsExceptionOnInitializationFailure() { + when(service.isEnabled()).thenReturn(true); + when(service.init()).thenThrow(new IllegalStateException()); + assertThrows(IllegalStateException.class, () -> runner.run(mock(ApplicationArguments.class))); + verify(service, times(1)).init(); } } diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerWithoutConfigurationTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerWithoutConfigurationTests.java deleted file mode 100644 index be584a3026..0000000000 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/core/util/StartupRunnerWithoutConfigurationTests.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package it.infn.mw.iam.test.core.util; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.TestPropertySource; - -import it.infn.mw.iam.IamLoginService; -import it.infn.mw.iam.config.IamProperties.DashboardProperties; -import it.infn.mw.iam.dashboard.DashboardConfigService; - -@SpringBootTest(classes = { IamLoginService.class }) -@TestPropertySource(properties = "iam.dashboard.enabled=false") -class StartupRunnerWithoutConfigurationTests { - - @MockBean - private DashboardConfigService dashboardConfigService; - - @Test - void testRunnerDoesNotInitializeDashboard() throws IllegalStateException { - verify(dashboardConfigService, times(0)).initDashboardClient(any(DashboardProperties.class), any(String.class)); - } -} diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java index 2ce8cdab74..d8d7feb485 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java @@ -17,32 +17,36 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import static org.hamcrest.MatcherAssert.assertThat; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; import org.mitre.oauth2.model.PKCEAlgorithm; +import java.text.ParseException; +import java.util.Optional; import java.util.Set; -import javax.transaction.Transactional; - import com.google.common.collect.Sets; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - import it.infn.mw.iam.dashboard.DashboardConfigService; import it.infn.mw.iam.persistence.repository.client.IamClientRepository; -import it.infn.mw.iam.IamLoginService; +import it.infn.mw.iam.api.client.management.service.DefaultClientManagementService; import it.infn.mw.iam.api.common.client.AuthorizationGrantType; +import it.infn.mw.iam.config.IamProperties; import it.infn.mw.iam.config.IamProperties.DashboardProperties; -@SpringBootTest(classes = { IamLoginService.class }) -@Transactional +@ExtendWith(MockitoExtension.class) class DashboardConfigServiceTest { private static final String CLIENT_ID = "dashboard-client"; @@ -53,97 +57,80 @@ class DashboardConfigServiceTest { private static final Set AUTH_GRAND_TYPE = Set.of(AuthorizationGrantType.CODE.getGrantType(), AuthorizationGrantType.REFRESH_TOKEN.getGrantType()); - private ClientDetailsEntity clientDashboard; - - @Autowired - private DashboardConfigService dashboardConfigService; - - @Autowired - private IamClientRepository iamClientDetailsRepository; - - @BeforeEach - void setUp() { - clientDashboard = new ClientDetailsEntity(); - clientDashboard.setClientId(CLIENT_ID); - clientDashboard.setClientSecret(CLIENT_SECRET); - clientDashboard.setScope(SCOPES); - clientDashboard.setGrantTypes(AUTH_GRAND_TYPE); - clientDashboard.setRedirectUris(Set.of("http://localhost:8080/api/auth/oauth2/callback/indigo-iam")); - clientDashboard.setCodeChallengeMethod(PKCEAlgorithm.S256); - clientDashboard.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC); - iamClientDetailsRepository.save(clientDashboard); - } + @Mock + IamClientRepository clientRepository; + + @Mock + DefaultClientManagementService clientService; + + @Mock + IamProperties iamProperties; - @AfterEach - void tearDown() { - iamClientDetailsRepository.delete(clientDashboard); + private DashboardConfigService getService() { + return new DashboardConfigService(clientRepository, clientService, iamProperties); } @Test void testCheckRecordConfiguration() { ClientDetailsEntity client = createClientDashboard(CLIENT_ID, CLIENT_SECRET, BASE_URL, AUTH_GRAND_TYPE, SCOPES); - assertEquals(true, dashboardConfigService.checkRecordConfiguration(client, CLIENT_SECRET, BASE_URL)); + assertEquals(true, getService().checkRecordConfiguration(client, CLIENT_SECRET, BASE_URL)); } @Test - void testFailCheckRecordScopeConfiguration() { + void testFailCheckRecordWithWrongConfigurations() { + Set scopesWithoutRequired = Sets.newHashSet("FAKE_SCOPE"); ClientDetailsEntity client = createClientDashboard(CLIENT_ID, CLIENT_SECRET, BASE_URL, AUTH_GRAND_TYPE, - Sets.newHashSet("openid")); + scopesWithoutRequired); + assertEquals(false, getService().checkRecordConfiguration(client, CLIENT_SECRET, BASE_URL)); - assertEquals(false, dashboardConfigService.checkRecordConfiguration(client, CLIENT_SECRET, BASE_URL)); - } + scopesWithoutRequired = Sets.newHashSet("openid"); + client = createClientDashboard(CLIENT_ID, CLIENT_SECRET, BASE_URL, AUTH_GRAND_TYPE, scopesWithoutRequired); + assertEquals(false, getService().checkRecordConfiguration(client, CLIENT_SECRET, BASE_URL)); - @Test - void testFailCheckRecordClientSecretConfiguration() { - ClientDetailsEntity client = createClientDashboard(CLIENT_ID, CLIENT_SECRET, BASE_URL, AUTH_GRAND_TYPE, SCOPES); + client = createClientDashboard(CLIENT_ID, "test_secret", BASE_URL, AUTH_GRAND_TYPE, SCOPES); + assertEquals(false, getService().checkRecordConfiguration(client, CLIENT_SECRET, BASE_URL)); - assertEquals(false, dashboardConfigService.checkRecordConfiguration(client, "test_secret", BASE_URL)); + client = createClientDashboard(CLIENT_ID, CLIENT_SECRET, "https://fake.url", AUTH_GRAND_TYPE, SCOPES); + assertEquals(false, getService().checkRecordConfiguration(client, CLIENT_SECRET, BASE_URL)); + + client = createClientDashboard(CLIENT_ID, CLIENT_SECRET, BASE_URL, + Set.of(AuthorizationGrantType.CODE.getGrantType()), + SCOPES); + assertEquals(false, getService().checkRecordConfiguration(client, CLIENT_SECRET, BASE_URL)); } @Test - void testInitDashboardClient() { - DashboardProperties properties = new DashboardProperties(); - properties.setClientId(CLIENT_ID); - properties.setClientSecret(CLIENT_SECRET); + void testInit() { + ClientDetailsEntity dashboard = setClientDashboard(); + when(clientRepository.findByClientId(CLIENT_ID)).thenReturn(Optional.of(dashboard)); - assertEquals(true, dashboardConfigService.initDashboardClient(properties, BASE_URL)); + mockDashboardProperties(true, CLIENT_ID, CLIENT_SECRET); + assertEquals(true, getService().init()); } @Test - void testInitDashboardClientInsertDashboard() { - assertThat(iamClientDetailsRepository.findByClientId(CLIENT_ID + "-new").isPresent(), is(false)); - - DashboardProperties properties = new DashboardProperties(); - properties.setClientId(CLIENT_ID + "-new"); - properties.setClientSecret(CLIENT_SECRET); - - assertEquals(true, dashboardConfigService.initDashboardClient(properties, BASE_URL)); - - iamClientDetailsRepository.findByClientId(CLIENT_ID + "-new").ifPresentOrElse(c -> { - assertEquals(CLIENT_ID + "-new", c.getClientId()); - assertEquals(clientDashboard.getScope(), c.getScope()); - }, () -> { - throw new AssertionError("Client not found"); - }); + void testInitInsertNewDashboardClient() throws ParseException { + String newClientId = "new-" + CLIENT_ID; + mockDashboardProperties(true, newClientId, CLIENT_SECRET); + + assertThat(clientRepository.findByClientId(newClientId).isPresent(), is(false)); + assertEquals(true, getService().init()); + verify(clientService, times(1)).saveNewClient(any()); + verify(clientRepository, times(0)).save(any()); } @Test - void testInitDashboardClientUpdateDashboard() { - assertThat(iamClientDetailsRepository.findByClientId(CLIENT_ID + "-new").isPresent(), is(false)); - - DashboardProperties properties = new DashboardProperties(); - properties.setClientId(CLIENT_ID); - properties.setClientSecret(CLIENT_SECRET); - - assertEquals(true, dashboardConfigService.initDashboardClient(properties, BASE_URL)); - - iamClientDetailsRepository.findByClientId(CLIENT_ID).ifPresentOrElse(c -> { - assertEquals(CLIENT_ID, c.getClientId()); - assertEquals(clientDashboard.getScope(), c.getScope()); - }, () -> { - throw new AssertionError("Client not found"); - }); + void testInitUpdateDashboard() throws ParseException { + String newClientId = "new-" + CLIENT_ID; + mockDashboardProperties(true, CLIENT_ID, CLIENT_SECRET); + ClientDetailsEntity dashboard = setClientDashboard(); + when(clientRepository.findByClientId(CLIENT_ID)).thenReturn(Optional.of(dashboard)); + + assertThat(clientRepository.findByClientId(newClientId).isPresent(), is(false)); + assertEquals(true, getService().init()); + verify(clientService, times(0)).saveNewClient(any()); + verify(clientRepository, times(1)).save(any()); } private ClientDetailsEntity createClientDashboard(String clientId, String clientSecret, @@ -158,4 +145,16 @@ private ClientDetailsEntity createClientDashboard(String clientId, String client client.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC); return client; } + + private ClientDetailsEntity setClientDashboard() { + return createClientDashboard(CLIENT_ID, CLIENT_SECRET, BASE_URL, AUTH_GRAND_TYPE, SCOPES); + } + + private void mockDashboardProperties(boolean isENabled, String clientId, String secret) { + DashboardProperties properties = Mockito.mock(DashboardProperties.class); + lenient().when(properties.isEnabled()).thenReturn(isENabled); + lenient().when(properties.getClientId()).thenReturn(clientId); + lenient().when(properties.getClientSecret()).thenReturn(secret); + when(iamProperties.getDashboard()).thenReturn(properties); + } } From 48e0c963a5d866697180702f49a02f6caa26bbcd Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Fri, 20 Mar 2026 18:27:00 +0100 Subject: [PATCH 25/26] Remove useless import --- .../src/main/java/it/infn/mw/iam/config/IamConfig.java | 2 -- .../infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/config/IamConfig.java b/iam-login-service/src/main/java/it/infn/mw/iam/config/IamConfig.java index df43a9f875..ef162bc72f 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/config/IamConfig.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/config/IamConfig.java @@ -28,7 +28,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; @@ -45,7 +44,6 @@ import it.infn.mw.iam.api.account.AccountUtils; import it.infn.mw.iam.api.account.multi_factor_authentication.IamTotpMfaService; import it.infn.mw.iam.api.scim.converter.SshKeyConverter; -import it.infn.mw.iam.config.IamProperties.DashboardProperties; import it.infn.mw.iam.config.mfa.IamTotpMfaProperties; import it.infn.mw.iam.core.oauth.attributes.AttributeMapHelper; import it.infn.mw.iam.core.oauth.profile.JWTProfile; diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java index d8d7feb485..20d1fdbde5 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/dashboard/DashboardConfigServiceTest.java @@ -50,7 +50,7 @@ class DashboardConfigServiceTest { private static final String CLIENT_ID = "dashboard-client"; - private static final String CLIENT_SECRET = "secret"; + private static final String CLIENT_SECRET = "dashboard-client-secret-01234567890"; private static final String BASE_URL = "http://localhost:8080"; private static final Set SCOPES = Sets.newHashSet("openid", "profile", "email", "iam:admin.read", "iam:admin.write", "scim:read", "scim:write", "offline_access"); From d4ad58e8c6d1b8b6456ae2127f6b706d1a73ab85 Mon Sep 17 00:00:00 2001 From: SteDev2 Date: Fri, 20 Mar 2026 19:39:35 +0100 Subject: [PATCH 26/26] Refactor variable naming --- .../java/it/infn/mw/iam/dashboard/DashboardConfigService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java b/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java index 23f83e56fa..ff5419db5c 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/dashboard/DashboardConfigService.java @@ -83,8 +83,8 @@ public boolean init() { } ClientDetailsEntity client = dashboardRecord.get(); - boolean isConfigured = checkRecordConfiguration(client, clientSecret, url); - if (!isConfigured) { + boolean isValid = checkRecordConfiguration(client, clientSecret, url); + if (!isValid) { LOG.warn("The record is not properly configured. Updating Dashboard client."); updateRecordDashboard(client, clientSecret, url); }