diff --git a/dsc-helper/build.gradle b/dsc-helper/build.gradle new file mode 100644 index 000000000..452da6906 --- /dev/null +++ b/dsc-helper/build.gradle @@ -0,0 +1,51 @@ +/* + * Builds the cbsnexora-dsc-helper.jar consumed by the JNLP from /api/v1/dsc/helper.jar. + * + * Targets Java 1.8 so the broadest set of client JREs (incl. legacy OpenWebStart installs) + * can launch the helper. The JAR is a "fat" jar with Gson shaded in — JNLP can only + * list one cleanly, and avoiding network round-trips for tiny Gson is a win. + */ +plugins { + id 'java' +} + +group = 'com.cbsnexora' +version = '1.0.0' + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'com.google.code.gson:gson:2.10.1' +} + +jar { + archiveBaseName = 'cbsnexora-dsc-helper' + archiveClassifier = '' + + manifest { + attributes( + 'Main-Class' : 'com.cbsnexora.dsc.CbsnexoraDscHelper', + 'Permissions' : 'all-permissions', + 'Application-Name' : 'CBSnexora DSC Helper', + 'Codebase' : '*', + 'Implementation-Version': project.version + ) + } + + // Shade Gson into the same jar so JNLP only needs one entry. + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } + // Strip signatures from shaded deps so re-signing the fat jar is clean. + exclude 'META-INF/*.SF' + exclude 'META-INF/*.DSA' + exclude 'META-INF/*.RSA' +} diff --git a/dsc-helper/settings.gradle b/dsc-helper/settings.gradle new file mode 100644 index 000000000..2e412615f --- /dev/null +++ b/dsc-helper/settings.gradle @@ -0,0 +1,21 @@ +/* + * Standalone Gradle project for the CBSnexora DSC helper JAR. Intentionally NOT included in the + * root fineract settings.gradle — different Java target (1.8) and a tiny dependency tree. + * + * Build: + * cd dsc-helper && gradle clean build + * → dsc-helper/build/libs/cbsnexora-dsc-helper.jar (fat jar with Gson embedded) + * + * Sign (self-signed for now; replace cert later without rebuilding): + * keytool -genkeypair -alias cbsnexora -keyalg RSA -keysize 2048 \ + * -validity 730 -keystore cbsnexora-keystore.jks + * jarsigner -keystore cbsnexora-keystore.jks build/libs/cbsnexora-dsc-helper.jar cbsnexora + * + * Deploy: + * cp build/libs/cbsnexora-dsc-helper.jar ~/.cbsnexora/dsc-helper.jar + * (path is configurable via fineract.dsc.helper.jar.path; default is ~/.cbsnexora/dsc-helper.jar) + * + * Fineract will then stream it on demand at GET /api/v1/dsc/helper.jar — the JNLP + * already points at that URL. + */ +rootProject.name = 'cbsnexora-dsc-helper' diff --git a/dsc-helper/src/main/java/com/cbsnexora/dsc/CbsnexoraDscHelper.java b/dsc-helper/src/main/java/com/cbsnexora/dsc/CbsnexoraDscHelper.java new file mode 100644 index 000000000..bd07bd725 --- /dev/null +++ b/dsc-helper/src/main/java/com/cbsnexora/dsc/CbsnexoraDscHelper.java @@ -0,0 +1,419 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + */ +package com.cbsnexora.dsc; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import javax.swing.*; +import javax.swing.table.DefaultTableModel; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * Entry point launched by Java Web Start. Reads its single argument as JSON: + *
+ *   {
+ *     "randomNo": "...",
+ *     "mode": "ENROLL" | "SIGN_CHALLENGE",
+ *     "callbackUrl": "https://.../api/v1/dsc/{enroll|login/dsc}/{rn}/{callback|verify}",
+ *     "challenge": "",        // SIGN_CHALLENGE only
+ *     "expectedSerial": "",   // optional — locks selection to a single cert
+ *     "title": "...",
+ *     "codebase": "..."
+ *   }
+ * 
+ * + *

Windows uses the OS keystore ({@code Windows-MY}). Linux/Mac falls back to a PKCS11 token + * driver the user points at via {@link TokenFrame}. + * + *

Server is the source of truth for what gets persisted — this helper extracts and POSTs; + * the server parses, validates, and stores. + */ +public final class CbsnexoraDscHelper { + + /** A freshly-launched JNLP must be less than this many minutes old. Prevents stale-link reuse. */ + private static final long MAX_LINK_AGE_MINUTES = 5; + + private static HelperArgs cfg; + private static KeyStore keyStore; + private static Provider pkcs11Provider; // Linux/Mac + + public static void main(final String[] args) { + try { + if (!validateJnlpAge()) { + showError("This signing link has expired. Please request a fresh one from the portal.", + "Link Expired"); + System.exit(1); + } + + if (args == null || args.length < 1 || args[0] == null || args[0].isEmpty()) { + showError("Helper launched without arguments.", "Bad Launch"); + System.exit(1); + } + + cfg = HelperArgs.parse(args[0]); + + // Token picker is now shown on every OS — Windows users can pick either the OS-native + // certificate store or a PKCS11 .dll for vendor-specific USB tokens (ePass2003, eToken, + // ProxKey, etc.). Linux / macOS only see PKCS11 options. + SwingUtilities.invokeLater(CbsnexoraDscHelper::launchTokenPicker); + } catch (final Exception e) { + e.printStackTrace(); + showError("Unexpected error: " + e.getMessage(), "Error"); + System.exit(1); + } + } + + // ── Token picker (cross-platform) ─────────────────────────────────────── + + private static void launchTokenPicker() { + final TokenFrame tokenFrame = new TokenFrame(); + tokenFrame.setProceedAction(selection -> { + try { + if (TokenFrame.SELECTION_WINDOWS_MY.equals(selection)) { + // OS-native keystore — no PIN dialog needed; the platform handles authn. + keyStore = KeyStore.getInstance("Windows-MY"); + keyStore.load(null, null); + } else { + // PKCS11 library — drives a Swing PIN dialog via the callback handler. + initializePkcs11(selection); + keyStore = loadPkcs11KeyStore(); + if (keyStore == null) { + showError("Failed to open token keystore. Wrong PIN?", "Login Failed"); + return; + } + } + tokenFrame.dispose(); + showCertSelectionWindow(); + } catch (final Exception e) { + e.printStackTrace(); + showError("Could not open keystore: " + e.getMessage(), "Token Error"); + } + }); + tokenFrame.setVisible(true); + } + + private static void initializePkcs11(final String libraryPath) throws Exception { + final String config = "name = CBSnexoraDSC\nlibrary = " + libraryPath + "\n"; + final ByteArrayInputStream in = new ByteArrayInputStream(config.getBytes(StandardCharsets.UTF_8)); + try { + // Java 9+ + final Class sunPkcs11 = Class.forName("sun.security.pkcs11.SunPKCS11"); + pkcs11Provider = (Provider) sunPkcs11.getConstructor(InputStream.class).newInstance(in); + } catch (final ClassNotFoundException e) { + // Fallback — same path; preserved for old JREs that resolve the class differently. + pkcs11Provider = (Provider) Class.forName("sun.security.pkcs11.SunPKCS11") + .getConstructor(InputStream.class).newInstance(in); + } + Security.removeProvider(pkcs11Provider.getName()); + Security.addProvider(pkcs11Provider); + } + + private static KeyStore loadPkcs11KeyStore() throws Exception { + final AuthProvider authProvider = (AuthProvider) pkcs11Provider; + final KeyStore.CallbackHandlerProtection protection = new KeyStore.CallbackHandlerProtection( + new SwingPasswordCallbackHandler()); + final KeyStore.Builder builder = KeyStore.Builder.newInstance("PKCS11", authProvider, protection); + return builder.getKeyStore(); + } + + // ── Certificate selection UI (shared between Windows and PKCS11) ──────── + + private static void showCertSelectionWindow() { + final JFrame frame = new JFrame(cfg.title != null ? cfg.title : "Select Digital Signature Certificate"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setLayout(new BorderLayout(10, 10)); + + final DefaultTableModel model = new DefaultTableModel( + new Object[] { "Common Name", "Serial No", "Issuer", "Expires" }, 0) { + @Override + public boolean isCellEditable(final int row, final int column) { + return false; + } + }; + final JTable table = new JTable(model); + table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + table.getColumnModel().getColumn(0).setPreferredWidth(180); + table.getColumnModel().getColumn(1).setPreferredWidth(180); + table.getColumnModel().getColumn(2).setPreferredWidth(260); + table.getColumnModel().getColumn(3).setPreferredWidth(100); + + final SimpleDateFormat dateFmt = new SimpleDateFormat("dd-MMM-yyyy"); + // Fully qualified — java.awt.List exists too and collides with the wildcard import. + final java.util.List aliasList = new ArrayList<>(); + + try { + final Enumeration en = keyStore.aliases(); + while (en.hasMoreElements()) { + final String alias = en.nextElement(); + final java.security.cert.Certificate cert = keyStore.getCertificate(alias); + if (!(cert instanceof X509Certificate)) { + continue; + } + final X509Certificate x = (X509Certificate) cert; + final String serial = x.getSerialNumber().toString(16).toUpperCase(Locale.ROOT); + + // SIGN_CHALLENGE with expectedSerial → only show the one cert the server expects. + if ("SIGN_CHALLENGE".equals(cfg.mode) && cfg.expectedSerial != null + && !cfg.expectedSerial.equalsIgnoreCase(serial)) { + continue; + } + + model.addRow(new Object[] { + extractCN(x.getSubjectX500Principal().getName()), + serial, + x.getIssuerX500Principal().getName(), + dateFmt.format(x.getNotAfter()) + }); + aliasList.add(alias); + } + } catch (final KeyStoreException e) { + e.printStackTrace(); + showError("Could not read certificates: " + e.getMessage(), "Keystore Error"); + return; + } + + if (model.getRowCount() == 0) { + showError("SIGN_CHALLENGE".equals(cfg.mode) + ? "No DSC matching the expected serial was found in this keystore." + : "No certificates were found in the keystore.", "Nothing to Sign"); + System.exit(1); + } + + final JButton proceed = new JButton(cfg.mode.equals("ENROLL") ? "Map Selected DSC" : "Sign with Selected DSC"); + proceed.setEnabled(false); + table.getSelectionModel().addListSelectionListener(e -> proceed.setEnabled(table.getSelectedRow() >= 0)); + + final JButton cancel = new JButton("Cancel"); + cancel.addActionListener(e -> System.exit(0)); + + proceed.addActionListener(e -> { + final int row = table.getSelectedRow(); + if (row < 0) { + return; + } + final String alias = aliasList.get(row); + try { + if ("ENROLL".equals(cfg.mode)) { + performEnroll(alias); + } else if ("SIGN_CHALLENGE".equals(cfg.mode)) { + performSignChallenge(alias); + } else { + showError("Unknown mode: " + cfg.mode, "Bad Mode"); + return; + } + JOptionPane.showMessageDialog(frame, "Done. You can close this window and return to the browser.", + "Success", JOptionPane.INFORMATION_MESSAGE); + frame.dispose(); + System.exit(0); + } catch (final Exception ex) { + ex.printStackTrace(); + showError("Operation failed: " + ex.getMessage(), "Error"); + } + }); + + final JPanel buttons = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 10)); + buttons.add(cancel); + buttons.add(proceed); + + frame.add(new JScrollPane(table), BorderLayout.CENTER); + frame.add(buttons, BorderLayout.SOUTH); + frame.setSize(880, 420); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + // ── ENROLL: send the cert (base64 DER) up to the server ───────────────── + + private static void performEnroll(final String alias) throws Exception { + final X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias); + final String certB64 = Base64.getEncoder().encodeToString(cert.getEncoded()); + final Map body = new LinkedHashMap<>(); + body.put("certB64", certB64); + body.put("dscName", extractCN(cert.getSubjectX500Principal().getName())); + body.put("dscType", "SIGNING"); + postJson(cfg.callbackUrl, body); + } + + // ── SIGN_CHALLENGE: RSA-SHA256(challenge bytes) → POST signature ──────── + + private static void performSignChallenge(final String alias) throws Exception { + final PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, null); + if (privateKey == null) { + throw new GeneralSecurityException("Private key for alias " + alias + " is not accessible"); + } + final X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias); + // Sanity: chain + cert exist (also surfaces token issues early). + final Certificate[] chain = keyStore.getCertificateChain(alias); + if (chain == null || chain.length == 0) { + throw new GeneralSecurityException("Cert chain for alias " + alias + " is missing"); + } + final Signature sig = Signature.getInstance("SHA256withRSA"); + sig.initSign(privateKey); + sig.update(cfg.challenge.getBytes(StandardCharsets.UTF_8)); + final String signatureB64 = Base64.getEncoder().encodeToString(sig.sign()); + + final Map body = new LinkedHashMap<>(); + body.put("certSerial", cert.getSerialNumber().toString(16).toUpperCase(Locale.ROOT)); + body.put("signatureB64", signatureB64); + postJson(cfg.callbackUrl, body); + } + + // ── HTTP helpers ──────────────────────────────────────────────────────── + + private static void postJson(final String urlStr, final Object body) throws IOException { + final String json = new Gson().toJson(body); + final URL url = new URL(urlStr); + final HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + conn.setRequestProperty("Accept", "application/json"); + // The helper runs outside the browser (JWS-launched JVM process) so it doesn't get the + // tenant header from any HTTP interceptor — pass it explicitly from the JNLP args. The + // backend's TenantAwareBasicAuthenticationFilter requires this header (or a + // ?tenantIdentifier= query param) on every /api/* request. + if (cfg != null && cfg.tenantIdentifier != null && !cfg.tenantIdentifier.isEmpty()) { + conn.setRequestProperty("Fineract-Platform-TenantId", cfg.tenantIdentifier); + } + conn.setDoOutput(true); + try (OutputStream out = conn.getOutputStream()) { + out.write(json.getBytes(StandardCharsets.UTF_8)); + } + + final int code = conn.getResponseCode(); + // Read body for diagnostics; surface non-2xx to the user. + final String resp; + try (InputStream in = code >= 400 ? conn.getErrorStream() : conn.getInputStream(); + ByteArrayOutputStream sink = new ByteArrayOutputStream()) { + if (in != null) { + final byte[] buf = new byte[2048]; + int n; + while ((n = in.read(buf)) > 0) { + sink.write(buf, 0, n); + } + } + resp = sink.toString("UTF-8"); + } + if (code < 200 || code >= 300) { + throw new IOException("Server returned HTTP " + code + ": " + resp); + } + } + + // ── JNLP age check — same shape as your existing tender helper ────────── + + private static boolean validateJnlpAge() { + // Look for whatever filename our UI uses today AND the legacy name. If any candidate + // exists and is recent, accept. If none exist, accept (some browsers stream JNLP directly + // to JWS without saving). We only fail if a candidate exists AND is too old AND no fresh + // candidate is present — the server-side challenge TTL is the real authority on staleness. + try { + final String[] candidates = { "cbsnexora-dsc.jnlp", "SignTender.jnlp" }; + final String userProfile = System.getenv("USERPROFILE"); + final String downloads = (userProfile != null ? userProfile : System.getProperty("user.home")) + + File.separator + "Downloads"; + boolean anyExist = false; + long youngestMinutes = Long.MAX_VALUE; + for (final String name : candidates) { + final File f = new File(downloads, name); + if (f.exists()) { + anyExist = true; + final long m = (System.currentTimeMillis() - f.lastModified()) / 60_000L; + if (m < youngestMinutes) { + youngestMinutes = m; + } + } + } + // No JNLP on disk → JWS likely streamed it directly. Don't block. + if (!anyExist) { + return true; + } + return youngestMinutes <= MAX_LINK_AGE_MINUTES; + } catch (final Exception ignored) { + return true; // never block on env weirdness + } + } + + // ── Misc ──────────────────────────────────────────────────────────────── + + private static String extractCN(final String dn) { + if (dn == null) { + return ""; + } + for (final String part : dn.split(",")) { + final String[] kv = part.trim().split("=", 2); + if (kv.length == 2 && "CN".equalsIgnoreCase(kv[0].trim())) { + return kv[1].trim(); + } + } + return dn; + } + + private static void showError(final String message, final String title) { + JOptionPane.showMessageDialog(null, message, title, JOptionPane.ERROR_MESSAGE); + } + + /** Parsed JSON-arg payload. Mutable for convenience; not exposed beyond this class. */ + static final class HelperArgs { + String randomNo; + String mode; // ENROLL | SIGN_CHALLENGE + String callbackUrl; + String challenge; // SIGN_CHALLENGE only + String expectedSerial; // optional + String codebase; + String tenantIdentifier; // Fineract tenant — sent as Fineract-Platform-TenantId on callback + String title; + + static HelperArgs parse(final String json) { + final JsonElement root = JsonParser.parseString(json); + if (!root.isJsonObject()) { + throw new IllegalArgumentException("argument is not a JSON object"); + } + final JsonObject o = root.getAsJsonObject(); + final HelperArgs a = new HelperArgs(); + a.randomNo = optString(o, "randomNo"); + a.mode = optString(o, "mode"); + a.callbackUrl = optString(o, "callbackUrl"); + a.challenge = optString(o, "challenge"); + a.expectedSerial = optString(o, "expectedSerial"); + a.codebase = optString(o, "codebase"); + a.tenantIdentifier = optString(o, "tenantIdentifier"); + a.title = optString(o, "title"); + if (a.mode == null || a.callbackUrl == null || a.randomNo == null) { + throw new IllegalArgumentException("randomNo, mode, callbackUrl are required"); + } + if ("SIGN_CHALLENGE".equals(a.mode) && (a.challenge == null || a.challenge.isEmpty())) { + throw new IllegalArgumentException("challenge is required for SIGN_CHALLENGE mode"); + } + return a; + } + + private static String optString(final JsonObject o, final String k) { + return o.has(k) && !o.get(k).isJsonNull() ? o.get(k).getAsString() : null; + } + } + + private CbsnexoraDscHelper() { + } +} diff --git a/dsc-helper/src/main/java/com/cbsnexora/dsc/SwingPasswordCallbackHandler.java b/dsc-helper/src/main/java/com/cbsnexora/dsc/SwingPasswordCallbackHandler.java new file mode 100644 index 000000000..4a6a864bd --- /dev/null +++ b/dsc-helper/src/main/java/com/cbsnexora/dsc/SwingPasswordCallbackHandler.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + */ +package com.cbsnexora.dsc; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.swing.*; +import java.awt.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.IOException; +import java.util.concurrent.CancellationException; + +/** + * Pops a Swing dialog asking for the PKCS11 token PIN. Used by the {@code KeyStore.Builder} on + * Linux/Mac when opening a hardware-token keystore — Windows-MY doesn't need it (the OS keystore + * handles auth via the platform UI). + */ +public class SwingPasswordCallbackHandler implements CallbackHandler { + + @Override + public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (final Callback cb : callbacks) { + if (cb instanceof PasswordCallback) { + handlePasswordCallback((PasswordCallback) cb); + } else { + throw new UnsupportedCallbackException(cb, "Callback not supported: " + cb.getClass().getName()); + } + } + } + + private void handlePasswordCallback(final PasswordCallback passCb) { + final JPanel panel = new JPanel(new GridLayout(2, 1, 5, 5)); + panel.add(new JLabel(passCb.getPrompt() != null ? passCb.getPrompt() : "Enter token PIN:")); + final JPasswordField pin = new JPasswordField(20); + panel.add(pin); + + final JOptionPane pane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION); + final JDialog dialog = pane.createDialog(null, "Token PIN"); + dialog.addWindowListener(new WindowAdapter() { + @Override + public void windowOpened(final WindowEvent e) { + pin.requestFocusInWindow(); + } + }); + dialog.setVisible(true); + dialog.dispose(); + + final Object val = pane.getValue(); + final int choice = val instanceof Integer ? (Integer) val : JOptionPane.CANCEL_OPTION; + if (choice == JOptionPane.OK_OPTION) { + passCb.setPassword(pin.getPassword()); + } else { + // Mirrors the original tender helper: user cancel → exit. Throwing the cancel up to + // KeyStore.Builder would leave the JNLP launch hanging. + throw new CancellationException("Token PIN entry cancelled by user"); + } + } +} diff --git a/dsc-helper/src/main/java/com/cbsnexora/dsc/TokenFrame.java b/dsc-helper/src/main/java/com/cbsnexora/dsc/TokenFrame.java new file mode 100644 index 000000000..6f655693f --- /dev/null +++ b/dsc-helper/src/main/java/com/cbsnexora/dsc/TokenFrame.java @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + */ +package com.cbsnexora.dsc; + +import javax.swing.*; +import java.awt.*; +import java.io.File; +import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.Map; +import java.util.function.Consumer; + +/** + * Cross-platform DSC token / store picker. On Windows the first entry is "Windows Certificate + * Store" (which routes to the {@code Windows-MY} keystore — what most Indian DSC USB tokens auto- + * register with). Below that are common PKCS11 libraries for ePass2003 / eToken / ProxKey / etc., + * plus an "Other" option that takes a custom {@code .dll} / {@code .so} / {@code .dylib} path. + * + *

On Linux / macOS the Windows entry is omitted and only PKCS11 options are listed. + * + *

The caller registers a {@link #setProceedAction(Consumer)} callback that receives either: + *

    + *
  • {@link #SELECTION_WINDOWS_MY} — caller opens {@code KeyStore.getInstance("Windows-MY")}
  • + *
  • An absolute filesystem path to a PKCS11 module — caller spins up {@code SunPKCS11}
  • + *
+ */ +public class TokenFrame extends JFrame { + + /** Sentinel returned to the proceed callback when the user picks the OS-native Windows store. */ + public static final String SELECTION_WINDOWS_MY = "WINDOWS-MY"; + + private final Map presets = new LinkedHashMap<>(); + private final JComboBox combo = new JComboBox<>(); + private final JTextField customPath = new JTextField(28); + private final JButton browseButton = new JButton("Browse..."); + private final JButton proceedButton = new JButton("Proceed"); + private final JButton cancelButton = new JButton("Cancel"); + private final JPanel customPanel; + private Consumer proceedAction = sel -> {}; + + public TokenFrame() { + super("Select Digital Signature Token"); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + loadPresets(); + for (final String name : presets.keySet()) { + combo.addItem(name); + } + combo.addItem("Other (browse for library file)"); + + // Layout + final JPanel main = new JPanel(new BorderLayout(10, 10)); + main.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); + + final JPanel chooser = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 8)); + chooser.add(new JLabel("Token / Keystore:")); + chooser.add(combo); + main.add(chooser, BorderLayout.NORTH); + + customPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 8, 8)); + customPanel.add(new JLabel("Library:")); + customPanel.add(customPath); + customPanel.add(browseButton); + customPanel.setVisible(false); + main.add(customPanel, BorderLayout.CENTER); + + final JPanel actions = new JPanel(new FlowLayout(FlowLayout.RIGHT, 8, 8)); + actions.add(cancelButton); + actions.add(proceedButton); + main.add(actions, BorderLayout.SOUTH); + + add(main); + + // Hooks + combo.addActionListener(e -> { + final boolean isOther = combo.getSelectedItem() != null + && combo.getSelectedItem().toString().startsWith("Other"); + customPanel.setVisible(isOther); + pack(); + }); + browseButton.addActionListener(e -> { + final JFileChooser fc = new JFileChooser(); + if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + customPath.setText(fc.getSelectedFile().getAbsolutePath()); + } + }); + cancelButton.addActionListener(e -> System.exit(0)); + proceedButton.addActionListener(e -> { + final String selection = resolveSelection(); + if (selection == null) { + JOptionPane.showMessageDialog(this, + "Please pick a token / keystore option (or browse to a library file).", + "No Selection", JOptionPane.WARNING_MESSAGE); + return; + } + proceedAction.accept(selection); + }); + + setMinimumSize(new Dimension(560, 180)); + pack(); + setLocationRelativeTo(null); + } + + public void setProceedAction(final Consumer action) { + this.proceedAction = action != null ? action : sel -> {}; + } + + /** Translates the UI state into the value handed to the proceed callback. */ + private String resolveSelection() { + final Object item = combo.getSelectedItem(); + if (item == null) { + return null; + } + if (item.toString().startsWith("Other")) { + final String path = customPath.getText(); + return (path != null && !path.trim().isEmpty()) ? path.trim() : null; + } + return presets.get(item.toString()); + } + + /** + * Default token / keystore presets. Adjust paths per install if your customers run different + * driver locations — most CCA-licensed CA tokens stick to the defaults below. + */ + private void loadPresets() { + final String os = System.getProperty("os.name", "").toLowerCase(Locale.ROOT); + if (os.contains("win")) { + presets.put("Windows Certificate Store (default)", SELECTION_WINDOWS_MY); + presets.put("ePass2003 (eMudhra / Capricorn)", "C:\\Windows\\System32\\eps2003csp11.dll"); + presets.put("ePass2003 v2", "C:\\Windows\\System32\\eps2003csp11v2.dll"); + presets.put("SafeNet eToken 5110", "C:\\Windows\\System32\\eTPKCS11.dll"); + presets.put("WatchData ProxKey", "C:\\Windows\\System32\\WDPKCS.dll"); + presets.put("Aladdin eToken", "C:\\Windows\\System32\\eTPkcs11.dll"); + presets.put("HYP2003 / Trust Key", "C:\\Windows\\System32\\SignatureP11.dll"); + presets.put("mToken CryptoID", "C:\\Windows\\System32\\mToken.dll"); + } else if (os.contains("mac") || os.contains("darwin")) { + presets.put("ePass2003 (eMudhra / Capricorn)", "/usr/local/lib/libcastle_v2.dylib"); + presets.put("SafeNet eToken", "/usr/local/lib/libeTPkcs11.dylib"); + presets.put("WatchData ProxKey", "/usr/local/lib/libwdpkcs.dylib"); + } else { + // Linux + presets.put("ePass2003 (eMudhra / Capricorn)", "/usr/lib/x86_64-linux-gnu/libcastle_v2.so"); + presets.put("ePass2003 alt path", "/usr/lib/libcastle_v2.so"); + presets.put("SafeNet eToken", "/usr/lib/libeToken.so"); + presets.put("SafeNet eToken (Ubuntu)", "/usr/lib/x86_64-linux-gnu/libeToken.so"); + presets.put("WatchData ProxKey", "/usr/lib/libwdpkcs.so"); + presets.put("OpenSC (generic)", "/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so"); + } + + // Drop entries whose underlying file doesn't exist — keeps the picker honest. + presets.values().removeIf(path -> !SELECTION_WINDOWS_MY.equals(path) && !new File(path).exists()); + + // Always keep at least Windows-MY on Windows even if the rest were stripped. + if (os.contains("win") && presets.isEmpty()) { + presets.put("Windows Certificate Store (default)", SELECTION_WINDOWS_MY); + } + } +} diff --git a/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/BankUserDsc.java b/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/BankUserDsc.java new file mode 100644 index 000000000..17b818ffe --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/BankUserDsc.java @@ -0,0 +1,162 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.PrePersist; +import jakarta.persistence.Table; +import jakarta.persistence.Version; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; +import org.apache.fineract.useradministration.domain.AppUser; + +/** + * One row per DSC mapping. A user may have multiple rows — we filter by {@link CertStatus} to find + * active mappings. The cert serial is globally unique (one cert in the world has one serial), so it + * doubles as a natural key. + * + *

Mirrors the shape of an externally-reference {@code bank_user_dsc} table from the procurement + * project, with Fineract conventions layered on top: FK to {@code m_appuser}, snake_case columns, + * proper DATE/TIMESTAMP types, soft-delete + audit columns, and optimistic locking via + * {@code version}. + */ +@Entity +@Table(name = "m_bank_user_dsc") +@Getter +@Setter +@NoArgsConstructor +public class BankUserDsc extends AbstractPersistableCustom { + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "appuser_id", nullable = false) + private AppUser appUser; + + /** + * Free-text user identifier retained for parity with the source schema (often the username or + * an external system ID). The {@link #appUser} FK is authoritative; this is for display / + * cross-system reconciliation only. + */ + @Column(name = "dsc_user_id", length = 64) + private String dscUserId; + + @Column(name = "dsc_serial_no", length = 128, nullable = false, unique = true) + private String dscSerialNo; + + @Column(name = "dsc_name", length = 255) + private String dscName; + + /** Base64-encoded SubjectPublicKeyInfo. Used at login-factor time to verify the signature. */ + @Column(name = "dsc_public_key", columnDefinition = "TEXT", nullable = false) + private String dscPublicKey; + + @Column(name = "dsc_expiry_date", nullable = false) + private LocalDate dscExpiryDate; + + @Column(name = "date_of_submission", nullable = false) + private OffsetDateTime dateOfSubmission; + + @Enumerated(EnumType.STRING) + @Column(name = "dsc_type", length = 32, nullable = false) + private DscType dscType; + + @Column(name = "common_name", length = 255) + private String commonName; + + /** Cert subject DN. */ + @Column(name = "issued_to", length = 512) + private String issuedTo; + + /** Cert issuer DN — useful in the UI ("eMudhra Sub CA for Class 3 ..."). */ + @Column(name = "issued_by", length = 512) + private String issuedBy; + + @Enumerated(EnumType.STRING) + @Column(name = "cert_status", length = 16, nullable = false) + private CertStatus certStatus; + + @Column(name = "revoked_on") + private OffsetDateTime revokedOn; + + @Column(name = "revoked_by") + private Long revokedBy; + + @Column(name = "revoke_reason", length = 255) + private String revokeReason; + + @Column(name = "is_active", nullable = false) + private boolean active; + + @Column(name = "is_deleted", nullable = false) + private boolean deleted; + + @Column(name = "created_by") + private Long createdBy; + + @Column(name = "created_at", nullable = false, updatable = false) + private OffsetDateTime createdAt; + + @Column(name = "modified_by") + private Long modifiedBy; + + @Column(name = "modified_on") + private OffsetDateTime modifiedOn; + + @Column(name = "deleted_by") + private Long deletedBy; + + @Column(name = "deleted_at") + private OffsetDateTime deletedAt; + + @Version + @Column(name = "version", nullable = false) + private Integer version; + + @PrePersist + private void prePersist() { + final OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC); + if (createdAt == null) { + createdAt = now; + } + if (dateOfSubmission == null) { + dateOfSubmission = now; + } + if (certStatus == null) { + certStatus = CertStatus.ACTIVE; + } + } + + /** + * Convenience flip: returns true if this row is the kind we'd present in the UI / accept as an + * MFA factor. Cheaper to call than re-checking the three flags everywhere. + */ + public boolean isUsable() { + return active && !deleted && certStatus == CertStatus.ACTIVE; + } +} diff --git a/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/BankUserDscRepository.java b/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/BankUserDscRepository.java new file mode 100644 index 000000000..71be2b92a --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/BankUserDscRepository.java @@ -0,0 +1,50 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.domain; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface BankUserDscRepository extends JpaRepository { + + /** All non-deleted rows for the user, newest first — admin view + self list. */ + List findByAppUser_IdAndDeletedFalseOrderByIdDesc(Long userId); + + /** Only rows usable as an MFA factor (active + not deleted + cert_status=ACTIVE). */ + List findByAppUser_IdAndActiveTrueAndDeletedFalseAndCertStatus(Long userId, CertStatus certStatus); + + /** Lookup by globally-unique cert serial — used at login-verify time. */ + Optional findByDscSerialNo(String dscSerialNo); + + /** Used at login-verify time to confirm "this user enrolled this serial". */ + Optional findByAppUser_IdAndDscSerialNo(Long userId, String dscSerialNo); + + /** + * Login gate: returns true if the user has at least one usable DSC mapping that's also not + * past its notAfter date. The {@code dscExpiryDate >= today} clause prevents 2FA-gating a + * user who's enrolled but whose cert silently expired between log-ins — they'd otherwise be + * stuck in a verification loop with a cert the helper would sign with but the server would + * accept (the verify side filters too, but the gate is what kicks the user into the DSC flow + * in the first place). + */ + boolean existsByAppUser_IdAndActiveTrueAndDeletedFalseAndCertStatusAndDscExpiryDateGreaterThanEqual( + Long userId, CertStatus certStatus, LocalDate today); +} diff --git a/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/CertStatus.java b/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/CertStatus.java new file mode 100644 index 000000000..355770b6d --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/CertStatus.java @@ -0,0 +1,46 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.domain; + +/** + * Lifecycle of a DSC mapping row in m_bank_user_dsc. + * + *

    + *
  • {@link #ACTIVE} — enrolled and usable as an MFA factor.
  • + *
  • {@link #REVOKED} — voluntarily removed by user or admin; kept for audit.
  • + *
  • {@link #EXPIRED} — cert past notAfter date; flipped by a scheduled job, not on read.
  • + *
+ */ +public enum CertStatus { + ACTIVE, + REVOKED, + EXPIRED; + + public static CertStatus fromString(final String s) { + if (s == null) { + return null; + } + for (final CertStatus t : values()) { + if (t.name().equalsIgnoreCase(s)) { + return t; + } + } + return null; + } +} diff --git a/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/DscChallenge.java b/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/DscChallenge.java new file mode 100644 index 000000000..1a5193666 --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/DscChallenge.java @@ -0,0 +1,133 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.PrePersist; +import jakarta.persistence.Table; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; +import org.apache.fineract.useradministration.domain.AppUser; + +/** + * One-shot challenge nonce for a DSC enroll or login-2FA cycle. + * + *

The {@link #randomNo} is the public handle the JNLP file and the helper callback use; it must + * be a sufficiently long random value (UUID v4) to make guessing infeasible. The {@link #challenge} + * is the raw bytes the helper actually signs in LOGIN_2FA flow. + * + *

The row is single-use: once {@code status} flips to VERIFIED or FAILED, further callbacks + * referencing the same randomNo must be rejected. Cleanup of EXPIRED/FAILED rows is left to a + * scheduled job (TBD) — we keep them for audit. + */ +@Entity +@Table(name = "m_dsc_challenge") +@Getter +@Setter +@NoArgsConstructor +public class DscChallenge extends AbstractPersistableCustom { + + @Column(name = "random_no", length = 64, nullable = false, unique = true) + private String randomNo; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "appuser_id") + private AppUser appUser; + + @Enumerated(EnumType.STRING) + @Column(name = "purpose", length = 32, nullable = false) + private DscChallengePurpose purpose; + + /** Random 32-byte payload (hex-encoded) the helper signs with the user's DSC private key. */ + @Column(name = "challenge", length = 128, nullable = false) + private String challenge; + + @Enumerated(EnumType.STRING) + @Column(name = "status", length = 16, nullable = false) + private DscChallengeStatus status; + + /** Serial returned by the helper — set in callback; the server verifies it matches the user's enrolled cert. */ + @Column(name = "cert_serial_returned", length = 128) + private String certSerialReturned; + + /** Base64-encoded RSA-SHA256 signature returned by the helper. NULL for ENROLL purpose. */ + @Column(name = "signature_b64", columnDefinition = "TEXT") + private String signatureB64; + + /** FK populated on successful LOGIN_2FA verify — tells us *which* DSC row was used. */ + @Column(name = "dsc_id") + private Long dscId; + + @Column(name = "error_message", length = 512) + private String errorMessage; + + @Column(name = "attempts", nullable = false) + private int attempts; + + @Column(name = "expires_at", nullable = false) + private OffsetDateTime expiresAt; + + @Column(name = "verified_at") + private OffsetDateTime verifiedAt; + + /** + * TFA access token minted during a successful LOGIN_2FA verify. Held on the row until the + * browser claims it via {@code POST /v1/login/dsc/{rn}/claim}; that call clears the field by + * stamping {@link #tfaTokenClaimedAt}. Single-use. + */ + @Column(name = "tfa_token", length = 255) + private String tfaToken; + + @Column(name = "tfa_token_claimed_at") + private OffsetDateTime tfaTokenClaimedAt; + + @Column(name = "created_at", nullable = false, updatable = false) + private OffsetDateTime createdAt; + + @Column(name = "ip_address", length = 64) + private String ipAddress; + + @Column(name = "user_agent", length = 512) + private String userAgent; + + @PrePersist + private void prePersist() { + if (createdAt == null) { + createdAt = OffsetDateTime.now(ZoneOffset.UTC); + } + if (status == null) { + status = DscChallengeStatus.PENDING; + } + } + + /** True if the row is past its TTL — used by the verify endpoint before any signature math. */ + public boolean isExpired() { + return expiresAt != null && OffsetDateTime.now(ZoneOffset.UTC).isAfter(expiresAt); + } +} diff --git a/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/DscChallengePurpose.java b/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/DscChallengePurpose.java new file mode 100644 index 000000000..030b59f27 --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/DscChallengePurpose.java @@ -0,0 +1,48 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.domain; + +/** + * Why a particular {@code m_dsc_challenge} row was created. + * + *

    + *
  • {@link #ENROLL} — helper returns cert metadata only; server upserts m_bank_user_dsc.
  • + *
  • {@link #LOGIN_2FA} — helper signs the challenge nonce; server verifies the signature + * against the stored public key and grants TWOFACTOR_AUTHENTICATED on success.
  • + *
  • {@link #APPROVAL} — same as LOGIN_2FA but bound to a specific business action. + * Future use; persistence layer is ready.
  • + *
+ */ +public enum DscChallengePurpose { + ENROLL, + LOGIN_2FA, + APPROVAL; + + public static DscChallengePurpose fromString(final String s) { + if (s == null) { + return null; + } + for (final DscChallengePurpose t : values()) { + if (t.name().equalsIgnoreCase(s)) { + return t; + } + } + return null; + } +} diff --git a/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/DscChallengeRepository.java b/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/DscChallengeRepository.java new file mode 100644 index 000000000..265fb8186 --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/DscChallengeRepository.java @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.domain; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface DscChallengeRepository extends JpaRepository { + + /** Single lookup used by every callback / status / verify endpoint. */ + Optional findByRandomNo(String randomNo); +} diff --git a/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/DscChallengeStatus.java b/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/DscChallengeStatus.java new file mode 100644 index 000000000..b4a4454ac --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/DscChallengeStatus.java @@ -0,0 +1,38 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.domain; + +public enum DscChallengeStatus { + PENDING, + VERIFIED, + FAILED, + EXPIRED; + + public static DscChallengeStatus fromString(final String s) { + if (s == null) { + return null; + } + for (final DscChallengeStatus t : values()) { + if (t.name().equalsIgnoreCase(s)) { + return t; + } + } + return null; + } +} diff --git a/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/DscType.java b/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/DscType.java new file mode 100644 index 000000000..71f86cd7f --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/useradministration/dsc/domain/DscType.java @@ -0,0 +1,36 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.domain; + +public enum DscType { + SIGNING, + ENCRYPTION; + + public static DscType fromString(final String s) { + if (s == null) { + return null; + } + for (final DscType t : values()) { + if (t.name().equalsIgnoreCase(s)) { + return t; + } + } + return null; + } +} diff --git a/fineract-provider/logs/fineract.log b/fineract-provider/logs/fineract.log index b4a85d253..eb488c704 100644 --- a/fineract-provider/logs/fineract.log +++ b/fineract-provider/logs/fineract.log @@ -4739,3 +4739,1623 @@ org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationExcep 2026-05-12 11:29:00.082 INFO [Scheduler1_Worker-3] o.s.batch.core.job.SimpleStepHandler : Executing step: [SEND_ASYNCHRONOUS_EVENTS_STEP] 2026-05-12 11:29:00.089 INFO [Scheduler1_Worker-3] o.s.batch.core.step.AbstractStep : Step: [SEND_ASYNCHRONOUS_EVENTS_STEP] executed in 6ms 2026-05-12 11:29:00.094 INFO [Scheduler1_Worker-3] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=SEND_ASYNCHRONOUS_EVENTS]] completed with the following parameters: [{'run.id':'{value=9736, type=class java.lang.Long, identifying=true}'}] and the following status: [COMPLETED] in 18ms +2026-05-12 12:15:12.017 INFO [main] o.apache.fineract.ServerApplication : Starting ServerApplication using Java 21.0.10 with PID 20168 (D:\CompanyProject\GitCBS\JAVA-Backend-CBS-MAIN\fineract-provider\build\classes\java\main started by ny313 in D:\CompanyProject\GitCBS\JAVA-Backend-CBS-MAIN\fineract-provider) +2026-05-12 12:15:12.035 INFO [main] o.apache.fineract.ServerApplication : No active profile set, falling back to 1 default profile: "default" +2026-05-12 12:15:23.847 INFO [main] o.s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode +2026-05-12 12:15:23.848 INFO [main] o.s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. +2026-05-12 12:15:24.994 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.documentmanagement.domain.DocumentRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository +2026-05-12 12:15:24.996 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.documentmanagement.domain.ImageRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository +2026-05-12 12:15:25.002 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.mix.domain.MixReportXBRLNamespaceRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository +2026-05-12 12:15:25.004 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.mix.domain.MixTaxonomyMappingRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository +2026-05-12 12:15:25.073 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.mix.domain.MixTaxonomyRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository +2026-05-12 12:15:25.157 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.command.persistence.domain.CommandRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository +2026-05-12 12:15:26.623 INFO [main] o.s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 2746 ms. Found 214 JPA repository interfaces. +2026-05-12 12:15:26.634 INFO [main] o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean 'jpaAuditingHandler' with a different definition: replacing [Root bean: class=org.springframework.data.auditing.AuditingHandler; scope=; abstract=false; lazyInit=null; autowireMode=2; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=null; factoryMethodName=from; initMethodNames=null; destroyMethodNames=null] with [Root bean: class=org.apache.fineract.infrastructure.core.auditing.CustomAuditingHandler; scope=prototype; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null] +2026-05-12 12:15:26.736 INFO [main] o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean 'transactionManager' with a different definition: replacing [Root bean: class=null; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=JPAConfig; factoryMethodName=transactionManager; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in class path resource [org/apache/fineract/infrastructure/core/config/jpa/JPAConfig.class]] with [Root bean: class=null; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=scheduledJobRunnerConfig; factoryMethodName=transactionManager; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in class path resource [org/apache/fineract/infrastructure/jobs/ScheduledJobRunnerConfig.class]] +2026-05-12 12:15:26.740 INFO [main] o.s.b.c.c.annotation.BatchRegistrar : Bean jobRepository already defined in the application context, skipping the registration of a jobRepository +2026-05-12 12:15:26.740 INFO [main] o.s.b.c.c.annotation.BatchRegistrar : Bean jobExplorer already defined in the application context, skipping the registration of a jobExplorer +2026-05-12 12:15:26.740 INFO [main] o.s.b.c.c.annotation.BatchRegistrar : Bean jobLauncher already defined in the application context, skipping the registration of a jobLauncher +2026-05-12 12:15:26.744 INFO [main] o.s.b.c.c.annotation.BatchRegistrar : Finished Spring Batch infrastructure beans configuration in 4 ms. +2026-05-12 12:15:28.735 INFO [main] o.s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode +2026-05-12 12:15:28.736 INFO [main] o.s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JDBC repositories in DEFAULT mode. +2026-05-12 12:15:29.030 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.provisioning.domain.ProvisioningEntryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.031 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.adhocquery.domain.AdHocRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.031 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.cob.domain.LoanAccountLockRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.033 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormatRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.033 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.bulkimport.domain.ImportDocumentRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.033 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.email.domain.EmailCampaignRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.033 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.email.domain.EmailConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.033 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.email.domain.EmailMessageRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.035 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.sms.domain.SmsCampaignRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.035 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.configuration.domain.ExternalServicesPropertiesRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.035 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditBureauConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.035 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditBureauLoanProductMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.035 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditBureauRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.035 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditReportRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.036 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.OrganisationCreditBureauRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.036 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.TokenRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.036 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.EntityDatatableChecksRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.037 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.RegisteredDatatableRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.037 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.ReportParameterRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.037 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.ReportParameterUsageRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.037 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.ReportRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.038 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityAccessRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.038 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityRelationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.039 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityToEntityMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.039 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.hooks.domain.HookConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.039 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.hooks.domain.HookRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.039 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.hooks.domain.HookTemplateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.039 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.JobParameterRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.040 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.040 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.ScheduledJobRunHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.040 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.SchedulerDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.040 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.service.aggregationjob.domain.JournalEntryAggregationTrackingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.040 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.service.aggregationjob.domain.JournalEntrySummaryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.040 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.041 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.041 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobRunHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.042 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.security.otp.domain.OtpLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.044 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.sms.domain.SmsMessageRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.044 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.survey.domain.LikelihoodRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.044 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.interoperation.domain.InteropIdentifierRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.044 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.notification.domain.NotificationMapperRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.044 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.notification.domain.NotificationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.044 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.holiday.domain.HolidayRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.044 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.office.domain.OfficeTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.046 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.provisioning.domain.ProvisioningCategoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.046 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.provisioning.domain.ProvisioningCriteriaDefinitionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.047 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.provisioning.domain.ProvisioningCriteriaRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.047 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.staff.domain.StaffRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.047 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.048 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.AccountAssociationsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.049 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.049 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.AccountTransferRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.049 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.StandingInstructionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.050 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.address.domain.AddressRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.050 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.address.domain.FieldConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.050 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.calendar.domain.CalendarHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.050 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.051 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.calendar.domain.CalendarRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.051 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.ckyc.domain.CkycBatchRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.051 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientAddressRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.051 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.051 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientFamilyMembersRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.051 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientNonPersonRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.051 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.051 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientTransferDetailsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.051 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagementRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.053 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.collateralmanagement.domain.CollateralManagementDomainRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.053 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.collateral.domain.LoanCollateralRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.053 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.fund.domain.FundRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.054 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.group.domain.GroupLevelRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.054 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.group.domain.GroupRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.054 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.group.domain.GroupRoleRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.054 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorFundingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.055 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorFundingTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.055 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.055 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanproduct.productmix.domain.ProductMixRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.055 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.meeting.attendance.domain.ClientAttendanceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.055 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.meeting.domain.MeetingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.055 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.note.domain.NoteRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.055 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.rate.domain.RateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.056 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDatedChecksRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.056 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.DepositAccountOnHoldTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.056 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FdBaseInterestRateEffectiveRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.056 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FdBaseInterestRateSlabHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.058 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FixedDepositAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.058 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FixedDepositProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.059 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateEffectiveRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.059 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateSlabHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.059 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdInterestRateEffectiveRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.059 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdInterestRateSlabHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.060 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RecurringDepositAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.060 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.account.domain.SelfBeneficiariesTPTRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.060 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.device.domain.DeviceRegistrationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.060 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.pockets.domain.PocketAccountMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.060 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.pockets.domain.PocketRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.060 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.registration.domain.SelfServiceRegistrationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.060 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.060 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.062 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividentPayOutDetailsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.062 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareproducts.domain.ShareProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.063 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.spm.domain.LookupTableRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.063 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.spm.domain.ScorecardRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.063 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.spm.domain.SurveyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.064 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.template.domain.TemplateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.064 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.AppUserPreviousPasswordRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.065 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.AppUserRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.065 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.PasswordValidationPolicyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.066 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.PermissionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.067 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.RoleRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.069 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerJournalEntryMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.069 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerLoanProductAttributesRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.069 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.069 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerTransferJournalEntryMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.069 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerTransferLoanMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.069 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerTransferRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.069 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanorigination.domain.LoanOriginatorMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.069 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanorigination.domain.LoanOriginatorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.071 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.071 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.071 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.071 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyActionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.071 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.072 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.072 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.072 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanAccrualActivityRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.072 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.072 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanApprovedAmountHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.073 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidByRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.073 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.073 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagementRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.073 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetailsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.073 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.075 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.075 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanStatusChangeHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.075 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.075 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.075 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.076 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgingParameterRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.076 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.076 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.076 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.076 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.077 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.077 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.077 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductDepositLimitKycRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.077 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityConfigRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.077 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimitRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.077 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.078 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.078 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartSlabRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.078 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.GSIMRepositoy; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.078 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RecurringDepositProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.078 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.078 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountFacilityConfigRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.078 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.080 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.080 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKycRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.080 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfigRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.080 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimitRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.080 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.080 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.cob.domain.BatchBusinessStepRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.080 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.CashierRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.080 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.CashierTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.081 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.TellerRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.083 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.TellerTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.084 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.closure.domain.GLClosureRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.084 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.financialactivityaccount.domain.FinancialActivityAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.084 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.glaccount.domain.TrialBalanceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.085 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.085 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.085 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.rule.domain.AccountingRuleRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.085 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.floatingrates.domain.FloatingRateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.085 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.charge.domain.ChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.092 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.tax.domain.TaxComponentRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.093 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.tax.domain.TaxGroupRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.094 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.glaccount.domain.GLAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.094 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.commands.domain.CommandSourceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.094 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.businessdate.domain.BusinessDateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.094 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.cache.domain.PlatformCacheRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.094 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.codes.domain.CodeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.096 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.codes.domain.CodeValueRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.096 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.configuration.domain.GlobalConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.096 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.rbifeed.domain.RbiFeedItemRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.096 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.096 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.monetary.domain.OrganisationCurrencyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.097 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.office.domain.OfficeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.098 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.cfcfrms.domain.CfcfrmsSimulatorLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.098 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.cfcfrms.domain.ComplaintAuditLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.099 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.cfcfrms.domain.ComplaintTicketRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.099 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientForm60Repository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.102 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientIdentifierRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.103 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientKycRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.103 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientNomineeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.103 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.103 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientEouRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.103 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientImportExportRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.103 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientMsmeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.103 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientMudraBorrowerRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.104 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientNonPrioritySectorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.104 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientPrioritySectorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.104 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientStartupIndiaRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.104 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.mobilelookup.domain.MobileLookupLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.104 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.105 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.105 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.sanctions.domain.SanctionsEntryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.105 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.sanctions.domain.SanctionsScreeningLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.105 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.dsc.domain.BankUserDscRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.105 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.dsc.domain.DscChallengeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.106 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.mfa.domain.UserMfaFactorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:15:29.480 INFO [main] o.s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 741 ms. Found 6 JDBC repository interfaces. +2026-05-12 12:15:32.812 INFO [main] o.s.i.c.DefaultConfiguringBeanFactoryPostProcessor : No bean named 'errorChannel' has been explicitly defined. Therefore, a default PublishSubscribeChannel will be created. +2026-05-12 12:15:32.827 INFO [main] o.s.i.c.DefaultConfiguringBeanFactoryPostProcessor : No bean named 'integrationHeaderChannelRegistry' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created. +2026-05-12 12:15:34.394 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'fineract-org.apache.fineract.infrastructure.core.config.FineractProperties' of type [org.apache.fineract.infrastructure.core.config.FineractProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:15:34.424 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'cacheConfig' of type [org.apache.fineract.infrastructure.core.config.cache.CacheConfig$$SpringCGLIB$$0] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:15:35.051 INFO [main] org.reflections.Reflections : Reflections took 27 ms to scan 1 urls, producing 0 keys and 0 values +2026-05-12 12:15:35.066 WARN [main] o.a.f.i.c.config.cache.CacheConfig : The following cache configurations are defined but cache does not exists: [userTFAccessToken] +2026-05-12 12:15:35.071 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'ehCacheManager' of type [org.springframework.cache.jcache.JCacheCacheManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:15:35.101 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'defaultCacheManager' of type [org.apache.fineract.infrastructure.core.config.cache.TransactionBoundCacheManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:15:35.117 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'runtimeDelegatingCacheManager' of type [org.apache.fineract.infrastructure.cache.service.RuntimeDelegatingCacheManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:15:35.128 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'platformCacheConfiguration' of type [org.apache.fineract.infrastructure.cache.PlatformCacheConfiguration$$SpringCGLIB$$0] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:15:35.254 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'samplingConfiguration' of type [org.apache.fineract.infrastructure.core.diagnostics.performance.sampling.core.SamplingConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:15:35.270 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'samplingServiceFactory' of type [org.apache.fineract.infrastructure.core.diagnostics.performance.sampling.core.SamplingServiceFactory] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:15:36.960 INFO [main] o.s.b.w.e.tomcat.TomcatWebServer : Tomcat initialized with port 8443 (https) +2026-05-12 12:15:36.985 INFO [main] o.a.catalina.core.StandardService : Starting service [Tomcat] +2026-05-12 12:15:36.985 INFO [main] o.a.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.49] +2026-05-12 12:15:37.036 INFO [main] o.a.c.c.C.[.[.[/fineract-provider] : Initializing Spring embedded WebApplicationContext +2026-05-12 12:15:37.038 INFO [main] o.s.b.w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 24961 ms +2026-05-12 12:15:37.324 INFO [main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... +2026-05-12 12:15:37.635 INFO [main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@616a370b +2026-05-12 12:15:37.636 INFO [main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. +2026-05-12 12:15:37.914 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : Upgrading tenant store DB at localhost:5432 +2026-05-12 12:15:37.914 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.username: postgres +2026-05-12 12:15:37.914 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.password: **** +2026-05-12 12:15:37.914 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.parameters: +2026-05-12 12:15:37.914 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.timezone: Asia/Kolkata +2026-05-12 12:15:37.914 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.description: Default Demo Tenant +2026-05-12 12:15:37.914 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.identifier: default +2026-05-12 12:15:37.914 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.name: fineract_default +2026-05-12 12:15:37.914 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - liquibase.analytics.enabled: false +2026-05-12 12:15:55.759 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : Tenant store upgrade finished +2026-05-12 12:15:55.761 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : Upgrading all tenants +2026-05-12 12:15:55.780 INFO [ThreadPoolTaskExecutor-1] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : Upgrade for tenant default has started +2026-05-12 12:15:56.406 INFO [ThreadPoolTaskExecutor-1] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Starting... +2026-05-12 12:15:56.460 INFO [ThreadPoolTaskExecutor-1] com.zaxxer.hikari.pool.HikariPool : HikariPool-2 - Added connection org.postgresql.jdbc.PgConnection@44713ebc +2026-05-12 12:15:56.461 INFO [ThreadPoolTaskExecutor-1] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Start completed. +2026-05-12 12:16:10.442 ERROR [ThreadPoolTaskExecutor-1] liquibase.changelog : ChangeSet db/changelog/tenant/parts/0295_unique_email_mobile_on_m_appuser.xml::0295_add_unique_email_to_m_appuser::fineract encountered an exception. +liquibase.exception.DatabaseException: ERROR: could not create unique index "uq_m_appuser_email" + Detail: Key (email)=(demomfi@mifos.org) is duplicated. [Failed SQL: (0) ALTER TABLE public.m_appuser ADD CONSTRAINT uq_m_appuser_email UNIQUE (email)] + at liquibase.executor.jvm.JdbcExecutor$ExecuteStatementCallback.doInStatement(JdbcExecutor.java:520) + at liquibase.executor.jvm.JdbcExecutor.execute(JdbcExecutor.java:85) + at liquibase.executor.jvm.JdbcExecutor.execute(JdbcExecutor.java:188) + at liquibase.executor.AbstractExecutor.execute(AbstractExecutor.java:148) + at liquibase.database.AbstractJdbcDatabase.executeStatements(AbstractJdbcDatabase.java:1198) + at liquibase.changelog.ChangeSet.execute(ChangeSet.java:816) + at liquibase.changelog.visitor.UpdateVisitor.executeAcceptedChange(UpdateVisitor.java:127) + at liquibase.changelog.visitor.UpdateVisitor.visit(UpdateVisitor.java:71) + at liquibase.changelog.ChangeLogIterator.lambda$run$0(ChangeLogIterator.java:133) + at liquibase.Scope.lambda$child$0(Scope.java:216) + at liquibase.Scope.child(Scope.java:225) + at liquibase.Scope.child(Scope.java:215) + at liquibase.Scope.child(Scope.java:194) + at liquibase.changelog.ChangeLogIterator.lambda$run$1(ChangeLogIterator.java:122) + at liquibase.Scope.lambda$child$0(Scope.java:216) + at liquibase.Scope.child(Scope.java:225) + at liquibase.Scope.child(Scope.java:215) + at liquibase.Scope.child(Scope.java:194) + at liquibase.Scope.child(Scope.java:282) + at liquibase.Scope.child(Scope.java:286) + at liquibase.changelog.ChangeLogIterator.run(ChangeLogIterator.java:91) + at liquibase.command.core.AbstractUpdateCommandStep.lambda$run$0(AbstractUpdateCommandStep.java:114) + at liquibase.Scope.lambda$child$0(Scope.java:216) + at liquibase.Scope.child(Scope.java:225) + at liquibase.Scope.child(Scope.java:215) + at liquibase.Scope.child(Scope.java:194) + at liquibase.command.core.AbstractUpdateCommandStep.run(AbstractUpdateCommandStep.java:112) + at liquibase.command.core.UpdateCommandStep.run(UpdateCommandStep.java:100) + at liquibase.command.CommandScope.lambda$execute$6(CommandScope.java:263) + at liquibase.Scope.child(Scope.java:225) + at liquibase.Scope.child(Scope.java:201) + at liquibase.command.CommandScope.execute(CommandScope.java:251) + at liquibase.Liquibase.lambda$update$0(Liquibase.java:216) + at liquibase.Scope.lambda$child$0(Scope.java:216) + at liquibase.Scope.child(Scope.java:225) + at liquibase.Scope.child(Scope.java:215) + at liquibase.Scope.child(Scope.java:194) + at liquibase.Liquibase.runInScope(Liquibase.java:1366) + at liquibase.Liquibase.update(Liquibase.java:205) + at liquibase.Liquibase.update(Liquibase.java:188) + at liquibase.integration.spring.SpringLiquibase.performUpdate(SpringLiquibase.java:327) + at liquibase.integration.spring.SpringLiquibase.lambda$afterPropertiesSet$0(SpringLiquibase.java:279) + at liquibase.Scope.lambda$child$0(Scope.java:216) + at liquibase.Scope.child(Scope.java:225) + at liquibase.Scope.child(Scope.java:215) + at liquibase.Scope.child(Scope.java:194) + at liquibase.integration.spring.SpringLiquibase.afterPropertiesSet(SpringLiquibase.java:272) + at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.upgradeIndividualTenant(TenantDatabaseUpgradeService.java:205) + at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.lambda$upgradeIndividualTenants$1(TenantDatabaseUpgradeService.java:146) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) + at java.base/java.lang.Thread.run(Thread.java:1583) +Caused by: org.postgresql.util.PSQLException: ERROR: could not create unique index "uq_m_appuser_email" + Detail: Key (email)=(demomfi@mifos.org) is duplicated. + at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2736) + at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2421) + at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:372) + at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:525) + at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:435) + at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:357) + at org.postgresql.jdbc.PgStatement.executeCachedSql(PgStatement.java:342) + at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:318) + at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:313) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:95) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at liquibase.executor.jvm.JdbcExecutor$ExecuteStatementCallback.doInStatement(JdbcExecutor.java:512) + ... 52 common frames omitted +2026-05-12 12:16:10.585 INFO [ThreadPoolTaskExecutor-1] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Shutdown initiated... +2026-05-12 12:16:10.591 INFO [ThreadPoolTaskExecutor-1] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Shutdown completed. +2026-05-12 12:16:10.594 ERROR [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : Exception: +java.util.concurrent.ExecutionException: java.lang.RuntimeException: Exception while upgrading tenant default + at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122) + at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191) + at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.upgradeIndividualTenants(TenantDatabaseUpgradeService.java:155) + at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.afterPropertiesSet(TenantDatabaseUpgradeService.java:93) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1873) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1822) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:607) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:365) + at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:135) + at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:691) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:513) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1375) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1205) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:365) + at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:135) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1725) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1474) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:606) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1698) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791) + at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1760) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791) + at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1760) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791) + at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansWithAnnotation(DefaultListableBeanFactory.java:796) + at org.springframework.context.support.AbstractApplicationContext.getBeansWithAnnotation(AbstractApplicationContext.java:1440) + at org.apache.fineract.infrastructure.core.jersey.JerseyConfig.setup(JerseyConfig.java:58) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMethod.invoke(InitDestroyAnnotationBeanPostProcessor.java:457) + at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:401) + at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:219) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:429) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1818) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:607) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1760) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791) + at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:413) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1375) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1205) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207) + at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:230) + at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:221) + at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addServletContextInitializerBeans(ServletContextInitializerBeans.java:106) + at org.springframework.boot.web.servlet.ServletContextInitializerBeans.(ServletContextInitializerBeans.java:95) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getServletContextInitializerBeans(ServletWebServerApplicationContext.java:271) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.selfInitialize(ServletWebServerApplicationContext.java:245) + at org.springframework.boot.web.embedded.tomcat.TomcatStarter.onStartup(TomcatStarter.java:52) + at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:4452) + at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164) + at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1201) + at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1191) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) + at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:81) + at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145) + at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:747) + at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:770) + at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164) + at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1201) + at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1191) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) + at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:81) + at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145) + at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:747) + at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:201) + at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164) + at org.apache.catalina.core.StandardService.startInternal(StandardService.java:410) + at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164) + at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:868) + at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164) + at org.apache.catalina.startup.Tomcat.start(Tomcat.java:436) + at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:128) + at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.(TomcatWebServer.java:107) + at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:517) + at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:219) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:193) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:167) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:621) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) + at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:149) + at org.apache.fineract.ServerApplication.main(ServerApplication.java:57) +Caused by: java.lang.RuntimeException: Exception while upgrading tenant default + at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.upgradeIndividualTenant(TenantDatabaseUpgradeService.java:208) + at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.lambda$upgradeIndividualTenants$1(TenantDatabaseUpgradeService.java:146) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) + at java.base/java.lang.Thread.run(Thread.java:1583) +Caused by: liquibase.exception.LiquibaseException: liquibase.exception.CommandExecutionException: liquibase.exception.LiquibaseException: liquibase.exception.MigrationFailedException: Migration failed for changeset db/changelog/tenant/parts/0295_unique_email_mobile_on_m_appuser.xml::0295_add_unique_email_to_m_appuser::fineract: + Reason: liquibase.exception.DatabaseException: ERROR: could not create unique index "uq_m_appuser_email" + Detail: Key (email)=(demomfi@mifos.org) is duplicated. [Failed SQL: (0) ALTER TABLE public.m_appuser ADD CONSTRAINT uq_m_appuser_email UNIQUE (email)] + at liquibase.integration.spring.SpringLiquibase.afterPropertiesSet(SpringLiquibase.java:289) + at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.upgradeIndividualTenant(TenantDatabaseUpgradeService.java:205) + ... 5 common frames omitted +Caused by: liquibase.exception.CommandExecutionException: liquibase.exception.LiquibaseException: liquibase.exception.MigrationFailedException: Migration failed for changeset db/changelog/tenant/parts/0295_unique_email_mobile_on_m_appuser.xml::0295_add_unique_email_to_m_appuser::fineract: + Reason: liquibase.exception.DatabaseException: ERROR: could not create unique index "uq_m_appuser_email" + Detail: Key (email)=(demomfi@mifos.org) is duplicated. [Failed SQL: (0) ALTER TABLE public.m_appuser ADD CONSTRAINT uq_m_appuser_email UNIQUE (email)] + at liquibase.command.CommandScope.lambda$execute$6(CommandScope.java:310) + at liquibase.Scope.child(Scope.java:225) + at liquibase.Scope.child(Scope.java:201) + at liquibase.command.CommandScope.execute(CommandScope.java:251) + at liquibase.Liquibase.lambda$update$0(Liquibase.java:216) + at liquibase.Scope.lambda$child$0(Scope.java:216) + at liquibase.Scope.child(Scope.java:225) + at liquibase.Scope.child(Scope.java:215) + at liquibase.Scope.child(Scope.java:194) + at liquibase.Liquibase.runInScope(Liquibase.java:1366) + at liquibase.Liquibase.update(Liquibase.java:205) + at liquibase.Liquibase.update(Liquibase.java:188) + at liquibase.integration.spring.SpringLiquibase.performUpdate(SpringLiquibase.java:327) + at liquibase.integration.spring.SpringLiquibase.lambda$afterPropertiesSet$0(SpringLiquibase.java:279) + at liquibase.Scope.lambda$child$0(Scope.java:216) + at liquibase.Scope.child(Scope.java:225) + at liquibase.Scope.child(Scope.java:215) + at liquibase.Scope.child(Scope.java:194) + at liquibase.integration.spring.SpringLiquibase.afterPropertiesSet(SpringLiquibase.java:272) + ... 6 common frames omitted +Caused by: liquibase.exception.LiquibaseException: liquibase.exception.MigrationFailedException: Migration failed for changeset db/changelog/tenant/parts/0295_unique_email_mobile_on_m_appuser.xml::0295_add_unique_email_to_m_appuser::fineract: + Reason: liquibase.exception.DatabaseException: ERROR: could not create unique index "uq_m_appuser_email" + Detail: Key (email)=(demomfi@mifos.org) is duplicated. [Failed SQL: (0) ALTER TABLE public.m_appuser ADD CONSTRAINT uq_m_appuser_email UNIQUE (email)] + at liquibase.changelog.ChangeLogIterator.run(ChangeLogIterator.java:155) + at liquibase.command.core.AbstractUpdateCommandStep.lambda$run$0(AbstractUpdateCommandStep.java:114) + at liquibase.Scope.lambda$child$0(Scope.java:216) + at liquibase.Scope.child(Scope.java:225) + at liquibase.Scope.child(Scope.java:215) + at liquibase.Scope.child(Scope.java:194) + at liquibase.command.core.AbstractUpdateCommandStep.run(AbstractUpdateCommandStep.java:112) + at liquibase.command.core.UpdateCommandStep.run(UpdateCommandStep.java:100) + at liquibase.command.CommandScope.lambda$execute$6(CommandScope.java:263) + ... 24 common frames omitted +Caused by: liquibase.exception.MigrationFailedException: Migration failed for changeset db/changelog/tenant/parts/0295_unique_email_mobile_on_m_appuser.xml::0295_add_unique_email_to_m_appuser::fineract: + Reason: liquibase.exception.DatabaseException: ERROR: could not create unique index "uq_m_appuser_email" + Detail: Key (email)=(demomfi@mifos.org) is duplicated. [Failed SQL: (0) ALTER TABLE public.m_appuser ADD CONSTRAINT uq_m_appuser_email UNIQUE (email)] + at liquibase.changelog.ChangeSet.execute(ChangeSet.java:873) + at liquibase.changelog.visitor.UpdateVisitor.executeAcceptedChange(UpdateVisitor.java:127) + at liquibase.changelog.visitor.UpdateVisitor.visit(UpdateVisitor.java:71) + at liquibase.changelog.ChangeLogIterator.lambda$run$0(ChangeLogIterator.java:133) + at liquibase.Scope.lambda$child$0(Scope.java:216) + at liquibase.Scope.child(Scope.java:225) + at liquibase.Scope.child(Scope.java:215) + at liquibase.Scope.child(Scope.java:194) + at liquibase.changelog.ChangeLogIterator.lambda$run$1(ChangeLogIterator.java:122) + at liquibase.Scope.lambda$child$0(Scope.java:216) + at liquibase.Scope.child(Scope.java:225) + at liquibase.Scope.child(Scope.java:215) + at liquibase.Scope.child(Scope.java:194) + at liquibase.Scope.child(Scope.java:282) + at liquibase.Scope.child(Scope.java:286) + at liquibase.changelog.ChangeLogIterator.run(ChangeLogIterator.java:91) + ... 32 common frames omitted +Caused by: liquibase.exception.DatabaseException: ERROR: could not create unique index "uq_m_appuser_email" + Detail: Key (email)=(demomfi@mifos.org) is duplicated. [Failed SQL: (0) ALTER TABLE public.m_appuser ADD CONSTRAINT uq_m_appuser_email UNIQUE (email)] + at liquibase.executor.jvm.JdbcExecutor$ExecuteStatementCallback.doInStatement(JdbcExecutor.java:520) + at liquibase.executor.jvm.JdbcExecutor.execute(JdbcExecutor.java:85) + at liquibase.executor.jvm.JdbcExecutor.execute(JdbcExecutor.java:188) + at liquibase.executor.AbstractExecutor.execute(AbstractExecutor.java:148) + at liquibase.database.AbstractJdbcDatabase.executeStatements(AbstractJdbcDatabase.java:1198) + at liquibase.changelog.ChangeSet.execute(ChangeSet.java:816) + ... 47 common frames omitted +Caused by: org.postgresql.util.PSQLException: ERROR: could not create unique index "uq_m_appuser_email" + Detail: Key (email)=(demomfi@mifos.org) is duplicated. + at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2736) + at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2421) + at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:372) + at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:525) + at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:435) + at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:357) + at org.postgresql.jdbc.PgStatement.executeCachedSql(PgStatement.java:342) + at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:318) + at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:313) + at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:95) + at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) + at liquibase.executor.jvm.JdbcExecutor$ExecuteStatementCallback.doInStatement(JdbcExecutor.java:512) + ... 52 common frames omitted +2026-05-12 12:16:10.616 ERROR [main] o.s.b.w.e.tomcat.TomcatStarter : Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. Message: Error creating bean with name 'org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration': Unsatisfied dependency expressed through constructor parameter 1: Error creating bean with name 'jerseyConfig': Invocation of init method failed +2026-05-12 12:16:10.771 INFO [main] o.a.catalina.core.StandardService : Stopping service [Tomcat] +2026-05-12 12:16:10.781 WARN [main] o.a.c.loader.WebappClassLoaderBase : The web application [fineract-provider] appears to have started a thread named [HikariPool-1:housekeeper] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread: + java.base/jdk.internal.misc.Unsafe.park(Native Method) + java.base/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:269) + java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:1763) + java.base/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1182) + java.base/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:899) + java.base/java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1070) + java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) + java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) + java.base/java.lang.Thread.run(Thread.java:1583) +2026-05-12 12:16:10.786 WARN [main] o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Unable to start web server +2026-05-12 12:16:10.787 INFO [main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... +2026-05-12 12:16:10.797 INFO [main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed. +2026-05-12 12:16:10.925 INFO [main] o.s.b.a.l.ConditionEvaluationReportLogger : + +Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. +2026-05-12 12:16:11.039 ERROR [main] o.s.boot.SpringApplication : Application run failed +org.springframework.context.ApplicationContextException: Unable to start web server + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:170) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:621) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) + at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:149) + at org.apache.fineract.ServerApplication.main(ServerApplication.java:57) +Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat + at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:147) + at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.(TomcatWebServer.java:107) + at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:517) + at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:219) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:193) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:167) + ... 7 common frames omitted +Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration': Unsatisfied dependency expressed through constructor parameter 1: Error creating bean with name 'jerseyConfig': Invocation of init method failed + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:804) + at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:413) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1375) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1205) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207) + at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:230) + at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:221) + at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addServletContextInitializerBeans(ServletContextInitializerBeans.java:106) + at org.springframework.boot.web.servlet.ServletContextInitializerBeans.(ServletContextInitializerBeans.java:95) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getServletContextInitializerBeans(ServletWebServerApplicationContext.java:271) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.selfInitialize(ServletWebServerApplicationContext.java:245) + at org.springframework.boot.web.embedded.tomcat.TomcatStarter.onStartup(TomcatStarter.java:52) + at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:4452) + at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164) + at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1201) + at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1191) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) + at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:81) + at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145) + at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:747) + at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:770) + at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164) + at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1201) + at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1191) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) + at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:81) + at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145) + at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:747) + at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:201) + at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164) + at org.apache.catalina.core.StandardService.startInternal(StandardService.java:410) + at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164) + at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:868) + at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:164) + at org.apache.catalina.startup.Tomcat.start(Tomcat.java:436) + at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:128) + ... 12 common frames omitted +Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jerseyConfig': Invocation of init method failed + at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:222) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:429) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1818) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:607) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1760) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791) + ... 61 common frames omitted +Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'journalEntriesApiResource' defined in file [D:\CompanyProject\GitCBS\JAVA-Backend-CBS-MAIN\fineract-provider\build\classes\java\main\org\apache\fineract\accounting\journalentry\api\JournalEntriesApiResource.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'springSecurityPlatformSecurityContext' defined in URL [jar:file:/D:/CompanyProject/GitCBS/JAVA-Backend-CBS-MAIN/fineract-security/build/libs/fineract-security-0.1.0-SNAPSHOT.jar!/org/apache/fineract/infrastructure/security/service/SpringSecurityPlatformSecurityContext.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'configurationDomainServiceJpa' defined in file [D:\CompanyProject\GitCBS\JAVA-Backend-CBS-MAIN\fineract-provider\build\classes\java\main\org\apache\fineract\infrastructure\configuration\domain\ConfigurationDomainServiceJpa.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'permissionRepository' defined in org.apache.fineract.useradministration.domain.PermissionRepository defined in @EnableJpaRepositories declared on JPAConfig: Cannot resolve reference to bean 'jpaSharedEM_entityManagerFactory' while setting bean property 'entityManager' + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:804) + at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansWithAnnotation(DefaultListableBeanFactory.java:796) + at org.springframework.context.support.AbstractApplicationContext.getBeansWithAnnotation(AbstractApplicationContext.java:1440) + at org.apache.fineract.infrastructure.core.jersey.JerseyConfig.setup(JerseyConfig.java:58) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMethod.invoke(InitDestroyAnnotationBeanPostProcessor.java:457) + at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:401) + at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:219) + ... 74 common frames omitted +Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'springSecurityPlatformSecurityContext' defined in URL [jar:file:/D:/CompanyProject/GitCBS/JAVA-Backend-CBS-MAIN/fineract-security/build/libs/fineract-security-0.1.0-SNAPSHOT.jar!/org/apache/fineract/infrastructure/security/service/SpringSecurityPlatformSecurityContext.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'configurationDomainServiceJpa' defined in file [D:\CompanyProject\GitCBS\JAVA-Backend-CBS-MAIN\fineract-provider\build\classes\java\main\org\apache\fineract\infrastructure\configuration\domain\ConfigurationDomainServiceJpa.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'permissionRepository' defined in org.apache.fineract.useradministration.domain.PermissionRepository defined in @EnableJpaRepositories declared on JPAConfig: Cannot resolve reference to bean 'jpaSharedEM_entityManagerFactory' while setting bean property 'entityManager' + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:804) + at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1760) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791) + ... 91 common frames omitted +Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'configurationDomainServiceJpa' defined in file [D:\CompanyProject\GitCBS\JAVA-Backend-CBS-MAIN\fineract-provider\build\classes\java\main\org\apache\fineract\infrastructure\configuration\domain\ConfigurationDomainServiceJpa.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'permissionRepository' defined in org.apache.fineract.useradministration.domain.PermissionRepository defined in @EnableJpaRepositories declared on JPAConfig: Cannot resolve reference to bean 'jpaSharedEM_entityManagerFactory' while setting bean property 'entityManager' + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:804) + at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1395) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1232) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1760) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791) + ... 105 common frames omitted +Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'permissionRepository' defined in org.apache.fineract.useradministration.domain.PermissionRepository defined in @EnableJpaRepositories declared on JPAConfig: Cannot resolve reference to bean 'jpaSharedEM_entityManagerFactory' while setting bean property 'entityManager' + at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:377) + at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:135) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1725) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1474) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:606) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1698) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1643) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791) + ... 119 common frames omitted +Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaSharedEM_entityManagerFactory': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument + at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:377) + at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:135) + at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:691) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:513) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1375) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1205) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:569) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:365) + ... 132 common frames omitted +Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'tenantDatabaseUpgradeService' defined in file [D:\CompanyProject\GitCBS\JAVA-Backend-CBS-MAIN\fineract-provider\build\classes\java\main\org\apache\fineract\infrastructure\core\service\migration\TenantDatabaseUpgradeService.class]: Tenant upgrades had exceptions + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1826) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:607) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:373) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) + at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:365) + ... 144 common frames omitted +Caused by: java.lang.RuntimeException: Tenant upgrades had exceptions + at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.upgradeIndividualTenants(TenantDatabaseUpgradeService.java:167) + at org.apache.fineract.infrastructure.core.service.migration.TenantDatabaseUpgradeService.afterPropertiesSet(TenantDatabaseUpgradeService.java:93) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1873) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1822) + ... 153 common frames omitted +2026-05-12 12:23:33.585 INFO [main] o.apache.fineract.ServerApplication : Starting ServerApplication using Java 21.0.10 with PID 20308 (D:\CompanyProject\GitCBS\JAVA-Backend-CBS-MAIN\fineract-provider\build\classes\java\main started by ny313 in D:\CompanyProject\GitCBS\JAVA-Backend-CBS-MAIN\fineract-provider) +2026-05-12 12:23:33.591 INFO [main] o.apache.fineract.ServerApplication : No active profile set, falling back to 1 default profile: "default" +2026-05-12 12:23:43.148 INFO [main] o.s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode +2026-05-12 12:23:43.149 INFO [main] o.s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. +2026-05-12 12:23:45.136 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.documentmanagement.domain.DocumentRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository +2026-05-12 12:23:45.138 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.documentmanagement.domain.ImageRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository +2026-05-12 12:23:45.147 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.mix.domain.MixReportXBRLNamespaceRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository +2026-05-12 12:23:45.150 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.mix.domain.MixTaxonomyMappingRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository +2026-05-12 12:23:45.152 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.mix.domain.MixTaxonomyRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository +2026-05-12 12:23:45.271 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.command.persistence.domain.CommandRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository +2026-05-12 12:23:47.710 INFO [main] o.s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 4532 ms. Found 214 JPA repository interfaces. +2026-05-12 12:23:47.719 INFO [main] o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean 'jpaAuditingHandler' with a different definition: replacing [Root bean: class=org.springframework.data.auditing.AuditingHandler; scope=; abstract=false; lazyInit=null; autowireMode=2; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=null; factoryMethodName=from; initMethodNames=null; destroyMethodNames=null] with [Root bean: class=org.apache.fineract.infrastructure.core.auditing.CustomAuditingHandler; scope=prototype; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null] +2026-05-12 12:23:47.792 INFO [main] o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean 'transactionManager' with a different definition: replacing [Root bean: class=null; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=JPAConfig; factoryMethodName=transactionManager; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in class path resource [org/apache/fineract/infrastructure/core/config/jpa/JPAConfig.class]] with [Root bean: class=null; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=scheduledJobRunnerConfig; factoryMethodName=transactionManager; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in class path resource [org/apache/fineract/infrastructure/jobs/ScheduledJobRunnerConfig.class]] +2026-05-12 12:23:47.796 INFO [main] o.s.b.c.c.annotation.BatchRegistrar : Bean jobRepository already defined in the application context, skipping the registration of a jobRepository +2026-05-12 12:23:47.796 INFO [main] o.s.b.c.c.annotation.BatchRegistrar : Bean jobExplorer already defined in the application context, skipping the registration of a jobExplorer +2026-05-12 12:23:47.796 INFO [main] o.s.b.c.c.annotation.BatchRegistrar : Bean jobLauncher already defined in the application context, skipping the registration of a jobLauncher +2026-05-12 12:23:47.798 INFO [main] o.s.b.c.c.annotation.BatchRegistrar : Finished Spring Batch infrastructure beans configuration in 3 ms. +2026-05-12 12:23:48.848 INFO [main] o.s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode +2026-05-12 12:23:48.849 INFO [main] o.s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JDBC repositories in DEFAULT mode. +2026-05-12 12:23:49.419 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.provisioning.domain.ProvisioningEntryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.421 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.adhocquery.domain.AdHocRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.422 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.cob.domain.LoanAccountLockRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.425 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormatRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.426 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.bulkimport.domain.ImportDocumentRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.426 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.email.domain.EmailCampaignRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.427 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.email.domain.EmailConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.428 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.email.domain.EmailMessageRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.429 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.sms.domain.SmsCampaignRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.429 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.configuration.domain.ExternalServicesPropertiesRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.430 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditBureauConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.430 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditBureauLoanProductMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.431 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditBureauRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.432 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditReportRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.433 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.OrganisationCreditBureauRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.433 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.TokenRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.434 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.EntityDatatableChecksRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.435 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.RegisteredDatatableRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.435 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.ReportParameterRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.436 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.ReportParameterUsageRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.436 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.ReportRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.437 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityAccessRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.437 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityRelationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.438 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityToEntityMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.439 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.hooks.domain.HookConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.439 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.hooks.domain.HookRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.440 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.hooks.domain.HookTemplateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.440 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.JobParameterRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.441 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.441 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.ScheduledJobRunHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.441 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.SchedulerDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.443 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.service.aggregationjob.domain.JournalEntryAggregationTrackingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.443 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.service.aggregationjob.domain.JournalEntrySummaryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.444 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.444 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.447 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobRunHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.450 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.security.otp.domain.OtpLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.451 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.sms.domain.SmsMessageRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.451 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.survey.domain.LikelihoodRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.451 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.interoperation.domain.InteropIdentifierRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.453 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.notification.domain.NotificationMapperRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.454 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.notification.domain.NotificationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.454 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.holiday.domain.HolidayRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.454 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.office.domain.OfficeTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.455 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.provisioning.domain.ProvisioningCategoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.456 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.provisioning.domain.ProvisioningCriteriaDefinitionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.457 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.provisioning.domain.ProvisioningCriteriaRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.457 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.staff.domain.StaffRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.458 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.459 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.AccountAssociationsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.460 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.460 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.AccountTransferRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.461 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.StandingInstructionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.462 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.address.domain.AddressRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.462 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.address.domain.FieldConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.464 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.calendar.domain.CalendarHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.464 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.465 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.calendar.domain.CalendarRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.466 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.ckyc.domain.CkycBatchRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.466 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientAddressRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.466 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.467 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientFamilyMembersRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.467 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientNonPersonRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.467 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.467 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientTransferDetailsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.469 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagementRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.470 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.collateralmanagement.domain.CollateralManagementDomainRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.471 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.collateral.domain.LoanCollateralRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.472 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.fund.domain.FundRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.472 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.group.domain.GroupLevelRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.472 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.group.domain.GroupRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.474 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.group.domain.GroupRoleRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.474 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorFundingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.474 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorFundingTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.475 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.475 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanproduct.productmix.domain.ProductMixRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.475 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.meeting.attendance.domain.ClientAttendanceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.477 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.meeting.domain.MeetingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.477 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.note.domain.NoteRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.477 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.rate.domain.RateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.477 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDatedChecksRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.478 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.DepositAccountOnHoldTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.478 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FdBaseInterestRateEffectiveRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.479 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FdBaseInterestRateSlabHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.484 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FixedDepositAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.485 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FixedDepositProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.485 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateEffectiveRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.486 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateSlabHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.486 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdInterestRateEffectiveRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.487 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdInterestRateSlabHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.487 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RecurringDepositAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.488 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.account.domain.SelfBeneficiariesTPTRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.489 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.device.domain.DeviceRegistrationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.490 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.pockets.domain.PocketAccountMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.490 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.pockets.domain.PocketRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.491 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.registration.domain.SelfServiceRegistrationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.491 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.492 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.492 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividentPayOutDetailsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.492 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareproducts.domain.ShareProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.494 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.spm.domain.LookupTableRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.494 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.spm.domain.ScorecardRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.494 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.spm.domain.SurveyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.494 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.template.domain.TemplateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.496 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.AppUserPreviousPasswordRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.498 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.AppUserRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.499 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.PasswordValidationPolicyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.500 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.PermissionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.501 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.RoleRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.502 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerJournalEntryMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.502 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerLoanProductAttributesRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.503 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.503 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerTransferJournalEntryMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.504 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerTransferLoanMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.505 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerTransferRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.505 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanorigination.domain.LoanOriginatorMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.505 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanorigination.domain.LoanOriginatorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.505 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.507 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.507 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.507 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyActionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.508 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.508 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.509 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.509 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanAccrualActivityRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.510 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.510 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanApprovedAmountHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.512 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidByRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.513 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.514 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagementRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.514 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetailsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.515 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.515 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.515 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanStatusChangeHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.515 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.517 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.517 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.517 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgingParameterRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.519 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.519 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.519 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.520 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.520 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.521 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.521 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductDepositLimitKycRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.522 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityConfigRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.522 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimitRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.523 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.523 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.523 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartSlabRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.525 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.GSIMRepositoy; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.525 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RecurringDepositProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.526 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.526 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountFacilityConfigRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.526 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.527 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.528 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKycRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.528 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfigRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.529 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimitRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.529 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.531 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.cob.domain.BatchBusinessStepRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.531 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.CashierRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.531 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.CashierTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.533 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.TellerRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.533 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.TellerTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.533 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.closure.domain.GLClosureRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.535 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.financialactivityaccount.domain.FinancialActivityAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.535 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.glaccount.domain.TrialBalanceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.535 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.536 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.536 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.rule.domain.AccountingRuleRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.538 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.floatingrates.domain.FloatingRateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.538 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.charge.domain.ChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.549 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.tax.domain.TaxComponentRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.551 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.tax.domain.TaxGroupRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.557 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.glaccount.domain.GLAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.557 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.commands.domain.CommandSourceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.558 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.businessdate.domain.BusinessDateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.559 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.cache.domain.PlatformCacheRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.560 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.codes.domain.CodeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.562 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.codes.domain.CodeValueRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.564 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.configuration.domain.GlobalConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.564 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.rbifeed.domain.RbiFeedItemRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.565 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.565 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.monetary.domain.OrganisationCurrencyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.567 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.office.domain.OfficeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.567 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.cfcfrms.domain.CfcfrmsSimulatorLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.568 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.cfcfrms.domain.ComplaintAuditLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.569 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.cfcfrms.domain.ComplaintTicketRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.570 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientForm60Repository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.577 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientIdentifierRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.578 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientKycRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.580 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientNomineeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.582 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.583 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientEouRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.583 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientImportExportRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.585 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientMsmeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.586 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientMudraBorrowerRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.587 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientNonPrioritySectorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.588 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientPrioritySectorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.593 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientStartupIndiaRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.593 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.mobilelookup.domain.MobileLookupLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.595 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.595 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.597 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.sanctions.domain.SanctionsEntryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.598 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.sanctions.domain.SanctionsScreeningLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.598 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.dsc.domain.BankUserDscRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.599 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.dsc.domain.DscChallengeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:49.599 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.mfa.domain.UserMfaFactorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:23:50.350 INFO [main] o.s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 1499 ms. Found 6 JDBC repository interfaces. +2026-05-12 12:23:53.529 INFO [main] o.s.i.c.DefaultConfiguringBeanFactoryPostProcessor : No bean named 'errorChannel' has been explicitly defined. Therefore, a default PublishSubscribeChannel will be created. +2026-05-12 12:23:53.549 INFO [main] o.s.i.c.DefaultConfiguringBeanFactoryPostProcessor : No bean named 'integrationHeaderChannelRegistry' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created. +2026-05-12 12:23:55.121 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'fineract-org.apache.fineract.infrastructure.core.config.FineractProperties' of type [org.apache.fineract.infrastructure.core.config.FineractProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:23:55.138 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'cacheConfig' of type [org.apache.fineract.infrastructure.core.config.cache.CacheConfig$$SpringCGLIB$$0] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:23:55.350 INFO [main] org.reflections.Reflections : Reflections took 10 ms to scan 1 urls, producing 0 keys and 0 values +2026-05-12 12:23:55.355 WARN [main] o.a.f.i.c.config.cache.CacheConfig : The following cache configurations are defined but cache does not exists: [userTFAccessToken] +2026-05-12 12:23:55.356 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'ehCacheManager' of type [org.springframework.cache.jcache.JCacheCacheManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:23:55.373 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'defaultCacheManager' of type [org.apache.fineract.infrastructure.core.config.cache.TransactionBoundCacheManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:23:55.382 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'runtimeDelegatingCacheManager' of type [org.apache.fineract.infrastructure.cache.service.RuntimeDelegatingCacheManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:23:55.386 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'platformCacheConfiguration' of type [org.apache.fineract.infrastructure.cache.PlatformCacheConfiguration$$SpringCGLIB$$0] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:23:55.438 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'samplingConfiguration' of type [org.apache.fineract.infrastructure.core.diagnostics.performance.sampling.core.SamplingConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:23:55.446 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'samplingServiceFactory' of type [org.apache.fineract.infrastructure.core.diagnostics.performance.sampling.core.SamplingServiceFactory] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:23:56.441 INFO [main] o.s.b.w.e.tomcat.TomcatWebServer : Tomcat initialized with port 8443 (https) +2026-05-12 12:23:56.463 INFO [main] o.a.catalina.core.StandardService : Starting service [Tomcat] +2026-05-12 12:23:56.463 INFO [main] o.a.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.49] +2026-05-12 12:23:56.523 INFO [main] o.a.c.c.C.[.[.[/fineract-provider] : Initializing Spring embedded WebApplicationContext +2026-05-12 12:23:56.524 INFO [main] o.s.b.w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 22865 ms +2026-05-12 12:23:56.835 INFO [main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... +2026-05-12 12:23:57.107 INFO [main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@c3976d4 +2026-05-12 12:23:57.108 INFO [main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. +2026-05-12 12:23:57.320 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : Upgrading tenant store DB at localhost:5432 +2026-05-12 12:23:57.320 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.username: postgres +2026-05-12 12:23:57.320 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.password: **** +2026-05-12 12:23:57.320 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.parameters: +2026-05-12 12:23:57.320 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.timezone: Asia/Kolkata +2026-05-12 12:23:57.320 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.description: Default Demo Tenant +2026-05-12 12:23:57.320 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.identifier: default +2026-05-12 12:23:57.320 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.name: fineract_default +2026-05-12 12:23:57.320 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - liquibase.analytics.enabled: false +2026-05-12 12:24:09.169 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : Tenant store upgrade finished +2026-05-12 12:24:09.169 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : Upgrading all tenants +2026-05-12 12:24:09.185 INFO [ThreadPoolTaskExecutor-1] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : Upgrade for tenant default has started +2026-05-12 12:24:10.441 INFO [ThreadPoolTaskExecutor-1] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Starting... +2026-05-12 12:24:10.564 INFO [ThreadPoolTaskExecutor-1] com.zaxxer.hikari.pool.HikariPool : HikariPool-2 - Added connection org.postgresql.jdbc.PgConnection@518ffe14 +2026-05-12 12:24:10.565 INFO [ThreadPoolTaskExecutor-1] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Start completed. +2026-05-12 12:24:21.982 INFO [ThreadPoolTaskExecutor-1] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : Upgrade for tenant default has finished +2026-05-12 12:24:21.983 INFO [ThreadPoolTaskExecutor-1] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Shutdown initiated... +2026-05-12 12:24:21.985 INFO [ThreadPoolTaskExecutor-1] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Shutdown completed. +2026-05-12 12:24:21.985 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : Tenant upgrades have successfully finished +2026-05-12 12:24:25.051 INFO [main] o.s.o.j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'jpa-pu' +2026-05-12 12:24:28.155 INFO [main] o.s.d.j.r.query.QueryEnhancerFactory : EclipseLink is in classpath; If applicable, EQL parser will be used. +2026-05-12 12:24:28.157 INFO [main] o.s.d.j.r.query.QueryEnhancerFactory : Hibernate is in classpath; If applicable, HQL parser will be used. +2026-05-12 12:24:31.794 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation pattern: inject-blind +2026-05-12 12:24:31.796 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation pattern: detect-entry-point +2026-05-12 12:24:31.796 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation pattern: inject-timing +2026-05-12 12:24:31.796 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation pattern: detect-backend +2026-05-12 12:24:31.796 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation pattern: detect-column +2026-05-12 12:24:31.796 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation pattern: detect-out-of-bands +2026-05-12 12:24:31.796 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation pattern: inject-stacked-query +2026-05-12 12:24:31.801 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation pattern: inject-comment +2026-05-12 12:24:31.803 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation profile: main +2026-05-12 12:24:31.804 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation profile: adhoc +2026-05-12 12:24:31.804 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation profile: column +2026-05-12 12:24:35.327 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:24:35.328 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract org.apache.fineract.infrastructure.jobs.data.JobDetailData org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetailRepository.getDataByShortName(java.lang.String) +2026-05-12 12:24:35.370 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract org.apache.fineract.infrastructure.jobs.data.JobDetailData org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetailRepository.getDataById(java.lang.Long) +2026-05-12 12:24:35.397 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetailRepository.getAllData() +2026-05-12 12:24:40.221 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:24:40.222 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.domain.LoanRepository.findAllStayedLockedByCobBusinessDate(java.time.LocalDate) +2026-05-12 12:24:45.778 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.Optional org.apache.fineract.portfolio.loanaccount.domain.LoanRepository.findLoanDataForExternalTransferByLoanId(java.lang.Long) +2026-05-12 12:24:46.139 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.domain.LoanRepository.findAllLoansBehindOnDisbursementDate(java.time.LocalDate,java.util.List,java.util.Collection) +2026-05-12 12:24:46.146 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.domain.LoanRepository.findAllLoansBehindByLoanIdsAndStatuses(java.time.LocalDate,java.util.List,java.util.Collection) +2026-05-12 12:24:46.164 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.domain.LoanRepository.findAllLoansBehindOrNullByLoanIdsAndStatuses(java.time.LocalDate,java.util.List,java.util.Collection) +2026-05-12 12:24:46.183 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.domain.LoanRepository.findOldestCOBProcessedLoan(java.time.LocalDate,java.util.Collection) +2026-05-12 12:24:47.178 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:24:47.178 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository.findTransactionDataForForeclosureIncome(org.apache.fineract.portfolio.loanaccount.domain.Loan,java.time.LocalDate) +2026-05-12 12:24:47.188 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository.fetchTotalUnpaidChargesForLoan(org.apache.fineract.portfolio.loanaccount.domain.Loan) +2026-05-12 12:24:47.365 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract org.apache.fineract.portfolio.loanaccount.data.CumulativeIncomeFromIncomePosting org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository.findCumulativeIncomeByLoanAndType(org.apache.fineract.portfolio.loanaccount.domain.Loan) +2026-05-12 12:24:47.402 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.Collection org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository.fetchLoanTransactionsByTypeAndLessOrEqualDate(org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType,java.time.LocalDate) +2026-05-12 12:24:47.784 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:24:47.784 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository.findAllSavingsIdsOldestCobProcessed() +2026-05-12 12:24:47.796 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository.findAllSavingsIdsBehindDate(java.time.LocalDate,java.util.List) +2026-05-12 12:24:47.803 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository.findAllSavingsIdsBehindDateOrNull(java.time.LocalDate,java.util.List) +2026-05-12 12:24:47.852 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository.findAccrualData(java.time.LocalDate,java.lang.Long,java.lang.Integer,java.lang.Integer) +2026-05-12 12:24:52.146 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:24:52.148 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.infrastructure.event.external.repository.ExternalEventRepository.findByStatusOrderByBusinessDateAscIdAsc(org.apache.fineract.infrastructure.event.external.repository.domain.ExternalEventStatus,org.springframework.data.domain.Pageable) +2026-05-12 12:24:59.033 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:24:59.035 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract org.apache.fineract.organisation.monetary.data.CurrencyData org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepository.findCurrencyDataByCode(java.lang.String) +2026-05-12 12:24:59.041 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepository.findAllSorted(org.springframework.data.domain.Sort) +2026-05-12 12:25:00.297 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:25:00.297 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository.findInstallmentDelinquencyTags(java.lang.Long) +2026-05-12 12:25:01.783 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:25:01.783 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository.findRepaymentPeriodDataByLoanId(java.lang.Long) +2026-05-12 12:25:01.805 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract org.apache.fineract.portfolio.loanaccount.data.AmortizationAllocationBaseTransactionDTO org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository.findBaseTransactionInfo(java.lang.Long,java.lang.Long) +2026-05-12 12:25:04.717 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:25:04.717 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract org.apache.fineract.portfolio.loanaccount.data.AmortizationAllocationBaseTransactionDTO org.apache.fineract.portfolio.loanaccount.repository.LoanBuyDownFeeBalanceRepository.findBaseTransactionInfo(java.lang.Long,java.lang.Long) +2026-05-12 12:25:04.929 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:25:04.929 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.Collection org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository.fetchLoanScheduleDataByDueDateAndObligationsMet(org.apache.fineract.portfolio.loanaccount.domain.LoanStatus,java.time.LocalDate,boolean,java.util.List) +2026-05-12 12:25:04.933 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.Collection org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository.fetchLoanScheduleDataByDueDateAndObligationsMet(org.apache.fineract.portfolio.loanaccount.domain.LoanStatus,java.time.LocalDate,boolean) +2026-05-12 12:25:10.218 INFO [main] o.a.f.i.e.b.s.BusinessEventNotifierServiceImpl : External event posting is disabled +2026-05-12 12:25:12.655 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:25:12.657 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.organisation.monetary.domain.OrganisationCurrencyRepository.findAllSorted(org.springframework.data.domain.Sort) +2026-05-12 12:25:19.639 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:25:19.664 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMappingRepository.findAmortizationMappingsByBaseTransactionAndLoan(java.lang.Long,java.lang.Long) +2026-05-12 12:25:39.327 INFO [main] o.s.b.c.r.s.JobRepositoryFactoryBean : No database type set, using meta data indicating: POSTGRES +2026-05-12 12:25:39.424 INFO [main] o.s.b.c.l.s.TaskExecutorJobLauncher : No TaskExecutor has been set, defaulting to synchronous executor. +2026-05-12 12:25:46.233 INFO [main] o.a.f.i.r.p.ReportingProcessServiceProvider : Registered report service 'org.apache.fineract.infrastructure.dataqueries.service.DatatableReportingProcessService@70047ef3' for type/s '[Table, Chart, SMS]' +2026-05-12 12:26:03.673 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:26:03.675 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository.findLoanTermVariationsByLoanIdAndTermType(long,int) +2026-05-12 12:26:03.687 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository.findLoanTermVariationsByLoanId(long) +2026-05-12 12:26:03.695 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository.findLoanTermVariationsByExternalLoanIdAndTermType(org.apache.fineract.infrastructure.core.domain.ExternalId,int) +2026-05-12 12:26:03.972 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:26:03.972 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.domain.LoanApprovedAmountHistoryRepository.findAllByLoanId(java.lang.Long,org.springframework.data.domain.Pageable) +2026-05-12 12:26:16.158 ERROR [main] o.s.b.w.e.tomcat.TomcatStarter : Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. Message: Error creating bean with name 'org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration': Unsatisfied dependency expressed through constructor parameter 1: Error creating bean with name 'jerseyConfig': Invocation of init method failed +2026-05-12 12:26:16.301 INFO [main] o.a.catalina.core.StandardService : Stopping service [Tomcat] +2026-05-12 12:26:16.312 WARN [main] o.a.c.loader.WebappClassLoaderBase : The web application [fineract-provider] appears to have started a thread named [HikariPool-1:housekeeper] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread: + java.base/jdk.internal.misc.Unsafe.park(Native Method) + java.base/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:269) + java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:1763) + java.base/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1182) + java.base/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:899) + java.base/java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1070) + java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) + java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) + java.base/java.lang.Thread.run(Thread.java:1583) +2026-05-12 12:26:16.315 WARN [main] o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Unable to start web server +2026-05-12 12:26:16.505 INFO [main] o.s.o.j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'jpa-pu' +2026-05-12 12:26:16.512 INFO [main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... +2026-05-12 12:26:16.515 INFO [main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed. +2026-05-12 12:26:16.602 INFO [main] o.s.b.a.l.ConditionEvaluationReportLogger : + +Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. +2026-05-12 12:26:16.823 ERROR [main] o.s.b.d.LoggingFailureAnalysisReporter : + +*************************** +APPLICATION FAILED TO START +*************************** + +Description: + +Parameter 6 of constructor in org.apache.fineract.useradministration.dsc.service.DscLoginChallengeServiceImpl required a bean of type 'org.apache.fineract.infrastructure.security.domain.TFAccessTokenRepository' that could not be found. + + +Action: + +Consider defining a bean of type 'org.apache.fineract.infrastructure.security.domain.TFAccessTokenRepository' in your configuration. + +2026-05-12 12:31:50.117 INFO [main] o.apache.fineract.ServerApplication : Starting ServerApplication using Java 21.0.10 with PID 9140 (D:\CompanyProject\GitCBS\JAVA-Backend-CBS-MAIN\fineract-provider\build\classes\java\main started by ny313 in D:\CompanyProject\GitCBS\JAVA-Backend-CBS-MAIN\fineract-provider) +2026-05-12 12:31:50.123 INFO [main] o.apache.fineract.ServerApplication : No active profile set, falling back to 1 default profile: "default" +2026-05-12 12:32:00.128 INFO [main] o.s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode +2026-05-12 12:32:00.130 INFO [main] o.s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. +2026-05-12 12:32:02.103 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.documentmanagement.domain.DocumentRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository +2026-05-12 12:32:02.105 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.documentmanagement.domain.ImageRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository +2026-05-12 12:32:02.121 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.mix.domain.MixReportXBRLNamespaceRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository +2026-05-12 12:32:02.126 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.mix.domain.MixTaxonomyMappingRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository +2026-05-12 12:32:02.131 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.mix.domain.MixTaxonomyRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository +2026-05-12 12:32:02.306 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JPA - Could not safely identify store assignment for repository candidate interface org.apache.fineract.command.persistence.domain.CommandRepository; If you want this repository to be a JPA repository, consider annotating your entities with one of these annotations: jakarta.persistence.Entity, jakarta.persistence.MappedSuperclass (preferred), or consider extending one of the following types with your repository: org.springframework.data.jpa.repository.JpaRepository +2026-05-12 12:32:05.421 INFO [main] o.s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 5248 ms. Found 214 JPA repository interfaces. +2026-05-12 12:32:05.432 INFO [main] o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean 'jpaAuditingHandler' with a different definition: replacing [Root bean: class=org.springframework.data.auditing.AuditingHandler; scope=; abstract=false; lazyInit=null; autowireMode=2; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=null; factoryMethodName=from; initMethodNames=null; destroyMethodNames=null] with [Root bean: class=org.apache.fineract.infrastructure.core.auditing.CustomAuditingHandler; scope=prototype; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null] +2026-05-12 12:32:05.616 INFO [main] o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean 'transactionManager' with a different definition: replacing [Root bean: class=null; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=JPAConfig; factoryMethodName=transactionManager; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in class path resource [org/apache/fineract/infrastructure/core/config/jpa/JPAConfig.class]] with [Root bean: class=null; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=scheduledJobRunnerConfig; factoryMethodName=transactionManager; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in class path resource [org/apache/fineract/infrastructure/jobs/ScheduledJobRunnerConfig.class]] +2026-05-12 12:32:05.619 INFO [main] o.s.b.c.c.annotation.BatchRegistrar : Bean jobRepository already defined in the application context, skipping the registration of a jobRepository +2026-05-12 12:32:05.619 INFO [main] o.s.b.c.c.annotation.BatchRegistrar : Bean jobExplorer already defined in the application context, skipping the registration of a jobExplorer +2026-05-12 12:32:05.619 INFO [main] o.s.b.c.c.annotation.BatchRegistrar : Bean jobLauncher already defined in the application context, skipping the registration of a jobLauncher +2026-05-12 12:32:05.623 INFO [main] o.s.b.c.c.annotation.BatchRegistrar : Finished Spring Batch infrastructure beans configuration in 5 ms. +2026-05-12 12:32:07.536 INFO [main] o.s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode +2026-05-12 12:32:07.537 INFO [main] o.s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JDBC repositories in DEFAULT mode. +2026-05-12 12:32:07.977 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.provisioning.domain.ProvisioningEntryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.978 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.adhocquery.domain.AdHocRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.979 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.cob.domain.LoanAccountLockRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.980 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormatRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.981 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.bulkimport.domain.ImportDocumentRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.981 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.email.domain.EmailCampaignRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.981 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.email.domain.EmailConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.981 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.email.domain.EmailMessageRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.982 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.campaigns.sms.domain.SmsCampaignRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.982 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.configuration.domain.ExternalServicesPropertiesRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.982 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditBureauConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.982 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditBureauLoanProductMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.983 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditBureauRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.983 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.CreditReportRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.983 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.OrganisationCreditBureauRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.983 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.creditbureau.domain.TokenRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.983 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.EntityDatatableChecksRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.984 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.RegisteredDatatableRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.984 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.ReportParameterRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.985 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.ReportParameterUsageRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.985 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.dataqueries.domain.ReportRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.985 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityAccessRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.985 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityRelationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.985 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityToEntityMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.985 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.hooks.domain.HookConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.985 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.hooks.domain.HookRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.985 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.hooks.domain.HookTemplateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.986 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.JobParameterRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.986 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.986 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.ScheduledJobRunHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.986 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.domain.SchedulerDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.986 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.service.aggregationjob.domain.JournalEntryAggregationTrackingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.987 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.jobs.service.aggregationjob.domain.JournalEntrySummaryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.987 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.987 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.988 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobRunHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.988 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.security.otp.domain.OtpLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.989 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.sms.domain.SmsMessageRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.989 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.survey.domain.LikelihoodRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.989 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.interoperation.domain.InteropIdentifierRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.990 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.notification.domain.NotificationMapperRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.990 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.notification.domain.NotificationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.990 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.holiday.domain.HolidayRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.990 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.office.domain.OfficeTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.991 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.provisioning.domain.ProvisioningCategoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.991 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.provisioning.domain.ProvisioningCriteriaDefinitionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.991 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.provisioning.domain.ProvisioningCriteriaRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.992 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.staff.domain.StaffRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.992 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.993 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.AccountAssociationsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.993 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.993 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.AccountTransferRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.994 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.account.domain.StandingInstructionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.994 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.address.domain.AddressRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.995 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.address.domain.FieldConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.995 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.calendar.domain.CalendarHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.995 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.996 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.calendar.domain.CalendarRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.996 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.ckyc.domain.CkycBatchRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.996 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientAddressRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.997 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.997 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientFamilyMembersRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.997 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientNonPersonRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.997 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.997 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientTransferDetailsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.998 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagementRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.998 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.collateralmanagement.domain.CollateralManagementDomainRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.998 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.collateral.domain.LoanCollateralRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.998 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.fund.domain.FundRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.998 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.group.domain.GroupLevelRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.998 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.group.domain.GroupRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.998 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.group.domain.GroupRoleRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.999 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorFundingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.999 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorFundingTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.999 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.999 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanproduct.productmix.domain.ProductMixRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.999 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.meeting.attendance.domain.ClientAttendanceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.999 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.meeting.domain.MeetingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:07.999 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.note.domain.NoteRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.000 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.rate.domain.RateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.000 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDatedChecksRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.000 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.DepositAccountOnHoldTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.001 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FdBaseInterestRateEffectiveRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.001 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FdBaseInterestRateSlabHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.003 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FixedDepositAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.003 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.FixedDepositProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.004 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateEffectiveRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.004 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdBaseInterestRateSlabHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.004 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdInterestRateEffectiveRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.004 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RdInterestRateSlabHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.004 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RecurringDepositAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.004 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.account.domain.SelfBeneficiariesTPTRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.004 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.device.domain.DeviceRegistrationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.005 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.pockets.domain.PocketAccountMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.005 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.pockets.domain.PocketRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.005 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.self.registration.domain.SelfServiceRegistrationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.005 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.005 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.006 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareproducts.domain.ShareProductDividentPayOutDetailsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.006 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.shareproducts.domain.ShareProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.006 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.spm.domain.LookupTableRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.006 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.spm.domain.ScorecardRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.006 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.spm.domain.SurveyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.007 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.template.domain.TemplateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.007 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.AppUserPreviousPasswordRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.008 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.AppUserRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.008 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.PasswordValidationPolicyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.008 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.PermissionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.008 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.domain.RoleRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.008 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerJournalEntryMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.008 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerLoanProductAttributesRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.008 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.008 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerTransferJournalEntryMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.009 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerTransferLoanMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.009 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.investor.domain.ExternalAssetOwnerTransferRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.010 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanorigination.domain.LoanOriginatorMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.010 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanorigination.domain.LoanOriginatorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.010 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.012 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.012 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.DelinquencyRangeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.012 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyActionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.012 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.LoanDelinquencyTagHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.012 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.012 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.012 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanAccrualActivityRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.013 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.013 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanApprovedAmountHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.014 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidByRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.014 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.014 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagementRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.014 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetailsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.014 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.014 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.015 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanStatusChangeHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.015 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.015 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.015 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.015 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgingParameterRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.015 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistoryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.015 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.015 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.015 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.015 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.015 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.015 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductDepositLimitKycRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.015 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityConfigRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.015 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductFacilityLimitRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.015 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.currentproduct.domain.CurrentProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.017 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.017 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.interestratechart.domain.InterestRateChartSlabRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.017 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.GSIMRepositoy; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.017 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.RecurringDepositProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.017 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.017 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountFacilityConfigRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.019 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.019 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.019 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductDepositLimitKycRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.019 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityConfigRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.019 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductFacilityLimitRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.020 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.savings.domain.SavingsProductRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.020 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.cob.domain.BatchBusinessStepRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.020 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.CashierRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.020 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.CashierTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.020 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.TellerRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.020 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.teller.domain.TellerTransactionRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.021 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.closure.domain.GLClosureRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.021 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.financialactivityaccount.domain.FinancialActivityAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.021 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.glaccount.domain.TrialBalanceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.021 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.021 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.021 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.rule.domain.AccountingRuleRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.021 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.floatingrates.domain.FloatingRateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.021 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.charge.domain.ChargeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.028 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.tax.domain.TaxComponentRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.030 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.tax.domain.TaxGroupRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.033 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.accounting.glaccount.domain.GLAccountRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.033 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.commands.domain.CommandSourceRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.033 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.businessdate.domain.BusinessDateRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.033 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.cache.domain.PlatformCacheRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.033 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.codes.domain.CodeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.033 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.codes.domain.CodeValueRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.034 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.configuration.domain.GlobalConfigurationRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.034 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.infrastructure.rbifeed.domain.RbiFeedItemRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.034 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.034 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.monetary.domain.OrganisationCurrencyRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.034 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.organisation.office.domain.OfficeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.034 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.cfcfrms.domain.CfcfrmsSimulatorLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.034 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.cfcfrms.domain.ComplaintAuditLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.035 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.cfcfrms.domain.ComplaintTicketRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.035 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientForm60Repository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.037 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientIdentifierRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.037 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientKycRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.037 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientNomineeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.037 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.domain.ClientRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.038 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientEouRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.038 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientImportExportRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.038 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientMsmeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.039 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientMudraBorrowerRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.039 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientNonPrioritySectorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.039 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientPrioritySectorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.039 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.client.entityclassification.domain.ClientStartupIndiaRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.039 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.mobilelookup.domain.MobileLookupLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.039 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetailRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.039 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.039 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.sanctions.domain.SanctionsEntryRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.039 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.portfolio.sanctions.domain.SanctionsScreeningLogRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.039 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.dsc.domain.BankUserDscRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.039 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.dsc.domain.DscChallengeRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.039 INFO [main] o.s.d.r.c.RepositoryConfigurationExtensionSupport : Spring Data JDBC - Could not safely identify store assignment for repository candidate interface org.apache.fineract.useradministration.mfa.domain.UserMfaFactorRepository; If you want this repository to be a JDBC repository, consider annotating your entities with one of these annotations: org.springframework.data.relational.core.mapping.Table. +2026-05-12 12:32:08.383 INFO [main] o.s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 843 ms. Found 6 JDBC repository interfaces. +2026-05-12 12:32:12.464 INFO [main] o.s.i.c.DefaultConfiguringBeanFactoryPostProcessor : No bean named 'errorChannel' has been explicitly defined. Therefore, a default PublishSubscribeChannel will be created. +2026-05-12 12:32:12.489 INFO [main] o.s.i.c.DefaultConfiguringBeanFactoryPostProcessor : No bean named 'integrationHeaderChannelRegistry' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created. +2026-05-12 12:32:14.869 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'fineract-org.apache.fineract.infrastructure.core.config.FineractProperties' of type [org.apache.fineract.infrastructure.core.config.FineractProperties] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:32:14.933 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'cacheConfig' of type [org.apache.fineract.infrastructure.core.config.cache.CacheConfig$$SpringCGLIB$$0] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:32:15.508 INFO [main] org.reflections.Reflections : Reflections took 25 ms to scan 1 urls, producing 0 keys and 0 values +2026-05-12 12:32:15.521 WARN [main] o.a.f.i.c.config.cache.CacheConfig : The following cache configurations are defined but cache does not exists: [userTFAccessToken] +2026-05-12 12:32:15.527 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'ehCacheManager' of type [org.springframework.cache.jcache.JCacheCacheManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:32:15.554 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'defaultCacheManager' of type [org.apache.fineract.infrastructure.core.config.cache.TransactionBoundCacheManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:32:15.571 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'runtimeDelegatingCacheManager' of type [org.apache.fineract.infrastructure.cache.service.RuntimeDelegatingCacheManager] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:32:15.583 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'platformCacheConfiguration' of type [org.apache.fineract.infrastructure.cache.PlatformCacheConfiguration$$SpringCGLIB$$0] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:32:15.715 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'samplingConfiguration' of type [org.apache.fineract.infrastructure.core.diagnostics.performance.sampling.core.SamplingConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:32:15.735 WARN [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker : Bean 'samplingServiceFactory' of type [org.apache.fineract.infrastructure.core.diagnostics.performance.sampling.core.SamplingServiceFactory] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [samplingBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. +2026-05-12 12:32:18.253 INFO [main] o.s.b.w.e.tomcat.TomcatWebServer : Tomcat initialized with port 8443 (https) +2026-05-12 12:32:18.308 INFO [main] o.a.catalina.core.StandardService : Starting service [Tomcat] +2026-05-12 12:32:18.309 INFO [main] o.a.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.49] +2026-05-12 12:32:18.421 INFO [main] o.a.c.c.C.[.[.[/fineract-provider] : Initializing Spring embedded WebApplicationContext +2026-05-12 12:32:18.422 INFO [main] o.s.b.w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 28241 ms +2026-05-12 12:32:19.219 INFO [main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... +2026-05-12 12:32:19.926 INFO [main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@15c203cf +2026-05-12 12:32:19.931 INFO [main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. +2026-05-12 12:32:20.527 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : Upgrading tenant store DB at localhost:5432 +2026-05-12 12:32:20.527 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.username: postgres +2026-05-12 12:32:20.527 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.password: **** +2026-05-12 12:32:20.528 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.parameters: +2026-05-12 12:32:20.528 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.timezone: Asia/Kolkata +2026-05-12 12:32:20.529 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.description: Default Demo Tenant +2026-05-12 12:32:20.529 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.identifier: default +2026-05-12 12:32:20.530 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - fineract.tenant.name: fineract_default +2026-05-12 12:32:20.530 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : - liquibase.analytics.enabled: false +2026-05-12 12:32:45.216 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : Tenant store upgrade finished +2026-05-12 12:32:45.217 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : Upgrading all tenants +2026-05-12 12:32:45.224 INFO [ThreadPoolTaskExecutor-1] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : Upgrade for tenant default has started +2026-05-12 12:32:46.220 INFO [ThreadPoolTaskExecutor-1] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Starting... +2026-05-12 12:32:46.280 INFO [ThreadPoolTaskExecutor-1] com.zaxxer.hikari.pool.HikariPool : HikariPool-2 - Added connection org.postgresql.jdbc.PgConnection@42918535 +2026-05-12 12:32:46.280 INFO [ThreadPoolTaskExecutor-1] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Start completed. +2026-05-12 12:32:56.823 INFO [ThreadPoolTaskExecutor-1] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : Upgrade for tenant default has finished +2026-05-12 12:32:56.825 INFO [ThreadPoolTaskExecutor-1] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Shutdown initiated... +2026-05-12 12:32:56.834 INFO [ThreadPoolTaskExecutor-1] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Shutdown completed. +2026-05-12 12:32:56.836 INFO [main] o.a.f.i.c.s.m.TenantDatabaseUpgradeService : Tenant upgrades have successfully finished +2026-05-12 12:32:59.883 INFO [main] o.s.o.j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'jpa-pu' +2026-05-12 12:33:02.473 INFO [main] o.s.d.j.r.query.QueryEnhancerFactory : EclipseLink is in classpath; If applicable, EQL parser will be used. +2026-05-12 12:33:02.474 INFO [main] o.s.d.j.r.query.QueryEnhancerFactory : Hibernate is in classpath; If applicable, HQL parser will be used. +2026-05-12 12:33:04.406 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation pattern: inject-blind +2026-05-12 12:33:04.407 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation pattern: detect-entry-point +2026-05-12 12:33:04.408 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation pattern: inject-timing +2026-05-12 12:33:04.408 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation pattern: detect-backend +2026-05-12 12:33:04.408 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation pattern: detect-column +2026-05-12 12:33:04.408 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation pattern: detect-out-of-bands +2026-05-12 12:33:04.408 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation pattern: inject-stacked-query +2026-05-12 12:33:04.410 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation pattern: inject-comment +2026-05-12 12:33:04.411 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation profile: main +2026-05-12 12:33:04.416 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation profile: adhoc +2026-05-12 12:33:04.417 INFO [main] o.a.f.i.s.utils.DefaultSqlValidator : Setup SQL validation profile: column +2026-05-12 12:33:05.934 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:33:05.935 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetailRepository.getAllData() +2026-05-12 12:33:05.981 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract org.apache.fineract.infrastructure.jobs.data.JobDetailData org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetailRepository.getDataByShortName(java.lang.String) +2026-05-12 12:33:05.986 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract org.apache.fineract.infrastructure.jobs.data.JobDetailData org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetailRepository.getDataById(java.lang.Long) +2026-05-12 12:33:08.921 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:33:08.921 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.domain.LoanRepository.findAllStayedLockedByCobBusinessDate(java.time.LocalDate) +2026-05-12 12:33:13.420 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.Optional org.apache.fineract.portfolio.loanaccount.domain.LoanRepository.findLoanDataForExternalTransferByLoanId(java.lang.Long) +2026-05-12 12:33:13.563 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.domain.LoanRepository.findAllLoansBehindOrNullByLoanIdsAndStatuses(java.time.LocalDate,java.util.List,java.util.Collection) +2026-05-12 12:33:13.566 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.domain.LoanRepository.findAllLoansBehindOnDisbursementDate(java.time.LocalDate,java.util.List,java.util.Collection) +2026-05-12 12:33:13.570 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.domain.LoanRepository.findAllLoansBehindByLoanIdsAndStatuses(java.time.LocalDate,java.util.List,java.util.Collection) +2026-05-12 12:33:13.580 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.domain.LoanRepository.findOldestCOBProcessedLoan(java.time.LocalDate,java.util.Collection) +2026-05-12 12:33:13.972 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:33:13.972 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository.fetchTotalUnpaidChargesForLoan(org.apache.fineract.portfolio.loanaccount.domain.Loan) +2026-05-12 12:33:14.304 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository.findTransactionDataForForeclosureIncome(org.apache.fineract.portfolio.loanaccount.domain.Loan,java.time.LocalDate) +2026-05-12 12:33:14.391 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract org.apache.fineract.portfolio.loanaccount.data.CumulativeIncomeFromIncomePosting org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository.findCumulativeIncomeByLoanAndType(org.apache.fineract.portfolio.loanaccount.domain.Loan) +2026-05-12 12:33:14.426 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.Collection org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository.fetchLoanTransactionsByTypeAndLessOrEqualDate(org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType,java.time.LocalDate) +2026-05-12 12:33:14.640 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:33:14.640 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository.findAllSavingsIdsBehindDate(java.time.LocalDate,java.util.List) +2026-05-12 12:33:14.644 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository.findAllSavingsIdsBehindDateOrNull(java.time.LocalDate,java.util.List) +2026-05-12 12:33:14.656 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository.findAllSavingsIdsOldestCobProcessed() +2026-05-12 12:33:14.711 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository.findAccrualData(java.time.LocalDate,java.lang.Long,java.lang.Integer,java.lang.Integer) +2026-05-12 12:33:16.728 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:33:16.728 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.infrastructure.event.external.repository.ExternalEventRepository.findByStatusOrderByBusinessDateAscIdAsc(org.apache.fineract.infrastructure.event.external.repository.domain.ExternalEventStatus,org.springframework.data.domain.Pageable) +2026-05-12 12:33:22.155 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:33:22.155 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepository.findAllSorted(org.springframework.data.domain.Sort) +2026-05-12 12:33:22.164 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract org.apache.fineract.organisation.monetary.data.CurrencyData org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepository.findCurrencyDataByCode(java.lang.String) +2026-05-12 12:33:23.092 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:33:23.093 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.delinquency.domain.LoanInstallmentDelinquencyTagRepository.findInstallmentDelinquencyTags(java.lang.Long) +2026-05-12 12:33:23.952 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:33:23.953 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository.findRepaymentPeriodDataByLoanId(java.lang.Long) +2026-05-12 12:33:23.966 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract org.apache.fineract.portfolio.loanaccount.data.AmortizationAllocationBaseTransactionDTO org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository.findBaseTransactionInfo(java.lang.Long,java.lang.Long) +2026-05-12 12:33:26.244 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:33:26.244 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract org.apache.fineract.portfolio.loanaccount.data.AmortizationAllocationBaseTransactionDTO org.apache.fineract.portfolio.loanaccount.repository.LoanBuyDownFeeBalanceRepository.findBaseTransactionInfo(java.lang.Long,java.lang.Long) +2026-05-12 12:33:26.429 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:33:26.429 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.Collection org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository.fetchLoanScheduleDataByDueDateAndObligationsMet(org.apache.fineract.portfolio.loanaccount.domain.LoanStatus,java.time.LocalDate,boolean,java.util.List) +2026-05-12 12:33:26.433 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.Collection org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository.fetchLoanScheduleDataByDueDateAndObligationsMet(org.apache.fineract.portfolio.loanaccount.domain.LoanStatus,java.time.LocalDate,boolean) +2026-05-12 12:33:29.723 INFO [main] o.a.f.i.e.b.s.BusinessEventNotifierServiceImpl : External event posting is disabled +2026-05-12 12:33:31.510 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:33:31.510 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.organisation.monetary.domain.OrganisationCurrencyRepository.findAllSorted(org.springframework.data.domain.Sort) +2026-05-12 12:33:33.312 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:33:33.312 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.domain.LoanAmortizationAllocationMappingRepository.findAmortizationMappingsByBaseTransactionAndLoan(java.lang.Long,java.lang.Long) +2026-05-12 12:33:40.789 INFO [main] o.s.b.c.r.s.JobRepositoryFactoryBean : No database type set, using meta data indicating: POSTGRES +2026-05-12 12:33:40.817 INFO [main] o.s.b.c.l.s.TaskExecutorJobLauncher : No TaskExecutor has been set, defaulting to synchronous executor. +2026-05-12 12:33:44.359 INFO [main] o.a.f.i.r.p.ReportingProcessServiceProvider : Registered report service 'org.apache.fineract.infrastructure.dataqueries.service.DatatableReportingProcessService@3e9cf334' for type/s '[Table, Chart, SMS]' +2026-05-12 12:33:52.907 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:33:52.907 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository.findLoanTermVariationsByLoanId(long) +2026-05-12 12:33:52.911 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository.findLoanTermVariationsByLoanIdAndTermType(long,int) +2026-05-12 12:33:52.914 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository.findLoanTermVariationsByExternalLoanIdAndTermType(org.apache.fineract.infrastructure.core.domain.ExternalId,int) +2026-05-12 12:33:53.057 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : Usage of Spring Data projections detected on persistence provider EclipseLink; Make sure the following query methods declare result columns in exactly the order the accessors are declared in the projecting interface or the order of parameters for DTOs: +2026-05-12 12:33:53.057 INFO [main] o.s.d.j.r.s.JpaRepositoryFactory$EclipseLinkProjectionQueryCreationListener : - public abstract java.util.List org.apache.fineract.portfolio.loanaccount.domain.LoanApprovedAmountHistoryRepository.findAllByLoanId(java.lang.Long,org.springframework.data.domain.Pageable) +2026-05-12 12:34:54.849 INFO [main] org.reflections.Reflections : Reflections took 213 ms to scan 1 urls, producing 56 keys and 185 values +2026-05-12 12:35:01.742 INFO [main] com.zaxxer.hikari.HikariDataSource : fineract_default_pool - Starting... +2026-05-12 12:35:01.816 INFO [main] com.zaxxer.hikari.pool.HikariPool : fineract_default_pool - Added connection org.postgresql.jdbc.PgConnection@2595f288 +2026-05-12 12:35:01.839 INFO [main] com.zaxxer.hikari.HikariDataSource : fineract_default_pool - Start completed. +2026-05-12 12:35:01.909 INFO [main] o.a.f.o.monetary.domain.MoneyHelper : Initialized rounding mode for tenant `default`: HALF_EVEN +2026-05-12 12:35:01.917 INFO [main] o.a.f.i.c.s.MoneyHelperStartupInitializationService : Starting MoneyHelper initialization for all tenants... +2026-05-12 12:35:01.921 INFO [main] o.a.f.i.c.s.MoneyHelperStartupInitializationService : MoneyHelper initialization completed +2026-05-12 12:35:01.996 INFO [main] o.a.f.i.e.e.s.message.MessageFactory : Message source set to dd0349d0-c92f-4c95-a712-c539abcaad44 +2026-05-12 12:35:18.022 INFO [main] o.a.f.i.e.e.s.ExternalEventConfigurationValidationService : Validating external event configuration for default +2026-05-12 12:35:23.040 WARN [main] o.a.f.i.c.b.FineractWebApplicationConfiguration : Fineract is running in web application mode +2026-05-12 12:35:23.075 INFO [main] o.s.s.c.a.a.c.InitializeAuthenticationProviderBeanManagerConfigurer$InitializeAuthenticationProviderManagerConfigurer : Global AuthenticationManager configured with AuthenticationProvider bean with name customAuthenticationProvider +2026-05-12 12:35:23.083 WARN [main] o.s.s.c.a.a.c.InitializeUserDetailsBeanManagerConfigurer$InitializeUserDetailsManagerConfigurer : Global AuthenticationManager configured with an AuthenticationProvider bean. UserDetailsService beans will not be used by Spring Security for automatically configuring username/password login. Consider removing the AuthenticationProvider bean. Alternatively, consider using the UserDetailsService in a manually instantiated DaoAuthenticationProvider. If the current configuration is intentional, to turn off this warning, increase the logging level of 'org.springframework.security.config.annotation.authentication.configuration.InitializeUserDetailsBeanManagerConfigurer' to ERROR +2026-05-12 12:35:24.257 INFO [main] o.s.s.w.a.c.ChannelProcessingFilter : Validated configuration attributes +2026-05-12 12:35:31.684 INFO [main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoints beneath base path '/actuator' +2026-05-12 12:35:39.733 INFO [main] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor +2026-05-12 12:35:39.787 INFO [main] o.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl +2026-05-12 12:35:39.787 INFO [main] org.quartz.core.QuartzScheduler : Quartz Scheduler v2.5.0 created. +2026-05-12 12:35:39.793 INFO [main] org.quartz.simpl.RAMJobStore : RAMJobStore initialized. +2026-05-12 12:35:39.797 INFO [main] org.quartz.core.QuartzScheduler : Scheduler meta-data: Quartz Scheduler (v2.5.0) 'quartzScheduler' with instanceId 'NON_CLUSTERED' + Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally. + NOT STARTED. + Currently in standby mode. + Number of jobs executed: 0 + Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads. + Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered. + +2026-05-12 12:35:39.797 INFO [main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'quartzScheduler' initialized from an externally provided properties instance. +2026-05-12 12:35:39.797 INFO [main] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.5.0 +2026-05-12 12:35:39.798 INFO [main] org.quartz.core.QuartzScheduler : JobFactory set to: org.springframework.scheduling.quartz.SpringBeanJobFactory@7e34571a +2026-05-12 12:35:50.222 INFO [main] o.s.i.endpoint.EventDrivenConsumer : Adding {logging-channel-adapter:_org.springframework.integration.errorLogger} as a subscriber to the 'errorChannel' channel +2026-05-12 12:35:50.231 INFO [main] o.s.i.c.PublishSubscribeChannel : Channel 'application.errorChannel' has 1 subscriber(s). +2026-05-12 12:35:50.243 INFO [main] o.s.i.endpoint.EventDrivenConsumer : started bean '_org.springframework.integration.errorLogger' +2026-05-12 12:35:50.243 INFO [main] o.s.i.endpoint.EventDrivenConsumer : Adding {message-handler} as a subscriber to the 'outboundRequests' channel +2026-05-12 12:35:50.243 INFO [main] o.s.i.channel.DirectChannel : Channel 'application.outboundRequests' has 1 subscriber(s). +2026-05-12 12:35:50.243 INFO [main] o.s.i.endpoint.EventDrivenConsumer : started bean 'outboundFlow.org.springframework.integration.config.ConsumerEndpointFactoryBean#0'; from source: 'org.apache.fineract.infrastructure.springbatch.messagehandler.spring.SpringEventManagerConfig.outboundFlow()' +2026-05-12 12:35:50.243 INFO [main] o.s.i.e.i.ApplicationEventListeningMessageProducer : started bean 'eventListener'; defined in: 'class path resource [org/apache/fineract/infrastructure/springbatch/messagehandler/spring/SpringEventWorkerConfig.class]'; from source: 'org.apache.fineract.infrastructure.springbatch.messagehandler.spring.SpringEventWorkerConfig.eventListener()' +2026-05-12 12:35:50.243 INFO [main] o.s.i.endpoint.EventDrivenConsumer : Adding {bridge} as a subscriber to the 'inboundFlow.channel#0' channel +2026-05-12 12:35:50.243 INFO [main] o.s.i.channel.DirectChannel : Channel 'application.inboundFlow.channel#0' has 1 subscriber(s). +2026-05-12 12:35:50.243 INFO [main] o.s.i.endpoint.EventDrivenConsumer : started bean 'inboundFlow.org.springframework.integration.config.ConsumerEndpointFactoryBean#0'; from source: 'org.apache.fineract.infrastructure.springbatch.messagehandler.spring.SpringEventWorkerConfig.inboundFlow()' +2026-05-12 12:35:50.279 INFO [main] o.s.i.endpoint.PollingConsumer : started bean 'org.springframework.integration.dsl.StandardIntegrationFlow#0.org.springframework.integration.config.ConsumerEndpointFactoryBean#0' +2026-05-12 12:35:51.744 INFO [main] o.a.t.u.net.NioEndpoint.certificate : Connector [https-jsse-nio-8443], TLS virtual host [_default_], certificate type [UNDEFINED] configured from keystore [C:\Users\ny313\.keystore] using alias [tomcat] with trust store [null] +2026-05-12 12:35:51.905 INFO [main] o.s.b.w.e.tomcat.TomcatWebServer : Tomcat started on port 8443 (https) with context path '/fineract-provider' +2026-05-12 12:35:51.915 INFO [main] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now +2026-05-12 12:35:51.920 INFO [main] org.quartz.core.QuartzScheduler : Scheduler quartzScheduler_$_NON_CLUSTERED started. +2026-05-12 12:35:51.942 INFO [main] o.apache.fineract.ServerApplication : Started ServerApplication in 243.826 seconds (process running for 244.901) +2026-05-12 12:35:52.449 INFO [FineractEvent-15] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor +2026-05-12 12:35:52.454 INFO [FineractEvent-15] o.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl +2026-05-12 12:35:52.465 INFO [FineractEvent-15] org.quartz.core.QuartzScheduler : Quartz Scheduler v2.5.0 created. +2026-05-12 12:35:52.468 INFO [FineractEvent-15] org.quartz.simpl.RAMJobStore : RAMJobStore initialized. +2026-05-12 12:35:52.469 INFO [FineractEvent-15] org.quartz.core.QuartzScheduler : Scheduler meta-data: Quartz Scheduler (v2.5.0) 'Scheduler1' with instanceId 'NON_CLUSTERED' + Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally. + NOT STARTED. + Currently in standby mode. + Number of jobs executed: 0 + Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 7 threads. + Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered. + +2026-05-12 12:35:52.469 INFO [FineractEvent-15] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'Scheduler1' initialized from an externally provided properties instance. +2026-05-12 12:35:52.469 INFO [FineractEvent-15] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.5.0 +2026-05-12 12:35:52.476 INFO [FineractEvent-15] org.quartz.core.QuartzScheduler : JobFactory set to: org.springframework.scheduling.quartz.AdaptableJobFactory@6105d906 +2026-05-12 12:35:52.478 INFO [FineractEvent-15] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now +2026-05-12 12:35:52.479 INFO [FineractEvent-15] org.quartz.core.QuartzScheduler : Scheduler Scheduler1_$_NON_CLUSTERED started. +2026-05-12 12:35:52.506 INFO [FineractEvent-16] o.s.b.c.l.support.SimpleJobOperator : Checking status of job execution with id=12585 +2026-05-12 12:35:52.975 INFO [FineractEvent-16] o.s.b.c.l.support.SimpleJobOperator : Attempting to resume job with name=SEND_ASYNCHRONOUS_EVENTS and parameters={'run.id':'{value=9682, type=class java.lang.Long, identifying=true}'} +2026-05-12 12:35:53.333 INFO [FineractEvent-15] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor +2026-05-12 12:35:53.335 INFO [FineractEvent-15] o.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl +2026-05-12 12:35:53.542 INFO [FineractEvent-15] org.quartz.core.QuartzScheduler : Quartz Scheduler v2.5.0 created. +2026-05-12 12:35:53.544 INFO [FineractEvent-15] org.quartz.simpl.RAMJobStore : RAMJobStore initialized. +2026-05-12 12:35:53.544 INFO [FineractEvent-15] org.quartz.core.QuartzScheduler : Scheduler meta-data: Quartz Scheduler (v2.5.0) 'Scheduler1group3' with instanceId 'NON_CLUSTERED' + Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally. + NOT STARTED. + Currently in standby mode. + Number of jobs executed: 0 + Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 1 threads. + Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered. + +2026-05-12 12:35:53.545 INFO [FineractEvent-15] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'Scheduler1group3' initialized from an externally provided properties instance. +2026-05-12 12:35:53.545 INFO [FineractEvent-15] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.5.0 +2026-05-12 12:35:53.546 INFO [FineractEvent-15] org.quartz.core.QuartzScheduler : JobFactory set to: org.springframework.scheduling.quartz.AdaptableJobFactory@3548b3b2 +2026-05-12 12:35:53.548 INFO [FineractEvent-15] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now +2026-05-12 12:35:53.549 INFO [FineractEvent-15] org.quartz.core.QuartzScheduler : Scheduler Scheduler1group3_$_NON_CLUSTERED started. +2026-05-12 12:35:53.644 INFO [FineractEvent-15] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor +2026-05-12 12:35:53.646 INFO [FineractEvent-15] o.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl +2026-05-12 12:35:53.647 INFO [FineractEvent-15] org.quartz.core.QuartzScheduler : Quartz Scheduler v2.5.0 created. +2026-05-12 12:35:53.647 INFO [FineractEvent-15] org.quartz.simpl.RAMJobStore : RAMJobStore initialized. +2026-05-12 12:35:53.647 INFO [FineractEvent-15] org.quartz.core.QuartzScheduler : Scheduler meta-data: Quartz Scheduler (v2.5.0) 'Scheduler1group4' with instanceId 'NON_CLUSTERED' + Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally. + NOT STARTED. + Currently in standby mode. + Number of jobs executed: 0 + Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 1 threads. + Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered. + +2026-05-12 12:35:53.648 INFO [FineractEvent-15] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'Scheduler1group4' initialized from an externally provided properties instance. +2026-05-12 12:35:53.648 INFO [FineractEvent-15] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.5.0 +2026-05-12 12:35:53.648 INFO [FineractEvent-15] org.quartz.core.QuartzScheduler : JobFactory set to: org.springframework.scheduling.quartz.AdaptableJobFactory@526d1ab5 +2026-05-12 12:35:53.648 INFO [FineractEvent-15] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now +2026-05-12 12:35:53.648 INFO [FineractEvent-15] org.quartz.core.QuartzScheduler : Scheduler Scheduler1group4_$_NON_CLUSTERED started. +2026-05-12 12:35:53.671 INFO [FineractEvent-15] org.quartz.impl.StdSchedulerFactory : Using default implementation for ThreadExecutor +2026-05-12 12:35:53.674 INFO [FineractEvent-15] o.quartz.core.SchedulerSignalerImpl : Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl +2026-05-12 12:35:53.674 INFO [FineractEvent-15] org.quartz.core.QuartzScheduler : Quartz Scheduler v2.5.0 created. +2026-05-12 12:35:53.674 INFO [FineractEvent-15] org.quartz.simpl.RAMJobStore : RAMJobStore initialized. +2026-05-12 12:35:53.674 INFO [FineractEvent-15] org.quartz.core.QuartzScheduler : Scheduler meta-data: Quartz Scheduler (v2.5.0) 'Scheduler1group1' with instanceId 'NON_CLUSTERED' + Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally. + NOT STARTED. + Currently in standby mode. + Number of jobs executed: 0 + Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 1 threads. + Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered. + +2026-05-12 12:35:53.674 INFO [FineractEvent-15] org.quartz.impl.StdSchedulerFactory : Quartz scheduler 'Scheduler1group1' initialized from an externally provided properties instance. +2026-05-12 12:35:53.674 INFO [FineractEvent-15] org.quartz.impl.StdSchedulerFactory : Quartz scheduler version: 2.5.0 +2026-05-12 12:35:53.674 INFO [FineractEvent-15] org.quartz.core.QuartzScheduler : JobFactory set to: org.springframework.scheduling.quartz.AdaptableJobFactory@14b6d5e6 +2026-05-12 12:35:53.674 INFO [FineractEvent-15] o.s.s.quartz.SchedulerFactoryBean : Starting Quartz Scheduler now +2026-05-12 12:35:53.674 INFO [FineractEvent-15] org.quartz.core.QuartzScheduler : Scheduler Scheduler1group1_$_NON_CLUSTERED started. +2026-05-12 12:35:53.883 INFO [FineractEvent-15] o.a.f.i.j.s.JobSchedulerServiceImpl : Scheduling batch jobs has finished +2026-05-12 12:36:00.144 INFO [Scheduler1_Worker-1] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=SEND_ASYNCHRONOUS_EVENTS]] launched with the following parameters: [{'run.id':'{value=9737, type=class java.lang.Long, identifying=true}'}] +2026-05-12 12:36:00.201 INFO [Scheduler1_Worker-1] o.s.batch.core.job.SimpleStepHandler : Executing step: [SEND_ASYNCHRONOUS_EVENTS_STEP] +2026-05-12 12:36:00.238 INFO [Scheduler1_Worker-1] o.s.batch.core.step.AbstractStep : Step: [SEND_ASYNCHRONOUS_EVENTS_STEP] executed in 37ms +2026-05-12 12:36:00.254 INFO [Scheduler1_Worker-1] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=SEND_ASYNCHRONOUS_EVENTS]] completed with the following parameters: [{'run.id':'{value=9737, type=class java.lang.Long, identifying=true}'}] and the following status: [COMPLETED] in 68ms +2026-05-12 12:37:00.132 INFO [Scheduler1_Worker-2] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=SEND_ASYNCHRONOUS_EVENTS]] launched with the following parameters: [{'run.id':'{value=9738, type=class java.lang.Long, identifying=true}'}] +2026-05-12 12:37:00.160 INFO [Scheduler1_Worker-2] o.s.batch.core.job.SimpleStepHandler : Executing step: [SEND_ASYNCHRONOUS_EVENTS_STEP] +2026-05-12 12:37:00.177 INFO [Scheduler1_Worker-2] o.s.batch.core.step.AbstractStep : Step: [SEND_ASYNCHRONOUS_EVENTS_STEP] executed in 17ms +2026-05-12 12:37:00.199 INFO [Scheduler1_Worker-2] o.s.b.c.l.s.TaskExecutorJobLauncher : Job: [SimpleJob: [name=SEND_ASYNCHRONOUS_EVENTS]] completed with the following parameters: [{'run.id':'{value=9738, type=class java.lang.Long, identifying=true}'}] and the following status: [COMPLETED] in 56ms diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java index 8381962a9..c580b2aea 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/config/SecurityConfig.java @@ -152,6 +152,21 @@ public class SecurityConfig { .requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/login/otp/verify")).permitAll() .requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/login/otp/email/generate")).permitAll() .requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/login/otp/email/verify")).permitAll() + // DSC enrollment — nonce-gated endpoints the JWS helper and the browser hit + // without a Fineract auth header. The randomNo is the capability token. + .requestMatchers(API_MATCHER.matcher(HttpMethod.GET, "/api/*/dsc/enroll/*.jnlp")).permitAll() + .requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/dsc/enroll/*/callback")).permitAll() + .requestMatchers(API_MATCHER.matcher(HttpMethod.GET, "/api/*/dsc/enroll/*/status")).permitAll() + // Helper JAR — Java Web Start fetches this without an auth header. The JAR is + // (self-)signed; that signature is what the JVM enforces, not Fineract auth. + .requestMatchers(API_MATCHER.matcher(HttpMethod.GET, "/api/*/dsc/helper.jar")).permitAll() + // DSC login factor — JNLP / helper-verify / status are nonce-gated. The + // /challenge create endpoint is NOT permitAll'd here because it must be reached + // by a basic-authed (but not-yet-2FA'd) user — handled by the twofactor matcher + // block below for /api/*/login/dsc which only requires fullyAuthenticated. + .requestMatchers(API_MATCHER.matcher(HttpMethod.GET, "/api/*/login/dsc/*.jnlp")).permitAll() + .requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/login/dsc/*/verify")).permitAll() + .requestMatchers(API_MATCHER.matcher(HttpMethod.GET, "/api/*/login/dsc/*/status")).permitAll() .requestMatchers(API_MATCHER.matcher(HttpMethod.PUT, "/api/*/instance-mode")).permitAll() // businessdate .requestMatchers(API_MATCHER.matcher(HttpMethod.GET, "/api/*/businessdate/*")) @@ -348,6 +363,13 @@ public class SecurityConfig { .requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/twofactor/validate")).fullyAuthenticated() .requestMatchers(API_MATCHER.matcher("/api/*/twofactor")).fullyAuthenticated() + // POST /login/dsc/challenge requires a basic-authed user (no TFA yet) — same + // posture as /twofactor/validate. The other /login/dsc/** paths are nonce-gated + // permitAll above. + .requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/login/dsc/challenge")).fullyAuthenticated() + // /claim hands the minted TFA token back to the browser — must be basic-authed + // so we can prove the caller owns the challenge. + .requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/login/dsc/*/claim")).fullyAuthenticated() .requestMatchers(API_MATCHER.matcher("/api/**")) .access(allOf(authorizationManagers.toArray(new AuthorizationManager[0]))); }).httpBasic(hb -> hb.authenticationEntryPoint(basicAuthenticationEntryPoint())).csrf(AbstractHttpConfigurer::disable) @@ -402,7 +424,12 @@ public class SecurityConfig { .getBean(org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService.class); org.apache.fineract.useradministration.mfa.domain.UserMfaFactorRepository userMfaFactorRepository = applicationContext .getBean(org.apache.fineract.useradministration.mfa.domain.UserMfaFactorRepository.class); - return new TwoFactorAuthenticationFilter(twoFactorService, configurationDomainService, userMfaFactorRepository); + // DSC factor: same dependency pickup as AuthorizationServerConfig — the filter's + // "user has any factor" check now also accepts an active DSC mapping. + org.apache.fineract.useradministration.dsc.domain.BankUserDscRepository bankUserDscRepository = applicationContext + .getBean(org.apache.fineract.useradministration.dsc.domain.BankUserDscRepository.class); + return new TwoFactorAuthenticationFilter(twoFactorService, configurationDomainService, userMfaFactorRepository, + bankUserDscRepository); } public FineractInstanceModeApiFilter fineractInstanceModeApiFilter() { diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/config/AuthorizationServerConfig.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/config/AuthorizationServerConfig.java index dd0b93d29..7d6c76125 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/config/AuthorizationServerConfig.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/config/AuthorizationServerConfig.java @@ -291,7 +291,12 @@ public class AuthorizationServerConfig { .getBean(org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService.class); org.apache.fineract.useradministration.mfa.domain.UserMfaFactorRepository userMfaFactorRepository = applicationContext .getBean(org.apache.fineract.useradministration.mfa.domain.UserMfaFactorRepository.class); - return new TwoFactorAuthenticationFilter(twoFactorService, configurationDomainService, userMfaFactorRepository); + // DSC factor: the filter's 2FA gate now also accepts an active DSC mapping as a "user has + // a factor" signal. Same Spring context lookup pattern as the other dependencies above. + org.apache.fineract.useradministration.dsc.domain.BankUserDscRepository bankUserDscRepository = applicationContext + .getBean(org.apache.fineract.useradministration.dsc.domain.BankUserDscRepository.class); + return new TwoFactorAuthenticationFilter(twoFactorService, configurationDomainService, userMfaFactorRepository, + bankUserDscRepository); } public FineractInstanceModeApiFilter fineractInstanceModeApiFilter() { diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/otp/service/EmailLoginOtpServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/otp/service/EmailLoginOtpServiceImpl.java index f9b3eee1d..2f41ee110 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/otp/service/EmailLoginOtpServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/otp/service/EmailLoginOtpServiceImpl.java @@ -22,8 +22,10 @@ import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; import java.util.HexFormat; import org.apache.fineract.infrastructure.email.service.EmailSenderService; import org.apache.fineract.infrastructure.email.service.EmailTemplateService; @@ -57,12 +59,17 @@ public class EmailLoginOtpServiceImpl implements EmailLoginOtpService { private static final String STATUS_VERIFIED = "VERIFIED"; private static final String STATUS_EXPIRED = "EXPIRED"; + private static final DateTimeFormatter SUBJECT_TIMESTAMP = DateTimeFormatter.ofPattern("dd-MM-yy-HH:mm"); + @Value("${fineract.security.otp.ttl-seconds:300}") private int ttlSeconds; @Value("${fineract.security.otp.max-attempts:5}") private int maxAttempts; + @Value("${fineract.email.from-name:CBSnexora}") + private String appName; + private final OtpLogRepository otpLogRepository; private final AppUserRepository appUserRepository; private final EmailSenderService emailSenderService; @@ -120,7 +127,7 @@ public class EmailLoginOtpServiceImpl implements EmailLoginOtpService { // Render the branded template — same look and feel for OTP, welcome, password-reset, alerts. final String html = emailTemplateService.renderOtp(recipientName, otp, validityMinutes); - final String subject = "Your CBSnexora Sign-in OTP"; + final String subject = "Your OTP for Login - " + appName + " (" + LocalDateTime.now().format(SUBJECT_TIMESTAMP) + ")"; final boolean sent = emailSenderService.sendHtml(email, subject, html); if (sent) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientKycWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientKycWritePlatformService.java index 74fc8a895..cfc03ae6b 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientKycWritePlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientKycWritePlatformService.java @@ -34,6 +34,18 @@ public interface ClientKycWritePlatformService { */ void initiateKycInline(Client client, String kycJson); + /** + * Create-or-update inline counterpart to {@link #initiateKycInline} — used by the edit-client + * flow so the {@code kyc} sub-object on a {@code PUT /clients/{id}} behaves symmetrically with + * the create-time inline behaviour. If a KYC row already exists for this client, the editable + * fields are merged in and the status is bumped to IN_PROGRESS (matching the existing + * {@link #updateKyc} semantics). If no row exists yet, it falls back to {@link #initiateKycInline}. + * + *

