From 2c11a7cfadafd5894716254f70a1a0a48842ecc6 Mon Sep 17 00:00:00 2001 From: Enrico Vianello Date: Mon, 13 Nov 2023 18:18:24 +0100 Subject: [PATCH 1/3] Back to bouncycastle v1.58 Aligned with voms-api-java version --- iam-login-service/pom.xml | 9 ++------- iam-test-client/pom.xml | 7 +------ iam-voms-aa/pom.xml | 9 ++------- pom.xml | 14 ++++---------- 4 files changed, 9 insertions(+), 30 deletions(-) diff --git a/iam-login-service/pom.xml b/iam-login-service/pom.xml index 688d4221e..e094207d3 100644 --- a/iam-login-service/pom.xml +++ b/iam-login-service/pom.xml @@ -279,17 +279,12 @@ org.bouncycastle - bcpkix-jdk18on + bcpkix-jdk15on org.bouncycastle - bcprov-jdk18on - - - - org.bouncycastle - bcutil-jdk18on + bcprov-jdk15on diff --git a/iam-test-client/pom.xml b/iam-test-client/pom.xml index 61fa5037c..414702fb9 100644 --- a/iam-test-client/pom.xml +++ b/iam-test-client/pom.xml @@ -38,12 +38,7 @@ org.bouncycastle - bcpkix-jdk18on - - - - org.bouncycastle - bcutil-jdk18on + bcpkix-jdk15on diff --git a/iam-voms-aa/pom.xml b/iam-voms-aa/pom.xml index c39aa0bf8..eb85937c4 100644 --- a/iam-voms-aa/pom.xml +++ b/iam-voms-aa/pom.xml @@ -148,17 +148,12 @@ org.bouncycastle - bcprov-jdk18on + bcprov-jdk15on org.bouncycastle - bcpkix-jdk18on - - - - org.bouncycastle - bcutil-jdk18on + bcpkix-jdk15on diff --git a/pom.xml b/pom.xml index b38c68c51..2ee23ab42 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ 1.16.2 - 1.3.6.cnaf-20231030 + 1.3.6.cnaf-20231113 2.5.2.RELEASE 3.3.2 @@ -83,7 +83,7 @@ 2.3.2 2.3.2 - 1.76 + 1.58 @ @@ -217,19 +217,13 @@ org.bouncycastle - bcpkix-jdk18on + bcpkix-jdk15on ${bouncycastle.version} org.bouncycastle - bcprov-jdk18on - ${bouncycastle.version} - - - - org.bouncycastle - bcutil-jdk18on + bcprov-jdk15on ${bouncycastle.version} From 8a10e8ee46f849f0d564621c64bc687d72cf1e35 Mon Sep 17 00:00:00 2001 From: Roberta Miccoli <85555840+rmiccoli@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:30:44 +0100 Subject: [PATCH 2/3] Delete UNIQUE constraint on subject_dn column of iam_x509_cert table (#672) and replace it with a common index --- .../ext_authn/x509/X509AuthenticationIntegrationTests.java | 6 ++++++ .../it/infn/mw/iam/test/ext_authn/x509/X509TestSupport.java | 4 ++-- .../mw/iam/persistence/repository/IamAccountRepository.java | 2 +- .../db/migration/h2/V97__delete_unique_subject_dn.sql | 2 ++ .../db/migration/mysql/V97__delete_unique_subject_dn.sql | 4 ++++ 5 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 iam-persistence/src/main/resources/db/migration/h2/V97__delete_unique_subject_dn.sql create mode 100644 iam-persistence/src/main/resources/db/migration/mysql/V97__delete_unique_subject_dn.sql diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/ext_authn/x509/X509AuthenticationIntegrationTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/ext_authn/x509/X509AuthenticationIntegrationTests.java index 82ed97a05..a4cf1b9cd 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/ext_authn/x509/X509AuthenticationIntegrationTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/ext_authn/x509/X509AuthenticationIntegrationTests.java @@ -219,6 +219,9 @@ public void testx509AccountLinking() throws Exception { .andExpect( flash().attribute(ACCOUNT_LINKING_DASHBOARD_MESSAGE_KEY, equalTo(confirmationMsg))); + linkedAccount = iamAccountRepo.findByCertificateSubject(TEST_0_SUBJECT) + .orElseThrow(() -> new AssertionFailedError("Expected user linked to certificate not found")); + assertThat(linkedAccount.getX509Certificates().size(), is(2)); } @@ -263,6 +266,9 @@ public void testx509AccountLinkingWithDifferentSubjectAndIssuer() throws Excepti .andExpect( flash().attribute(ACCOUNT_LINKING_DASHBOARD_MESSAGE_KEY, equalTo(confirmationMsg))); + linkedAccount = iamAccountRepo.findByCertificateSubject(TEST_1_SUBJECT) + .orElseThrow(() -> new AssertionFailedError("Expected user linked to certificate not found")); + assertThat(linkedAccount.getX509Certificates().size(), is(2)); } diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/ext_authn/x509/X509TestSupport.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/ext_authn/x509/X509TestSupport.java index da9cc9704..c6cba6b2f 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/ext_authn/x509/X509TestSupport.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/ext_authn/x509/X509TestSupport.java @@ -329,7 +329,7 @@ private HttpHeaders test0SSLHeaders(boolean verified, String verificationError) private HttpHeaders test2SSLHeaders(boolean verified, String verificationError) { HttpHeaders headers = new HttpHeaders(); headers.add(DefaultX509AuthenticationCredentialExtractor.Headers.CLIENT_CERT.getHeader(), - TEST_0_CERT_STRING_NGINX); + TEST_1_CERT_STRING_NGINX); headers.add(DefaultX509AuthenticationCredentialExtractor.Headers.SUBJECT.getHeader(), TEST_1_SUBJECT); @@ -365,7 +365,7 @@ private HttpHeaders test2SSLHeaders(boolean verified, String verificationError) private HttpHeaders test1SSLHeaders(boolean verified, String verificationError) { HttpHeaders headers = new HttpHeaders(); headers.add(DefaultX509AuthenticationCredentialExtractor.Headers.CLIENT_CERT.getHeader(), - TEST_0_CERT_STRING_NGINX); + TEST_1_CERT_STRING_NGINX); headers.add(DefaultX509AuthenticationCredentialExtractor.Headers.SUBJECT.getHeader(), TEST_0_SUBJECT); diff --git a/iam-persistence/src/main/java/it/infn/mw/iam/persistence/repository/IamAccountRepository.java b/iam-persistence/src/main/java/it/infn/mw/iam/persistence/repository/IamAccountRepository.java index 2a20c853d..225a9637e 100644 --- a/iam-persistence/src/main/java/it/infn/mw/iam/persistence/repository/IamAccountRepository.java +++ b/iam-persistence/src/main/java/it/infn/mw/iam/persistence/repository/IamAccountRepository.java @@ -65,7 +65,7 @@ Optional findByUsernameWithDifferentUUID(@Param("username") String u Optional findByEmailWithDifferentUUID(@Param("emailAddress") String emailAddress, @Param("uuid") String uuid); - @Query("select a from IamAccount a join a.x509Certificates c where c.subjectDn = :subject") + @Query("select distinct a from IamAccount a join a.x509Certificates c where c.subjectDn = :subject") Optional findByCertificateSubject(@Param("subject") String subject); @Query("select a from IamAccount a join a.x509Certificates c where c.certificate = :certificate") diff --git a/iam-persistence/src/main/resources/db/migration/h2/V97__delete_unique_subject_dn.sql b/iam-persistence/src/main/resources/db/migration/h2/V97__delete_unique_subject_dn.sql new file mode 100644 index 000000000..b63dbad8d --- /dev/null +++ b/iam-persistence/src/main/resources/db/migration/h2/V97__delete_unique_subject_dn.sql @@ -0,0 +1,2 @@ +ALTER TABLE iam_x509_cert DROP CONSTRAINT CONSTRAINT_32; +CREATE INDEX idx_subject_dn ON iam_x509_cert(subject_dn); \ No newline at end of file diff --git a/iam-persistence/src/main/resources/db/migration/mysql/V97__delete_unique_subject_dn.sql b/iam-persistence/src/main/resources/db/migration/mysql/V97__delete_unique_subject_dn.sql new file mode 100644 index 000000000..7ff229130 --- /dev/null +++ b/iam-persistence/src/main/resources/db/migration/mysql/V97__delete_unique_subject_dn.sql @@ -0,0 +1,4 @@ +-- Drop unique constraint on subject dn +ALTER TABLE iam_x509_cert DROP INDEX subject_dn; +-- Add index on subject_dn +ALTER TABLE iam_x509_cert ADD INDEX idx_subject_dn (subject_dn); \ No newline at end of file From 07b5dd446597b64f94a5ef9e66f71757cc56998d Mon Sep 17 00:00:00 2001 From: Enrico Vianello Date: Mon, 11 Dec 2023 15:49:40 +0100 Subject: [PATCH 3/3] Fix client's tokens lifetime and authentication method management (#677) --- .../DefaultClientManagementService.java | 24 ++- .../DefaultClientRegistrationService.java | 8 +- .../api/client/service/ClientConverter.java | 61 +++--- .../service/DefaultClientDefaultsService.java | 34 ++- .../ClientRegistrationProperties.java | 14 +- .../core/web/util/IamViewInfoInterceptor.java | 6 +- .../src/main/resources/application.yml | 5 +- .../main/webapp/WEB-INF/tags/iamHeader.tag | 20 +- .../tokensettings.component.html | 17 +- .../tokensettings/tokensettings.component.js | 15 +- .../ClientManagementAPIIntegrationTests.java | 59 ++++-- ...ClientRegistrationAPIIntegrationTests.java | 2 +- .../client/RegistrationAccessTokenTests.java | 9 +- .../ProtectedResourceIntegrationTests.java | 198 ++++++++++++++++++ .../proxy/ProxyServiceIntegrationTests.java | 20 +- .../ClientManagementAPIControllerTests.java | 42 ++++ .../ClientRegistrationAPIControllerTests.java | 83 ++++++-- ...okenEndpointClientAuthenticationTests.java | 15 ++ .../ClientRegistrationTestSupport.java | 14 +- .../client/ClientManagementServiceTests.java | 2 +- .../db/migration/test/V100000___test_data.sql | 14 +- pom.xml | 2 +- 22 files changed, 513 insertions(+), 151 deletions(-) create mode 100644 iam-login-service/src/test/java/it/infn/mw/iam/test/api/mitre/ProtectedResourceIntegrationTests.java diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/service/DefaultClientManagementService.java b/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/service/DefaultClientManagementService.java index eb7faf6ec..2298908e9 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/service/DefaultClientManagementService.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/api/client/management/service/DefaultClientManagementService.java @@ -17,6 +17,8 @@ import static it.infn.mw.iam.api.client.util.ClientSuppliers.accountNotFound; import static it.infn.mw.iam.api.client.util.ClientSuppliers.clientNotFound; +import static java.util.Objects.isNull; +import static org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod.NONE; import java.text.ParseException; import java.time.Clock; @@ -29,7 +31,6 @@ import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.OAuth2AccessTokenEntity; import org.mitre.openid.connect.service.OIDCTokenService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -71,8 +72,6 @@ public class DefaultClientManagementService implements ClientManagementService { private final IamTokenService tokenService; private final ApplicationEventPublisher eventPublisher; - - @Autowired public DefaultClientManagementService(Clock clock, ClientService clientService, ClientConverter converter, ClientDefaultsService defaultsService, UserConverter userConverter, IamAccountRepository accountRepo, OIDCTokenService oidcTokenService, @@ -106,13 +105,13 @@ public ListResponseDTO retrieveAllClients(Pageable pageable @Override public Optional retrieveClientByClientId(String clientId) { - return clientService.findClientByClientId(clientId).map(converter::registeredClientDtoFromEntity); + return clientService.findClientByClientId(clientId) + .map(converter::registeredClientDtoFromEntity); } @Validated(OnClientCreation.class) @Override - public RegisteredClientDTO saveNewClient(RegisteredClientDTO client) - throws ParseException { + public RegisteredClientDTO saveNewClient(RegisteredClientDTO client) throws ParseException { ClientDetailsEntity entity = converter.entityFromClientManagementRequest(client); entity.setDynamicallyRegistered(false); @@ -150,6 +149,12 @@ public RegisteredClientDTO updateClient(String clientId, RegisteredClientDTO cli newClient.setAuthorities(oldClient.getAuthorities()); newClient.setDynamicallyRegistered(oldClient.isDynamicallyRegistered()); + if (NONE.equals(newClient.getTokenEndpointAuthMethod())) { + newClient.setClientSecret(null); + } else if (isNull(client.getClientSecret())) { + client.setClientSecret(defaultsService.generateClientSecret()); + } + newClient = clientService.updateClient(newClient); eventPublisher.publishEvent(new ClientUpdatedEvent(this, newClient)); return converter.registeredClientDtoFromEntity(newClient); @@ -227,15 +232,16 @@ private OAuth2AccessTokenEntity createRegistrationAccessTokenForClient( return tokenService.saveAccessToken(token); } + @Override public RegisteredClientDTO rotateRegistrationAccessToken(@NotBlank String clientId) { ClientDetailsEntity client = clientService.findClientByClientId(clientId).orElseThrow(clientNotFound(clientId)); OAuth2AccessTokenEntity rat = - Optional.ofNullable(oidcTokenService.rotateRegistrationAccessTokenForClient(client)) - .orElse(createRegistrationAccessTokenForClient(client)); - + Optional.ofNullable(oidcTokenService.rotateRegistrationAccessTokenForClient(client)) + .orElse(createRegistrationAccessTokenForClient(client)); + tokenService.saveAccessToken(rat); eventPublisher.publishEvent(new ClientRegistrationAccessTokenRotatedEvent(this, client)); diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/api/client/registration/service/DefaultClientRegistrationService.java b/iam-login-service/src/main/java/it/infn/mw/iam/api/client/registration/service/DefaultClientRegistrationService.java index a88b25808..adb68cfa6 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/api/client/registration/service/DefaultClientRegistrationService.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/api/client/registration/service/DefaultClientRegistrationService.java @@ -36,7 +36,6 @@ import org.mitre.oauth2.model.OAuth2AccessTokenEntity; import org.mitre.oauth2.service.SystemScopeService; import org.mitre.openid.connect.service.OIDCTokenService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.ApplicationEventPublisher; import org.springframework.security.access.AccessDeniedException; @@ -61,7 +60,6 @@ import it.infn.mw.iam.audit.events.client.ClientRegistrationAccessTokenRotatedEvent; import it.infn.mw.iam.audit.events.client.ClientRemovedEvent; import it.infn.mw.iam.audit.events.client.ClientUpdatedEvent; -import it.infn.mw.iam.config.IamProperties; import it.infn.mw.iam.config.client_registration.ClientRegistrationProperties; import it.infn.mw.iam.config.client_registration.ClientRegistrationProperties.ClientRegistrationAuthorizationPolicy; import it.infn.mw.iam.core.IamTokenService; @@ -99,13 +97,11 @@ public class DefaultClientRegistrationService implements ClientRegistrationServi private final ApplicationEventPublisher eventPublisher; - @Autowired public DefaultClientRegistrationService(Clock clock, ClientService clientService, AccountUtils accountUtils, ClientConverter converter, ClientDefaultsService defaultsService, OIDCTokenService clientTokenService, IamTokenService tokenService, SystemScopeService scopeService, ClientRegistrationProperties registrationProperties, - IamProperties iamProperties, ScopeMatcherRegistry scopeMatcherRegistry, - ApplicationEventPublisher aep) { + ScopeMatcherRegistry scopeMatcherRegistry, ApplicationEventPublisher aep) { this.clock = clock; this.clientService = clientService; @@ -439,7 +435,7 @@ public RegisteredClientDTO redeemClient(@NotBlank String clientId, final IamAccount account = accountUtils.getAuthenticatedUserAccount(authentication).orElseThrow(noAuthUserError()); - + client = clientService.linkClientToAccount(client, account); eventPublisher.publishEvent(new AccountClientOwnerAssigned(this, account, client)); diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/api/client/service/ClientConverter.java b/iam-login-service/src/main/java/it/infn/mw/iam/api/client/service/ClientConverter.java index 54b4692ab..c298dda2d 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/api/client/service/ClientConverter.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/api/client/service/ClientConverter.java @@ -26,7 +26,6 @@ import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod; import org.mitre.oauth2.model.PKCEAlgorithm; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.google.common.base.Strings; @@ -46,15 +45,14 @@ public class ClientConverter { private final IamProperties iamProperties; private final String clientRegistrationBaseUrl; + private final ClientRegistrationProperties clientRegistrationProperties; - private final ClientRegistrationProperties clientProperties; - - @Autowired - public ClientConverter(IamProperties properties, ClientRegistrationProperties clientProperties) { + public ClientConverter(IamProperties properties, + ClientRegistrationProperties clientRegistrationProperties) { this.iamProperties = properties; + this.clientRegistrationProperties = clientRegistrationProperties; clientRegistrationBaseUrl = String.format("%s%s", iamProperties.getBaseUrl(), ClientRegistrationApiController.ENDPOINT); - this.clientProperties = clientProperties; } private Set cloneSet(Set stringSet) { @@ -70,28 +68,16 @@ public ClientDetailsEntity entityFromClientManagementRequest(RegisteredClientDTO throws ParseException { ClientDetailsEntity client = entityFromRegistrationRequest(dto); - if (dto.getAccessTokenValiditySeconds() != null) { + if (dto.getAccessTokenValiditySeconds() != null && dto.getAccessTokenValiditySeconds() > 0) { client.setAccessTokenValiditySeconds(dto.getAccessTokenValiditySeconds()); - } else { - client.setAccessTokenValiditySeconds( - clientProperties.getClientDefaults().getDefaultAccessTokenValiditySeconds()); } - - if (dto.getRefreshTokenValiditySeconds() != null) { + // Refresh Token validity seconds zero value is valid and means infinite duration + if (dto.getRefreshTokenValiditySeconds() != null && dto.getRefreshTokenValiditySeconds() >= 0) { client.setRefreshTokenValiditySeconds(dto.getRefreshTokenValiditySeconds()); - } else { - client.setRefreshTokenValiditySeconds( - clientProperties.getClientDefaults().getDefaultRefreshTokenValiditySeconds()); } - - if (dto.getIdTokenValiditySeconds() != null) { - if (dto.getIdTokenValiditySeconds() <= 0) { - client.setIdTokenValiditySeconds(null); - } else { - client.setIdTokenValiditySeconds(dto.getIdTokenValiditySeconds()); - } + if (dto.getIdTokenValiditySeconds() != null && dto.getIdTokenValiditySeconds() > 0) { + client.setIdTokenValiditySeconds(dto.getIdTokenValiditySeconds()); } - if (dto.getDeviceCodeValiditySeconds() != null && dto.getDeviceCodeValiditySeconds() > 0) { client.setDeviceCodeValiditySeconds(dto.getDeviceCodeValiditySeconds()); } @@ -105,6 +91,11 @@ public ClientDetailsEntity entityFromClientManagementRequest(RegisteredClientDTO client.setCodeChallengeMethod(pkceAlgo); } + if (dto.getTokenEndpointAuthMethod() != null) { + client + .setTokenEndpointAuthMethod(AuthMethod.getByValue(dto.getTokenEndpointAuthMethod().name())); + } + client.setRequireAuthTime(Boolean.valueOf(dto.isRequireAuthTime())); return client; @@ -196,16 +187,19 @@ public ClientDetailsEntity entityFromRegistrationRequest(RegisteredClientDTO dto client.setLogoUri(dto.getLogoUri()); client.setPolicyUri(dto.getPolicyUri()); - + client.setRedirectUris(cloneSet(dto.getRedirectUris())); client.setScope(cloneSet(dto.getScope())); - - client.setGrantTypes(new HashSet<>()); + + client.setGrantTypes(new HashSet<>()); if (!isNull(dto.getGrantTypes())) { client.setGrantTypes( - dto.getGrantTypes().stream().map(AuthorizationGrantType::getGrantType).collect(toSet())); + dto.getGrantTypes() + .stream() + .map(AuthorizationGrantType::getGrantType) + .collect(toSet())); } if (dto.getScope().contains("offline_access")) { @@ -219,9 +213,7 @@ public ClientDetailsEntity entityFromRegistrationRequest(RegisteredClientDTO dto client.setContacts(cloneSet(dto.getContacts())); - if (isNull(dto.getTokenEndpointAuthMethod())) { - client.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC); - } else { + if (!isNull(dto.getTokenEndpointAuthMethod())) { client .setTokenEndpointAuthMethod(AuthMethod.getByValue(dto.getTokenEndpointAuthMethod().name())); } @@ -231,10 +223,11 @@ public ClientDetailsEntity entityFromRegistrationRequest(RegisteredClientDTO dto client.setCodeChallengeMethod(pkceAlgo); } - client.setAccessTokenValiditySeconds( - clientProperties.getClientDefaults().getDefaultAccessTokenValiditySeconds()); - client.setRefreshTokenValiditySeconds( - clientProperties.getClientDefaults().getDefaultRefreshTokenValiditySeconds()); + // bypasses MitreID default setting to zero inside client's entity + client.setAccessTokenValiditySeconds(clientRegistrationProperties.getClientDefaults().getDefaultAccessTokenValiditySeconds()); + client.setRefreshTokenValiditySeconds(clientRegistrationProperties.getClientDefaults().getDefaultRefreshTokenValiditySeconds()); + client.setIdTokenValiditySeconds(clientRegistrationProperties.getClientDefaults().getDefaultIdTokenValiditySeconds()); + client.setDeviceCodeValiditySeconds(clientRegistrationProperties.getClientDefaults().getDefaultDeviceCodeValiditySeconds()); return client; } 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 4a97f1b21..20bcdabb5 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 @@ -20,14 +20,12 @@ import java.math.BigInteger; import java.security.SecureRandom; import java.util.EnumSet; -import java.util.HashSet; import java.util.Set; import java.util.UUID; import org.apache.commons.codec.binary.Base64; import org.mitre.oauth2.model.ClientDetailsEntity; import org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.google.common.collect.Sets; @@ -46,7 +44,6 @@ public class DefaultClientDefaultsService implements ClientDefaultsService { private final ClientRegistrationProperties properties; - @Autowired public DefaultClientDefaultsService(ClientRegistrationProperties properties) { this.properties = properties; } @@ -58,19 +55,36 @@ public ClientDetailsEntity setupClientDefaults(ClientDetailsEntity client) { client.setClientId(UUID.randomUUID().toString()); } - client - .setIdTokenValiditySeconds(properties.getClientDefaults().getDefaultIdTokenValiditySeconds()); + if (client.getAccessTokenValiditySeconds() == null + || client.getAccessTokenValiditySeconds() == 0) { + client.setAccessTokenValiditySeconds( + properties.getClientDefaults().getDefaultAccessTokenValiditySeconds()); + } + + if (client.getRefreshTokenValiditySeconds() == null) { + client.setRefreshTokenValiditySeconds( + properties.getClientDefaults().getDefaultRefreshTokenValiditySeconds()); + } - client.setDeviceCodeValiditySeconds( - properties.getClientDefaults().getDefaultDeviceCodeValiditySeconds()); + if (client.getIdTokenValiditySeconds() == null || client.getIdTokenValiditySeconds() == 0) { + client.setIdTokenValiditySeconds( + properties.getClientDefaults().getDefaultIdTokenValiditySeconds()); + } + + if (client.getDeviceCodeValiditySeconds() == null + || client.getDeviceCodeValiditySeconds() == 0) { + client.setDeviceCodeValiditySeconds( + properties.getClientDefaults().getDefaultDeviceCodeValiditySeconds()); + } client.setAllowIntrospection(true); - if (isNull(client.getContacts())) { - client.setContacts(new HashSet<>()); + if (isNull(client.getTokenEndpointAuthMethod())) { + client.setTokenEndpointAuthMethod(AuthMethod.SECRET_BASIC); } - if (AUTH_METHODS_REQUIRING_SECRET.contains(client.getTokenEndpointAuthMethod())) { + if (isNull(client.getClientSecret()) + && AUTH_METHODS_REQUIRING_SECRET.contains(client.getTokenEndpointAuthMethod())) { client.setClientSecret(generateClientSecret()); } diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/config/client_registration/ClientRegistrationProperties.java b/iam-login-service/src/main/java/it/infn/mw/iam/config/client_registration/ClientRegistrationProperties.java index 0d5fb2aff..1147b7188 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/config/client_registration/ClientRegistrationProperties.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/config/client_registration/ClientRegistrationProperties.java @@ -17,8 +17,6 @@ import static it.infn.mw.iam.config.client_registration.ClientRegistrationProperties.ClientRegistrationAuthorizationPolicy.ANYONE; -import java.util.concurrent.TimeUnit; - import javax.validation.constraints.NotNull; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -30,14 +28,14 @@ public class ClientRegistrationProperties { public static class ClientDefaultsProperties { - @NotNull(message = "Provide a default access token lifetime") + @NotNull(message = "Provide a default access token lifetime") private int defaultAccessTokenValiditySeconds; - - @NotNull(message = "Provide a default refresh token lifetime") + @NotNull(message = "Provide a default id token lifetime") + private int defaultIdTokenValiditySeconds; + @NotNull(message = "Provide a default device code lifetime") + private int defaultDeviceCodeValiditySeconds; + @NotNull(message = "Provide a default refresh token lifetime") private int defaultRefreshTokenValiditySeconds; - - private int defaultIdTokenValiditySeconds = (int) TimeUnit.MINUTES.toSeconds(10); - private int defaultDeviceCodeValiditySeconds = (int) TimeUnit.MINUTES.toSeconds(10); private int defaultRegistrationAccessTokenValiditySeconds = -1; diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/core/web/util/IamViewInfoInterceptor.java b/iam-login-service/src/main/java/it/infn/mw/iam/core/web/util/IamViewInfoInterceptor.java index c481a8078..5a7cdc865 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/core/web/util/IamViewInfoInterceptor.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/core/web/util/IamViewInfoInterceptor.java @@ -63,10 +63,10 @@ public class IamViewInfoInterceptor implements HandlerInterceptor { @Autowired IamProperties iamProperties; - + @Autowired ClientRegistrationProperties clientRegistrationProperties; - + @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { @@ -81,7 +81,7 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons request.setAttribute(IAM_SAML_PROPERTIES_KEY, samlProperties); request.setAttribute(RCAUTH_ENABLED_KEY, rcAuthProperties.isEnabled()); - + request.setAttribute(CLIENT_DEFAULTS_PROPERTIES_KEY, clientRegistrationProperties.getClientDefaults()); if (iamProperties.getVersionedStaticResources().isEnableVersioning()) { diff --git a/iam-login-service/src/main/resources/application.yml b/iam-login-service/src/main/resources/application.yml index bf051419b..9fb2907f1 100644 --- a/iam-login-service/src/main/resources/application.yml +++ b/iam-login-service/src/main/resources/application.yml @@ -215,8 +215,9 @@ client-registration: enable: ${IAM_CLIENT_REGISTRATION_ENABLE:true} client-defaults: default-access-token-validity-seconds: ${DEFAULT_ACCESS_TOKEN_VALIDITY_SECONDS:3600} - default-refresh-token-validity-seconds: ${DEFAULT_REFRESH_TOKEN_VALIDITY_SECONDS:108000} - + default-device-code-validity-seconds: ${DEFAULT_DEVICE_CODE_VALIDITY_SECONDS:600} + default-id-token-validity-seconds: ${DEFAULT_ID_TOKEN_VALIDITY_SECONDS:600} + default-refresh-token-validity-seconds: ${DEFAULT_REFRESH_TOKEN_VALIDITY_SECONDS:2592000} management: health: diff --git a/iam-login-service/src/main/webapp/WEB-INF/tags/iamHeader.tag b/iam-login-service/src/main/webapp/WEB-INF/tags/iamHeader.tag index 3abad14d6..e62cadd8f 100644 --- a/iam-login-service/src/main/webapp/WEB-INF/tags/iamHeader.tag +++ b/iam-login-service/src/main/webapp/WEB-INF/tags/iamHeader.tag @@ -81,21 +81,13 @@ function getRegistrationEnabled() { } function getAccountLinkingEnabled() { - return ${loginPageConfiguration.accountLinkingEnabled}; + return ${loginPageConfiguration.accountLinkingEnabled}; } function getOrganisationName() { return '${iamOrganisationName}'; } -function getAccessTokenValiditySeconds() { - return ${clientDefaultsProperties.defaultAccessTokenValiditySeconds}; -} - -function getRefreshTokenValiditySeconds() { - return ${clientDefaultsProperties.defaultRefreshTokenValiditySeconds}; -} - function getOidcEnabled() { return ${loginPageConfiguration.oidcEnabled}; } @@ -109,6 +101,14 @@ function getRcauthEnabled() { } function getExternalAuthenticationEnabled() { - return ${loginPageConfiguration.externalAuthenticationEnabled}; + return ${loginPageConfiguration.externalAuthenticationEnabled}; +} + +function getAccessTokenValiditySeconds() { + return ${clientDefaultsProperties.defaultAccessTokenValiditySeconds}; +} + +function getRefreshTokenValiditySeconds() { + return ${clientDefaultsProperties.defaultRefreshTokenValiditySeconds}; } diff --git a/iam-login-service/src/main/webapp/resources/iam/apps/dashboard-app/components/clients/client/tokensettings/tokensettings.component.html b/iam-login-service/src/main/webapp/resources/iam/apps/dashboard-app/components/clients/client/tokensettings/tokensettings.component.html index 3820c1cd0..5ce4e9ce5 100644 --- a/iam-login-service/src/main/webapp/resources/iam/apps/dashboard-app/components/clients/client/tokensettings/tokensettings.component.html +++ b/iam-login-service/src/main/webapp/resources/iam/apps/dashboard-app/components/clients/client/tokensettings/tokensettings.component.html @@ -20,20 +20,19 @@
+ ng-model="$ctrl.client.access_token_validity_seconds"/>
-
+ placeholder="600" ng-required ng-min="30"/>
@@ -57,11 +56,11 @@ -

- This will setup after how many seconds the refresh token - expires. Type 0 to set an infinite lifetime. -

+ ng-required ng-min="30"/> +

+ This will setup after how many seconds the refresh token + expires. Type 0 to set an infinite lifetime. +

diff --git a/iam-login-service/src/main/webapp/resources/iam/apps/dashboard-app/components/clients/client/tokensettings/tokensettings.component.js b/iam-login-service/src/main/webapp/resources/iam/apps/dashboard-app/components/clients/client/tokensettings/tokensettings.component.js index 62a005fa3..c17c46a7e 100644 --- a/iam-login-service/src/main/webapp/resources/iam/apps/dashboard-app/components/clients/client/tokensettings/tokensettings.component.js +++ b/iam-login-service/src/main/webapp/resources/iam/apps/dashboard-app/components/clients/client/tokensettings/tokensettings.component.js @@ -26,27 +26,28 @@ self.toggleOfflineAccess = toggleOfflineAccess; self.canIssueRefreshTokens = false; self.hasDeviceCodeGrantType = false; - self.accessTokenValiditySeconds = getAccessTokenValiditySeconds(); - self.refreshTokenValiditySeconds = getRefreshTokenValiditySeconds(); + + self.accessTokenDefaultValiditySeconds = getAccessTokenValiditySeconds(); + self.refreshTokenDefaultValiditySeconds = getRefreshTokenValiditySeconds(); + self.$onInit = function () { console.debug('TokenSettingsController.self', self); if (self.client.access_token_validity_seconds == null) { - self.client.access_token_validity_seconds = self.accessTokenValiditySeconds; + self.client.access_token_validity_seconds = self.accessTokenDefaultValiditySeconds; } - if (self.client.refresh_token_validity_seconds == null) { - self.client.refresh_token_validity_seconds = self.refreshTokenValiditySeconds; + self.client.refresh_token_validity_seconds = self.refreshTokenDefaultValiditySeconds; } $scope.$watch('$ctrl.client.access_token_validity_seconds', function handleChange(newVal, oldVal) { - if (newVal <= 0) { + if (newVal < 0) { self.client.access_token_validity_seconds = 0; } }); $scope.$watch('$ctrl.client.refresh_token_validity_seconds', function handleChange(newVal, oldVal) { - if (newVal <= 0) { + if (newVal < 0) { self.client.refresh_token_validity_seconds = 0; } }); diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/api/client/ClientManagementAPIIntegrationTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/api/client/ClientManagementAPIIntegrationTests.java index 60fe54400..dac6229bf 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/api/client/ClientManagementAPIIntegrationTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/api/client/ClientManagementAPIIntegrationTests.java @@ -20,7 +20,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasSize; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -28,6 +28,8 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import java.util.Optional; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -43,12 +45,14 @@ import it.infn.mw.iam.IamLoginService; import it.infn.mw.iam.api.client.management.ClientManagementAPIController; import it.infn.mw.iam.api.common.client.RegisteredClientDTO; +import it.infn.mw.iam.persistence.repository.client.IamClientRepository; import it.infn.mw.iam.test.api.TestSupport; import it.infn.mw.iam.test.core.CoreControllerTestSupport; import it.infn.mw.iam.test.oauth.client_registration.ClientRegistrationTestSupport.ClientJsonStringBuilder; import it.infn.mw.iam.test.util.WithMockOAuthUser; import it.infn.mw.iam.test.util.annotation.IamMockMvcIntegrationTest; import it.infn.mw.iam.test.util.oauth.MockOAuth2Filter; +import org.mitre.oauth2.model.ClientDetailsEntity; @IamMockMvcIntegrationTest @SpringBootTest(classes = {IamLoginService.class, CoreControllerTestSupport.class}) @@ -63,6 +67,8 @@ public class ClientManagementAPIIntegrationTests extends TestSupport { @Autowired private MockOAuth2Filter mockOAuth2Filter; + @Autowired + private IamClientRepository clientRepo; @BeforeEach public void setup() { @@ -94,7 +100,7 @@ private void clientManagementFailsWithResponseForClient(ResultMatcher response, private void paginatedGetClientsTest() throws Exception { mvc.perform(get(ClientManagementAPIController.ENDPOINT)) .andExpect(OK) - .andExpect(jsonPath("$.totalResults").value(18)) + .andExpect(jsonPath("$.totalResults").value(19)) .andExpect(jsonPath("$.itemsPerPage").value(10)) .andExpect(jsonPath("$.startIndex").value(1)) .andExpect(jsonPath("$.Resources", hasSize(10))) @@ -102,11 +108,11 @@ private void paginatedGetClientsTest() throws Exception { mvc.perform(get(ClientManagementAPIController.ENDPOINT).param("startIndex", "11")) .andExpect(OK) - .andExpect(jsonPath("$.totalResults").value(18)) - .andExpect(jsonPath("$.itemsPerPage").value(8)) + .andExpect(jsonPath("$.totalResults").value(19)) + .andExpect(jsonPath("$.itemsPerPage").value(9)) .andExpect(jsonPath("$.startIndex").value(11)) - .andExpect(jsonPath("$.Resources", hasSize(8))) - .andExpect(jsonPath("$.Resources[0].client_id").value("public-dc-client")); + .andExpect(jsonPath("$.Resources", hasSize(9))) + .andExpect(jsonPath("$.Resources[0].client_id").value("public-client")); } @Test @@ -198,8 +204,16 @@ public void setTokenLifetimesWorks() throws Exception { .getContentAsString(); RegisteredClientDTO client = mapper.readValue(responseJson, RegisteredClientDTO.class); - assertTrue(client.getAccessTokenValiditySeconds().equals(3600)); - assertTrue(client.getRefreshTokenValiditySeconds().equals(108000)); + assertEquals(3600, client.getAccessTokenValiditySeconds()); + assertEquals(2592000, client.getRefreshTokenValiditySeconds()); + assertEquals(600, client.getIdTokenValiditySeconds()); + assertEquals(600, client.getDeviceCodeValiditySeconds()); + + Optional clientDB = clientRepo.findByClientId(client.getClientId()); + assertEquals(client.getAccessTokenValiditySeconds(), clientDB.get().getAccessTokenValiditySeconds()); + assertEquals(client.getRefreshTokenValiditySeconds(), clientDB.get().getRefreshTokenValiditySeconds()); + assertEquals(client.getIdTokenValiditySeconds(), clientDB.get().getIdTokenValiditySeconds()); + assertEquals(client.getDeviceCodeValiditySeconds(), clientDB.get().getDeviceCodeValiditySeconds()); clientJson = ClientJsonStringBuilder.builder() .scopes("openid") @@ -216,8 +230,14 @@ public void setTokenLifetimesWorks() throws Exception { .getContentAsString(); client = mapper.readValue(responseJson, RegisteredClientDTO.class); - assertTrue(client.getAccessTokenValiditySeconds().equals(0)); - assertTrue(client.getRefreshTokenValiditySeconds().equals(0)); + assertEquals(3600, client.getAccessTokenValiditySeconds()); + assertEquals(0, client.getRefreshTokenValiditySeconds()); + + clientDB = clientRepo.findByClientId(client.getClientId()); + assertEquals(client.getAccessTokenValiditySeconds(), clientDB.get().getAccessTokenValiditySeconds()); + assertEquals(client.getRefreshTokenValiditySeconds(), clientDB.get().getRefreshTokenValiditySeconds()); + assertEquals(client.getIdTokenValiditySeconds(), clientDB.get().getIdTokenValiditySeconds()); + assertEquals(client.getDeviceCodeValiditySeconds(), clientDB.get().getDeviceCodeValiditySeconds()); clientJson = ClientJsonStringBuilder.builder() .scopes("openid") @@ -234,14 +254,20 @@ public void setTokenLifetimesWorks() throws Exception { .getContentAsString(); client = mapper.readValue(responseJson, RegisteredClientDTO.class); - assertTrue(client.getAccessTokenValiditySeconds().equals(10)); - assertTrue(client.getRefreshTokenValiditySeconds().equals(10)); + assertEquals(10, client.getAccessTokenValiditySeconds()); + assertEquals(10, client.getRefreshTokenValiditySeconds()); + + clientDB = clientRepo.findByClientId(client.getClientId()); + assertEquals(client.getAccessTokenValiditySeconds(), clientDB.get().getAccessTokenValiditySeconds()); + assertEquals(client.getRefreshTokenValiditySeconds(), clientDB.get().getRefreshTokenValiditySeconds()); + assertEquals(client.getIdTokenValiditySeconds(), clientDB.get().getIdTokenValiditySeconds()); + assertEquals(client.getDeviceCodeValiditySeconds(), clientDB.get().getDeviceCodeValiditySeconds()); } @Test @WithMockUser(username = "admin", roles = {"ADMIN", "USER"}) - public void negativeTokenLifetimesNotAllowed() throws Exception { + public void negativeAccessTokenLifetimesSetToDefault() throws Exception { String clientJson = ClientJsonStringBuilder.builder().scopes("openid").accessTokenValiditySeconds(-1).build(); @@ -251,8 +277,13 @@ public void negativeTokenLifetimesNotAllowed() throws Exception { .content(clientJson)) .andExpect(BAD_REQUEST) .andExpect(jsonPath("$.error", containsString("must be greater than or equal to 0"))); + } - clientJson = + @Test + @WithMockUser(username = "admin", roles = {"ADMIN", "USER"}) + public void negativeRefreshTokenLifetimesSetToInfinite() throws Exception { + + String clientJson = ClientJsonStringBuilder.builder().scopes("openid").refreshTokenValiditySeconds(-1).build(); mvc diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/api/client/ClientRegistrationAPIIntegrationTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/api/client/ClientRegistrationAPIIntegrationTests.java index e1dddb7c2..37c03a4be 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/api/client/ClientRegistrationAPIIntegrationTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/api/client/ClientRegistrationAPIIntegrationTests.java @@ -137,4 +137,4 @@ public void tokenLifetimesAreNotEditable() throws Exception { } -} +} \ No newline at end of file diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/api/client/RegistrationAccessTokenTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/api/client/RegistrationAccessTokenTests.java index b33eaa9c9..b3a447048 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/api/client/RegistrationAccessTokenTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/api/client/RegistrationAccessTokenTests.java @@ -24,6 +24,8 @@ import static org.hamcrest.Matchers.not; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import java.text.ParseException; + import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -32,6 +34,9 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import com.nimbusds.jwt.JWT; +import com.nimbusds.jwt.JWTParser; + import io.restassured.RestAssured; import it.infn.mw.iam.api.client.management.service.ClientManagementService; import it.infn.mw.iam.api.common.client.RegisteredClientDTO; @@ -69,7 +74,7 @@ public void setup() { } @Test - public void testRatWorkAsExpected() { + public void testRatWorkAsExpected() throws ParseException { String clientJson = ClientJsonStringBuilder.builder().scopes("openid").build(); @@ -87,6 +92,8 @@ public void testRatWorkAsExpected() { // @formatter:on assertThat(registerResponse.getRegistrationAccessToken(), notNullValue()); + JWT jwt = JWTParser.parse(registerResponse.getRegistrationAccessToken()); + assertThat(jwt.getJWTClaimsSet().getExpirationTime(), nullValue()); assertThat(registerResponse.getScope(), not(empty())); RegisteredClientDTO getResponse = diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/api/mitre/ProtectedResourceIntegrationTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/api/mitre/ProtectedResourceIntegrationTests.java new file mode 100644 index 000000000..4dd2124f3 --- /dev/null +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/api/mitre/ProtectedResourceIntegrationTests.java @@ -0,0 +1,198 @@ +/** + * 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.api.mitre; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mitre.openid.connect.web.ProtectedResourceRegistrationEndpoint; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.test.context.junit4.SpringRunner; + +import com.nimbusds.jwt.JWTParser; + +import io.restassured.RestAssured; +import io.restassured.response.ValidatableResponse; +import it.infn.mw.iam.api.client.management.service.ClientManagementService; +import it.infn.mw.iam.api.common.client.RegisteredClientDTO; +import it.infn.mw.iam.test.oauth.client_registration.ClientRegistrationTestSupport.ClientJsonStringBuilder; +import it.infn.mw.iam.test.util.annotation.IamRandomPortIntegrationTest; + +@RunWith(SpringRunner.class) +@IamRandomPortIntegrationTest +public class ProtectedResourceIntegrationTests { + + @Value("${local.server.port}") + private Integer iamPort; + + @Autowired + private ClientManagementService managementService; + + private ValidatableResponse doCreateProtectedResource(String clientJson) { + + return RestAssured.given() + .port(iamPort) + .contentType(APPLICATION_JSON_VALUE) + .body(clientJson) + .log() + .all(true) + .when() + .post("/" + ProtectedResourceRegistrationEndpoint.URL) + .then() + .log() + .all(true); + } + + private ValidatableResponse doGetProtectedResource(String clientId, String rat) { + + return RestAssured.given() + .port(iamPort) + .accept(APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + rat) + .log() + .all(true) + .when() + .get("/" + ProtectedResourceRegistrationEndpoint.URL + "/" + clientId) + .then() + .log() + .all(true); + } + + private ValidatableResponse doUpdateProtectedResource(String clientId, String clientJson, + String rat) { + + return RestAssured.given() + .port(iamPort) + .contentType(APPLICATION_JSON_VALUE) + .accept(APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + rat) + .body(clientJson) + .log() + .all(true) + .when() + .put("/" + ProtectedResourceRegistrationEndpoint.URL + "/" + clientId) + .then() + .log() + .all(true); + } + + private ValidatableResponse doDeleteProtectedResource(String clientId, String rat) { + + return RestAssured.given() + .port(iamPort) + .accept(APPLICATION_JSON_VALUE) + .header("Authorization", "Bearer " + rat) + .log() + .all(true) + .when() + .delete("/" + ProtectedResourceRegistrationEndpoint.URL + "/" + clientId) + .then() + .log() + .all(true); + } + + @Test + public void protectedResourceLifeCycle() throws Exception { + + final String NAME = "protected-resource"; + String clientJson = ClientJsonStringBuilder.builder().name(NAME).scopes("openid").build(); + + // create protected resource + RegisteredClientDTO testedResource = + doCreateProtectedResource(clientJson).statusCode(HttpStatus.CREATED.value()) + .extract() + .as(RegisteredClientDTO.class); + + // verify registration access token exists and expiration is null + assertNull(JWTParser.parse(testedResource.getRegistrationAccessToken()) + .getJWTClaimsSet() + .getExpirationTime()); + + // retrieve protected resource directly from db + RegisteredClientDTO fromDb = + managementService.retrieveClientByClientId(testedResource.getClientId()).get(); + assertEquals(testedResource.getClientId(), fromDb.getClientId()); + assertTrue(fromDb.getGrantTypes().isEmpty()); + assertTrue(fromDb.getResponseTypes().isEmpty()); + assertTrue(fromDb.getRedirectUris().isEmpty()); + assertEquals(NAME, fromDb.getClientName()); + assertEquals(0, fromDb.getAccessTokenValiditySeconds()); + assertEquals(0, fromDb.getIdTokenValiditySeconds()); + assertEquals(0, fromDb.getRefreshTokenValiditySeconds()); + assertTrue(fromDb.isDynamicallyRegistered()); + assertTrue(fromDb.isAllowIntrospection()); + assertFalse(fromDb.getScope().isEmpty()); + assertEquals(1, fromDb.getScope().size()); + assertTrue(fromDb.getScope().contains("openid")); + + + // retrieve protected resource from API + RegisteredClientDTO fromAPI = doGetProtectedResource(testedResource.getClientId(), + testedResource.getRegistrationAccessToken()).statusCode(HttpStatus.OK.value()) + .extract() + .as(RegisteredClientDTO.class); + + assertEquals(testedResource.getClientId(), fromAPI.getClientId()); + assertEquals(NAME, fromAPI.getClientName()); + assertNull(fromAPI.getGrantTypes()); + assertNull(fromAPI.getResponseTypes()); + assertNull(fromAPI.getRedirectUris()); + assertNull(fromAPI.getAccessTokenValiditySeconds()); + assertNull(fromAPI.getIdTokenValiditySeconds()); + assertNull(fromAPI.getRefreshTokenValiditySeconds()); + assertEquals(0, fromAPI.getClientSecretExpiresAt().toInstant().getEpochSecond()); + assertFalse(fromAPI.getScope().isEmpty()); + assertEquals(1, fromAPI.getScope().size()); + assertTrue(fromAPI.getScope().contains("openid")); + + // update protected resource from API + clientJson = ClientJsonStringBuilder.builder() + .clientId(testedResource.getClientId()) + .name(NAME) + .scopes("openid email") + .build(); + RegisteredClientDTO updated = doUpdateProtectedResource(testedResource.getClientId(), + clientJson, testedResource.getRegistrationAccessToken()).statusCode(HttpStatus.OK.value()) + .extract() + .as(RegisteredClientDTO.class); + + assertEquals(testedResource.getClientId(), updated.getClientId()); + assertEquals(NAME, updated.getClientName()); + assertNull(updated.getGrantTypes()); + assertNull(updated.getResponseTypes()); + assertNull(updated.getRedirectUris()); + assertNull(updated.getAccessTokenValiditySeconds()); + assertNull(updated.getIdTokenValiditySeconds()); + assertNull(updated.getRefreshTokenValiditySeconds()); + assertEquals(0, updated.getClientSecretExpiresAt().toInstant().getEpochSecond()); + assertFalse(updated.getScope().isEmpty()); + assertEquals(2, updated.getScope().size()); + assertTrue(updated.getScope().contains("openid")); + assertTrue(updated.getScope().contains("email")); + + doDeleteProtectedResource(testedResource.getClientId(), + testedResource.getRegistrationAccessToken()).statusCode(HttpStatus.NO_CONTENT.value()); + + assertTrue(managementService.retrieveClientByClientId(testedResource.getClientId()).isEmpty()); + } +} \ No newline at end of file diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/api/proxy/ProxyServiceIntegrationTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/api/proxy/ProxyServiceIntegrationTests.java index ea47ea235..40d4cdae7 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/api/proxy/ProxyServiceIntegrationTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/api/proxy/ProxyServiceIntegrationTests.java @@ -23,6 +23,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.sql.Date; +import java.time.Duration; +import java.time.Instant; import org.apache.commons.lang3.RandomStringUtils; import org.junit.After; @@ -31,7 +33,6 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; @@ -44,7 +45,6 @@ import it.infn.mw.iam.persistence.model.IamX509ProxyCertificate; import it.infn.mw.iam.persistence.repository.IamAccountRepository; import it.infn.mw.iam.test.core.CoreControllerTestSupport; -import it.infn.mw.iam.test.rcauth.RCAuthTestSupport; import it.infn.mw.iam.test.util.WithAnonymousUser; import it.infn.mw.iam.test.util.WithMockOAuthUser; import it.infn.mw.iam.test.util.annotation.IamMockMvcIntegrationTest; @@ -52,13 +52,8 @@ @RunWith(SpringRunner.class) @IamMockMvcIntegrationTest -@SpringBootTest(classes = {IamLoginService.class, RCAuthTestSupport.class, - CoreControllerTestSupport.class, ProxyCertificateClockConfig.class}, - webEnvironment = WebEnvironment.MOCK) -@TestPropertySource(properties = {"proxycert.enabled=true", "rcauth.enabled=true", - "rcauth.client-id=" + RCAuthTestSupport.CLIENT_ID, - "rcauth.client-secret=" + RCAuthTestSupport.CLIENT_SECRET, - "rcauth.issuer=" + RCAuthTestSupport.ISSUER}) +@SpringBootTest(classes = {IamLoginService.class, CoreControllerTestSupport.class}) +@TestPropertySource(properties = {"proxycert.enabled=true"}) public class ProxyServiceIntegrationTests extends ProxyCertificateTestSupport { @Autowired @@ -91,8 +86,11 @@ private void linkProxyToTestAccount() throws Exception { IamX509Certificate cert = testAccount.getX509Certificates().iterator().next(); IamX509ProxyCertificate proxyCert = new IamX509ProxyCertificate(); - proxyCert.setChain(generateTest0Proxy(NOW, ONE_YEAR_FROM_NOW)); - proxyCert.setExpirationTime(Date.from(ONE_YEAR_FROM_NOW)); + Instant current = Instant.now(); + Instant plusOneYearDuration = current.plus(Duration.ofDays(365)); + + proxyCert.setChain(generateTest0Proxy(current, plusOneYearDuration)); + proxyCert.setExpirationTime(Date.from(plusOneYearDuration)); proxyCert.setCertificate(cert); cert.setProxy(proxyCert); diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/client/management/ClientManagementAPIControllerTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/client/management/ClientManagementAPIControllerTests.java index 85170ed5b..444c87049 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/client/management/ClientManagementAPIControllerTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/client/management/ClientManagementAPIControllerTests.java @@ -15,7 +15,13 @@ */ package it.infn.mw.iam.test.client.management; +import static it.infn.mw.iam.api.common.client.AuthorizationGrantType.CLIENT_CREDENTIALS; +import static it.infn.mw.iam.api.common.client.TokenEndpointAuthenticationMethod.client_secret_basic; +import static it.infn.mw.iam.api.common.client.TokenEndpointAuthenticationMethod.none; import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod.NONE; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -27,6 +33,7 @@ import org.junit.Before; import org.junit.jupiter.api.Test; import org.junit.runner.RunWith; +import org.mitre.oauth2.model.ClientDetailsEntity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.junit4.SpringRunner; @@ -40,6 +47,7 @@ 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 it.infn.mw.iam.persistence.repository.client.IamClientRepository; import it.infn.mw.iam.test.util.WithAnonymousUser; import it.infn.mw.iam.test.util.annotation.IamMockMvcIntegrationTest; import it.infn.mw.iam.test.util.oauth.MockOAuth2Filter; @@ -58,11 +66,15 @@ class ClientManagementAPIControllerTests { @Autowired private ObjectMapper mapper; + @Autowired + private IamClientRepository clientRepository; + public static final String IAM_CLIENTS_API_URL = "/iam/api/clients/"; public static final ResultMatcher UNAUTHORIZED = status().isUnauthorized(); public static final ResultMatcher BAD_REQUEST = status().isBadRequest(); public static final ResultMatcher CREATED = status().isCreated(); + public static final ResultMatcher OK = status().isOk(); @Before public void setup() { @@ -90,6 +102,36 @@ public void createClientWithAnonymousUser() throws JsonProcessingException, Exce .andExpect(UNAUTHORIZED); } + @Test + @WithMockUser(username = "admin", roles = "ADMIN") + public void updateAuthMethodToNone() throws JsonProcessingException, Exception { + + RegisteredClientDTO client = new RegisteredClientDTO(); + client.setClientName("test-client-creation"); + client.setClientId("test-client-creation"); + client.setGrantTypes(Sets.newHashSet(CLIENT_CREDENTIALS)); + client.setScope(Sets.newHashSet("test")); + + mvc + .perform(post(IAM_CLIENTS_API_URL).contentType(APPLICATION_JSON) + .content(mapper.writeValueAsString(client))) + .andExpect(CREATED) + .andExpect(jsonPath("$.token_endpoint_auth_method", is(client_secret_basic.name()))); + + client.setTokenEndpointAuthMethod(none); + + mvc + .perform(put(IAM_CLIENTS_API_URL + "/test-client-creation").contentType(APPLICATION_JSON) + .content(mapper.writeValueAsString(client))) + .andExpect(OK) + .andExpect(jsonPath("$.token_endpoint_auth_method", is("none"))); + + ClientDetailsEntity clientEntity = clientRepository.findByClientId("test-client-creation").get(); + assertEquals(NONE, clientEntity.getTokenEndpointAuthMethod()); + assertNull(clientEntity.getClientSecret()); + + } + @Test @WithMockUser(username = "admin", roles = "ADMIN") public void createClientRaiseParseException() throws JsonProcessingException, Exception { diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/client/registration/ClientRegistrationAPIControllerTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/client/registration/ClientRegistrationAPIControllerTests.java index 8a7048fea..0fd8c5893 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/client/registration/ClientRegistrationAPIControllerTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/client/registration/ClientRegistrationAPIControllerTests.java @@ -15,7 +15,9 @@ */ package it.infn.mw.iam.test.client.registration; +import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.CoreMatchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -27,6 +29,7 @@ import org.junit.Before; import org.junit.jupiter.api.Test; import org.junit.runner.RunWith; +import org.mitre.oauth2.model.ClientDetailsEntity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; @@ -39,6 +42,7 @@ 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 it.infn.mw.iam.persistence.repository.client.IamClientRepository; import it.infn.mw.iam.test.util.WithAnonymousUser; import it.infn.mw.iam.test.util.annotation.IamMockMvcIntegrationTest; import it.infn.mw.iam.test.util.oauth.MockOAuth2Filter; @@ -57,6 +61,9 @@ class ClientRegistrationAPIControllerTests { @Autowired private ObjectMapper mapper; + @Autowired + private IamClientRepository clientRepository; + public static final String IAM_CLIENT_REGISTRATION_API_URL = "/iam/api/client-registration/"; public static final ResultMatcher UNAUTHORIZED = status().isUnauthorized(); @@ -73,6 +80,47 @@ public void cleanupOAuthUser() { mockOAuth2Filter.cleanupSecurityContext(); } + @Test + @WithAnonymousUser + public void registerClientWithNullValuesAndCheckDefaultValues() + throws JsonProcessingException, Exception { + + RegisteredClientDTO client = new RegisteredClientDTO(); + client.setClientName("test-client-creation"); + client.setGrantTypes(Sets.newHashSet(AuthorizationGrantType.CLIENT_CREDENTIALS)); + client.setScope(Sets.newHashSet("test")); + client.setAccessTokenValiditySeconds(null); + client.setRefreshTokenValiditySeconds(null); + client.setTokenEndpointAuthMethod(null); + + RegisteredClientDTO response = mapper.readValue(mvc + .perform(post(IAM_CLIENT_REGISTRATION_API_URL).contentType(APPLICATION_JSON) + .content(mapper.writeValueAsString(client))) + .andExpect(CREATED) + .andExpect(jsonPath("$.client_id").exists()) + .andExpect(jsonPath("$.client_secret").exists()) + .andExpect(jsonPath("$.client_name", is("test-client-creation"))) + .andExpect(jsonPath("$.scope", is("test"))) + .andExpect(jsonPath("$.grant_types").isArray()) + .andExpect(jsonPath("$.grant_types", hasItem("client_credentials"))) + .andExpect(jsonPath("$.token_endpoint_auth_method", is("client_secret_basic"))) + .andExpect(jsonPath("$.dynamically_registered", is(true))) + .andExpect(jsonPath("$.registration_access_token").exists()) + .andExpect(jsonPath("$.allow_introspection").doesNotExist()) + .andExpect(jsonPath("$.access_token_validity_seconds").doesNotExist()) + .andExpect(jsonPath("$.refresh_token_validity_seconds").doesNotExist()) + .andExpect(jsonPath("$.id_token_validity_seconds").doesNotExist()) + .andExpect(jsonPath("$.device_code_validity_seconds").doesNotExist()) + .andReturn() + .getResponse() + .getContentAsString(), RegisteredClientDTO.class); + + ClientDetailsEntity createdClient = clientRepository.findByClientId(response.getClientId()).get(); + + assertEquals(3600, createdClient.getAccessTokenValiditySeconds()); + assertEquals(2592000, createdClient.getRefreshTokenValiditySeconds()); + } + @Test @WithAnonymousUser public void registerClientRaiseParseException() throws JsonProcessingException, Exception { @@ -123,7 +171,8 @@ public void registerClientRaiseJwkUriValidationException() @Test @WithAnonymousUser - public void registerClientPrivateJwtValidationException() throws JsonProcessingException, Exception { + public void registerClientPrivateJwtValidationException() + throws JsonProcessingException, Exception { final String URI_STRING = "http://localhost:8080/jwk"; final String NOT_A_JSON_STRING = "This is not a JSON string"; @@ -134,13 +183,14 @@ public void registerClientPrivateJwtValidationException() throws JsonProcessingE client.setScope(Sets.newHashSet("test")); client.setTokenEndpointAuthMethod(TokenEndpointAuthenticationMethod.private_key_jwt); - String expectedMessage = "registerClient.request: private_key_jwt requires a jwks uri or a jwk value"; + String expectedMessage = + "registerClient.request: private_key_jwt requires a jwks uri or a jwk value"; mvc - .perform(post(IAM_CLIENT_REGISTRATION_API_URL).contentType(APPLICATION_JSON) - .content(mapper.writeValueAsString(client))) - .andExpect(BAD_REQUEST) - .andExpect(jsonPath("$.error", is(expectedMessage))); + .perform(post(IAM_CLIENT_REGISTRATION_API_URL).contentType(APPLICATION_JSON) + .content(mapper.writeValueAsString(client))) + .andExpect(BAD_REQUEST) + .andExpect(jsonPath("$.error", is(expectedMessage))); client.setJwk(NOT_A_JSON_STRING); client.setJwksUri(URI_STRING); @@ -158,7 +208,8 @@ public void registerClientPrivateJwtValidationException() throws JsonProcessingE @Test @WithAnonymousUser - public void updateClientPrivateJwtValidationException() throws JsonProcessingException, Exception { + public void updateClientPrivateJwtValidationException() + throws JsonProcessingException, Exception { RegisteredClientDTO client = new RegisteredClientDTO(); client.setClientName("test-client-creation"); @@ -166,19 +217,21 @@ public void updateClientPrivateJwtValidationException() throws JsonProcessingExc client.setScope(Sets.newHashSet("test")); mvc - .perform(post(IAM_CLIENT_REGISTRATION_API_URL).contentType(APPLICATION_JSON) - .content(mapper.writeValueAsString(client))) - .andExpect(CREATED); + .perform(post(IAM_CLIENT_REGISTRATION_API_URL).contentType(APPLICATION_JSON) + .content(mapper.writeValueAsString(client))) + .andExpect(CREATED); client.setTokenEndpointAuthMethod(TokenEndpointAuthenticationMethod.private_key_jwt); - String expectedMessage = "updateClient.request: private_key_jwt requires a jwks uri or a jwk value"; + String expectedMessage = + "updateClient.request: private_key_jwt requires a jwks uri or a jwk value"; mvc - .perform(put(IAM_CLIENT_REGISTRATION_API_URL + client.getClientId()).contentType(APPLICATION_JSON) - .content(mapper.writeValueAsString(client))) - .andExpect(BAD_REQUEST) - .andExpect(jsonPath("$.error", is(expectedMessage))); + .perform( + put(IAM_CLIENT_REGISTRATION_API_URL + client.getClientId()).contentType(APPLICATION_JSON) + .content(mapper.writeValueAsString(client))) + .andExpect(BAD_REQUEST) + .andExpect(jsonPath("$.error", is(expectedMessage))); } diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/oauth/TokenEndpointClientAuthenticationTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/oauth/TokenEndpointClientAuthenticationTests.java index 9b699b130..db94e4c5e 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/oauth/TokenEndpointClientAuthenticationTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/oauth/TokenEndpointClientAuthenticationTests.java @@ -129,6 +129,21 @@ public void testTokenEndpointBasicClientAuthentication() throws Exception { // @formatter:on } + @Test + public void testTokenEndpointPublicClientAuthentication() throws Exception { + + String clientId = "public-client"; + + // @formatter:off + mvc.perform(post(TOKEN_ENDPOINT) + .param("grant_type", GRANT_TYPE) + .param("client_id", clientId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.scope", containsString("profile"))) + .andExpect(jsonPath("$.scope", containsString("email"))); + // @formatter:on + } + @Test public void testTokenEndpointOptionsMethodAllowed() throws Exception { mvc.perform(options(TOKEN_ENDPOINT)).andExpect(status().isOk()); diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/oauth/client_registration/ClientRegistrationTestSupport.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/oauth/client_registration/ClientRegistrationTestSupport.java index 78d3c3fcd..9fc1de16b 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/oauth/client_registration/ClientRegistrationTestSupport.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/oauth/client_registration/ClientRegistrationTestSupport.java @@ -17,6 +17,7 @@ import static com.google.common.collect.Sets.newHashSet; import static org.mitre.oauth2.model.RegisteredClientFields.CLAIMS_REDIRECT_URIS; +import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_ID; import static org.mitre.oauth2.model.RegisteredClientFields.CLIENT_NAME; import static org.mitre.oauth2.model.RegisteredClientFields.CONTACTS; import static org.mitre.oauth2.model.RegisteredClientFields.GRANT_TYPES; @@ -45,13 +46,14 @@ public static class ClientJsonStringBuilder { static final Joiner JOINER = Joiner.on(RegisteredClientFields.SCOPE_SEPARATOR); + String clientId = null; String name = "test_client"; Set redirectUris = Sets.newHashSet("http://localhost:9090"); Set grantTypes = Sets.newHashSet("client_credentials"); Set scopes = Sets.newHashSet(); Set responseTypes = Sets.newHashSet(); - Integer accessTokenValiditySeconds; - Integer refreshTokenValiditySeconds; + Integer accessTokenValiditySeconds = null; + Integer refreshTokenValiditySeconds = null; private ClientJsonStringBuilder() {} @@ -59,6 +61,11 @@ public static ClientJsonStringBuilder builder() { return new ClientJsonStringBuilder(); } + public ClientJsonStringBuilder clientId(String clientId) { + this.clientId = clientId; + return this; + } + public ClientJsonStringBuilder name(String name) { this.name = name; return this; @@ -97,6 +104,7 @@ public ClientJsonStringBuilder refreshTokenValiditySeconds( public String build() { JsonObject json = new JsonObject(); + json.addProperty(CLIENT_ID, clientId); json.addProperty(CLIENT_NAME, name); json.addProperty(SCOPE, JOINER.join(scopes)); json.add(REDIRECT_URIS, getAsArray(redirectUris)); @@ -110,8 +118,6 @@ public String build() { return json.toString(); } - - } protected String setToString(Set scopes) { diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/service/client/ClientManagementServiceTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/service/client/ClientManagementServiceTests.java index 6b29737f8..44b8cb335 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/service/client/ClientManagementServiceTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/service/client/ClientManagementServiceTests.java @@ -101,7 +101,7 @@ public void testPagedClientLookup() { ListResponseDTO clients = managementService.retrieveAllClients(pageable); - assertThat(clients.getTotalResults(), is(18L)); + assertThat(clients.getTotalResults(), is(19L)); assertThat(clients.getItemsPerPage(), is(10)); assertThat(clients.getStartIndex(), is(1)); assertThat(clients.getResources().get(0).getClientId(), is("admin-client-ro")); diff --git a/iam-persistence/src/main/resources/db/migration/test/V100000___test_data.sql b/iam-persistence/src/main/resources/db/migration/test/V100000___test_data.sql index db5561968..aa9e5523f 100644 --- a/iam-persistence/src/main/resources/db/migration/test/V100000___test_data.sql +++ b/iam-persistence/src/main/resources/db/migration/test/V100000___test_data.sql @@ -12,7 +12,7 @@ INSERT INTO client_details (id, client_id, client_secret, client_name, dynamical (9, 'token-exchange-subject', 'secret', 'Token Exchange grant client subject', false, null, 3600, 600, true, 'SECRET_POST',false, CURRENT_TIMESTAMP()), (10, 'registration-client', 'secret', 'Registration service test client', false, null, 3600, 600, true, 'SECRET_POST',false, CURRENT_TIMESTAMP()), (11, 'token-lookup-client', 'secret', 'Token lookup client', false, null, 3600, 600, true, 'SECRET_BASIC', false, CURRENT_TIMESTAMP()); - + INSERT INTO client_details (id, client_id, client_secret, client_name, dynamically_registered, refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection, token_endpoint_auth_method, require_auth_time, device_code_validity_seconds, created_at) VALUES @@ -38,8 +38,9 @@ INSERT INTO client_details (id, client_id, client_secret, client_name, dynamical refresh_token_validity_seconds, access_token_validity_seconds, id_token_validity_seconds, allow_introspection, token_endpoint_auth_method, require_auth_time, created_at) VALUES (17, 'admin-client-ro', 'secret', 'Admin client (read-only)', false, null, 3600, 600, true, 'SECRET_POST',false, CURRENT_TIMESTAMP()), -(18, 'admin-client-rw', 'secret', 'Admin client (read-write)', false, null, 3600, 600, true, 'SECRET_POST',false, CURRENT_TIMESTAMP()); - +(18, 'admin-client-rw', 'secret', 'Admin client (read-write)', false, null, 3600, 600, true, 'SECRET_POST',false, CURRENT_TIMESTAMP()), +(19, 'public-client', null, 'Public client', false, 3600, 3600, 600, true, 'NONE', false, CURRENT_TIMESTAMP()); + INSERT INTO client_scope (owner_id, scope) VALUES (1, 'openid'), (1, 'profile'), @@ -146,7 +147,9 @@ INSERT INTO client_scope (owner_id, scope) VALUES (14, 'phone'), (17, 'iam:admin.read'), (18, 'iam:admin.read'), - (18, 'iam:admin.write'); + (18, 'iam:admin.write'), + (19, 'profile'), + (19, 'email'); INSERT INTO client_redirect_uri (owner_id, redirect_uri) VALUES @@ -194,7 +197,8 @@ INSERT INTO client_grant_type (owner_id, grant_type) VALUES (17, 'authorization_code'), (18, 'client_credentials'), (18, 'urn:ietf:params:oauth:grant-type:device_code'), - (18, 'authorization_code'); + (18, 'authorization_code'), + (19, 'client_credentials'); INSERT INTO iam_user_info(ID,GIVENNAME,FAMILYNAME, EMAIL, EMAILVERIFIED, BIRTHDATE, GENDER) VALUES (2, 'Test', 'User', 'test@iam.test', true, '1950-01-01','M'), diff --git a/pom.xml b/pom.xml index 2ee23ab42..74b68b494 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ 1.16.2 - 1.3.6.cnaf-20231113 + 1.3.6.cnaf-20231129 2.5.2.RELEASE 3.3.2