Skip to content

Commit

Permalink
ES-1885 added additionalConfig field to client-detail (#995)
Browse files Browse the repository at this point in the history
* added additionalConfig column to client-detail

Signed-off-by: Sachin Rana <[email protected]>

* updated according to review comments

Signed-off-by: Sachin Rana <[email protected]>

* worked on code quality

Signed-off-by: Sachin Rana <[email protected]>

---------

Signed-off-by: Sachin Rana <[email protected]>
  • Loading branch information
sacrana0 authored Nov 22, 2024
1 parent 75fede5 commit 1d89a79
Show file tree
Hide file tree
Showing 12 changed files with 293 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@
*/
package io.mosip.esignet.entity;

import io.mosip.esignet.core.util.ClientAdditionalConfigConverter;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import java.time.LocalDateTime;
import java.util.Map;

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

Expand Down Expand Up @@ -73,4 +76,8 @@ public class ClientDetail {

@Column(name = "upd_dtimes")
private LocalDateTime updatedtimes;

@Convert(converter = ClientAdditionalConfigConverter.class)
@Column(name = "additional_config", columnDefinition = "jsonb")
private Map<String, Object> additionalConfig;
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,23 @@ private ClientDetailResponse getClientDetailResponse(ClientDetail clientDetail)
return response;
}

private ClientDetailResponseV2 getClientDetailResponseV2(ClientDetail clientDetail) {
ClientDetailResponseV2 response = new ClientDetailResponseV2();
response.setClientId(clientDetail.getId());
response.setName(clientDetail.getName());
response.setRpId(clientDetail.getRpId());
response.setLogoUri(clientDetail.getLogoUri());
response.setRedirectUris(clientDetail.getRedirectUris());
response.setPublicKey(clientDetail.getPublicKey());
response.setClaims(clientDetail.getClaims());
response.setAcrValues(clientDetail.getAcrValues());
response.setStatus(clientDetail.getStatus());
response.setGrantTypes(clientDetail.getGrantTypes());
response.setClientAuthMethods(clientDetail.getClientAuthMethods());
response.setAdditionalConfig(clientDetail.getAdditionalConfig());
return response;
}

private String getClientNameLanguageMapAsJsonString(Map<String, String> clientNameMap, String clientName) {
clientNameMap.put(Constants.NONE_LANG_KEY, clientName);
JSONObject clientNameObject = new JSONObject(clientNameMap);
Expand Down Expand Up @@ -185,6 +202,7 @@ public io.mosip.esignet.core.dto.ClientDetail getClientDetails(String clientId)
dto.setLogoUri(result.get().getLogoUri());
dto.setStatus(result.get().getStatus());
dto.setPublicKey(result.get().getPublicKey());
dto.setAdditionalConfig(result.get().getAdditionalConfig());
TypeReference<List<String>> typeReference = new TypeReference<List<String>>() {};
try {
if(result.get().getClaims() != null)
Expand All @@ -200,23 +218,37 @@ public io.mosip.esignet.core.dto.ClientDetail getClientDetails(String clientId)
return dto;
}

@CacheEvict(value = Constants.CLIENT_DETAIL_CACHE, key = "#clientDetailCreateRequestV2.getClientId()")
@Override
public ClientDetailResponse createOAuthClient(ClientDetailCreateRequestV2 clientDetailCreateRequestV2) throws EsignetException {
Optional<ClientDetail> result = clientDetailRepository.findById(clientDetailCreateRequestV2.getClientId());
if (result.isPresent()) {
log.error("Duplicate Client Id : {}", ErrorConstants.DUPLICATE_CLIENT_ID);
throw new EsignetException(ErrorConstants.DUPLICATE_CLIENT_ID);
ClientDetail clientDetail = buildClient(clientDetailCreateRequestV2);
try {
clientDetail = clientDetailRepository.save(clientDetail);
} catch (ConstraintViolationException cve) {
log.error("Failed to create client details", cve);
throw new EsignetException(ErrorConstants.DUPLICATE_PUBLIC_KEY);
}

ClientDetail clientDetail = buildClientDetailEntity(clientDetailCreateRequestV2);
auditWrapper.logAudit(AuditHelper.getClaimValue(SecurityContextHolder.getContext(), claimName),
Action.OAUTH_CLIENT_CREATE, ActionStatus.SUCCESS, AuditHelper.buildAuditDto(clientDetailCreateRequestV2.getClientId()), null);

String clientName = getClientNameLanguageMapAsJsonString(
clientDetailCreateRequestV2.getClientNameLangMap(),
clientDetailCreateRequestV2.getClientName()
);
clientDetail.setName(clientName);
return getClientDetailResponse(clientDetail);
}

@CacheEvict(value = Constants.CLIENT_DETAIL_CACHE, key = "#clientId")
@Override
public ClientDetailResponse updateOAuthClient(String clientId, ClientDetailUpdateRequestV2 clientDetailUpdateRequestV2) throws EsignetException {
ClientDetail clientDetail = buildClient(clientId, clientDetailUpdateRequestV2);
clientDetail = clientDetailRepository.save(clientDetail);
auditWrapper.logAudit(AuditHelper.getClaimValue(SecurityContextHolder.getContext(), claimName),
Action.OAUTH_CLIENT_UPDATE, ActionStatus.SUCCESS, AuditHelper.buildAuditDto(clientId), null);

return getClientDetailResponse(clientDetail);
}

@Override
public ClientDetailResponseV2 createClient(ClientDetailCreateRequestV3 clientDetailCreateRequestV3) throws EsignetException {
ClientDetail clientDetail = buildClient(clientDetailCreateRequestV3);
clientDetail.setAdditionalConfig(clientDetailCreateRequestV3.getAdditionalConfig());
try {
clientDetail = clientDetailRepository.save(clientDetail);
} catch (ConstraintViolationException cve) {
Expand All @@ -225,34 +257,53 @@ public ClientDetailResponse createOAuthClient(ClientDetailCreateRequestV2 client
}

auditWrapper.logAudit(AuditHelper.getClaimValue(SecurityContextHolder.getContext(), claimName),
Action.OAUTH_CLIENT_CREATE, ActionStatus.SUCCESS, AuditHelper.buildAuditDto(clientDetailCreateRequestV2.getClientId()), null);
Action.OAUTH_CLIENT_CREATE, ActionStatus.SUCCESS, AuditHelper.buildAuditDto(clientDetailCreateRequestV3.getClientId()), null);

return getClientDetailResponse(clientDetail);
return getClientDetailResponseV2(clientDetail);
}

@CacheEvict(value = Constants.CLIENT_DETAIL_CACHE, key = "#clientId")
@Override
public ClientDetailResponse updateOAuthClient(String clientId, ClientDetailUpdateRequestV2 clientDetailUpdateRequestV2) throws EsignetException {
public ClientDetailResponseV2 updateClient(String clientId, ClientDetailUpdateRequestV3 clientDetailUpdateRequestV3) throws EsignetException {
ClientDetail clientDetail = buildClient(clientId, clientDetailUpdateRequestV3);
clientDetail.setAdditionalConfig(clientDetailUpdateRequestV3.getAdditionalConfig());
clientDetail = clientDetailRepository.save(clientDetail);

auditWrapper.logAudit(AuditHelper.getClaimValue(SecurityContextHolder.getContext(), claimName),
Action.OAUTH_CLIENT_UPDATE, ActionStatus.SUCCESS, AuditHelper.buildAuditDto(clientId), null);

return getClientDetailResponseV2(clientDetail);
}

public ClientDetail buildClient(ClientDetailCreateRequestV2 clientDetailCreateRequestV2) {
Optional<ClientDetail> result = clientDetailRepository.findById(clientDetailCreateRequestV2.getClientId());
if (result.isPresent()) {
log.error("Duplicate Client Id : {}", ErrorConstants.DUPLICATE_CLIENT_ID);
throw new EsignetException(ErrorConstants.DUPLICATE_CLIENT_ID);
}
ClientDetail clientDetail = buildClientDetailEntity(clientDetailCreateRequestV2);
String clientName = getClientNameLanguageMapAsJsonString(
clientDetailCreateRequestV2.getClientNameLangMap(),
clientDetailCreateRequestV2.getClientName()
);
clientDetail.setName(clientName);
return clientDetail;
}

public ClientDetail buildClient(String clientId, ClientDetailUpdateRequestV2 clientDetailUpdateRequestV2) {
Optional<ClientDetail> result = clientDetailRepository.findById(clientId);
if (!result.isPresent()) {
log.error("Invalid Client Id : {}", ErrorConstants.INVALID_CLIENT_ID);
throw new EsignetException(ErrorConstants.INVALID_CLIENT_ID);
}

ClientDetail clientDetail = buildClientDetailEntity(result.get(), clientDetailUpdateRequestV2);

String clientName = getClientNameLanguageMapAsJsonString(
clientDetailUpdateRequestV2.getClientNameLangMap(),
clientDetailUpdateRequestV2.getClientName()
);
clientDetail.setName(clientName);

clientDetail = clientDetailRepository.save(clientDetail);

auditWrapper.logAudit(AuditHelper.getClaimValue(SecurityContextHolder.getContext(), claimName),
Action.OAUTH_CLIENT_UPDATE, ActionStatus.SUCCESS, AuditHelper.buildAuditDto(clientId), null);

return getClientDetailResponse(clientDetail);
return clientDetail;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,46 @@ public void createClientV2_withExistingClientId_thenFail() {
}
}

@Test
public void createClientV3_withValidDetail_thenPass() {
ClientDetailCreateRequestV3 clientCreateV3ReqDto = new ClientDetailCreateRequestV3();
Map<String, String> clientnameLangMap = new HashMap<>();
clientnameLangMap.put("eng", "client_name_v1");
clientCreateV3ReqDto.setClientId("mock_id_v1");
clientCreateV3ReqDto.setClientName("client_name_v1");
clientCreateV3ReqDto.setClientNameLangMap(clientnameLangMap);
clientCreateV3ReqDto.setLogoUri("http://service.com/logo.png");
clientCreateV3ReqDto.setPublicKey(PUBLIC_KEY);
clientCreateV3ReqDto.setRedirectUris(Arrays.asList("http://service.com/home"));
clientCreateV3ReqDto.setUserClaims(Arrays.asList("given_name"));
clientCreateV3ReqDto.setAuthContextRefs(Arrays.asList("mosip:idp:acr:static-code"));
clientCreateV3ReqDto.setRelyingPartyId("RELYING_PARTY_ID");
clientCreateV3ReqDto.setGrantTypes(Arrays.asList("authorization_code"));
clientCreateV3ReqDto.setClientAuthMethods(Arrays.asList("private_key_jwt"));
clientCreateV3ReqDto.setAdditionalConfig(new HashMap<>());

ClientDetail entity = new ClientDetail();
entity.setId("mock_id_v1");
entity.setStatus("active");
Mockito.when(clientDetailRepository.save(Mockito.any(ClientDetail.class))).thenReturn(entity);
ClientDetailResponseV2 clientDetailResponseV2 = clientManagementService.createClient(clientCreateV3ReqDto);
Assert.assertNotNull(clientDetailResponseV2);
Assert.assertTrue(clientDetailResponseV2.getClientId().equals("mock_id_v1"));
Assert.assertTrue(clientDetailResponseV2.getStatus().equals("active"));
}

@Test
public void createClientV3_withExistingClientId_thenFail() {
Mockito.when(clientDetailRepository.findById("client_id_v1")).thenReturn(Optional.of(new ClientDetail()));
ClientDetailCreateRequestV3 clientCreateV3ReqDto = new ClientDetailCreateRequestV3();
clientCreateV3ReqDto.setClientId("client_id_v1");
try {
clientManagementService.createClient(clientCreateV3ReqDto);
} catch (EsignetException ex) {
Assert.assertEquals(ex.getErrorCode(), ErrorConstants.DUPLICATE_CLIENT_ID);
}
}

@Test
public void updateClient_withNonExistingClientId_thenFail() {
Mockito.when(clientDetailRepository.findById("client_id_v1")).thenReturn(Optional.empty());
Expand Down Expand Up @@ -219,6 +259,50 @@ public void updateClientV2_withValidClientId_thenPass() throws EsignetException
Assert.assertTrue(clientDetailResponse.getStatus().equals("inactive"));
}

@Test
public void updateClientV3_withValidClientId_thenPass() throws EsignetException {
ClientDetail clientDetail = new ClientDetail();
clientDetail.setName("client_id_v1");
clientDetail.setId("client_id_v1");
clientDetail.setLogoUri("http://service.com/logo.png");
clientDetail.setClaims("[\"given_name\", \"birthdate\"]");
clientDetail.setAcrValues("[\"mosip:idp:acr:static-code\"]");
clientDetail.setClientAuthMethods("[\"private_key_jwt\"]");
clientDetail.setGrantTypes("[\"authorization_code\"]");
clientDetail.setRedirectUris("[\"https://service.com/home\",\"https://service.com/dashboard\", \"v1/idp\"]");
Mockito.when(clientDetailRepository.findById("client_id_v1")).thenReturn(Optional.of(clientDetail));

ClientDetailUpdateRequestV3 updateV3Request = new ClientDetailUpdateRequestV3();
updateV3Request.setClientNameLangMap(new HashMap<>());
updateV3Request.setClientName("client_name_v1");
updateV3Request.setLogoUri("http://service.com/logo.png");
updateV3Request.setRedirectUris(Arrays.asList("http://service.com/home"));
updateV3Request.setUserClaims(Arrays.asList("given_name"));
updateV3Request.setAuthContextRefs(Arrays.asList("mosip:idp:acr:static-code"));
updateV3Request.setGrantTypes(Arrays.asList("authorization_code"));
updateV3Request.setClientAuthMethods(Arrays.asList("private_key_jwt"));
updateV3Request.setAdditionalConfig(new HashMap<>());

ClientDetail entity = new ClientDetail();
entity.setId("client_id_v1");
entity.setStatus("inactive");
Mockito.when(clientDetailRepository.save(Mockito.any(ClientDetail.class))).thenReturn(entity);
ClientDetailResponseV2 clientDetailResponseV2 = clientManagementService.updateClient("client_id_v1", updateV3Request);
Assert.assertNotNull(clientDetailResponseV2);
Assert.assertTrue(clientDetailResponseV2.getClientId().equals("client_id_v1"));
Assert.assertTrue(clientDetailResponseV2.getStatus().equals("inactive"));
}

@Test
public void updateClientV3_withNonExistingClientId_thenFail() {
Mockito.when(clientDetailRepository.findById("client_id_v1")).thenReturn(Optional.empty());
try {
clientManagementService.updateClient("client_id_v1", null);
} catch (EsignetException ex) {
Assert.assertEquals(ex.getErrorCode(), ErrorConstants.INVALID_CLIENT_ID);
}
}

@Test
public void getClient_withValidClientId_thenPass() throws EsignetException {
ClientDetail clientDetail = new ClientDetail();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ CREATE TABLE IF NOT EXISTS client_detail(
grant_types character varying NOT NULL,
auth_methods character varying NOT NULL,
status character varying(20) NOT NULL,
additional_config character varying,
cr_dtimes timestamp NOT NULL,
upd_dtimes timestamp,
CONSTRAINT pk_client_detail PRIMARY KEY (id)
Expand Down
1 change: 1 addition & 0 deletions db_scripts/mosip_esignet/ddl/esignet-client_detail.sql
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ CREATE TABLE client_detail(
grant_types character varying NOT NULL,
auth_methods character varying NOT NULL,
status character varying(20) NOT NULL,
additional_config jsonb,
cr_dtimes timestamp NOT NULL,
upd_dtimes timestamp,
CONSTRAINT pk_clntdtl_id PRIMARY KEY (id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ public class ClientDetail implements Serializable {
private String status;
private List<String> grantTypes;
private List<String> clientAuthMethods;
private Map<String, Object> additionalConfig;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.mosip.esignet.core.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;
import java.util.Map;

@Data
@NoArgsConstructor
public class ClientDetailCreateRequestV3 extends ClientDetailCreateRequestV2 {
private Map<String, Object> additionalConfig;

public ClientDetailCreateRequestV3(String clientId, String clientName, Map<String, Object> publicKey, String relyingPartyId,
List<String> userClaims, List<String> authContextRefs, String logoUri,
List<String> redirectUris, List<String> grantTypes, List<String> clientAuthMethods,
Map<String, String> clientNameLangMap, Map<String, Object> additionalConfig) {
super(clientId, clientName, publicKey, relyingPartyId, userClaims, authContextRefs, logoUri, redirectUris,
grantTypes, clientAuthMethods, clientNameLangMap);
this.additionalConfig = additionalConfig;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.mosip.esignet.core.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Map;

@Data
public class ClientDetailResponseV2 extends ClientDetailResponse {
private String name;
private String rpId;
private String logoUri;
private String redirectUris;
private String publicKey;
private String claims;
private String acrValues;
private String grantTypes;
private String clientAuthMethods;
private Map<String, Object> additionalConfig;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.mosip.esignet.core.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;
import java.util.Map;

@Data
@NoArgsConstructor
public class ClientDetailUpdateRequestV3 extends ClientDetailUpdateRequestV2 {
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,29 @@

public interface ClientManagementService {

/**
* API to register relying party client version 3
*
* In this version there is a provision to provide additional configuration
* information for the client as a map
* @param clientDetailCreateRequestV3
* @return
* @throws EsignetException
*/
ClientDetailResponseV2 createClient(ClientDetailCreateRequestV3 clientDetailCreateRequestV3) throws EsignetException;

/**
* API to update registered relying party client version 3
*
* In this version there is a provision to provide additional configuration
* information for the client as a map
* @param clientId
* @param clientDetailUpdateRequestV3
* @return
* @throws EsignetException
*/
ClientDetailResponseV2 updateClient(String clientId, ClientDetailUpdateRequestV3 clientDetailUpdateRequestV3) throws EsignetException;

/**
* API to register relying party client
* @param clientDetailCreateRequest
Expand Down
Loading

0 comments on commit 1d89a79

Please sign in to comment.