Does NOT trigger PAN / Aadhaar verification — those still go through the dedicated + * verify-* endpoints. This method only keeps the KYC row's static fields in sync. + */ + void upsertKycInline(Client client, String kycJson); + /** Update PAN / Aadhaar / GSTIN details; triggers IN_PROGRESS status. */ CommandProcessingResult updateKyc(Long clientId, Long kycId, JsonCommand command); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientKycWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientKycWritePlatformServiceJpaRepositoryImpl.java index c3d9e90e3..2b21f7d9b 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientKycWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientKycWritePlatformServiceJpaRepositoryImpl.java @@ -937,6 +937,55 @@ public class ClientKycWritePlatformServiceJpaRepositoryImpl implements ClientKyc kycRepositoryWrapper.saveAndFlush(kyc); } + @Override + @Transactional + public void upsertKycInline(final Client client, final String kycJson) { + // No row yet → defer to the standard inline create path. This is the case where the user + // is editing a client who never had a KYC row created (e.g. created before KYC was rolled + // out, or KYC was skipped at create time). + if (!kycRepositoryWrapper.existsByClientId(client.getId())) { + LOG.info("upsertKycInline: no KYC row for client {}. Falling back to initiate.", client.getId()); + initiateKycInline(client, kycJson); + return; + } + + // Row exists → merge editable fields. Validation mirrors the explicit /kyc update endpoint + // so the user gets the same error messages whether they go through edit-client or + // edit-kyc directly. + final ClientKycCommand kycCommand = deserializer.commandFromApiJson( + kycJson, ClientKycApiConstants.UPDATE_REQUEST_PARAMS); + kycCommand.validateForUpdate(); + + final ClientKyc kyc = kycRepositoryWrapper.findByClientIdWithNotFoundDetection(client.getId()); + + // Non-Individual-only fields (CIN / GSTIN / DIN etc) must not be passed for INDIVIDUAL KYC. + final boolean isIndividualExisting = kyc.getKycType() != null && kyc.getKycType().isIndividual(); + final JsonElement parsedJson = this.fromApiJsonHelper.parse(kycJson); + final JsonCommand jsonCommand = JsonCommand.from(kycJson, parsedJson, this.fromApiJsonHelper, + "CLIENTKYC", null, null, null, client.getId(), null, null, null, null, null, null, null, null, null); + enforceNonIndividualOnlyFields(jsonCommand, isIndividualExisting); + + final ClientKycStatus currentStatus = kyc.getKycStatus(); + final Map changes = kyc.update(jsonCommand); + if (changes.isEmpty()) { + return; // no-op — edit-client sent the same KYC values that are already on the row + } + + // Same audit semantics as the explicit updateKyc endpoint: + // • PAN-edited on a VERIFIED row → reset PAN verification (forces re-verify) + // • Any change moves status to IN_PROGRESS so the row is no longer mistaken for a + // completed KYC until verify-pan / verify-aadhaar run again. + if (currentStatus.isVerified() && changes.containsKey("panNumber")) { + kyc.resetPanVerification(); + } + if (!kyc.getKycStatus().isInProgress()) { + kyc.markInProgress(); + } + kycRepositoryWrapper.saveAndFlush(kyc); + LOG.info("upsertKycInline: clientId={}, kycId={}, applied {} change(s), newStatus={}", + client.getId(), kyc.getId(), changes.size(), kyc.getKycStatusValue()); + } + // ── helpers ─────────────────────────────────────────────────────────────── private CommandProcessingResult buildResult(final JsonCommand command, final Client client, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java index 77be02962..2db65d188 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java @@ -1142,6 +1142,21 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP extractAndCreateClientNonPerson(clientForUpdate, command); } } + + // ── Inline KYC upsert (optional) ───────────────────────────────── + // Mirror of the create-side block at the top of createClient(): if the edit payload + // contains a `kyc` sub-object, hand it to the KYC service so the row is created (if + // missing) or its editable fields are updated (status bumps to IN_PROGRESS). + // Verification still requires the UI to call /verify-pan + /verify-aadhaar. + final JsonElement updateRoot = this.fromApiJsonHelper.parse(command.json()); + if (updateRoot.isJsonObject() && updateRoot.getAsJsonObject().has(ClientApiConstants.kycParamName)) { + final JsonElement kycElement = updateRoot.getAsJsonObject().get(ClientApiConstants.kycParamName); + if (kycElement != null && !kycElement.isJsonNull()) { + this.clientKycWritePlatformService.upsertKycInline(clientForUpdate, + this.fromApiJsonHelper.toJson(kycElement)); + } + } + return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityExternalId(clientForUpdate.getExternalId()) // diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/domain/AppUserRepository.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/domain/AppUserRepository.java index f7481d7bb..29bd14b32 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/useradministration/domain/AppUserRepository.java +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/domain/AppUserRepository.java @@ -31,4 +31,27 @@ public interface AppUserRepository extends JpaRepository, JpaSpec AppUser findAppUserByName(@Param("username") String username); Collection findByOfficeId(Long officeId); + + /** + * Case-insensitive email lookup. Used by the createUser flow to surface a clean + * "email already registered" error before hitting the DB unique constraint. + */ + @Query("SELECT u FROM AppUser u WHERE LOWER(u.email) = LOWER(:email)") + java.util.Optional findByEmailIgnoreCase(@Param("email") String email); + + /** + * Same as {@link #findByEmailIgnoreCase} but excludes a given user id — used by the updateUser + * flow so a user editing their profile but keeping the same email isn't told it's taken. + */ + @Query("SELECT u FROM AppUser u WHERE LOWER(u.email) = LOWER(:email) AND u.id <> :excludeUserId") + java.util.Optional findByEmailIgnoreCaseAndIdNot(@Param("email") String email, + @Param("excludeUserId") Long excludeUserId); + + /** Exact-match mobile lookup — used by the createUser flow's pre-flight unique check. */ + java.util.Optional findByMobileNo(String mobileNo); + + /** Same as {@link #findByMobileNo} but excludes a given user id, for the update flow. */ + @Query("SELECT u FROM AppUser u WHERE u.mobileNo = :mobileNo AND u.id <> :excludeUserId") + java.util.Optional findByMobileNoAndIdNot(@Param("mobileNo") String mobileNo, + @Param("excludeUserId") Long excludeUserId); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/api/DscApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/api/DscApiResource.java new file mode 100644 index 000000000..2f5849a71 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/api/DscApiResource.java @@ -0,0 +1,213 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.api; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.NotFoundException; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import java.io.IOException; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.useradministration.dsc.data.BankUserDscData; +import org.apache.fineract.useradministration.dsc.data.DscChallengeStatusResponse; +import org.apache.fineract.useradministration.dsc.data.DscEnrollCallbackRequest; +import org.apache.fineract.useradministration.dsc.data.DscEnrollStartResponse; +import org.apache.fineract.useradministration.dsc.service.DscEnrollmentWriteService; +import org.apache.fineract.useradministration.dsc.service.DscReadPlatformService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * Digital Signature Certificate (DSC) mappings + enrollment lifecycle. + * + *

    + *
  • POST /v1/dsc/enroll/start — kicks off enrollment; returns randomNo + jnlpUrl
  • + *
  • GET /v1/dsc/enroll/{randomNo}.jnlp — streams the JNLP that launches the helper
  • + *
  • POST /v1/dsc/enroll/{randomNo}/callback — helper POSTs cert details here
  • + *
  • GET /v1/dsc/enroll/{randomNo}/status — UI polls for completion
  • + *
  • GET /v1/dsc/me — current user's DSC mappings (masked serials)
  • + *
  • GET /v1/dsc/{dscId} — single mapping, unmasked
  • + *
  • DELETE /v1/dsc/{dscId} — soft-revoke
  • + *
