Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ES-1887] Added additionalConfig validator and new client-mgmt apis #1016

Merged
merged 8 commits into from
Dec 21, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

public class ErrorConstants {

public static final String INVALID_ADDITIONAL_CONFIG="invalid_additional_config";
public static final String INVALID_REQUEST="invalid_request";
public static final String INVALID_CLIENT_ID="invalid_client_id";
public static final String INVALID_CLIENT_NAME="invalid_client_name";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.mosip.esignet.core.dto;

import lombok.AllArgsConstructor;
import io.mosip.esignet.core.validator.ClientAdditionalConfig;
import lombok.Data;
import lombok.NoArgsConstructor;

Expand All @@ -10,6 +10,8 @@
@Data
@NoArgsConstructor
public class ClientDetailCreateRequestV3 extends ClientDetailCreateRequestV2 {

@ClientAdditionalConfig
private Map<String, Object> additionalConfig;

public ClientDetailCreateRequestV3(String clientId, String clientName, Map<String, Object> publicKey, String relyingPartyId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.mosip.esignet.core.dto;

import lombok.AllArgsConstructor;
import io.mosip.esignet.core.validator.ClientAdditionalConfig;
import lombok.Data;
import lombok.NoArgsConstructor;

Expand All @@ -10,10 +10,12 @@
@Data
@NoArgsConstructor
public class ClientDetailUpdateRequestV3 extends ClientDetailUpdateRequestV2 {

@ClientAdditionalConfig
private Map<String, Object> additionalConfig;

public ClientDetailUpdateRequestV3(String logUri, List<String> redirectUris, List<String> userClaims, List<String> authContextRefs, String status, List<String> grantTypes, String clientName, List<String> clientAuthMethods, Map<String,String> clientNameLangMap, Map<String, Object> additionalConfig){
super(logUri,redirectUris,userClaims,authContextRefs,status,grantTypes,clientName,clientAuthMethods, clientNameLangMap);
this.additionalConfig=additionalConfig;
public ClientDetailUpdateRequestV3(String logUri, List<String> redirectUris, List<String> userClaims, List<String> authContextRefs, String status, List<String> grantTypes, String clientName, List<String> clientAuthMethods, Map<String, String> clientNameLangMap, Map<String, Object> additionalConfig) {
super(logUri, redirectUris, userClaims, authContextRefs, status, grantTypes, clientName, clientAuthMethods, clientNameLangMap);
this.additionalConfig = additionalConfig;
}
}
sacrana0 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package io.mosip.esignet.core.util;

import java.util.*;

public class ClientAdditionalConfigValidatorTestData {

public static Map<String, Object> getValidAdditionalConfig() {
Map<String, Object> validAdditionalConfig = new HashMap<>();
validAdditionalConfig.put("userinfo_response_type", "JWS");
validAdditionalConfig.put("purpose", Map.ofEntries(
Map.entry("type", ""),
Map.entry("title", ""),
Map.entry("subTitle", "")
));
validAdditionalConfig.put("signup_banner_required", true);
validAdditionalConfig.put("forgot_pwd_link_required", true);
validAdditionalConfig.put("consent_expire_in_days", 1);
return validAdditionalConfig;
}

public static List<Map<String, Object>> getInvalidAdditionalConfigs() {
List<Map<String, Object>> invalidAdditionalConfigs = new ArrayList<>();

invalidAdditionalConfigs.add(null);

Map<String, Object> additionalConfig = getValidAdditionalConfig();
additionalConfig.put("userinfo_response_type", "ABC");
invalidAdditionalConfigs.add(additionalConfig);

additionalConfig = getValidAdditionalConfig();
additionalConfig.put("purpose", Collections.emptyMap());
invalidAdditionalConfigs.add(additionalConfig);

additionalConfig = getValidAdditionalConfig();
additionalConfig.put("purpose", Map.ofEntries(
Map.entry("type", ""),
Map.entry("title", 1), //anything other than string
Map.entry("subTitle", "")
));
invalidAdditionalConfigs.add(additionalConfig);

additionalConfig = getValidAdditionalConfig();
additionalConfig.put("signup_banner_required", 1); // anything other than boolean
invalidAdditionalConfigs.add(additionalConfig);

additionalConfig = getValidAdditionalConfig();
additionalConfig.put("forgot_pwd_link_required", 1); // anything other than boolean
invalidAdditionalConfigs.add(additionalConfig);

additionalConfig = getValidAdditionalConfig();
additionalConfig.put("consent_expire_in_days", ""); // anything other than number
invalidAdditionalConfigs.add(additionalConfig);

return invalidAdditionalConfigs;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.mosip.esignet.core.validator;

import io.mosip.esignet.core.constants.ErrorConstants;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target({FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = ClientAdditionalConfigValidator.class)
@Documented
public @interface ClientAdditionalConfig {
String message() default ErrorConstants.INVALID_ADDITIONAL_CONFIG;

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};
}
ase-101 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package io.mosip.esignet.core.validator;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.JsonSchema;
import com.networknt.schema.JsonSchemaFactory;
import com.networknt.schema.SpecVersion;
import com.networknt.schema.ValidationMessage;
import io.mosip.esignet.core.exception.EsignetException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Set;

@Slf4j
public class ClientAdditionalConfigValidator implements
ase-101 marked this conversation as resolved.
Show resolved Hide resolved
ConstraintValidator<ClientAdditionalConfig, Map<String, Object>> {

private String schemaUrl = "classpath:additional_config_request_schema.json";

private volatile JsonSchema cachedSchema;

private ObjectMapper objectMapper = new ObjectMapper();

private ResourceLoader resourceLoader = new DefaultResourceLoader();

@Override
public void initialize(ClientAdditionalConfig constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
}

@Override
public boolean isValid(Map<String, Object> additionalConfig, ConstraintValidatorContext context) {
if (additionalConfig == null) {
return false;
}
Set<ValidationMessage> errors = null;
try {
JsonNode jsonNode = objectMapper.valueToTree(additionalConfig);
errors = getCachedSchema().validate(jsonNode);
if (errors.isEmpty()) return true;
} catch (Exception e) {
log.error("Error validating additional_config schema: ", e);
}
log.error("Validation failed for additional_config ---> {}", errors);
return false;
}

private JsonSchema getCachedSchema() throws EsignetException {
if(cachedSchema!=null ) return cachedSchema;
synchronized (this) {
if (cachedSchema == null) {
InputStream schemaResponse = getResource(schemaUrl);
JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012);
cachedSchema = jsonSchemaFactory.getSchema(schemaResponse);
}
}
return cachedSchema;
}

private InputStream getResource(String url) {
try {
Resource resource = resourceLoader.getResource(url);
return resource.getInputStream();
} catch (IOException e) {
log.error("Failed to parse data: {}", url, e);
}
throw new EsignetException("invalid_configuration");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.mosip.esignet.core;

import io.mosip.esignet.core.exception.InvalidClientException;
import io.mosip.esignet.core.util.ClientAdditionalConfigConverter;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class ClientAdditionalConfigConverterTest {

private ClientAdditionalConfigConverter converter;

@BeforeEach
void setUp() {
converter = new ClientAdditionalConfigConverter();
}

@Test
public void convertToDatabaseColumn_NullMap_ReturnsNull() {
Assertions.assertNull(converter.convertToDatabaseColumn(null));
}

@Test
public void convertToEntityAttribute_NullJson_ReturnsNull() {
Assertions.assertNull(converter.convertToEntityAttribute(null));
}

@Test
public void convertToEntityAttribute_InvalidJson_ThrowsException() {
String invalidJson = "{\"invalid: value}";
Assertions.assertThrows(InvalidClientException.class, () ->
converter.convertToEntityAttribute(invalidJson)
);
}

}
Loading
Loading