Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
51 changes: 51 additions & 0 deletions mock-db-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Mock DB plugin

## About

Implementation for all the interfaces defined in esignet-integration-api. Mock DB plugin is built to use eSignet with any database

This library should be added as a runtime dependency to [esignet-service](https://github.com/mosip/esignet) for development purpose only.

**Note**: This is not production use implementation.

## Configurations

Refer [application.properties](src/main/resources/application.properties) for all the configurations required to use this plugin implementation. All the properties
are set with default values. If required values can be overridden in the host application by setting them as environment variable. Refer [esignet-service](https://github.com/mosip/esignet)
docker-compose file to see how the configuration property values can be changed.

Add "bindingtransaction" cache name in "mosip.esignet.cache.names" property.

## Databases
You have to create a new database, table and some user details entries as well.

```
-- Step 1: Create Database
CREATE DATABASE IF NOT EXISTS mock_db;

-- Step 2: Create User and Grant Privileges
CREATE USER IF NOT EXISTS 'mock_user'@'localhost' IDENTIFIED BY 'SecureP@ss123';
GRANT ALL PRIVILEGES ON mock_db.* TO 'mock_user'@'localhost';
FLUSH PRIVILEGES;

-- Step 3: Use the Database
USE mock_db;

-- Step 4: Create Table user_detail
CREATE TABLE IF NOT EXISTS user_detail (
id VARCHAR(12) PRIMARY KEY,
name VARCHAR(100) NOT NULL,
dob DATE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL
);

-- Step 5: Insert Sample Data
INSERT INTO user_detail (id, name, dob, email) VALUES
('3453434553', 'Alice Johnson', '1990-05-14', 'alice@example.com'),
('2583148061', 'Bob Smith', '1985-09-23', 'bob@example.com'),
('9834544352', 'Charlie Brown', '1992-07-11', 'charlie@example.com'),
('5236574533', 'Diana Ross', '1988-12-30', 'diana@example.com');
```

## License
This project is licensed under the terms of [Mozilla Public License 2.0](LICENSE).
79 changes: 79 additions & 0 deletions mock-db-plugin/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.mock.esignet.plugin</groupId>
<artifactId>mock-db-plugin</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>io.mosip.esignet</groupId>
<artifactId>esignet-integration-api</artifactId>
<version>1.5.1</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>io.mosip.esignet</groupId>
<artifactId>esignet-core</artifactId>
<version>1.5.1</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>9</source>
<target>9</target>
</configuration>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.mock.esignet.plugin.config;

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

@Configuration
public class MockDBConfig {
@Value("${org.mock.esignet.plugin.db-url}")
private String dbURL;

@Value("${org.mock.esignet.plugin.db-username}")
private String dbUsername;

@Value("${org.mock.esignet.plugin.db-password}")
private String dbPassword;

public DataSource dataSource() {
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setJdbcUrl(dbURL);
hikariDataSource.setUsername(dbUsername);
hikariDataSource.setPassword(dbPassword);
hikariDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
return hikariDataSource;
}

@Bean("mockPluginJdbcTemplate")
public JdbcTemplate mockPluginJdbcTemplate() {
return new JdbcTemplate(dataSource());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.mock.esignet.plugin.dto;

import lombok.AllArgsConstructor;
import lombok.Data;

@AllArgsConstructor
@Data
public class UserDetail {
private String id;
private String name;
private String dob;
private String email;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.mock.esignet.plugin.repositories;

import org.mock.esignet.plugin.dto.UserDetail;
import org.springframework.stereotype.Repository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
@Repository
public class UserDetailRepository {
private static final String QUERY = "select * from user_detail where id=?";

@Autowired
@Qualifier("mockPluginJdbcTemplate")
private JdbcTemplate mockPluginJdbcTemplate;

public UserDetail findUserById(String individualId) {
List<UserDetail> list = mockPluginJdbcTemplate.query(QUERY, new Object[]{individualId}, new RowMapper<UserDetail>() {
@Override
public UserDetail mapRow(ResultSet resultSet, int i) throws SQLException {
return new UserDetail(resultSet.getString(1),resultSet.getString(2), resultSet.getString(3), resultSet.getString(4));
}
});

if(!list.isEmpty()) {
return list.get(0);
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package org.mock.esignet.plugin.service;

import io.mosip.esignet.api.dto.*;
import io.mosip.esignet.api.exception.KycAuthException;
import io.mosip.esignet.api.exception.KycExchangeException;
import io.mosip.esignet.api.exception.KycSigningCertificateException;
import io.mosip.esignet.api.exception.SendOtpException;
import io.mosip.esignet.api.spi.Authenticator;

import java.util.List;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.mock.esignet.plugin.dto.UserDetail;
import org.mock.esignet.plugin.repositories.UserDetailRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.security.auth.x500.X500Principal;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.time.ZoneOffset;
import java.util.*;

@Component
public class MockDBAuthenticatorImpl implements Authenticator {

private Map<String, String> localMap = new HashMap<>();

@Autowired
private UserDetailRepository userDetailRepository;
private X509Certificate keyCertificate;
private KeyPair localKey;

@Override
public KycAuthResult doKycAuth(String relyingPartyId, String clientId, KycAuthDto kycAuthDto) throws KycAuthException {
UserDetail userDetail = userDetailRepository.findUserById(kycAuthDto.getIndividualId());

if (userDetail == null)
throw new KycAuthException("user_not_found");

boolean authStatus = false;
AuthChallenge authChallenge = kycAuthDto.getChallengeList().get(0);
switch (authChallenge.getAuthFactorType()) {
case "OTP":
authStatus = authChallenge.getChallenge().equals("111111");
break;
default:
throw new KycAuthException("invalid_auth_factor");
}

if(!authStatus)
throw new KycAuthException("auth_failed");

String token = UUID.randomUUID().toString();
localMap.put(token, kycAuthDto.getIndividualId());
KycAuthResult kycAuthResult = new KycAuthResult();
kycAuthResult.setKycToken(token);
kycAuthResult.setPartnerSpecificUserToken(token);
return kycAuthResult;
}

@Override
public KycExchangeResult doKycExchange(String relyingPartyId, String clientId, KycExchangeDto kycExchangeDto) throws KycExchangeException {
String storedKycToken = localMap.get(kycExchangeDto.getKycToken());
if (storedKycToken == null)
throw new KycExchangeException("invalid_kyc_token");

UserDetail userDetail = userDetailRepository.findUserById(storedKycToken);

JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder()
.subject(storedKycToken)
.issuer("eSignet")
.expirationTime(new Date(System.currentTimeMillis() + 3600 * 1000));

for (String claim : kycExchangeDto.getAcceptedClaims()) {
switch (claim) {
case "name":
builder.claim("name", userDetail.getName());
break;
case "birthdate":
builder.claim("birthDate", userDetail.getDob());
break;
case "email":
builder.claim("email", userDetail.getEmail());
break;
}
}

JWTClaimsSet claimsSet = builder.build();
String signedJWT = signJWT(claimsSet); //additionally can be encrypted with public key of relying party

KycExchangeResult kycExchangeResult = new KycExchangeResult();
kycExchangeResult.setEncryptedKyc(signedJWT);
return kycExchangeResult;
}



@Override
public SendOtpResult sendOtp(String relyingPartyId, String clientId, SendOtpDto sendOtpDto) throws SendOtpException {
SendOtpResult sendOtpResult = new SendOtpResult();
sendOtpResult.setTransactionId(sendOtpDto.getTransactionId());
sendOtpResult.setMaskedMobile("");
return sendOtpResult;
}

@Override
public boolean isSupportedOtpChannel(String channel) {
return true;
}

@Override
public List<KycSigningCertificateData> getAllKycSigningCertificates() throws KycSigningCertificateException {
KycSigningCertificateData kycSigningCertificateData = new KycSigningCertificateData();
try {

Base64.Encoder encoder = Base64.getMimeEncoder(64, "\n".getBytes());
String encodedCert = encoder.encodeToString(keyCertificate.getEncoded());
kycSigningCertificateData.setCertificateData("-----BEGIN CERTIFICATE-----\n" + encodedCert + "\n-----END CERTIFICATE-----");
kycSigningCertificateData.setExpiryAt(keyCertificate.getNotAfter().toInstant().atZone(ZoneOffset.UTC).toLocalDateTime());
kycSigningCertificateData.setIssuedAt(keyCertificate.getNotBefore().toInstant().atZone(ZoneOffset.UTC).toLocalDateTime());

} catch (CertificateEncodingException e) {
throw new RuntimeException(e);
}
//return public key from this method
return List.of(kycSigningCertificateData);
}

public void generateKeyCertificate() {
if (localKey == null || keyCertificate == null) {
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
localKey = keyPairGenerator.generateKeyPair();

X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
X500Principal dnName = new X500Principal("CN=Self-Signed, O=Example Org, C=US");
certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
certGen.setSubjectDN(dnName);
certGen.setIssuerDN(dnName); // Self-signed
certGen.setNotBefore(new Date(System.currentTimeMillis()));
certGen.setNotAfter(new Date(System.currentTimeMillis() + (365 * 24 * 60 * 60 * 1000L))); // 1 year validity
certGen.setPublicKey(localKey.getPublic());
certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
keyCertificate = certGen.generate(localKey.getPrivate(), "BC");
} catch (Exception e) {
e.printStackTrace();
}
}
}

private String signJWT(JWTClaimsSet claimsSet) throws KycExchangeException {
if(localKey == null || keyCertificate == null) {
generateKeyCertificate();
}

JWSHeader header = new JWSHeader(JWSAlgorithm.RS256);
SignedJWT signedJWT = new SignedJWT(header, claimsSet);
JWSSigner signer = new RSASSASigner((RSAPrivateKey)localKey.getPrivate());
try {
signedJWT.sign(signer);
} catch (JOSEException e) {
throw new KycExchangeException("signing_failed");
}
return signedJWT.serialize();
}
}
Loading
Loading