+ * + *

The .jnlp and /callback endpoints are nonce-gated (anyone with the randomNo can hit them). + * Everything else requires the authenticated user. + */ +@Path("/v1/dsc") +@Component +@Tag(name = "DSC", description = "Digital Signature Certificate enrollment and management") +@RequiredArgsConstructor +public class DscApiResource { + + private final DscEnrollmentWriteService enrollmentService; + private final DscReadPlatformService readService; + + /** + * Filesystem path where the signed helper JAR is dropped. Default lets ops put it next to the + * Fineract install so a redeploy of the .jar (after re-signing with a new cert) doesn't require + * rebuilding the Spring app — just {@code cp} into this path and the next JNLP launch picks it up. + */ + @Value("${fineract.dsc.helper.jar.path:${user.home}/.cbsnexora/dsc-helper.jar}") + private String helperJarPath; + + @POST + @Path("/enroll/start") + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Create a DSC enrollment challenge for the current user. Returns a randomNo " + + "plus the .jnlp URL the UI should trigger.") + public DscEnrollStartResponse start(@Context final HttpServletRequest request) { + final String codebase = deriveCodebase(request); + final String ip = request.getRemoteAddr(); + final String ua = request.getHeader("User-Agent"); + return enrollmentService.start(codebase, ip, ua); + } + + @GET + @Path("/enroll/{randomNo}.jnlp") + @Produces("application/x-java-jnlp-file") + @Operation(summary = "Streams the JNLP file. Nonce-gated; no auth header required so the browser " + + "can hand the URL directly to OpenWebStart.") + public Response jnlp(@PathParam("randomNo") final String randomNo, @Context final HttpServletRequest request) { + final String codebase = deriveCodebase(request); + final String body = enrollmentService.renderJnlp(randomNo, codebase); + return Response.ok(body, "application/x-java-jnlp-file") + .header("Content-Disposition", "attachment; filename=\"cbsnexora-dsc-enroll.jnlp\"") + .header("Cache-Control", "no-store") + .build(); + } + + @POST + @Path("/enroll/{randomNo}/callback") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Helper-driven endpoint. Accepts the base64 cert; server parses, validates " + + "and upserts the m_bank_user_dsc row.") + public DscChallengeStatusResponse callback(@PathParam("randomNo") final String randomNo, + final DscEnrollCallbackRequest req) { + return enrollmentService.callback(randomNo, req); + } + + @GET + @Path("/enroll/{randomNo}/status") + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "UI polls this. Returns PENDING until helper completes; then VERIFIED with " + + "the saved DSC row, or FAILED / EXPIRED with errorMessage.") + public DscChallengeStatusResponse status(@PathParam("randomNo") final String randomNo) { + return enrollmentService.getStatus(randomNo); + } + + @GET + @Path("/me") + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Current user's DSC mappings (serial numbers are masked).") + public List listMine() { + return readService.listForCurrentUser(); + } + + @GET + @Path("/user/{userId}") + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "DSC mappings for an arbitrary user — used by the admin view inside the " + + "user-details page. Returns the same shape as /me with serials masked. Read-only.") + public List listForUser(@PathParam("userId") final Long userId) { + return readService.listForUser(userId); + } + + @GET + @Path("/{dscId}") + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Single DSC mapping (full serial). Returns 404 if not owned by the caller.") + public BankUserDscData getById(@PathParam("dscId") final Long dscId) { + return readService.getById(dscId); + } + + @DELETE + @Path("/{dscId}") + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Soft-revokes a DSC mapping. Sets cert_status=REVOKED and is_deleted=true.") + public Response revoke(@PathParam("dscId") final Long dscId, @QueryParam("reason") final String reason) { + readService.revoke(dscId, reason); + return Response.noContent().build(); + } + + @GET + @Path("/helper.jar") + @Produces("application/java-archive") + @Operation(summary = "Streams the (self-)signed Java helper JAR referenced by the JNLP. " + + "Public — Java Web Start fetches without an auth header. The JAR file itself is " + + "code-signed; that's what the JVM enforces, not Fineract auth.") + public Response helperJar() { + final java.nio.file.Path jar = java.nio.file.Path.of(helperJarPath); + if (!java.nio.file.Files.isRegularFile(jar) || !java.nio.file.Files.isReadable(jar)) { + throw new NotFoundException("DSC helper JAR not deployed at " + helperJarPath); + } + final byte[] bytes; + try { + bytes = java.nio.file.Files.readAllBytes(jar); + } catch (final IOException e) { + throw new RuntimeException("Failed to read DSC helper JAR", e); + } + return Response.ok(bytes, "application/java-archive") + .header("Content-Disposition", "attachment; filename=\"cbsnexora-dsc-helper.jar\"") + .header("Cache-Control", "no-store") + .build(); + } + + /** + * Builds the public base URL the JNLP / helper should call back into. Honours + * {@code X-Forwarded-Proto} / {@code X-Forwarded-Host} so reverse-proxied deployments resolve + * the external hostname instead of the internal one. Includes the servlet context path because + * Fineract is typically mounted at {@code /fineract-provider}. + */ + private static String deriveCodebase(final HttpServletRequest request) { + final String fwdProto = request.getHeader("X-Forwarded-Proto"); + final String fwdHost = request.getHeader("X-Forwarded-Host"); + final String scheme = fwdProto != null && !fwdProto.isBlank() ? fwdProto : request.getScheme(); + final String host; + final int port; + if (fwdHost != null && !fwdHost.isBlank()) { + final int colon = fwdHost.indexOf(':'); + host = colon < 0 ? fwdHost : fwdHost.substring(0, colon); + port = colon < 0 ? -1 : Integer.parseInt(fwdHost.substring(colon + 1)); + } else { + host = request.getServerName(); + port = request.getServerPort(); + } + final StringBuilder sb = new StringBuilder(scheme).append("://").append(host); + final boolean defaultPort = ("https".equalsIgnoreCase(scheme) && port == 443) + || ("http".equalsIgnoreCase(scheme) && port == 80) || port < 0; + if (!defaultPort) { + sb.append(':').append(port); + } + sb.append(request.getContextPath()); + return sb.toString(); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/api/LoginDscApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/api/LoginDscApiResource.java new file mode 100644 index 000000000..2dc66441a --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/api/LoginDscApiResource.java @@ -0,0 +1,140 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.api; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.useradministration.dsc.data.DscLoginChallengeStartResponse; +import org.apache.fineract.useradministration.dsc.data.DscLoginStatusResponse; +import org.apache.fineract.useradministration.dsc.data.DscLoginVerifyRequest; +import org.apache.fineract.useradministration.dsc.service.DscLoginChallengeService; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +/** + * DSC-based login factor. After primary basic-auth succeeds, a user who has an enrolled DSC may + * pick the DSC option instead of SMS/EMAIL. The flow: + * + *

    + *
  • POST /v1/login/dsc/challenge — create challenge (requires basic auth, NOT TFA)
  • + *
  • GET /v1/login/dsc/{rn}.jnlp — JNLP for the helper (nonce-gated)
  • + *
  • POST /v1/login/dsc/{rn}/verify — helper POSTs signature (nonce-gated)
  • + *
  • GET /v1/login/dsc/{rn}/status — UI polls (nonce-gated)
  • + *
+ * + *

On successful verify, the response includes a {@code tfaToken} the UI immediately puts into + * the {@code Fineract-Platform-TFA-Token} header. Status polling never echoes the token back — + * the verify response is its only emission. + */ +@Path("/v1/login/dsc") +@Component +@ConditionalOnProperty("fineract.security.2fa.enabled") +@Tag(name = "Login DSC", description = "DSC-based two-factor authentication") +@RequiredArgsConstructor +public class LoginDscApiResource { + + private final DscLoginChallengeService loginChallengeService; + + @POST + @Path("/challenge") + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Create a DSC login challenge for the basic-authenticated user. Returns " + + "randomNo + jnlpUrl + statusUrl. Caller does NOT need TWOFACTOR_AUTHENTICATED.") + public DscLoginChallengeStartResponse challenge(@Context final HttpServletRequest request) { + return loginChallengeService.start(deriveCodebase(request), request.getRemoteAddr(), request.getHeader("User-Agent")); + } + + @GET + @Path("/{randomNo}.jnlp") + @Produces("application/x-java-jnlp-file") + @Operation(summary = "Streams the JNLP that launches the helper in SIGN_CHALLENGE mode.") + public Response jnlp(@PathParam("randomNo") final String randomNo, @Context final HttpServletRequest request) { + final String codebase = deriveCodebase(request); + final String body = loginChallengeService.renderJnlp(randomNo, codebase); + return Response.ok(body, "application/x-java-jnlp-file") + .header("Content-Disposition", "attachment; filename=\"cbsnexora-dsc-login.jnlp\"") + .header("Cache-Control", "no-store") + .build(); + } + + @POST + @Path("/{randomNo}/verify") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Helper POSTs cert serial + signature. Server verifies, then mints a TFA " + + "access token returned in the response.") + public DscLoginStatusResponse verify(@PathParam("randomNo") final String randomNo, final DscLoginVerifyRequest req) { + return loginChallengeService.verify(randomNo, req); + } + + @GET + @Path("/{randomNo}/status") + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "UI polls this. Returns PENDING/VERIFIED/FAILED/EXPIRED. Never echoes the " + + "TFA token — the browser fetches it via /claim once status flips to VERIFIED.") + public DscLoginStatusResponse status(@PathParam("randomNo") final String randomNo) { + return loginChallengeService.getStatus(randomNo); + } + + @POST + @Path("/{randomNo}/claim") + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Browser claims the minted TFA token after seeing status=VERIFIED. " + + "Requires basic auth (caller must own the challenge). Single-use — the stored token " + + "is cleared after this returns.") + public DscLoginStatusResponse claim(@PathParam("randomNo") final String randomNo) { + return loginChallengeService.claim(randomNo); + } + + /** Same codebase resolution as {@link DscApiResource} — honours reverse-proxy headers. */ + private static String deriveCodebase(final HttpServletRequest request) { + final String fwdProto = request.getHeader("X-Forwarded-Proto"); + final String fwdHost = request.getHeader("X-Forwarded-Host"); + final String scheme = fwdProto != null && !fwdProto.isBlank() ? fwdProto : request.getScheme(); + final String host; + final int port; + if (fwdHost != null && !fwdHost.isBlank()) { + final int colon = fwdHost.indexOf(':'); + host = colon < 0 ? fwdHost : fwdHost.substring(0, colon); + port = colon < 0 ? -1 : Integer.parseInt(fwdHost.substring(colon + 1)); + } else { + host = request.getServerName(); + port = request.getServerPort(); + } + final StringBuilder sb = new StringBuilder(scheme).append("://").append(host); + final boolean defaultPort = ("https".equalsIgnoreCase(scheme) && port == 443) + || ("http".equalsIgnoreCase(scheme) && port == 80) || port < 0; + if (!defaultPort) { + sb.append(':').append(port); + } + sb.append(request.getContextPath()); + return sb.toString(); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/BankUserDscData.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/BankUserDscData.java new file mode 100644 index 000000000..1b5d8b6b6 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/BankUserDscData.java @@ -0,0 +1,98 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.data; + +import java.time.LocalDate; +import java.time.OffsetDateTime; +import lombok.Builder; +import lombok.Getter; +import org.apache.fineract.useradministration.dsc.domain.BankUserDsc; +import org.apache.fineract.useradministration.dsc.domain.CertStatus; + +/** + * Wire representation of a row in {@code m_bank_user_dsc}. Public key is intentionally omitted + * from the default view — only the cert metadata the UI needs to display. Serial is sent masked + * (last 6 chars visible) so list views don't leak full serials. + */ +@Getter +@Builder +public class BankUserDscData { + + private final Long id; + private final Long appUserId; + private final String username; + private final String dscName; + private final String dscSerialNoMasked; + private final String dscSerialNoFull; + private final String dscType; + private final String commonName; + private final String issuedTo; + private final String issuedBy; + private final LocalDate dscExpiryDate; + private final OffsetDateTime dateOfSubmission; + private final String certStatus; + private final OffsetDateTime revokedOn; + private final String revokeReason; + + /** {@code maskSerial = true} for list views, false for the single-row detail endpoint. */ + public static BankUserDscData from(final BankUserDsc d, final boolean maskSerial) { + return BankUserDscData.builder() + .id(d.getId()) + .appUserId(d.getAppUser() != null ? d.getAppUser().getId() : null) + .username(d.getAppUser() != null ? d.getAppUser().getUsername() : null) + .dscName(d.getDscName()) + .dscSerialNoMasked(mask(d.getDscSerialNo())) + .dscSerialNoFull(maskSerial ? null : d.getDscSerialNo()) + .dscType(d.getDscType() != null ? d.getDscType().name() : null) + .commonName(d.getCommonName()) + .issuedTo(d.getIssuedTo()) + .issuedBy(d.getIssuedBy()) + .dscExpiryDate(d.getDscExpiryDate()) + .dateOfSubmission(d.getDateOfSubmission()) + .certStatus(effectiveStatus(d)) + .revokedOn(d.getRevokedOn()) + .revokeReason(d.getRevokeReason()) + .build(); + } + + /** + * Stored {@code cert_status} is authoritative for REVOKED, but ACTIVE rows whose + * {@code dscExpiryDate} has passed get reported as EXPIRED on the wire. The DB row stays + * ACTIVE until a maintenance sweep flips it, but the UI never sees a stale ACTIVE for an + * expired cert. + */ + private static String effectiveStatus(final BankUserDsc d) { + final CertStatus stored = d.getCertStatus(); + if (stored == null) { + return null; + } + if (stored == CertStatus.ACTIVE && d.getDscExpiryDate() != null + && d.getDscExpiryDate().isBefore(LocalDate.now())) { + return CertStatus.EXPIRED.name(); + } + return stored.name(); + } + + private static String mask(final String s) { + if (s == null || s.length() <= 6) { + return s; + } + return "•".repeat(Math.min(s.length() - 6, 12)) + s.substring(s.length() - 6); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/DscChallengeStatusResponse.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/DscChallengeStatusResponse.java new file mode 100644 index 000000000..144abdc1d --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/DscChallengeStatusResponse.java @@ -0,0 +1,36 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.data; + +import lombok.Builder; +import lombok.Getter; + +/** + * UI polls for this. {@code status} is {@code PENDING} until the helper completes, then flips to + * {@code VERIFIED} (with {@code dsc} populated on enrollment success) or {@code FAILED}/{@code EXPIRED}. + */ +@Getter +@Builder +public class DscChallengeStatusResponse { + + private final String randomNo; + private final String status; + private final String errorMessage; + private final BankUserDscData dsc; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/DscEnrollCallbackRequest.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/DscEnrollCallbackRequest.java new file mode 100644 index 000000000..af7f10fb3 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/DscEnrollCallbackRequest.java @@ -0,0 +1,45 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.data; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Body the Java helper POSTs back to {@code /v1/dsc/enroll/{randomNo}/callback}. + * + *

{@link #certB64} is the base64-encoded X.509 DER cert — server parses it as the source of + * truth for serial, issuer, subject, notAfter, and the public key. {@link #dscName} is an + * optional user-provided label. + */ +@Getter +@Setter +@NoArgsConstructor +public class DscEnrollCallbackRequest { + + /** Base64-encoded X.509 DER bytes. */ + private String certB64; + + /** Optional user-facing label, e.g. "Aditya's Primary Token". */ + private String dscName; + + /** Optional — SIGNING or ENCRYPTION. Defaults to SIGNING when null. */ + private String dscType; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/DscEnrollStartResponse.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/DscEnrollStartResponse.java new file mode 100644 index 000000000..8f45cc1d6 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/DscEnrollStartResponse.java @@ -0,0 +1,52 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.data; + +import lombok.Builder; +import lombok.Getter; + +/** + * Returned to the UI when an enrollment cycle is created. The UI uses {@code jnlpUrl} to trigger + * the browser download that launches the helper; it then polls {@code statusUrl} for completion. + */ +@Getter +@Builder +public class DscEnrollStartResponse { + + private final String randomNo; + private final String jnlpUrl; + private final String statusUrl; + private final int expiresInSeconds; + + /** + * Pre-stringified JSON the helper expects in its single launch argument. The UI embeds this + * verbatim into a locally-generated JNLP file's {@code } element, so JWS never has + * to fetch a JNLP from the server. + * + *

Includes the absolute {@code callbackUrl} the helper POSTs back to + the + * {@code tenantIdentifier} the helper uses as a request header. + */ + private final String helperArgsJson; + + /** + * Relative URL the UI XHRs to fetch the helper JAR bytes. The interceptor adds the auth + * + tenant headers so the request goes through normal channels — JWS is never involved. + */ + private final String helperJarUrl; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/DscLoginChallengeStartResponse.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/DscLoginChallengeStartResponse.java new file mode 100644 index 000000000..4a86fb543 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/DscLoginChallengeStartResponse.java @@ -0,0 +1,38 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.data; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class DscLoginChallengeStartResponse { + + private final String randomNo; + private final String jnlpUrl; + private final String statusUrl; + private final int expiresInSeconds; + + /** See {@link DscEnrollStartResponse#getHelperArgsJson()}. Same shape for the login flow. */ + private final String helperArgsJson; + + /** See {@link DscEnrollStartResponse#getHelperJarUrl()}. */ + private final String helperJarUrl; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/DscLoginStatusResponse.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/DscLoginStatusResponse.java new file mode 100644 index 000000000..2190a40cd --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/DscLoginStatusResponse.java @@ -0,0 +1,38 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.data; + +import lombok.Builder; +import lombok.Getter; + +/** + * Polled by the login UI. On {@code VERIFIED}, {@link #tfaToken} carries the {@code TFAccessToken} + * the UI puts in the {@code Fineract-Platform-TFA-Token} header for subsequent calls — same shape + * as what {@code /v1/twofactor/validate} returns for OTP-based flows. + */ +@Getter +@Builder +public class DscLoginStatusResponse { + + private final String randomNo; + private final String status; + private final String errorMessage; + private final String tfaToken; + private final Long tfaTokenLiveSeconds; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/DscLoginVerifyRequest.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/DscLoginVerifyRequest.java new file mode 100644 index 000000000..88a93dacf --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/data/DscLoginVerifyRequest.java @@ -0,0 +1,39 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.data; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Body the Java helper POSTs to {@code /v1/login/dsc/{randomNo}/verify}. Helper-driven, no Fineract + * auth header — the {@link #certSerial} + signature pair authenticates the user. + */ +@Getter +@Setter +@NoArgsConstructor +public class DscLoginVerifyRequest { + + /** Hex serial of the DSC the user signed with — must match the user's enrolled mapping. */ + private String certSerial; + + /** Base64-encoded RSA-SHA256 signature over the challenge bytes. */ + private String signatureB64; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/exception/DscChallengeInvalidException.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/exception/DscChallengeInvalidException.java new file mode 100644 index 000000000..3127ba190 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/exception/DscChallengeInvalidException.java @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.exception; + +import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +/** + * Thrown when a DSC challenge row is in a non-PENDING state — already verified, failed, or + * expired — and a callback tries to use it again. Single-shot enforcement. + */ +public class DscChallengeInvalidException extends AbstractPlatformDomainRuleException { + + public DscChallengeInvalidException(final String reason) { + super("error.msg.dsc.challenge.invalid", reason, reason); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/exception/DscChallengeNotFoundException.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/exception/DscChallengeNotFoundException.java new file mode 100644 index 000000000..969e14d28 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/exception/DscChallengeNotFoundException.java @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.exception; + +import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +public class DscChallengeNotFoundException extends AbstractPlatformResourceNotFoundException { + + public DscChallengeNotFoundException(final String randomNo) { + super("error.msg.dsc.challenge.not.found", "DSC challenge " + randomNo + " not found", randomNo); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/exception/DscNotFoundException.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/exception/DscNotFoundException.java new file mode 100644 index 000000000..ade8c179d --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/exception/DscNotFoundException.java @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.exception; + +import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +public class DscNotFoundException extends AbstractPlatformResourceNotFoundException { + + public DscNotFoundException(final Long dscId) { + super("error.msg.dsc.not.found", "DSC mapping " + dscId + " not found", dscId); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/exception/DscSerialAlreadyEnrolledException.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/exception/DscSerialAlreadyEnrolledException.java new file mode 100644 index 000000000..2d0535ddb --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/exception/DscSerialAlreadyEnrolledException.java @@ -0,0 +1,34 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.exception; + +import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +/** + * The DSC serial is globally unique — one cert in the world has one serial. If a user tries to + * enroll a cert already bound to a different appuser, we reject without revealing which user owns + * it. + */ +public class DscSerialAlreadyEnrolledException extends AbstractPlatformDomainRuleException { + + public DscSerialAlreadyEnrolledException(final String serial) { + super("error.msg.dsc.serial.already.enrolled", + "This Digital Signature Certificate is already enrolled in the system", serial); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscEnrollmentWriteService.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscEnrollmentWriteService.java new file mode 100644 index 000000000..20c765e63 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscEnrollmentWriteService.java @@ -0,0 +1,34 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.service; + +import org.apache.fineract.useradministration.dsc.data.DscChallengeStatusResponse; +import org.apache.fineract.useradministration.dsc.data.DscEnrollCallbackRequest; +import org.apache.fineract.useradministration.dsc.data.DscEnrollStartResponse; + +public interface DscEnrollmentWriteService { + + DscEnrollStartResponse start(String codebase, String ipAddress, String userAgent); + + String renderJnlp(String randomNo, String codebase); + + DscChallengeStatusResponse callback(String randomNo, DscEnrollCallbackRequest request); + + DscChallengeStatusResponse getStatus(String randomNo); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscEnrollmentWriteServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscEnrollmentWriteServiceImpl.java new file mode 100644 index 000000000..bd184ee49 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscEnrollmentWriteServiceImpl.java @@ -0,0 +1,255 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.service; + +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Optional; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.useradministration.domain.AppUser; +import org.apache.fineract.useradministration.dsc.data.BankUserDscData; +import org.apache.fineract.useradministration.dsc.data.DscChallengeStatusResponse; +import org.apache.fineract.useradministration.dsc.data.DscEnrollCallbackRequest; +import org.apache.fineract.useradministration.dsc.data.DscEnrollStartResponse; +import org.apache.fineract.useradministration.dsc.domain.BankUserDsc; +import org.apache.fineract.useradministration.dsc.domain.BankUserDscRepository; +import org.apache.fineract.useradministration.dsc.domain.CertStatus; +import org.apache.fineract.useradministration.dsc.domain.DscChallenge; +import org.apache.fineract.useradministration.dsc.domain.DscChallengePurpose; +import org.apache.fineract.useradministration.dsc.domain.DscChallengeRepository; +import org.apache.fineract.useradministration.dsc.domain.DscChallengeStatus; +import org.apache.fineract.useradministration.dsc.domain.DscType; +import org.apache.fineract.useradministration.dsc.exception.DscChallengeInvalidException; +import org.apache.fineract.useradministration.dsc.exception.DscChallengeNotFoundException; +import org.apache.fineract.useradministration.dsc.exception.DscSerialAlreadyEnrolledException; +import org.apache.fineract.useradministration.dsc.util.X509CertParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class DscEnrollmentWriteServiceImpl implements DscEnrollmentWriteService { + + private static final Logger LOG = LoggerFactory.getLogger(DscEnrollmentWriteServiceImpl.class); + + private final PlatformSecurityContext securityContext; + private final DscChallengeRepository challengeRepository; + private final BankUserDscRepository dscRepository; + private final X509CertParser certParser; + private final DscJnlpGenerator jnlpGenerator; + + @Value("${fineract.dsc.challenge.ttl-seconds:300}") + private int challengeTtlSeconds; + + @Override + @Transactional + public DscEnrollStartResponse start(final String codebase, final String ipAddress, final String userAgent) { + final AppUser user = securityContext.authenticatedUser(); + final String randomNo = UUID.randomUUID().toString(); + + final DscChallenge ch = new DscChallenge(); + ch.setRandomNo(randomNo); + ch.setAppUser(user); + ch.setPurpose(DscChallengePurpose.ENROLL); + // ENROLL flow doesn't sign anything — challenge column is just a placeholder marker. + ch.setChallenge("ENROLL"); + ch.setStatus(DscChallengeStatus.PENDING); + ch.setExpiresAt(OffsetDateTime.now(ZoneOffset.UTC).plusSeconds(challengeTtlSeconds)); + ch.setIpAddress(ipAddress); + ch.setUserAgent(userAgent); + challengeRepository.saveAndFlush(ch); + + LOG.info("DSC enroll started — user={}, randomNo={}, ttl={}s", user.getUsername(), randomNo, challengeTtlSeconds); + + // Args the helper expects in its launch argument. The UI embeds this into a JNLP it + // generates locally — JWS never fetches a JNLP from the server, only reads it from disk. + final java.util.Map helperArgs = new java.util.LinkedHashMap<>(); + helperArgs.put("randomNo", randomNo); + helperArgs.put("mode", "ENROLL"); + helperArgs.put("callbackUrl", codebase + "/api/v1/dsc/enroll/" + randomNo + "/callback"); + helperArgs.put("title", "Map Digital Signature Certificate"); + final String tenant = currentTenantIdentifier(); + if (tenant != null) { + helperArgs.put("tenantIdentifier", tenant); + } + final String helperArgsJson = new com.google.gson.Gson().toJson(helperArgs); + + return DscEnrollStartResponse.builder() + .randomNo(randomNo) + .jnlpUrl("/v1/dsc/enroll/" + randomNo + ".jnlp") + .statusUrl("/v1/dsc/enroll/" + randomNo + "/status") + .expiresInSeconds(challengeTtlSeconds) + .helperArgsJson(helperArgsJson) + .helperJarUrl("/v1/dsc/helper.jar") + .build(); + } + + @Override + @Transactional(readOnly = true) + public String renderJnlp(final String randomNo, final String codebase) { + final DscChallenge ch = challengeRepository.findByRandomNo(randomNo) + .orElseThrow(() -> new DscChallengeNotFoundException(randomNo)); + if (ch.getStatus() != DscChallengeStatus.PENDING || ch.isExpired()) { + throw new DscChallengeInvalidException("Challenge is no longer accepting helper launches"); + } + + final DscJnlpGenerator.JnlpContext ctx = new DscJnlpGenerator.JnlpContext(); + ctx.randomNo = randomNo; + ctx.mode = "ENROLL"; + ctx.callbackUrl = codebase + "/api/v1/dsc/enroll/" + randomNo + "/callback"; + ctx.codebase = codebase; + ctx.tenantIdentifier = currentTenantIdentifier(); + ctx.title = "Map Digital Signature Certificate"; + ctx.description = "Reads your DSC and maps it to your CBSnexora user account."; + return jnlpGenerator.generate(ctx); + } + + /** + * Tenant identifier the request is running under — resolved by {@code TenantAwareBasicAuthentication + * Filter} earlier in the chain and stashed in the thread-local context. We embed this in the + * JNLP so JWS's raw GET for the helper JAR can resolve the tenant via {@code ?tenantIdentifier=} + * (the filter already accepts that as a fallback to the header) and so the helper itself can + * set the {@code Fineract-Platform-TenantId} header on its callback POST. + */ + private String currentTenantIdentifier() { + try { + return org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil.getTenant().getTenantIdentifier(); + } catch (final Exception e) { + LOG.warn("Could not resolve current tenant for JNLP — JWS / helper may fail without ?tenantIdentifier", e); + return null; + } + } + + @Override + @Transactional + public DscChallengeStatusResponse callback(final String randomNo, final DscEnrollCallbackRequest req) { + final DscChallenge ch = challengeRepository.findByRandomNo(randomNo) + .orElseThrow(() -> new DscChallengeNotFoundException(randomNo)); + + if (ch.getStatus() != DscChallengeStatus.PENDING) { + throw new DscChallengeInvalidException("Challenge already terminal: " + ch.getStatus()); + } + if (ch.isExpired()) { + ch.setStatus(DscChallengeStatus.EXPIRED); + challengeRepository.saveAndFlush(ch); + throw new DscChallengeInvalidException("Challenge expired"); + } + if (req == null || req.getCertB64() == null || req.getCertB64().isBlank()) { + failChallenge(ch, "certB64 missing"); + throw new DscChallengeInvalidException("certB64 is required"); + } + + final X509CertParser.ParsedCert parsed; + try { + parsed = certParser.parse(req.getCertB64()); + } catch (final IllegalArgumentException e) { + failChallenge(ch, "cert parse error: " + e.getMessage()); + throw new DscChallengeInvalidException(e.getMessage()); + } + + // Globally unique — if this serial is already bound to a different user, refuse without + // revealing who owns it. + final Optional existing = dscRepository.findByDscSerialNo(parsed.getSerial()); + final AppUser owner = ch.getAppUser(); + if (existing.isPresent() && !existing.get().getAppUser().getId().equals(owner.getId())) { + failChallenge(ch, "serial already enrolled"); + throw new DscSerialAlreadyEnrolledException(parsed.getSerial()); + } + + // Upsert: re-enrolling the same serial for the same user refreshes name/pubkey/expiry and + // resurrects a soft-revoked row. + final BankUserDsc dsc = existing.orElseGet(BankUserDsc::new); + dsc.setAppUser(owner); + dsc.setDscUserId(owner.getUsername()); + dsc.setDscSerialNo(parsed.getSerial()); + dsc.setDscName(req.getDscName() != null && !req.getDscName().isBlank() ? req.getDscName() : parsed.getCommonName()); + dsc.setDscPublicKey(parsed.getPublicKeyB64()); + dsc.setDscExpiryDate(parsed.getNotAfter()); + dsc.setDateOfSubmission(OffsetDateTime.now(ZoneOffset.UTC)); + dsc.setDscType(parseTypeOrSigning(req.getDscType())); + dsc.setCommonName(parsed.getCommonName()); + dsc.setIssuedTo(parsed.getSubjectDn()); + dsc.setIssuedBy(parsed.getIssuerDn()); + dsc.setCertStatus(CertStatus.ACTIVE); + dsc.setActive(true); + dsc.setDeleted(false); + dsc.setRevokedOn(null); + dsc.setRevokedBy(null); + dsc.setRevokeReason(null); + if (dsc.getCreatedBy() == null) { + dsc.setCreatedBy(owner.getId()); + } else { + dsc.setModifiedBy(owner.getId()); + dsc.setModifiedOn(OffsetDateTime.now(ZoneOffset.UTC)); + } + final BankUserDsc saved = dscRepository.saveAndFlush(dsc); + + ch.setStatus(DscChallengeStatus.VERIFIED); + ch.setVerifiedAt(OffsetDateTime.now(ZoneOffset.UTC)); + ch.setCertSerialReturned(parsed.getSerial()); + ch.setDscId(saved.getId()); + challengeRepository.saveAndFlush(ch); + + LOG.info("DSC enrolled — user={}, dscId={}, serial={}", owner.getUsername(), saved.getId(), parsed.getSerial()); + + return DscChallengeStatusResponse.builder() + .randomNo(randomNo) + .status(DscChallengeStatus.VERIFIED.name()) + .dsc(BankUserDscData.from(saved, false)) + .build(); + } + + @Override + @Transactional(readOnly = true) + public DscChallengeStatusResponse getStatus(final String randomNo) { + final DscChallenge ch = challengeRepository.findByRandomNo(randomNo) + .orElseThrow(() -> new DscChallengeNotFoundException(randomNo)); + + BankUserDscData dscData = null; + if (ch.getStatus() == DscChallengeStatus.VERIFIED && ch.getDscId() != null) { + dscData = dscRepository.findById(ch.getDscId()) + .map(d -> BankUserDscData.from(d, false)) + .orElse(null); + } + return DscChallengeStatusResponse.builder() + .randomNo(randomNo) + .status(ch.getStatus().name()) + .errorMessage(ch.getErrorMessage()) + .dsc(dscData) + .build(); + } + + private void failChallenge(final DscChallenge ch, final String reason) { + ch.setStatus(DscChallengeStatus.FAILED); + ch.setErrorMessage(reason); + ch.setAttempts(ch.getAttempts() + 1); + challengeRepository.saveAndFlush(ch); + LOG.info("DSC enroll callback failed — randomNo={}, reason={}", ch.getRandomNo(), reason); + } + + private static DscType parseTypeOrSigning(final String s) { + final DscType t = DscType.fromString(s); + return t != null ? t : DscType.SIGNING; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscJnlpGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscJnlpGenerator.java new file mode 100644 index 000000000..d2241c88b --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscJnlpGenerator.java @@ -0,0 +1,125 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.service; + +import com.google.gson.Gson; +import java.util.LinkedHashMap; +import java.util.Map; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * Builds the JNLP XML the browser hands to Java Web Start / OpenWebStart. The helper JAR is + * downloaded fresh on every launch (no caching) so a server-side helper upgrade rolls out + * immediately. + * + *

Argument shape passed to {@code CbsnexoraDscHelper.main()} is a single JSON object — the + * helper parses it with Gson and dispatches on {@code mode}. + */ +@Component +public class DscJnlpGenerator { + + private static final Gson GSON = new Gson(); + + @Value("${fineract.dsc.helper.jar.url:/api/v1/dsc/helper.jar}") + private String helperJarUrl; + + @Value("${fineract.dsc.helper.main-class:com.cbsnexora.dsc.CbsnexoraDscHelper}") + private String mainClass; + + @Value("${fineract.dsc.helper.vendor:CBSnexora}") + private String vendor; + + public String generate(final JnlpContext ctx) { + final Map args = new LinkedHashMap<>(); + args.put("randomNo", ctx.randomNo); + args.put("mode", ctx.mode); + args.put("callbackUrl", ctx.callbackUrl); + if (ctx.challenge != null) { + args.put("challenge", ctx.challenge); + } + if (ctx.expectedSerial != null) { + args.put("expectedSerial", ctx.expectedSerial); + } + if (ctx.tenantIdentifier != null) { + // Helper reads this and adds `Fineract-Platform-TenantId: ` to its callback POST, + // because the helper isn't routed through the UI's auth interceptor — it makes raw + // HttpURLConnection calls. + args.put("tenantIdentifier", ctx.tenantIdentifier); + } + args.put("title", ctx.title); + + final String argJson = escapeForXml(GSON.toJson(args)); + // JWS will GET the JAR with no headers (it's a fresh process, not the browser). Embedding + // `?tenantIdentifier=` so Fineract's tenant filter (which already falls back to the query + // param when the header is absent) can resolve the tenant for this raw request. + final String jarUrlWithTenant = ctx.tenantIdentifier != null + ? appendQueryParam(helperJarUrl, "tenantIdentifier", ctx.tenantIdentifier) + : helperJarUrl; + final String absoluteJarUrl = jarUrlWithTenant.startsWith("http") + ? jarUrlWithTenant + : ctx.codebase + jarUrlWithTenant; + + return "\n" + + "\n" + + " \n" + + " " + escapeForXml(vendor) + "\n" + + " " + escapeForXml(ctx.title) + "\n" + + " " + escapeForXml(ctx.description) + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " " + argJson + "\n" + + " \n" + + ""; + } + + private static String escapeForXml(final String s) { + if (s == null) { + return ""; + } + return s.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """).replace("'", "'"); + } + + private static String appendQueryParam(final String url, final String name, final String value) { + if (url == null || name == null || value == null) { + return url; + } + final String sep = url.indexOf('?') >= 0 ? "&" : "?"; + return url + sep + name + "=" + java.net.URLEncoder.encode(value, java.nio.charset.StandardCharsets.UTF_8); + } + + public static class JnlpContext { + public String randomNo; + public String mode; // ENROLL | SIGN_CHALLENGE + public String callbackUrl; + public String challenge; // only for SIGN_CHALLENGE + public String expectedSerial; // only for SIGN_CHALLENGE — helper enforces user picks the right cert + public String codebase; // e.g. https://cbs.example.com/fineract-provider + public String tenantIdentifier; // embedded in helper.jar URL + args; helper uses it for callback POST header + public String title; + public String description; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscLoginChallengeService.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscLoginChallengeService.java new file mode 100644 index 000000000..67d61a3eb --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscLoginChallengeService.java @@ -0,0 +1,41 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.service; + +import org.apache.fineract.useradministration.dsc.data.DscLoginChallengeStartResponse; +import org.apache.fineract.useradministration.dsc.data.DscLoginStatusResponse; +import org.apache.fineract.useradministration.dsc.data.DscLoginVerifyRequest; + +public interface DscLoginChallengeService { + + DscLoginChallengeStartResponse start(String codebase, String ipAddress, String userAgent); + + String renderJnlp(String randomNo, String codebase); + + DscLoginStatusResponse verify(String randomNo, DscLoginVerifyRequest request); + + DscLoginStatusResponse getStatus(String randomNo); + + /** + * Browser-side, basic-authed claim of the minted TFA token. Single-use — once claimed, the + * stored token is cleared. Caller must own the challenge (i.e. be the same user the helper + * verified for); otherwise treated as not-found to prevent enumeration. + */ + DscLoginStatusResponse claim(String randomNo); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscLoginChallengeServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscLoginChallengeServiceImpl.java new file mode 100644 index 000000000..bb84dda12 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscLoginChallengeServiceImpl.java @@ -0,0 +1,282 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.service; + +import java.security.SecureRandom; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.HexFormat; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.infrastructure.security.domain.TFAccessToken; +import org.apache.fineract.infrastructure.security.domain.TFAccessTokenRepository; +import org.apache.fineract.infrastructure.security.service.AccessTokenGenerationService; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.infrastructure.security.service.TwoFactorConfigurationService; +import org.apache.fineract.useradministration.domain.AppUser; +import org.apache.fineract.useradministration.dsc.data.DscLoginChallengeStartResponse; +import org.apache.fineract.useradministration.dsc.data.DscLoginStatusResponse; +import org.apache.fineract.useradministration.dsc.data.DscLoginVerifyRequest; +import org.apache.fineract.useradministration.dsc.domain.BankUserDsc; +import org.apache.fineract.useradministration.dsc.domain.BankUserDscRepository; +import org.apache.fineract.useradministration.dsc.domain.DscChallenge; +import org.apache.fineract.useradministration.dsc.domain.DscChallengePurpose; +import org.apache.fineract.useradministration.dsc.domain.DscChallengeRepository; +import org.apache.fineract.useradministration.dsc.domain.DscChallengeStatus; +import org.apache.fineract.useradministration.dsc.exception.DscChallengeInvalidException; +import org.apache.fineract.useradministration.dsc.exception.DscChallengeNotFoundException; +import org.apache.fineract.useradministration.dsc.util.DscSignatureVerifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * Drives the DSC-based login factor: + * + *

    + *
  1. {@link #start} — partially-authenticated user (basic auth, no TFA yet) creates a challenge + * row containing a random hex string. UI renders the JNLP.
  2. + *
  3. {@link #verify} — helper POSTs signature; server checks the user has the cert serial + * enrolled and that RSA-SHA256({challenge}) matches the stored public key. On success a + * {@link TFAccessToken} is minted — same shape as OTP-based 2FA — and returned via status.
  4. + *
  5. {@link #getStatus} — UI polls; on VERIFIED the response carries the TFA token.
  6. + *
+ */ +@Service +@RequiredArgsConstructor +public class DscLoginChallengeServiceImpl implements DscLoginChallengeService { + + private static final Logger LOG = LoggerFactory.getLogger(DscLoginChallengeServiceImpl.class); + private static final int CHALLENGE_BYTES = 32; // 256 bits → 64 hex chars + private final SecureRandom secureRandom = new SecureRandom(); + + private final PlatformSecurityContext securityContext; + private final DscChallengeRepository challengeRepository; + private final BankUserDscRepository dscRepository; + private final DscSignatureVerifier signatureVerifier; + private final DscJnlpGenerator jnlpGenerator; + private final AccessTokenGenerationService accessTokenGenerationService; + private final TFAccessTokenRepository tfAccessTokenRepository; + private final TwoFactorConfigurationService twoFactorConfigurationService; + + @Value("${fineract.dsc.challenge.ttl-seconds:300}") + private int challengeTtlSeconds; + + @Override + @Transactional + public DscLoginChallengeStartResponse start(final String codebase, final String ipAddress, final String userAgent) { + final AppUser user = securityContext.authenticatedUser(); + final String randomNo = UUID.randomUUID().toString(); + final String challenge = newChallengeHex(); + + final DscChallenge ch = new DscChallenge(); + ch.setRandomNo(randomNo); + ch.setAppUser(user); + ch.setPurpose(DscChallengePurpose.LOGIN_2FA); + ch.setChallenge(challenge); + ch.setStatus(DscChallengeStatus.PENDING); + ch.setExpiresAt(OffsetDateTime.now(ZoneOffset.UTC).plusSeconds(challengeTtlSeconds)); + ch.setIpAddress(ipAddress); + ch.setUserAgent(userAgent); + challengeRepository.saveAndFlush(ch); + + LOG.info("DSC login challenge started — user={}, randomNo={}", user.getUsername(), randomNo); + + // Helper args for the UI to embed into a locally-built JNLP. The helper POSTs to the + // absolute callbackUrl and sets the tenant header from `tenantIdentifier`. + final java.util.Map helperArgs = new java.util.LinkedHashMap<>(); + helperArgs.put("randomNo", randomNo); + helperArgs.put("mode", "SIGN_CHALLENGE"); + helperArgs.put("callbackUrl", codebase + "/api/v1/login/dsc/" + randomNo + "/verify"); + helperArgs.put("challenge", challenge); + helperArgs.put("title", "Verify Digital Signature"); + try { + final String tenant = org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil + .getTenant().getTenantIdentifier(); + if (tenant != null) { + helperArgs.put("tenantIdentifier", tenant); + } + } catch (final Exception ignored) { + // single-tenant deploys without tenant context — leave out + } + final String helperArgsJson = new com.google.gson.Gson().toJson(helperArgs); + + return DscLoginChallengeStartResponse.builder() + .randomNo(randomNo) + .jnlpUrl("/v1/login/dsc/" + randomNo + ".jnlp") + .statusUrl("/v1/login/dsc/" + randomNo + "/status") + .expiresInSeconds(challengeTtlSeconds) + .helperArgsJson(helperArgsJson) + .helperJarUrl("/v1/dsc/helper.jar") + .build(); + } + + @Override + @Transactional(readOnly = true) + public String renderJnlp(final String randomNo, final String codebase) { + final DscChallenge ch = challengeRepository.findByRandomNo(randomNo) + .orElseThrow(() -> new DscChallengeNotFoundException(randomNo)); + if (ch.getStatus() != DscChallengeStatus.PENDING || ch.isExpired()) { + throw new DscChallengeInvalidException("Challenge is no longer accepting helper launches"); + } + final DscJnlpGenerator.JnlpContext ctx = new DscJnlpGenerator.JnlpContext(); + ctx.randomNo = randomNo; + ctx.mode = "SIGN_CHALLENGE"; + ctx.challenge = ch.getChallenge(); + ctx.callbackUrl = codebase + "/api/v1/login/dsc/" + randomNo + "/verify"; + ctx.codebase = codebase; + // Embed tenant so JWS's headerless GET for helper.jar resolves via the filter's + // ?tenantIdentifier= fallback, and so the helper can put the same value into the + // Fineract-Platform-TenantId header when it POSTs /verify. + try { + ctx.tenantIdentifier = org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil + .getTenant().getTenantIdentifier(); + } catch (final Exception ignored) { + // single-tenant deploys may not have a tenant in context — leave null + } + ctx.title = "Verify Digital Signature"; + ctx.description = "Signs a login challenge with your enrolled DSC to complete two-factor authentication."; + return jnlpGenerator.generate(ctx); + } + + @Override + @Transactional + public DscLoginStatusResponse verify(final String randomNo, final DscLoginVerifyRequest req) { + final DscChallenge ch = challengeRepository.findByRandomNo(randomNo) + .orElseThrow(() -> new DscChallengeNotFoundException(randomNo)); + if (ch.getStatus() != DscChallengeStatus.PENDING) { + throw new DscChallengeInvalidException("Challenge already terminal: " + ch.getStatus()); + } + if (ch.isExpired()) { + ch.setStatus(DscChallengeStatus.EXPIRED); + challengeRepository.saveAndFlush(ch); + throw new DscChallengeInvalidException("Challenge expired"); + } + if (req == null || req.getCertSerial() == null || req.getSignatureB64() == null) { + failChallenge(ch, "certSerial or signatureB64 missing"); + throw new DscChallengeInvalidException("certSerial and signatureB64 are required"); + } + + // The user we challenged must own the cert serial they signed with. Don't lift the user + // out of the request — read it off the challenge row to prevent serial-substitution attacks. + final AppUser owner = ch.getAppUser(); + final BankUserDsc dsc = dscRepository.findByAppUser_IdAndDscSerialNo(owner.getId(), req.getCertSerial()).orElse(null); + if (dsc == null || !dsc.isUsable()) { + failChallenge(ch, "cert serial not enrolled for this user"); + return DscLoginStatusResponse.builder().randomNo(randomNo).status(DscChallengeStatus.FAILED.name()) + .errorMessage("Selected DSC is not enrolled for this user").build(); + } + + final boolean ok = signatureVerifier.verify(ch.getChallenge(), req.getSignatureB64(), dsc.getDscPublicKey()); + if (!ok) { + failChallenge(ch, "signature verification failed"); + return DscLoginStatusResponse.builder().randomNo(randomNo).status(DscChallengeStatus.FAILED.name()) + .errorMessage("Signature does not match the enrolled DSC").build(); + } + + // Mint a TFA access token — same shape MfaFactorBackedTwoFactorServiceImpl creates after a + // successful OTP verify. Default (non-extended) live time. + final int liveSeconds = twoFactorConfigurationService.getAccessTokenLiveTime(); + final String token = accessTokenGenerationService.generateRandomToken(); + final TFAccessToken accessToken = TFAccessToken.create(token, owner, liveSeconds); + tfAccessTokenRepository.save(accessToken); + + ch.setStatus(DscChallengeStatus.VERIFIED); + ch.setVerifiedAt(OffsetDateTime.now(ZoneOffset.UTC)); + ch.setCertSerialReturned(req.getCertSerial()); + ch.setSignatureB64(req.getSignatureB64()); + ch.setDscId(dsc.getId()); + // Stash the token on the challenge row — the browser fetches it via /claim. The verify + // response is consumed by the helper, which we don't want carrying a Fineract bearer. + ch.setTfaToken(token); + challengeRepository.saveAndFlush(ch); + + LOG.info("DSC login verified — user={}, dscId={}, randomNo={}", owner.getUsername(), dsc.getId(), randomNo); + return DscLoginStatusResponse.builder() + .randomNo(randomNo) + .status(DscChallengeStatus.VERIFIED.name()) + .build(); + } + + @Override + @Transactional + public DscLoginStatusResponse claim(final String randomNo) { + final AppUser caller = securityContext.authenticatedUser(); + final DscChallenge ch = challengeRepository.findByRandomNo(randomNo) + .orElseThrow(() -> new DscChallengeNotFoundException(randomNo)); + + // Owner check — treat non-owner attempts the same as not-found so we don't leak + // randomNos belonging to other users. + if (ch.getAppUser() == null || !ch.getAppUser().getId().equals(caller.getId())) { + throw new DscChallengeNotFoundException(randomNo); + } + if (ch.getStatus() != DscChallengeStatus.VERIFIED) { + throw new DscChallengeInvalidException("Challenge is not in VERIFIED state"); + } + if (ch.getTfaToken() == null || ch.getTfaTokenClaimedAt() != null) { + // Already claimed — treat as not-found to keep the endpoint single-shot. + throw new DscChallengeInvalidException("Token already claimed"); + } + + final String token = ch.getTfaToken(); + ch.setTfaToken(null); + ch.setTfaTokenClaimedAt(OffsetDateTime.now(ZoneOffset.UTC)); + challengeRepository.saveAndFlush(ch); + + LOG.info("DSC login token claimed — user={}, randomNo={}", caller.getUsername(), randomNo); + return DscLoginStatusResponse.builder() + .randomNo(randomNo) + .status(DscChallengeStatus.VERIFIED.name()) + .tfaToken(token) + .tfaTokenLiveSeconds((long) twoFactorConfigurationService.getAccessTokenLiveTime()) + .build(); + } + + @Override + @Transactional(readOnly = true) + public DscLoginStatusResponse getStatus(final String randomNo) { + final DscChallenge ch = challengeRepository.findByRandomNo(randomNo) + .orElseThrow(() -> new DscChallengeNotFoundException(randomNo)); + + // For polling we deliberately do NOT echo the TFA token after the fact — that prevents a + // status URL with the randomNo from being reused to fetch the bearer. The UI gets the token + // exactly once, in the verify response. After that, /status reflects state only. + return DscLoginStatusResponse.builder() + .randomNo(randomNo) + .status(ch.getStatus().name()) + .errorMessage(ch.getErrorMessage()) + .build(); + } + + private void failChallenge(final DscChallenge ch, final String reason) { + ch.setStatus(DscChallengeStatus.FAILED); + ch.setErrorMessage(reason); + ch.setAttempts(ch.getAttempts() + 1); + challengeRepository.saveAndFlush(ch); + LOG.info("DSC login verify failed — randomNo={}, reason={}", ch.getRandomNo(), reason); + } + + private String newChallengeHex() { + final byte[] buf = new byte[CHALLENGE_BYTES]; + secureRandom.nextBytes(buf); + return HexFormat.of().formatHex(buf); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscReadPlatformService.java new file mode 100644 index 000000000..c8fb678cd --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscReadPlatformService.java @@ -0,0 +1,37 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.service; + +import java.util.List; +import org.apache.fineract.useradministration.dsc.data.BankUserDscData; + +public interface DscReadPlatformService { + + /** All non-deleted rows for the currently-authenticated user. */ + List listForCurrentUser(); + + /** Admin view — non-deleted rows for an arbitrary user. */ + List listForUser(Long userId); + + /** Single-row detail. The caller must already be authorised (self or admin). */ + BankUserDscData getById(Long dscId); + + /** Soft-revokes a DSC. Both the caller's own DSCs and (with admin perm) any user's. */ + void revoke(Long dscId, String reason); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscReadPlatformServiceImpl.java new file mode 100644 index 000000000..e6c7d008b --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/service/DscReadPlatformServiceImpl.java @@ -0,0 +1,97 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.service; + +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.useradministration.domain.AppUser; +import org.apache.fineract.useradministration.dsc.data.BankUserDscData; +import org.apache.fineract.useradministration.dsc.domain.BankUserDsc; +import org.apache.fineract.useradministration.dsc.domain.BankUserDscRepository; +import org.apache.fineract.useradministration.dsc.domain.CertStatus; +import org.apache.fineract.useradministration.dsc.exception.DscNotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class DscReadPlatformServiceImpl implements DscReadPlatformService { + + private static final Logger LOG = LoggerFactory.getLogger(DscReadPlatformServiceImpl.class); + + private final PlatformSecurityContext securityContext; + private final BankUserDscRepository dscRepository; + + @Override + @Transactional(readOnly = true) + public List listForCurrentUser() { + final AppUser user = securityContext.authenticatedUser(); + return dscRepository.findByAppUser_IdAndDeletedFalseOrderByIdDesc(user.getId()).stream() + .map(d -> BankUserDscData.from(d, true)) + .toList(); + } + + @Override + @Transactional(readOnly = true) + public List listForUser(final Long userId) { + return dscRepository.findByAppUser_IdAndDeletedFalseOrderByIdDesc(userId).stream() + .map(d -> BankUserDscData.from(d, true)) + .toList(); + } + + @Override + @Transactional(readOnly = true) + public BankUserDscData getById(final Long dscId) { + final BankUserDsc d = dscRepository.findById(dscId).orElseThrow(() -> new DscNotFoundException(dscId)); + // Non-admins can only see their own DSC. Don't reveal that the id exists for someone else — + // throw not-found instead. + final AppUser caller = securityContext.authenticatedUser(); + if (!d.getAppUser().getId().equals(caller.getId())) { + // TODO: relax this once a dedicated 'READ_DSC_ALL' admin permission is wired in. + throw new DscNotFoundException(dscId); + } + return BankUserDscData.from(d, false); + } + + @Override + @Transactional + public void revoke(final Long dscId, final String reason) { + final AppUser caller = securityContext.authenticatedUser(); + final BankUserDsc d = dscRepository.findById(dscId).orElseThrow(() -> new DscNotFoundException(dscId)); + if (!d.getAppUser().getId().equals(caller.getId())) { + // Same not-found masking as getById — don't leak existence to non-owners. + throw new DscNotFoundException(dscId); + } + d.setCertStatus(CertStatus.REVOKED); + d.setActive(false); + d.setDeleted(true); + d.setRevokedOn(OffsetDateTime.now(ZoneOffset.UTC)); + d.setRevokedBy(caller.getId()); + d.setRevokeReason(reason); + d.setDeletedBy(caller.getId()); + d.setDeletedAt(OffsetDateTime.now(ZoneOffset.UTC)); + dscRepository.saveAndFlush(d); + LOG.info("DSC revoked — dscId={}, user={}, reason={}", dscId, caller.getUsername(), reason); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/util/DscSignatureVerifier.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/util/DscSignatureVerifier.java new file mode 100644 index 000000000..f3dffdad7 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/util/DscSignatureVerifier.java @@ -0,0 +1,63 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.util; + +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; +import org.springframework.stereotype.Component; + +/** + * Verifies an RSA-SHA256 signature produced by the helper against the public key stored in + * {@code m_bank_user_dsc.dsc_public_key} (base64 of {@code SubjectPublicKeyInfo}). + * + *

The challenge bytes are the UTF-8 bytes of {@code DscChallenge.challenge}. The helper signs + * exactly those bytes — server signs the same — so any mismatch in encoding/casing fails verify. + */ +@Component +public class DscSignatureVerifier { + + /** + * @param challenge the original challenge string (server-side authoritative value) + * @param signatureB64 base64-encoded RSA-SHA256 signature returned by the helper + * @param publicKeyB64 base64 SubjectPublicKeyInfo of the user's enrolled cert + * @return true if the signature is valid for the challenge under the given key; false otherwise + * (including any decode/parse failure — we don't distinguish "wrong key" from "bad sig" + * to a caller because both are auth failures) + */ + public boolean verify(final String challenge, final String signatureB64, final String publicKeyB64) { + if (challenge == null || signatureB64 == null || publicKeyB64 == null) { + return false; + } + try { + final byte[] keyBytes = Base64.getDecoder().decode(publicKeyB64); + final PublicKey pubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(keyBytes)); + final byte[] sigBytes = Base64.getDecoder().decode(signatureB64); + final Signature sig = Signature.getInstance("SHA256withRSA"); + sig.initVerify(pubKey); + sig.update(challenge.getBytes(StandardCharsets.UTF_8)); + return sig.verify(sigBytes); + } catch (final Exception e) { + return false; + } + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/util/X509CertParser.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/util/X509CertParser.java new file mode 100644 index 000000000..ba2b63524 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/dsc/util/X509CertParser.java @@ -0,0 +1,124 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.fineract.useradministration.dsc.util; + +import java.io.ByteArrayInputStream; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Base64; +import javax.naming.InvalidNameException; +import javax.naming.ldap.LdapName; +import javax.naming.ldap.Rdn; +import lombok.Builder; +import lombok.Getter; +import org.springframework.stereotype.Component; + +/** + * Parses a base64-encoded X.509 DER certificate (sent by the helper) into the metadata fields the + * server persists. Server-side parsing is authoritative — we do not trust whatever the helper + * claims about itself. + * + *

Validation done here is minimal: well-formedness + notAfter > now. Chain validation, CRL/OCSP + * checks, and KeyUsage enforcement are intentionally out of scope for v1 and will be layered in + * Phase 4 hardening. + */ +@Component +public class X509CertParser { + + public ParsedCert parse(final String certB64) { + if (certB64 == null || certB64.isBlank()) { + throw new IllegalArgumentException("certB64 is required"); + } + final byte[] der; + try { + // Tolerate PEM headers/whitespace if the helper sends them — strip non-base64. + final String normalized = certB64.replaceAll("-----[A-Z ]+-----", "").replaceAll("\\s+", ""); + der = Base64.getDecoder().decode(normalized); + } catch (final IllegalArgumentException e) { + throw new IllegalArgumentException("certB64 is not valid base64"); + } + + final X509Certificate cert; + try { + final CertificateFactory cf = CertificateFactory.getInstance("X.509"); + cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(der)); + } catch (final CertificateException e) { + throw new IllegalArgumentException("certB64 is not a valid X.509 certificate", e); + } + + if (cert.getNotAfter().toInstant().isBefore(java.time.Instant.now())) { + throw new IllegalArgumentException("Certificate has already expired (notAfter=" + cert.getNotAfter() + ")"); + } + + // KeyUsage extension is the standard X.509 way to declare what a cert may be used for. + // bit 0 = digitalSignature, bit 1 = nonRepudiation. We require at least one of these so a + // pure-encryption cert (e.g. dataEncipherment bit only) can't be enrolled as a signing DSC. + // KeyUsage is optional in X.509 — null array means no constraint and we allow it. + final boolean[] keyUsage = cert.getKeyUsage(); + if (keyUsage != null && keyUsage.length > 1 + && !keyUsage[0] /* digitalSignature */ + && !keyUsage[1] /* nonRepudiation */) { + throw new IllegalArgumentException("Certificate KeyUsage does not permit signing — pick a Signing DSC, not an Encryption one"); + } + + final String subjectDn = cert.getSubjectX500Principal().getName(); + final String issuerDn = cert.getIssuerX500Principal().getName(); + final String commonName = extractCn(subjectDn); + final String serial = cert.getSerialNumber().toString(16).toUpperCase(); + final LocalDate notAfter = cert.getNotAfter().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + final String publicKeyB64 = Base64.getEncoder().encodeToString(cert.getPublicKey().getEncoded()); + + return ParsedCert.builder() + .serial(serial) + .subjectDn(subjectDn) + .issuerDn(issuerDn) + .commonName(commonName) + .notAfter(notAfter) + .publicKeyB64(publicKeyB64) + .build(); + } + + private static String extractCn(final String dn) { + try { + final LdapName name = new LdapName(dn); + for (final Rdn rdn : name.getRdns()) { + if ("CN".equalsIgnoreCase(rdn.getType())) { + return String.valueOf(rdn.getValue()); + } + } + } catch (final InvalidNameException ignored) { + // Fall through — return null if DN is malformed. + } + return null; + } + + @Getter + @Builder + public static class ParsedCert { + private final String serial; + private final String subjectDn; + private final String issuerDn; + private final String commonName; + private final LocalDate notAfter; + private final String publicKeyB64; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/useradministration/service/AppUserWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/useradministration/service/AppUserWritePlatformServiceJpaRepositoryImpl.java index ccf0e7b6e..0ba57146f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/useradministration/service/AppUserWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/useradministration/service/AppUserWritePlatformServiceJpaRepositoryImpl.java @@ -94,6 +94,15 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit this.fromApiJsonDeserializer.validateForCreate(command.json()); + // Pre-flight unique-checks for email + mobile so the UI gets a clean field-scoped + // error instead of the generic data-integrity error that fires after the DB UNIQUE + // constraint trips. The DB constraint is still the source of truth for race-condition + // safety — see handleDataIntegrityIssues() for that backstop. + assertEmailAndMobileUnique( + command.stringValueOfParameterNamed("email"), + command.stringValueOfParameterNamed("mobileNo"), + null); + final String officeIdParamName = "officeId"; final Long officeId = command.longValueOfParameterNamed(officeIdParamName); @@ -199,6 +208,13 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit this.fromApiJsonDeserializer.validateForUpdate(command.json(), currentUser); + // Same pre-flight as createUser, excluding the user being edited so they're not told + // their own email/mobile is taken by themselves. + assertEmailAndMobileUnique( + command.stringValueOfParameterNamed("email"), + command.stringValueOfParameterNamed("mobileNo"), + userId); + final AppUser userToUpdate = this.appUserRepository.findById(userId).orElseThrow(() -> new UserNotFoundException(userId)); final AppUserPreviousPassword currentPasswordToSaveAsPreview = getCurrentPasswordToSaveAsPreview(userToUpdate, command); @@ -342,6 +358,19 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit final String defaultMessage = "User with username " + username + " already exists."; return new PlatformDataIntegrityException("error.msg.user.duplicate.username", defaultMessage, "username", username); } + // Email / mobile race-condition backstop. The pre-flight check in createUser/updateUser + // catches almost all cases cleanly, but two parallel inserts can still race past it and + // collide at the DB unique constraint — translate that into the same field-scoped error. + if (realCause.getMessage().contains("uq_m_appuser_email")) { + final String email = command.stringValueOfParameterNamed("email"); + return new PlatformDataIntegrityException("error.msg.user.duplicate.email", + "User with email " + email + " already exists.", "email", email); + } + if (realCause.getMessage().contains("uq_m_appuser_mobile_no")) { + final String mobile = command.stringValueOfParameterNamed("mobileNo"); + return new PlatformDataIntegrityException("error.msg.user.duplicate.mobile", + "User with mobile number " + mobile + " already exists.", "mobileNo", mobile); + } // TODO: this needs to be fixed. The error condition should be independent from the underlying message and // naming of the constraint if (realCause.getMessage().contains("unique_self_client")) { @@ -352,4 +381,37 @@ public class AppUserWritePlatformServiceJpaRepositoryImpl implements AppUserWrit log.error("handleDataIntegrityIssues: Neither duplicate username nor existing user; unknown error occured", dve); return ErrorHandler.getMappable(dve, "error.msg.unknown.data.integrity.issue", "Unknown data integrity issue with resource."); } + + /** + * Pre-flight uniqueness check for {@code email} and {@code mobileNo}. Runs before the DB + * write so the UI gets a clean field-scoped error instead of the generic + * "data integrity issue" the constraint trap would produce. + * + *

On create, pass {@code excludeUserId = null}. On update, pass the id of the user being + * edited so they aren't told their own values collide with themselves. + * + *

Blank / null email is left to the existing {@link UserDataValidator#notBlank()} check + * (email is NOT NULL in {@code m_appuser}). Blank / null mobile is skipped — it's optional + * and multiple users with no mobile are valid. + */ + private void assertEmailAndMobileUnique(final String email, final String mobileNo, final Long excludeUserId) { + if (email != null && !email.isBlank()) { + final java.util.Optional emailOwner = excludeUserId == null + ? this.appUserRepository.findByEmailIgnoreCase(email) + : this.appUserRepository.findByEmailIgnoreCaseAndIdNot(email, excludeUserId); + if (emailOwner.isPresent()) { + throw new PlatformDataIntegrityException("error.msg.user.duplicate.email", + "User with email " + email + " already exists.", "email", email); + } + } + if (mobileNo != null && !mobileNo.isBlank()) { + final java.util.Optional mobileOwner = excludeUserId == null + ? this.appUserRepository.findByMobileNo(mobileNo) + : this.appUserRepository.findByMobileNoAndIdNot(mobileNo, excludeUserId); + if (mobileOwner.isPresent()) { + throw new PlatformDataIntegrityException("error.msg.user.duplicate.mobile", + "User with mobile number " + mobileNo + " already exists.", "mobileNo", mobileNo); + } + } + } } diff --git a/fineract-provider/src/main/resources/application.properties b/fineract-provider/src/main/resources/application.properties index a26ec2b0c..8460442a5 100644 --- a/fineract-provider/src/main/resources/application.properties +++ b/fineract-provider/src/main/resources/application.properties @@ -664,8 +664,6 @@ fineract.openproject.api-token=${FINERACT_OPENPROJECT_API_TOKEN:57dd9f933f113c72 # ── DIP / MNRL Mobile Number Lookup ────────────────────────────────────────── fineract.mobile-lookup.base-url=${FINERACT_MOBILE_LOOKUP_BASE_URL:http://localhost:8080} - - # ── Login OTP (2FA) — uses LincPay credentials above ───────────────────────── fineract.security.otp.lincpay.generate-path=${FINERACT_OTP_LINCPAY_GENERATE_PATH:/mobile/generate-otp} fineract.security.otp.lincpay.verify-path=${FINERACT_OTP_LINCPAY_VERIFY_PATH:/verify-mobile-otp} @@ -686,3 +684,4 @@ spring.mail.properties.mail.transport.protocol=smtp fineract.email.from-address=${FINERACT_EMAIL_FROM_ADDRESS:no-reply@cbsnexora.com} fineract.email.from-name=${FINERACT_EMAIL_FROM_NAME:CBSnexora} fineract.email.support-address=${FINERACT_EMAIL_SUPPORT_ADDRESS:support@cbsnexora.com} + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index 4eb246698..ff9bd1c49 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -308,4 +308,8 @@ + + + + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0292_create_m_bank_user_dsc.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0292_create_m_bank_user_dsc.xml new file mode 100644 index 000000000..b98e1de7b --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0292_create_m_bank_user_dsc.xml @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SELECT COUNT(*) FROM m_permission WHERE code = 'READ_DSC' + + + + + + + + + + + + + + + + + + + + + + + + + + + + SELECT COUNT(*) FROM m_role WHERE name = 'Self Service User' + + + INSERT INTO m_role_permission (role_id, permission_id) + SELECT r.id, p.id + FROM m_role r, m_permission p + WHERE r.name = 'Self Service User' + AND p.code IN ('READ_DSC', 'CREATE_DSC', 'DELETE_DSC') + AND NOT EXISTS ( + SELECT 1 FROM m_role_permission rp + WHERE rp.role_id = r.id AND rp.permission_id = p.id + ) + + + + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0293_create_m_dsc_challenge.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0293_create_m_dsc_challenge.xml new file mode 100644 index 000000000..a7ca02901 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0293_create_m_dsc_challenge.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0294_add_tfa_token_to_dsc_challenge.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0294_add_tfa_token_to_dsc_challenge.xml new file mode 100644 index 000000000..276a22036 --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0294_add_tfa_token_to_dsc_challenge.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0295_unique_email_mobile_on_m_appuser.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0295_unique_email_mobile_on_m_appuser.xml new file mode 100644 index 000000000..094af27ac --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0295_unique_email_mobile_on_m_appuser.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fineract-security/src/main/java/org/apache/fineract/infrastructure/security/api/AuthenticationApiResource.java b/fineract-security/src/main/java/org/apache/fineract/infrastructure/security/api/AuthenticationApiResource.java index 020beb125..7b5ce9d70 100644 --- a/fineract-security/src/main/java/org/apache/fineract/infrastructure/security/api/AuthenticationApiResource.java +++ b/fineract-security/src/main/java/org/apache/fineract/infrastructure/security/api/AuthenticationApiResource.java @@ -39,6 +39,7 @@ import java.util.Base64; import java.util.Collection; import java.util.Set; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer; @@ -65,6 +66,7 @@ import org.springframework.stereotype.Component; @Path("/v1/authentication") @Tag(name = "Authentication HTTP Basic", description = "An API capability that allows client applications to verify authentication details using HTTP Basic Authentication.") @RequiredArgsConstructor +@Slf4j public class AuthenticationApiResource { @Value("${fineract.security.2fa.enabled}") @@ -83,6 +85,7 @@ public class AuthenticationApiResource { private final ClientReadPlatformService clientReadPlatformService; private final ConfigurationDomainService configurationDomainService; private final UserMfaFactorRepository userMfaFactorRepository; + private final org.apache.fineract.useradministration.dsc.domain.BankUserDscRepository bankUserDscRepository; @POST @Consumes({ MediaType.APPLICATION_JSON }) @@ -145,8 +148,26 @@ public class AuthenticationApiResource { // safe rollout (the per-user gate naturally lets unenrolled users in) and instant // rollback (flip the c_configuration row off and everyone is unchallenged). final boolean enforcementEnabled = configurationDomainService.isMfaFactorEnforcementEnabled(); + // DSC factor check is wrapped so a misconfigured/missing m_bank_user_dsc table can't + // bring down the entire login path. If the lookup throws (table missing, columns + // renamed, JDBC down), we log and fall back to "no DSC factor enrolled" — the user + // can still authenticate via their OTP factors (or no 2FA if none enrolled). + boolean hasActiveDsc = false; + if (enforcementEnabled) { + try { + hasActiveDsc = bankUserDscRepository + .existsByAppUser_IdAndActiveTrueAndDeletedFalseAndCertStatusAndDscExpiryDateGreaterThanEqual( + principal.getId(), + org.apache.fineract.useradministration.dsc.domain.CertStatus.ACTIVE, + java.time.LocalDate.now()); + } catch (final Exception e) { + log.warn("DSC factor lookup failed for user {} during login — falling back to non-DSC: {}", + principal.getUsername(), e.getMessage()); + } + } final boolean userHasFactors = enforcementEnabled - && userMfaFactorRepository.existsByAppUser_IdAndVerifiedTrueAndDisabledOnIsNull(principal.getId()); + && (userMfaFactorRepository.existsByAppUser_IdAndVerifiedTrueAndDisabledOnIsNull(principal.getId()) + || hasActiveDsc); final boolean isTwoFactorRequired = this.twoFactorEnabled && enforcementEnabled && userHasFactors && !principal.hasSpecificPermissionTo(TwoFactorConstants.BYPASS_TWO_FACTOR_PERMISSION); Long userId = principal.getId(); diff --git a/fineract-security/src/main/java/org/apache/fineract/infrastructure/security/domain/TFAccessTokenRepository.java b/fineract-security/src/main/java/org/apache/fineract/infrastructure/security/domain/TFAccessTokenRepository.java index aaed99f4d..e08070f7e 100644 --- a/fineract-security/src/main/java/org/apache/fineract/infrastructure/security/domain/TFAccessTokenRepository.java +++ b/fineract-security/src/main/java/org/apache/fineract/infrastructure/security/domain/TFAccessTokenRepository.java @@ -19,11 +19,9 @@ package org.apache.fineract.infrastructure.security.domain; import org.apache.fineract.useradministration.domain.AppUser; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -@ConditionalOnProperty("fineract.security.2fa.enabled") public interface TFAccessTokenRepository extends JpaRepository, JpaSpecificationExecutor { TFAccessToken findByUserAndToken(AppUser user, String token); diff --git a/fineract-security/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java b/fineract-security/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java index 19e8a2f8a..5a18c2f8a 100644 --- a/fineract-security/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java +++ b/fineract-security/src/main/java/org/apache/fineract/infrastructure/security/filter/TenantAwareBasicAuthenticationFilter.java @@ -110,6 +110,45 @@ public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFil // in different origin (domain name) filterChain.doFilter(request, response); } else { + // Fully exempt paths: skip tenant resolution AND make Spring Security's + // BasicAuthenticationFilter ignore this request. + // + // Why this is non-trivial: Fineract has TWO basic-auth filters in the chain — + // this one (TenantAwareBasicAuthenticationFilter, extends BasicAuthenticationFilter) + // AND Spring's built-in BasicAuthenticationFilter that .httpBasic(…) adds on the + // SecurityFilterChain. Skipping this filter via filterChain.doFilter(…) hands the + // request to the built-in filter, which still sees the UI interceptor's + // Authorization header, tries to authenticate it (without tenant context → + // DB query fails), and returns 401 + WWW-Authenticate → the user gets the native + // browser Basic-auth dialog. + // + // Fix: wrap the request to strip the Authorization header for exempt paths so + // the built-in BasicAuthenticationFilter has nothing to act on. The controller + // is permitAll'd anyway, so missing auth is fine — the JAR endpoint just streams + // a file from disk. + if (isTenantExemptPath(request)) { + final jakarta.servlet.http.HttpServletRequestWrapper stripped = + new jakarta.servlet.http.HttpServletRequestWrapper(request) { + @Override + public String getHeader(final String name) { + if ("Authorization".equalsIgnoreCase(name)) { + return null; + } + return super.getHeader(name); + } + + @Override + public java.util.Enumeration getHeaders(final String name) { + if ("Authorization".equalsIgnoreCase(name)) { + return java.util.Collections.emptyEnumeration(); + } + return super.getHeaders(name); + } + }; + filterChain.doFilter(stripped, response); + return; + } + if (requestMatcher.matches(request)) { String tenantIdentifier = request.getHeader(TENANT_ID_REQUEST_HEADER); @@ -167,6 +206,33 @@ public class TenantAwareBasicAuthenticationFilter extends BasicAuthenticationFil } } + /** + * Paths that don't need a tenant context to serve a response. + * + *

Only {@code /api/v*\/dsc/helper.jar} qualifies: Java Web Start fetches the helper JAR + * with a raw GET (no headers, no UI interceptor), and the endpoint just streams a file from + * disk — no DB queries, no tenant-scoped state. + * + *

The JNLP endpoint deliberately does NOT qualify: it queries {@code m_dsc_challenge} + * which requires tenant context for Fineract's connection routing. JNLP fetches always come + * from the SPA's XHR (which sends the tenant header via the auth interceptor), so the normal + * tenant resolution path works for them. + * + *

Without this exemption, JWS receives a 400 + {@code WWW-Authenticate: Basic} response + * and pops up the native "Authentication Required" dialog. + */ + private boolean isTenantExemptPath(final HttpServletRequest request) { + final String uri = request.getRequestURI(); + if (uri == null) { + return false; + } + final boolean exempt = uri.contains("/dsc/helper.jar"); + if (exempt) { + log.debug("Tenant filter: exempting JWS-bound path '{}' from tenant requirement", uri); + } + return exempt; + } + @Override protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException { diff --git a/fineract-security/src/main/java/org/apache/fineract/infrastructure/security/filter/TwoFactorAuthenticationFilter.java b/fineract-security/src/main/java/org/apache/fineract/infrastructure/security/filter/TwoFactorAuthenticationFilter.java index c1c8fea4e..853a59860 100644 --- a/fineract-security/src/main/java/org/apache/fineract/infrastructure/security/filter/TwoFactorAuthenticationFilter.java +++ b/fineract-security/src/main/java/org/apache/fineract/infrastructure/security/filter/TwoFactorAuthenticationFilter.java @@ -28,6 +28,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.security.constants.TwoFactorConstants; import org.apache.fineract.infrastructure.security.data.FineractJwtAuthenticationToken; @@ -56,11 +57,13 @@ import org.springframework.web.filter.GenericFilterBean; * authority regardless of the value of the 'Fineract-Platform-TFA-Token' header. */ @RequiredArgsConstructor +@Slf4j public class TwoFactorAuthenticationFilter extends GenericFilterBean { private final TwoFactorService twoFactorService; private final ConfigurationDomainService configurationDomainService; private final UserMfaFactorRepository userMfaFactorRepository; + private final org.apache.fineract.useradministration.dsc.domain.BankUserDscRepository bankUserDscRepository; @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { @@ -90,8 +93,24 @@ public class TwoFactorAuthenticationFilter extends GenericFilterBean { // has no enrolled factors (or the global kill-switch is off). final boolean hasBypass = user.hasSpecificPermissionTo(TwoFactorConstants.BYPASS_TWO_FACTOR_PERMISSION); final boolean enforcementOn = configurationDomainService.isMfaFactorEnforcementEnabled(); + // DSC factor check wrapped: a misconfigured/missing m_bank_user_dsc must not bring + // down every protected request. Fall back to "no DSC factor" if the lookup throws. + boolean hasActiveDsc = false; + if (enforcementOn) { + try { + hasActiveDsc = bankUserDscRepository + .existsByAppUser_IdAndActiveTrueAndDeletedFalseAndCertStatusAndDscExpiryDateGreaterThanEqual( + user.getId(), + org.apache.fineract.useradministration.dsc.domain.CertStatus.ACTIVE, + java.time.LocalDate.now()); + } catch (final Exception e) { + log.warn("DSC factor lookup failed in 2FA filter for user {} — ignoring: {}", + user.getUsername(), e.getMessage()); + } + } final boolean userHasFactors = enforcementOn - && userMfaFactorRepository.existsByAppUser_IdAndVerifiedTrueAndDisabledOnIsNull(user.getId()); + && (userMfaFactorRepository.existsByAppUser_IdAndVerifiedTrueAndDisabledOnIsNull(user.getId()) + || hasActiveDsc); final boolean shouldChallenge = !hasBypass && enforcementOn && userHasFactors; if (shouldChallenge) {