Skip to content

PII issues fixes with backward compatibility [MOSIP-44379]#1030

Draft
GOKULRAJ136 wants to merge 112 commits into
mosip:developfrom
GOKULRAJ136:MOSIP-PII-new
Draft

PII issues fixes with backward compatibility [MOSIP-44379]#1030
GOKULRAJ136 wants to merge 112 commits into
mosip:developfrom
GOKULRAJ136:MOSIP-PII-new

Conversation

@GOKULRAJ136

@GOKULRAJ136 GOKULRAJ136 commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced user identity management with improved identifier handling and resolution capabilities
    • Added privacy-preserving identifier masking throughout system logs to protect user information
    • Introduced backward-compatibility mode for legacy user data handling
  • Chores

    • Updated MOSIP kernel dependencies to version 1.3.0

@coderabbitai

coderabbitai Bot commented Mar 3, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This pull request introduces a canonical user identity system for pre-registration that normalizes user identifiers to UUIDs, replacing plaintext identifiers in audit fields. It adds a new user_details database table, a UserDetailsService for UUID resolution and caching, an ApplicationIdentityMigrationService for data migration, and updates repositories and services to use batch lookups and masked logging. It includes backward-compatibility support via a configuration flag.

Changes

Cohort / File(s) Summary
Database Schema & Core Entities
db_scripts/mosip_prereg/ddl/user_details.sql, db_scripts/mosip_prereg/ddl.sql, pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/entity/UserDetails.java, pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/repository/UserDetailsRepository.java
Creates prereg.user_details table with UUID primary key, hash-based lookups, and encrypted identifiers; adds DDL include; defines JPA entity and repository for canonical user identity persistence.
User Identity Service
pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/service/UserDetailsService.java, pre-registration/pre-registration-core/src/test/java/io/mosip/preregistration/core/common/service/UserDetailsServiceTest.java
Core service providing UUID resolution, identifier hashing, encryption, caching, PII masking, and user matching with optional backward-compatibility fallback; includes comprehensive test coverage for resolution, encryption, and compatibility modes.
Entity Effective Accessors
pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/entity/ApplicationEntity.java, pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/entity/DemographicEntity.java, pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/entity/DocumentEntity.java, pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/entity/RegistrationBookingEntity.java, pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/entity/AvailibityEntity.java, pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/entity/RegistrationBookingEntityConsumed.java, pre-registration/pre-registration-datasync-service/src/main/java/io/mosip/preregistration/datasync/entity/InterfaceDataSyncEntity.java, pre-registration/pre-registration-datasync-service/src/main/java/io/mosip/preregistration/datasync/entity/ProcessedPreRegEntity.java
Adds getEffective* accessor methods across all audit entity classes to unify access to crBy/updBy/createdBy/updatedBy fields; updates documentation to reflect legacy plaintext or UUID semantics.
Application Identity Migration
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/ApplicationIdentityMigrationService.java, pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/ApplicationIdentityMigrationServiceTest.java
New transactional service to migrate raw user identifiers to canonical UUIDs across linked Application, Demographic, Document, and Booking entities; resolves effective user IDs with backward-compatibility support.
Repository Batch Query Methods
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/repository/ApplicationRepostiory.java, pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/repository/DemographicRepository.java, pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/repository/RegAppointmentRepository.java
Adds new Spring Data methods for batch user-ID lookups: findByCreatedByIn, findByCreatedByInBookingType, findByCreatedByInAndStatusCode*; creates new repository for RegistrationBookingEntity.
Application Service Refactoring
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/ApplicationService.java, pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/AppointmentServiceImpl.java, pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/DemographicService.java, pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/LoginService.java, pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/DocumentService.java, pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/CommonServiceUtil.java, pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DemographicServiceUtil.java, pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DocumentServiceUtil.java
Integrates canonical user identity via UserDetailsService and ApplicationIdentityMigrationService; replaces raw user comparisons with matchesUser(...); updates user-scoped queries to use batch lookups with getUserLookupIds(...); adds identifier masking in logs; uses getEffective* accessors for audit fields; triggers identity migration after persistence operations.
Login & OTP Logging
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/controller/LoginController.java, pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/OTPManager.java
Updates debug logging to mask user identifiers via userDetailsService.maskIdentifier(...); adds service injection and trailing newlines.
Data Sync Service Refactoring
pre-registration/pre-registration-datasync-service/src/main/java/io/mosip/preregistration/datasync/service/DataSyncService.java, pre-registration/pre-registration-datasync-service/src/main/java/io/mosip/preregistration/datasync/service/util/DataSyncServiceUtil.java
Derives audit user ID from Spring Security context instead of hardcoded "user"; integrates UserDetailsService for effective user resolution; adds HTTP error handling for downstream booking calls; masks identifiers in logs.
Batch Job Service
pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/impl/ApplicationConsumedStatusUpdater.java
Adds UserDetailsService integration to resolve canonical user UUIDs for consumed-table writes; resolves creator/updater fields from source entities using getEffective* accessors; supports backward-compatibility flag.
Configuration Properties
pre-registration/pre-registration-application-service/src/main/resources/application.properties, pre-registration/pre-registration-application-service/src/test/resources/application.properties, pre-registration/pre-registration-datasync-service/src/main/resources/application.properties, pre-registration/pre-registration-datasync-service/src/test/resources/application.properties
Introduces new mosip.prereg.pii.backward.compatibility flag (set to false) to control gradual migration to canonical user identities.
Maven Dependencies
pre-registration/pom.xml, pre-registration/pre-registration-application-service/pom.xml, pre-registration/pre-registration-core/pom.xml, pre-registration/pre-registration-datasync-service/pom.xml
Upgrades MOSIP kernel BOM version from 1.2.1-SNAPSHOT to 1.3.0; pins kernel-core and kernel-auth-adapter to 1.3.0.
Application Service Tests
pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/ApplicationServiceTest.java, pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/AppointmentServiceImplTest.java, pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/DemographicServiceTest.java, pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/DocumentServiceTest.java, pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/LoginServiceTest.java, pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/test/service/AppointmentServiceTest.java, pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/test/service/util/DemographicServiceUtilTest.java, pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/test/service/util/DocumentServiceUtilTest.java, pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/test/controller/LoginControllerTest.java, pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/OTPManagerTest.java
Mocks UserDetailsService and ApplicationIdentityMigrationService in test suites; updates repository mock expectations to use batch query methods; uses canonical UUID values instead of raw identifiers in fixtures; stubs user matching and resolution logic; adds backward-compatibility flag configuration.
Data Sync Tests
pre-registration/pre-registration-datasync-service/src/test/java/io/mosip/preregistration/datasync/test/config/TestSecurityConfig.java, pre-registration/pre-registration-datasync-service/src/test/java/io/mosip/preregistration/datasync/test/service/DataSyncServiceTest.java, pre-registration/pre-registration-datasync-service/src/test/java/io/mosip/preregistration/datasync/test/service/util/DataSyncServiceUtilTest.java
Renames test security bean to testUserDetailsService() and marks as @Primary; stubs AuthUserDetails methods; broadens Mockito matcher; updates file formatting.

Sequence Diagram

sequenceDiagram
    participant AuthUser as Auth User Request
    participant AppService as ApplicationService
    participant UDS as UserDetailsService
    participant UDRepo as UserDetailsRepository
    participant AIM as ApplicationIdentityMigrationService
    participant DB as Database
    participant Cache as Cache

    AuthUser->>AppService: addLostOrUpdateApplication(userId)
    AppService->>UDS: getUserLookupIds(userId, piiBackCompat)
    UDS->>UDS: normalizeIdentifier(userId)
    UDS->>UDS: computeIdentifierHash()
    UDS->>Cache: checkCache(hash)
    alt Cache Hit
        Cache-->>UDS: UserDetails UUID
    else Cache Miss
        UDS->>UDRepo: findByIdentifierHash(hash)
        alt Not Found
            UDS->>UDS: encryptIdentifier(userId)
            UDS->>UDRepo: save(new UserDetails)
            UDRepo->>DB: INSERT user_details
            DB-->>UDRepo: Saved with UUID
        else Found
            UDRepo-->>UDS: Existing UserDetails
        end
        UDS->>Cache: cachePut(hash, UserDetails)
    end
    UDS-->>AppService: [canonicalUUID, ...]
    
    AppService->>DB: findByCreatedByIn(lookupIds)
    DB-->>AppService: ApplicationEntities
    
    AppService->>AppService: Match authUser against entities
    AppService->>AIM: migrateRawUserToEffectiveUser(preRegId, effectiveUserId)
    AIM->>DB: findApplication(preRegId)
    AIM->>DB: findDemographic(preRegId)
    AIM->>DB: findDocuments(preRegId)
    AIM->>DB: findBooking(preRegId)
    
    AIM->>AIM: Update crBy, updBy, contactInfo
    AIM->>DB: save(updatedEntities)
    AIM-->>AppService: Migration complete
    
    AppService-->>AuthUser: Success Response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 Whiskers twitch with joy so bright,
UUIDs dance in hashes' light,
User IDs canonicalized with care,
Encryption keeps identities rare,
Backward-compat paths everywhere!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.58% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: introducing PII (Personally Identifiable Information) issue fixes while maintaining backward compatibility, with a reference to the tracking issue.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/ApplicationService.java (1)

553-567: ⚠️ Potential issue | 🟡 Minor

Authorization logic could allow unintended access with backward compatibility enabled.

When piiBackwardCompatibility is true and effectiveCrBy matches the raw authUserId, the method returns early (Line 563-564) without validating if the role check was intended. This could allow access if the raw user ID accidentally matches another user's canonical ID.

🛡️ Proposed fix to make the logic explicit
 		if (!effectiveCrBy.equals(canonicalAuthUserId)) {
-			if (piiBackwardCompatibility && authUserId != null && effectiveCrBy.equals(authUserId.trim())) {
-				return;
-			}
+			// Backward compatibility: also accept raw identifier match
+			if (piiBackwardCompatibility && authUserId != null && !authUserId.trim().isEmpty() 
+					&& effectiveCrBy.equals(authUserId.trim())) {
+				log.debug(LOGGER_SESSIONID, LOGGER_IDTYPE, LOGGER_ID, 
+					"Authorization passed via backward compatibility for application: " + applicationEntity.getApplicationId());
+				return;
+			}
 				throw new PreIdInvalidForUserIdException(ApplicationErrorCodes.PRG_APP_015.getCode(),
 						ApplicationErrorMessages.INVALID_APPLICATION_ID_FOR_USER.getMessage());
 		}
🧹 Nitpick comments (20)
pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/AppointmentServiceImplTest.java (1)

598-606: Limit helper class scope to this test class.

NullAuthority can be declared private static final since it is only test scaffolding used internally.

🔧 Suggested tweak
-static class NullAuthority implements GrantedAuthority {
+private static final class NullAuthority implements GrantedAuthority {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/AppointmentServiceImplTest.java`
around lines 598 - 606, The NullAuthority helper class should be made private
static final to limit its scope to this test class; locate the static inner
class named NullAuthority (implements GrantedAuthority) and change its
declaration from package-private static to private static final so it remains
test-scoped and immutable.
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/controller/LoginController.java (2)

269-297: Consider extracting to a shared utility class.

The AI summary indicates similar masking utilities exist in LoginService, AppointmentServiceImpl, DemographicService, and other classes. Centralizing this logic into a common utility would reduce duplication and ensure consistent masking behavior across the codebase.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/controller/LoginController.java`
around lines 269 - 297, Extract the masking logic from
LoginController.maskIdentifier into a shared utility (e.g.,
IdentifierMaskUtils.maskIdentifier) and replace duplicated implementations in
LoginService, AppointmentServiceImpl, DemographicService, and other classes to
call this single static utility method; ensure the utility preserves current
behavior for emails, phone numbers (with optional '+'), UUIDs and fallback
masking, keep null/blank handling, add unit tests for the utility, and update
imports/usages across callers so all components use
IdentifierMaskUtils.maskIdentifier.

290-294: Catch IllegalArgumentException instead of Exception.

UUID.fromString() throws IllegalArgumentException for invalid UUIDs. Catching the broad Exception type could mask unexpected errors.

Suggested fix
 		try {
 			UUID.fromString(trimmed);
 			return "***" + trimmed.substring(trimmed.length() - 6);
-		} catch (Exception ignored) {
+		} catch (IllegalArgumentException ignored) {
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/controller/LoginController.java`
around lines 290 - 294, Replace the broad catch in LoginController's UUID
masking logic with a catch for IllegalArgumentException only: when calling
UUID.fromString(trimmed) inside the method that returns masked UUID (the block
returning "***" + trimmed.substring(...)), change the catch(Exception ignored)
to catch(IllegalArgumentException ignored) so only invalid-UUID errors are
handled and other exceptions aren't accidentally suppressed; keep the existing
handling (silently ignore or log) consistent with current behavior.
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/LoginService.java (1)

478-483: Remove unreachable small-length phone branch.

Line 481-483 is unreachable because line 478 already constrains digits to 10..12. Removing it will simplify the method.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/LoginService.java`
around lines 478 - 483, In the LoginService class, remove the unreachable
small-length phone branch inside the trimmed.matches("\\+?\\d{10,12}") block:
delete the inner if (digits.length() <= 4) { return (hasPlus ? "+" : "") +
"****"; } since digits is already constrained to 10..12 by the regex; leave the
remaining masking logic in this block intact so the method compiles and behavior
is unchanged for valid 10–12 digit inputs.
db_scripts/mosip_prereg/ddl/user_details.sql (2)

16-16: Remove duplicate index on identifier_hash.

Line 16 (UNIQUE) already creates an index. idx_prereg_user_details_hash duplicates it and adds unnecessary write overhead.

Proposed DDL cleanup
-CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_prereg_user_details_hash
-ON prereg.user_details(identifier_hash);

Also applies to: 25-27

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@db_scripts/mosip_prereg/ddl/user_details.sql` at line 16, The schema defines
identifier_hash VARCHAR(128) NOT NULL UNIQUE which already creates a unique
index, so remove the redundant index creation for idx_prereg_user_details_hash
(and any duplicate index statements at lines corresponding to the other
occurrences mentioned) to avoid double indexing; update the DDL to drop or omit
the CREATE INDEX/ADD INDEX statements that reference
idx_prereg_user_details_hash while keeping the UNIQUE constraint on
identifier_hash in the user_details table definition.

28-29: Reconsider idx_prereg_user_details_active key choice.

This partial index keys on user_id, but user_id is already primary-key indexed. Unless you have a proven query pattern that benefits from this exact predicate+key, this is likely redundant.

Proposed DDL cleanup
-CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_prereg_user_details_active
-ON prereg.user_details(user_id) WHERE identifier_encrypted IS NOT NULL;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@db_scripts/mosip_prereg/ddl/user_details.sql` around lines 28 - 29, The
partial index idx_prereg_user_details_active on table user_details uses user_id
as its key while filtering on identifier_encrypted IS NOT NULL, which is
redundant because user_id is already primary-key indexed; either drop this index
or change it to index the column(s) actually used by queries that include the
predicate (for example index identifier_encrypted or the combination of
identifier_encrypted plus any frequently queried columns), and update DDL to
remove or replace CREATE INDEX CONCURRENTLY IF NOT EXISTS
idx_prereg_user_details_active with the appropriate index definition that
matches real query patterns.
pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/entity/AvailibityEntity.java (1)

90-96: Naming inconsistency across entities.

This entity uses getEffectiveCrBy() / getEffectiveUpdBy(), while InterfaceDataSyncEntity uses getEffectiveCreatedBy() / getEffectiveUpdatedBy(). Consider standardizing the naming convention across all entities for consistency and easier maintenance.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/entity/AvailibityEntity.java`
around lines 90 - 96, The AvailibityEntity exposes getEffectiveCrBy() and
getEffectiveUpdBy() which are inconsistent with InterfaceDataSyncEntity's
getEffectiveCreatedBy() and getEffectiveUpdatedBy(); rename AvailibityEntity's
methods to getEffectiveCreatedBy() and getEffectiveUpdatedBy(), update any
internal field mappings (crBy -> createdBy/upBy -> updatedBy or keep fields but
ensure getters map correctly), and search/replace all usages of
getEffectiveCrBy() and getEffectiveUpdBy() to the new method names to maintain
API consistency across entities.
pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/DocumentServiceTest.java (1)

161-177: Consider extracting this setup block into a test helper.

This setter chain is correct, but moving it into a small fixture/builder helper would reduce setup noise and future drift in this test class.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/DocumentServiceTest.java`
around lines 161 - 177, Extract the repeated DocumentEntity setup in
DocumentServiceTest into a test helper/builder to reduce noise: create a factory
method (e.g., buildTestDocumentEntity or DocumentEntityBuilder) that
encapsulates constructing DocumentEntity and setting fields used here
(demographicEntity, documentId, docName, docCatCode, docTypeCode, docFileFormat,
statusCode, langCode, crBy, crDtime, updBy, updDtime using
DateUtils.parseDateToLocalDateTime(new Date()), encryptedDateTime, docId,
docHash using HashUtill.hashUtill(cephBytes), refNumber) and replace the inline
block in tests with a call to that helper; keep the helper in test sources so
other tests can reuse it and update instantiations in DocumentServiceTest to use
the new builder API.
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/OTPManager.java (1)

144-155: Deduplicate canonical user resolution logic.

The create/update branches repeat the same mapping/fallback block. Extracting a helper will keep behavior consistent and reduce maintenance risk.

Refactor sketch
+	private String resolveActorIdWithFallback() {
+		String fallback = environment.getProperty(PreRegLoginConstant.MOSIP_PRE_REG_CLIENTID);
+		try {
+			UserDetails mappedUser = userDetailsService.findOrCreateByIdentifier(fallback);
+			if (mappedUser != null && mappedUser.getUserId() != null) {
+				return mappedUser.getUserId().toString();
+			}
+		} catch (Exception e) {
+			logger.warn(LOGGER_SESSIONID, LOGGER_IDTYPE, LOGGER_ID, "UserDetails mapping failed", e);
+		}
+		return fallback;
+	}
@@
-			try {
-				UserDetails mappedUser = userDetailsService.findOrCreateByIdentifier(environment.getProperty(PreRegLoginConstant.MOSIP_PRE_REG_CLIENTID));
-				if (mappedUser != null && mappedUser.getUserId() != null) {
-					otpTxn.setUpdBy(mappedUser.getUserId().toString());
-				} else {
-					otpTxn.setUpdBy(environment.getProperty(PreRegLoginConstant.MOSIP_PRE_REG_CLIENTID));
-				}
-			} catch (Exception e) {
-				logger.warn(LOGGER_SESSIONID, LOGGER_IDTYPE, LOGGER_ID, "UserDetails mapping failed for otp update", e);
-				otpTxn.setUpdBy(environment.getProperty(PreRegLoginConstant.MOSIP_PRE_REG_CLIENTID));
-			}
+			otpTxn.setUpdBy(resolveActorIdWithFallback());
@@
-			try {
-				UserDetails mappedUser = userDetailsService.findOrCreateByIdentifier(environment.getProperty(PreRegLoginConstant.MOSIP_PRE_REG_CLIENTID));
-				if (mappedUser != null && mappedUser.getUserId() != null) {
-					txn.setCrBy(mappedUser.getUserId().toString());
-				} else {
-					txn.setCrBy(environment.getProperty(PreRegLoginConstant.MOSIP_PRE_REG_CLIENTID));
-				}
-			} catch (Exception e) {
-				logger.warn(LOGGER_SESSIONID, LOGGER_IDTYPE, LOGGER_ID, "UserDetails mapping failed for otp create", e);
-				txn.setCrBy(environment.getProperty(PreRegLoginConstant.MOSIP_PRE_REG_CLIENTID));
-			}
+			txn.setCrBy(resolveActorIdWithFallback());

Also applies to: 166-177

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/OTPManager.java`
around lines 144 - 155, The create/update branches in OTPManager duplicate the
canonical user resolution logic: extract a private helper method (e.g.,
resolveCanonicalUpdater) that calls
userDetailsService.findOrCreateByIdentifier(environment.getProperty(PreRegLoginConstant.MOSIP_PRE_REG_CLIENTID)),
sets the updater value using otpTxn.setUpdBy(...) with the
mappedUser.getUserId().toString() fallback to the raw property, and logs
failures via logger.warn(...) with the exception; replace the duplicated blocks
in both create and update paths with a call to this helper to keep behavior
consistent and reduce maintenance.
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/AppointmentServiceImpl.java (1)

493-521: Consider centralizing maskIdentifier in a shared utility.

The masking logic is repeated in multiple services; consolidating it reduces drift and keeps masking policy consistent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/AppointmentServiceImpl.java`
around lines 493 - 521, The maskIdentifier implementation in
AppointmentServiceImpl is duplicated across services; extract this logic into a
single shared utility (e.g., IdentifierMasker.maskIdentifier or
MaskUtils.maskIdentifier) in a common/shared module, make the method public
static, move the exact logic from AppointmentServiceImpl.maskIdentifier into
that utility, update AppointmentServiceImpl and other services to call the new
utility method instead of their local copies, and update/merge unit tests to
reference the centralized method and remove duplicated implementations to keep
masking policy consistent.
pre-registration/pre-registration-core/src/test/java/io/mosip/preregistration/core/common/service/UserDetailsServiceTest.java (1)

39-84: Add edge-case tests for ciphertext encoding and blank identifiers.

Current tests use text-like encrypted bytes only and don’t cover whitespace identifier rejection. Adding both cases will protect the critical paths introduced in this PR.

🧪 Suggested test additions
+    `@Test`
+    public void testFindOrCreateRejectsBlankIdentifier() {
+        org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException.class,
+                () -> userDetailsService.findOrCreateByIdentifier("   "));
+    }
+
+    `@Test`
+    public void testEncryptDecryptWithBinaryCiphertextRoundTrip() {
+        when(userDetailsRepository.findByIdentifierHash(any())).thenReturn(Optional.empty());
+        byte[] binaryCipher = new byte[] {(byte)0xFF, (byte)0x00, (byte)0xA5, (byte)0x7F};
+        when(cryptoUtil.encrypt(any(), any())).thenReturn(binaryCipher);
+        when(cryptoUtil.decrypt(any(), any())).thenReturn("plain".getBytes(StandardCharsets.UTF_8));
+        when(userDetailsRepository.save(any())).thenAnswer(i -> i.getArgument(0));
+
+        UserDetails saved = userDetailsService.findOrCreateByIdentifier("user1");
+        when(userDetailsRepository.findById(saved.getUserId())).thenReturn(Optional.of(saved));
+        Optional<String> decrypted = userDetailsService.getDecryptedIdentifier(saved.getUserId());
+
+        assertTrue(decrypted.isPresent());
+        assertEquals("plain", decrypted.get());
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-core/src/test/java/io/mosip/preregistration/core/common/service/UserDetailsServiceTest.java`
around lines 39 - 84, Add two edge-case unit tests in UserDetailsServiceTest:
one that verifies findOrCreateByIdentifier and getDecryptedIdentifier handle
non-text/binary ciphertext bytes by mocking cryptoUtil.encrypt to return
non-UTF8 bytes (e.g., arbitrary byte array) and asserting decryption and
save/lookup still behave correctly; and another that verifies
findOrCreateByIdentifier rejects blank/whitespace identifiers by calling
userDetailsService.findOrCreateByIdentifier with blank inputs and asserting no
save on userDetailsRepository and that an appropriate empty/exceptional result
is returned. Use existing mocks (userDetailsRepository, cryptoUtil) and methods
(findOrCreateByIdentifier, getDecryptedIdentifier, findById) to set up
repository returns and verify interactions.
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DocumentServiceUtil.java (1)

292-297: Remove redundant crBy/updBy assignments in documentEntitySetter.

Line 292-293 are immediately overwritten by Line 295-296, which adds noise without effect.

♻️ Proposed cleanup
-		copyDocumentEntity.setCrBy(sourceEntity.getCrBy());
-		copyDocumentEntity.setUpdBy(sourceEntity.getUpdBy());
 		// copy canonical user references if present
 		copyDocumentEntity.setCrBy(sourceEntity.getEffectiveCrBy());
 		copyDocumentEntity.setUpdBy(sourceEntity.getEffectiveUpdBy());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DocumentServiceUtil.java`
around lines 292 - 297, The documentEntitySetter contains redundant assignments:
remove the initial copyDocumentEntity.setCrBy(sourceEntity.getCrBy()) and
copyDocumentEntity.setUpdBy(sourceEntity.getUpdBy()) since they are immediately
overwritten by copyDocumentEntity.setCrBy(sourceEntity.getEffectiveCrBy()) and
copyDocumentEntity.setUpdBy(sourceEntity.getEffectiveUpdBy()); keep only the
effective-crBy/updBy assignments (and the existing setLangCode) so the method
uses sourceEntity.getEffectiveCrBy()/getEffectiveUpdBy() and avoid the duplicate
setCrBy/setUpdBy calls.
pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/test/service/util/DocumentServiceUtilTest.java (1)

88-90: Please add explicit tests for canonical mapping behavior.

Line 88 adds UserDetailsService mocking, but the suite still doesn’t assert the new canonical crBy/updBy path (success and mapping-failure fallback cases) in dtoToEntity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/test/service/util/DocumentServiceUtilTest.java`
around lines 88 - 90, The test suite needs explicit unit tests verifying the new
canonical mapping path in dtoToEntity: add two tests in DocumentServiceUtilTest
that exercise dtoToEntity using the mocked UserDetailsService—(1) a success case
where UserDetailsService returns a UserDetails with the canonical identifier and
assert the resulting entity fields crBy and updBy are set to that canonical
value, and (2) a failure/fallback case where UserDetailsService returns null or
throws and assert dtoToEntity falls back to the previous behavior (e.g., uses
the original dto values or a default) for crBy/updBy; locate the call to
dtoToEntity and the UserDetailsService mock in the test class and update
assertions accordingly to validate both canonical mapping and fallback.
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/DemographicService.java (3)

978-988: getUserLookupIds is duplicated from ApplicationService.

This method is identical to the one in ApplicationService. All three helper methods should be centralized.

Would you like me to generate a utility class that consolidates these three duplicated methods (resolveCanonicalUserId, maskIdentifier, getUserLookupIds)? This would improve maintainability and ensure consistent behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/DemographicService.java`
around lines 978 - 988, The getUserLookupIds method is duplicated from
ApplicationService along with resolveCanonicalUserId and maskIdentifier;
consolidate these three helpers into a single utility class (e.g., UserIdUtils)
containing resolveCanonicalUserId, maskIdentifier, and getUserLookupIds as
static methods, move the shared logic there, update DemographicService and
ApplicationService to call UserIdUtils.resolveCanonicalUserId,
UserIdUtils.maskIdentifier, and UserIdUtils.getUserLookupIds (removing the local
copies), and ensure visibility, null-handling and piiBackwardCompatibility
behavior are preserved and covered by existing tests or add small unit tests for
the new utility methods.

948-976: maskIdentifier is duplicated from ApplicationService.

This method is identical to the one in ApplicationService. Extract to a shared utility such as UserDetailsService or a new PiiMaskingUtil class.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/DemographicService.java`
around lines 948 - 976, The maskIdentifier method in DemographicService is a
duplicate of the one in ApplicationService; extract the logic into a shared
utility class (e.g., PiiMaskingUtil) or an existing shared service (e.g.,
UserDetailsService) and replace the local method with a call to that utility.
Create a public static maskIdentifier(String) in PiiMaskingUtil (or a public
method on UserDetailsService), move the masking logic there, update
DemographicService to call PiiMaskingUtil.maskIdentifier(value) (or
userDetailsService.maskIdentifier(value)), and remove the duplicate private
maskIdentifier from DemographicService; ensure imports and any tests/refactors
reference the single shared implementation.

932-946: resolveCanonicalUserId is duplicated from ApplicationService.

This method is identical to the one in ApplicationService. Extract to a shared utility.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/DemographicService.java`
around lines 932 - 946, The resolveCanonicalUserId method is duplicated in
DemographicService and ApplicationService; extract it into a shared utility
(e.g., a new CanonicalUserUtil or UserIdMapper) and have both classes call that
utility. Move the logic that uses
userDetailsService.findOrCreateByIdentifier(...) and maskIdentifier(...) into
the utility, or alternatively keep the dependency on userDetailsService in the
callers and expose a utility method like
mapToCanonicalUserId(UserDetailsService, String) that performs the try/catch and
logging; update DemographicService.resolveCanonicalUserId and the one in
ApplicationService to delegate to the new utility, adjust imports, ensure the
logger and maskIdentifier are accessible (or pass a masking function/logger into
the utility), and run tests to verify no behavioral changes.
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/ApplicationService.java (4)

669-679: getUserLookupIds method duplicated across services.

The getUserLookupIds method is duplicated. Consider moving all three helper methods (resolveCanonicalUserId, maskIdentifier, getUserLookupIds) to a shared utility class like UserDetailsService or a new UserIdUtils class.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/ApplicationService.java`
around lines 669 - 679, The getUserLookupIds duplication should be removed from
ApplicationService and the three helper methods (resolveCanonicalUserId,
maskIdentifier, getUserLookupIds) should be extracted into a shared utility
class (e.g., UserIdUtils or add to existing UserDetailsService); create the new
class with those methods as public static (or instance) helpers, update
ApplicationService to call UserIdUtils.resolveCanonicalUserId(...) and
UserIdUtils.getUserLookupIds(...) (or inject UserDetailsService and call the
moved methods), and remove the duplicate implementations from other services so
all callers reference the single shared implementation.

436-444: Indentation is inconsistent and logic is hard to follow.

The authorization check block has inconsistent indentation making the logic flow difficult to understand. The nested conditionals with negation patterns could lead to maintenance issues.

♻️ Suggested refactor for clarity
-			String authUserId = authUserDetails().getUserId();
-			String canonicalAuthUserId = resolveCanonicalUserId(authUserId);
-			String effectiveCrBy = applicationEntity.getEffectiveCrBy() == null ? "" : applicationEntity.getEffectiveCrBy().trim();
-			if (!effectiveCrBy.equals(canonicalAuthUserId)) {
-				if (!(piiBackwardCompatibility && effectiveCrBy.equals(authUserId == null ? "" : authUserId.trim()))) {
-				throw new PreIdInvalidForUserIdException(ApplicationErrorCodes.PRG_APP_015.getCode(),
-						ApplicationErrorMessages.INVALID_APPLICATION_ID_FOR_USER.getMessage());
-				}
-			}	
+					String authUserId = authUserDetails().getUserId();
+					String canonicalAuthUserId = resolveCanonicalUserId(authUserId);
+					String effectiveCrBy = applicationEntity.getEffectiveCrBy() == null ? "" : applicationEntity.getEffectiveCrBy().trim();
+					boolean matchesCanonical = effectiveCrBy.equals(canonicalAuthUserId);
+					boolean matchesRaw = piiBackwardCompatibility && 
+						effectiveCrBy.equals(authUserId == null ? "" : authUserId.trim());
+					if (!matchesCanonical && !matchesRaw) {
+						throw new PreIdInvalidForUserIdException(ApplicationErrorCodes.PRG_APP_015.getCode(),
+								ApplicationErrorMessages.INVALID_APPLICATION_ID_FOR_USER.getMessage());
+					}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/ApplicationService.java`
around lines 436 - 444, Refactor the authorization check in ApplicationService
by normalizing values first (call authUserDetails().getUserId(),
resolveCanonicalUserId(authUserId), and trim
applicationEntity.getEffectiveCrBy() into a local effectiveCrBy variable) and
then replace the nested negated condition with a clear sequence: if
effectiveCrBy equals canonicalAuthUserId -> allow; else if
piiBackwardCompatibility && effectiveCrBy equals trimmed authUserId -> allow;
else throw PreIdInvalidForUserIdException using
ApplicationErrorCodes.PRG_APP_015 and
ApplicationErrorMessages.INVALID_APPLICATION_ID_FOR_USER. Ensure consistent
indentation and remove double-negation to improve readability around
authUserDetails(), resolveCanonicalUserId(), effectiveCrBy,
piiBackwardCompatibility, and the exception throw.

622-637: Extract resolveCanonicalUserId to a shared utility class.

This method is duplicated identically in both ApplicationService and DemographicService. Extract it to a shared utility class to avoid duplication and ensure consistent behavior across both services.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/ApplicationService.java`
around lines 622 - 637, The resolveCanonicalUserId method is duplicated in
ApplicationService and DemographicService; extract it into a shared utility
(e.g., IdentityUtils or UserIdResolver) and replace both copies with calls to
that utility. Move the logic that checks null/empty, calls
userDetailsService.findOrCreateByIdentifier(userId), trims
mappedUser.getUserId(), and returns "" on failure into a single static or
instance method (e.g., IdentityUtils.resolveCanonicalUserId(userId,
userDetailsService, logger)), preserve the existing logging call (include
maskIdentifier) by either accepting a Logger and maskIdentifier helper or
keeping maskIdentifier as a shared utility, and update ApplicationService and
DemographicService to delegate to the new utility (inject userDetailsService or
pass it as an argument) so behavior and logging remain identical.

639-667: Extensive duplication of maskIdentifier method across 7 files.

The identical maskIdentifier implementation is duplicated across ApplicationService, DemographicService, LoginService, AppointmentServiceImpl, CommonServiceUtil, DataSyncServiceUtil, and LoginController. This utility method should be centralized in a shared location (e.g., as a public static method in CommonServiceUtil) to eliminate code duplication across services and modules.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/ApplicationService.java`
around lines 639 - 667, The maskIdentifier method is duplicated across multiple
classes; extract it to a single shared utility and replace duplicates with calls
to that utility. Create a public static String maskIdentifier(String) in
CommonServiceUtil with the exact logic currently in
ApplicationService.maskIdentifier, then remove the duplicate private methods
from ApplicationService, DemographicService, LoginService,
AppointmentServiceImpl, DataSyncServiceUtil, and LoginController and update
those classes to call CommonServiceUtil.maskIdentifier(value) (adding imports as
needed); run compilation/tests to ensure signatures and visibility match and
behavior remains identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@db_scripts/mosip_prereg/ddl/user_details.sql`:
- Around line 17-20: The column pair identifier_encrypted and encrypted_dtimes
in the user_details DDL are currently inconsistent (identifier_encrypted
nullable but encrypted_dtimes NOT NULL); add a constraint so their presence is
paired: either make both nullable or add a CHECK constraint on the table (e.g.,
CHECK ((identifier_encrypted IS NULL AND encrypted_dtimes IS NULL) OR
(identifier_encrypted IS NOT NULL AND encrypted_dtimes IS NOT NULL))) to enforce
that identifier_encrypted and encrypted_dtimes are NULL or NOT NULL together,
referencing the identifier_encrypted and encrypted_dtimes columns in the
user_details table definition.
- Around line 25-29: Add db_scripts/mosip_prereg/ddl/user_details.sql to the
migration orchestration by adding an include/import entry for that file in
db_scripts/mosip_prereg/ddl.sql so the file is deployed, and edit
user_details.sql to remove the CONCURRENTLY keyword from the index statements:
replace the two "CREATE INDEX CONCURRENTLY IF NOT EXISTS
idx_prereg_user_details_hash" and "CREATE INDEX CONCURRENTLY IF NOT EXISTS
idx_prereg_user_details_active" statements with "CREATE INDEX IF NOT EXISTS"
versions (keeping the same index names idx_prereg_user_details_hash and
idx_prereg_user_details_active and their ON clauses) so the DDL runs
transactionally under the orchestration.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/repository/ApplicationRepostiory.java`:
- Around line 41-45: Guard against null or empty userIds before invoking the
repository methods in the service/caller that use
ApplicationRepostiory.findByCreatedByIn(...) and
ApplicationRepostiory.findByCreatedByInBookingType(...): if userIds == null ||
userIds.isEmpty() return Collections.emptyList() (or an equivalent empty result)
instead of calling the repository, so you never bind an empty collection to the
JPQL IN parameter; apply the same check before both findByCreatedByIn and
findByCreatedByInBookingType calls.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/AppointmentServiceImpl.java`:
- Around line 473-483: The code currently sets
applicationEntity.setUpdBy(authUserDetails().getUserId()) before attempting
canonicalization, so if userDetailsService.findOrCreateByIdentifier(...) fails
the raw auth ID is persisted; change the flow to not set updBy from
authUserDetails() up-front: remove that initial setUpdBy call and only call
applicationEntity.setUpdBy(mappedUser.getUserId().toString()) after a successful
mapping (i.e., when mappedUser != null && mappedUser.getUserId() != null); in
the catch block do not revert to or persist the raw auth ID (leave updBy null or
unchanged) and keep the existing warn log in userDetails mapping failure
handling.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/DemographicService.java`:
- Around line 880-930: The userValidation method's flow misuses
userDetailsService.findOrCreateByIdentifier (in the mappedPreregUser lookup)
which may create unintended user entries and the fallback raw ID check runs even
when the canonical lookup succeeded but didn't match; change the prereg lookup
to a read-only lookup (e.g., use or add a findByIdentifier/read-only method
instead of findOrCreateByIdentifier for mappedPreregUser), restructure logic in
userValidation to: resolve canonicalAuthUserId once (mappedUser), attempt a
non-creating lookup for prereg (mappedPreregUser) and only if that lookup
throws/fails or returns null fall back to raw ID comparison (trimmedAuthUserId
vs trimmedPreregUserId), and simplify control flow to avoid nested try/catch and
ambiguous return paths while keeping the same exception
(PreIdInvalidForUserIdException) when final checks fail.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/LoginService.java`:
- Around line 492-493: The current masking in LoginService (the branch computing
visible = Math.min(4, trimmed.length()) and returning "***" +
trimmed.substring(...)) leaks short identifiers; change the logic so that if
trimmed.length() <= 4 you return a fully masked value (a string of '*' repeated
trimmed.length()), otherwise compute visible = 4 and return a mask of '*'
repeated (trimmed.length() - visible) concatenated with
trimmed.substring(trimmed.length() - visible); update the code around the
visible calculation and return to use length-based star repetition instead of
the fixed "***" prefix.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/CommonServiceUtil.java`:
- Around line 200-205: The current strict-mode check in CommonServiceUtil allows
empty canonical/prereg IDs to be treated as a match; update the logic in the
block that uses trimmedCanonicalAuthUserId and trimmedPreregUserId (when
piiBackwardCompatibility is false) to fail-closed: if either
trimmedCanonicalAuthUserId or trimmedPreregUserId is null/empty/blank, throw
PreIdInvalidForUserIdException (using DemographicErrorCodes.PRG_PAM_APP_017 and
DemographicErrorMessages.INVALID_PREID_FOR_USER) instead of allowing the
comparison to proceed, and apply the same emptiness check and exception behavior
to the similar comparison block later (the other 210-216 block) to ensure
missing canonical IDs are treated as invalid ownership.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DemographicServiceUtil.java`:
- Around line 321-333: The current write path in DemographicServiceUtil catches
exceptions from userDetailsService.findOrCreateByIdentifier and unconditionally
falls back to legacy/raw identifiers (setting demographicEntity.setCrAppuserId,
setCreatedBy, setUpdatedBy), which violates strict-mode; change the catch and
corresponding flows (also at the other spots flagged) to honor the
piiBackwardCompatibility policy by invoking the existing helper that resolves
the appropriate identifier under strict vs backward-compat modes and only
persisting legacy identifiers when piiBackwardCompatibility allows it;
specifically, in the try/catch around
userDetailsService.findOrCreateByIdentifier and in the other create/save/update
blocks replace the unconditional fallback with a call to the helper (use the
helper method already used elsewhere) to compute the id and then
setCrAppuserId/setCreatedBy/setUpdatedBy accordingly so strict deployments never
persist raw identifiers on mapping failure.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DocumentServiceUtil.java`:
- Around line 182-191: The current try/catch in DocumentServiceUtil around
userDetailsService.findOrCreateByIdentifier may set crBy/updBy to the raw userId
on mapping failure; change the logic so that if an exception occurs or
mappedUser is null/has null userId you do NOT fall back to the raw userId—leave
documentEntity.crBy and updBy unset (or null) and only set them when
mappedUser.getUserId() is present; keep the catch to log the failure via
log.warn (including the exception) but remove any fallback assignment to userId
to avoid persisting raw identifiers.

In
`@pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/DemographicServiceTest.java`:
- Around line 1256-1258: The test helper getCanonicalUserIdString currently
creates a deterministic UUID via UUID.nameUUIDFromBytes(identifier.getBytes()),
but production UserDetailsService.findOrCreateByIdentifier uses
UUID.randomUUID(), causing a mismatch; update the test to mirror production by
generating random UUIDs (use UUID.randomUUID().toString() in the helper) or
change the mock setup in the test to not rely on a specific UUID (e.g., accept
any UUID or capture the generated UUID) so the test behavior matches
UserDetailsService.findOrCreateByIdentifier.

In
`@pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/test/service/AppointmentServiceTest.java`:
- Around line 53-56: The static System.setProperty in AppointmentServiceTest
mutates JVM-global state; remove that static block and set the property within
the test context instead (e.g., use JUnit 5 `@DynamicPropertySource` to register
"mosip.prereg.pii.backward.compatibility=false" for the Spring context, or
annotate AppointmentServiceTest with `@TestPropertySource`(properties =
"mosip.prereg.pii.backward.compatibility=false") / use Spring's
TestPropertyValues) so the property is scoped to this test only and does not
affect other tests or JVM-global state; update the AppointmentServiceTest setup
accordingly.

In
`@pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/service/UserDetailsService.java`:
- Around line 111-114: The code currently only checks for null from
normalize(identifier) but accepts whitespace-only inputs that normalize to empty
string; update the validation in UserDetailsService (the block using
normalize(identifier) and variable norm) to treat empty strings as invalid as
well—i.e., after calling normalize(identifier) throw the same
IllegalArgumentException when norm is null or norm.isEmpty() (or otherwise
blank) so whitespace-only identifiers are rejected before hashing/creation.
- Around line 60-86: Both encryptIdentifierIfConfigured and
decryptIdentifierIfConfigured are incorrectly converting raw ciphertext/bytes
to/from UTF-8 Strings which can corrupt binary data; change them to encode
encrypted byte[] to a safe textual form (e.g., Base64) when returning/storing
and decode from Base64 back to byte[] before passing to cryptoUtil.decrypt,
ensuring you use the same encoding in both methods and preserve existing
null/empty handling and LocalDateTime usage in the cryptoUtil calls.
- Around line 116-147: The find-or-create flow in findOrCreate can race: two
threads that both miss userDetailsRepository.findByIdentifierHash(hash) may each
insert and cause a unique-key violation; change the "create" branch to attempt
insert but catch the DB unique constraint exception (e.g.,
DataIntegrityViolationException or the underlying ConstraintViolationException),
then re-query userDetailsRepository.findByIdentifierHash(hash) and return the
found row instead of failing; ensure this handling surrounds the
userDetailsRepository.save(existing or u) calls and keep using
encryptIdentifierIfConfigured(identifier) and UserDetails fields as before so
the method becomes idempotent under concurrency.

In
`@pre-registration/pre-registration-datasync-service/src/main/java/io/mosip/preregistration/datasync/service/DataSyncService.java`:
- Around line 438-439: The call to authUserDetails().getUserId() used when
invoking serviceUtil.reverseDateSyncSave can NPE when the security principal is
absent; modify the invocation in DataSyncService so you compute a safe userId
first (call authUserDetails(), check for null and check getUserId() for
null/empty) and pass a fallback constant (the prior system user id used for
pre-registration) when auth info is missing before calling reverseDateSyncSave;
update the local variable reverseDatasyncReponse assignment to use that safe
userId.

In
`@pre-registration/pre-registration-datasync-service/src/main/java/io/mosip/preregistration/datasync/service/util/DataSyncServiceUtil.java`:
- Around line 892-906: getCanonicalUserId currently returns the raw userId on
mapping failure, which can reintroduce plaintext PII; change the fallback to a
non-PII value (e.g., return maskIdentifier(userId) or a configured pseudonymous
constant like ANONYMOUS_USER_ID) instead of the original userId, preserving the
existing log call; update the same fallback behavior in the other similar
method(s) that use userDetailsService.findOrCreateByIdentifier (the block around
the reverseDateSyncSave logic) so neither createdBy/crBy nor any persistence
uses the original plaintext identifier.

---

Nitpick comments:
In `@db_scripts/mosip_prereg/ddl/user_details.sql`:
- Line 16: The schema defines identifier_hash VARCHAR(128) NOT NULL UNIQUE which
already creates a unique index, so remove the redundant index creation for
idx_prereg_user_details_hash (and any duplicate index statements at lines
corresponding to the other occurrences mentioned) to avoid double indexing;
update the DDL to drop or omit the CREATE INDEX/ADD INDEX statements that
reference idx_prereg_user_details_hash while keeping the UNIQUE constraint on
identifier_hash in the user_details table definition.
- Around line 28-29: The partial index idx_prereg_user_details_active on table
user_details uses user_id as its key while filtering on identifier_encrypted IS
NOT NULL, which is redundant because user_id is already primary-key indexed;
either drop this index or change it to index the column(s) actually used by
queries that include the predicate (for example index identifier_encrypted or
the combination of identifier_encrypted plus any frequently queried columns),
and update DDL to remove or replace CREATE INDEX CONCURRENTLY IF NOT EXISTS
idx_prereg_user_details_active with the appropriate index definition that
matches real query patterns.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/controller/LoginController.java`:
- Around line 269-297: Extract the masking logic from
LoginController.maskIdentifier into a shared utility (e.g.,
IdentifierMaskUtils.maskIdentifier) and replace duplicated implementations in
LoginService, AppointmentServiceImpl, DemographicService, and other classes to
call this single static utility method; ensure the utility preserves current
behavior for emails, phone numbers (with optional '+'), UUIDs and fallback
masking, keep null/blank handling, add unit tests for the utility, and update
imports/usages across callers so all components use
IdentifierMaskUtils.maskIdentifier.
- Around line 290-294: Replace the broad catch in LoginController's UUID masking
logic with a catch for IllegalArgumentException only: when calling
UUID.fromString(trimmed) inside the method that returns masked UUID (the block
returning "***" + trimmed.substring(...)), change the catch(Exception ignored)
to catch(IllegalArgumentException ignored) so only invalid-UUID errors are
handled and other exceptions aren't accidentally suppressed; keep the existing
handling (silently ignore or log) consistent with current behavior.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/ApplicationService.java`:
- Around line 669-679: The getUserLookupIds duplication should be removed from
ApplicationService and the three helper methods (resolveCanonicalUserId,
maskIdentifier, getUserLookupIds) should be extracted into a shared utility
class (e.g., UserIdUtils or add to existing UserDetailsService); create the new
class with those methods as public static (or instance) helpers, update
ApplicationService to call UserIdUtils.resolveCanonicalUserId(...) and
UserIdUtils.getUserLookupIds(...) (or inject UserDetailsService and call the
moved methods), and remove the duplicate implementations from other services so
all callers reference the single shared implementation.
- Around line 436-444: Refactor the authorization check in ApplicationService by
normalizing values first (call authUserDetails().getUserId(),
resolveCanonicalUserId(authUserId), and trim
applicationEntity.getEffectiveCrBy() into a local effectiveCrBy variable) and
then replace the nested negated condition with a clear sequence: if
effectiveCrBy equals canonicalAuthUserId -> allow; else if
piiBackwardCompatibility && effectiveCrBy equals trimmed authUserId -> allow;
else throw PreIdInvalidForUserIdException using
ApplicationErrorCodes.PRG_APP_015 and
ApplicationErrorMessages.INVALID_APPLICATION_ID_FOR_USER. Ensure consistent
indentation and remove double-negation to improve readability around
authUserDetails(), resolveCanonicalUserId(), effectiveCrBy,
piiBackwardCompatibility, and the exception throw.
- Around line 622-637: The resolveCanonicalUserId method is duplicated in
ApplicationService and DemographicService; extract it into a shared utility
(e.g., IdentityUtils or UserIdResolver) and replace both copies with calls to
that utility. Move the logic that checks null/empty, calls
userDetailsService.findOrCreateByIdentifier(userId), trims
mappedUser.getUserId(), and returns "" on failure into a single static or
instance method (e.g., IdentityUtils.resolveCanonicalUserId(userId,
userDetailsService, logger)), preserve the existing logging call (include
maskIdentifier) by either accepting a Logger and maskIdentifier helper or
keeping maskIdentifier as a shared utility, and update ApplicationService and
DemographicService to delegate to the new utility (inject userDetailsService or
pass it as an argument) so behavior and logging remain identical.
- Around line 639-667: The maskIdentifier method is duplicated across multiple
classes; extract it to a single shared utility and replace duplicates with calls
to that utility. Create a public static String maskIdentifier(String) in
CommonServiceUtil with the exact logic currently in
ApplicationService.maskIdentifier, then remove the duplicate private methods
from ApplicationService, DemographicService, LoginService,
AppointmentServiceImpl, DataSyncServiceUtil, and LoginController and update
those classes to call CommonServiceUtil.maskIdentifier(value) (adding imports as
needed); run compilation/tests to ensure signatures and visibility match and
behavior remains identical.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/AppointmentServiceImpl.java`:
- Around line 493-521: The maskIdentifier implementation in
AppointmentServiceImpl is duplicated across services; extract this logic into a
single shared utility (e.g., IdentifierMasker.maskIdentifier or
MaskUtils.maskIdentifier) in a common/shared module, make the method public
static, move the exact logic from AppointmentServiceImpl.maskIdentifier into
that utility, update AppointmentServiceImpl and other services to call the new
utility method instead of their local copies, and update/merge unit tests to
reference the centralized method and remove duplicated implementations to keep
masking policy consistent.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/DemographicService.java`:
- Around line 978-988: The getUserLookupIds method is duplicated from
ApplicationService along with resolveCanonicalUserId and maskIdentifier;
consolidate these three helpers into a single utility class (e.g., UserIdUtils)
containing resolveCanonicalUserId, maskIdentifier, and getUserLookupIds as
static methods, move the shared logic there, update DemographicService and
ApplicationService to call UserIdUtils.resolveCanonicalUserId,
UserIdUtils.maskIdentifier, and UserIdUtils.getUserLookupIds (removing the local
copies), and ensure visibility, null-handling and piiBackwardCompatibility
behavior are preserved and covered by existing tests or add small unit tests for
the new utility methods.
- Around line 948-976: The maskIdentifier method in DemographicService is a
duplicate of the one in ApplicationService; extract the logic into a shared
utility class (e.g., PiiMaskingUtil) or an existing shared service (e.g.,
UserDetailsService) and replace the local method with a call to that utility.
Create a public static maskIdentifier(String) in PiiMaskingUtil (or a public
method on UserDetailsService), move the masking logic there, update
DemographicService to call PiiMaskingUtil.maskIdentifier(value) (or
userDetailsService.maskIdentifier(value)), and remove the duplicate private
maskIdentifier from DemographicService; ensure imports and any tests/refactors
reference the single shared implementation.
- Around line 932-946: The resolveCanonicalUserId method is duplicated in
DemographicService and ApplicationService; extract it into a shared utility
(e.g., a new CanonicalUserUtil or UserIdMapper) and have both classes call that
utility. Move the logic that uses
userDetailsService.findOrCreateByIdentifier(...) and maskIdentifier(...) into
the utility, or alternatively keep the dependency on userDetailsService in the
callers and expose a utility method like
mapToCanonicalUserId(UserDetailsService, String) that performs the try/catch and
logging; update DemographicService.resolveCanonicalUserId and the one in
ApplicationService to delegate to the new utility, adjust imports, ensure the
logger and maskIdentifier are accessible (or pass a masking function/logger into
the utility), and run tests to verify no behavioral changes.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/LoginService.java`:
- Around line 478-483: In the LoginService class, remove the unreachable
small-length phone branch inside the trimmed.matches("\\+?\\d{10,12}") block:
delete the inner if (digits.length() <= 4) { return (hasPlus ? "+" : "") +
"****"; } since digits is already constrained to 10..12 by the regex; leave the
remaining masking logic in this block intact so the method compiles and behavior
is unchanged for valid 10–12 digit inputs.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/OTPManager.java`:
- Around line 144-155: The create/update branches in OTPManager duplicate the
canonical user resolution logic: extract a private helper method (e.g.,
resolveCanonicalUpdater) that calls
userDetailsService.findOrCreateByIdentifier(environment.getProperty(PreRegLoginConstant.MOSIP_PRE_REG_CLIENTID)),
sets the updater value using otpTxn.setUpdBy(...) with the
mappedUser.getUserId().toString() fallback to the raw property, and logs
failures via logger.warn(...) with the exception; replace the duplicated blocks
in both create and update paths with a call to this helper to keep behavior
consistent and reduce maintenance.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DocumentServiceUtil.java`:
- Around line 292-297: The documentEntitySetter contains redundant assignments:
remove the initial copyDocumentEntity.setCrBy(sourceEntity.getCrBy()) and
copyDocumentEntity.setUpdBy(sourceEntity.getUpdBy()) since they are immediately
overwritten by copyDocumentEntity.setCrBy(sourceEntity.getEffectiveCrBy()) and
copyDocumentEntity.setUpdBy(sourceEntity.getEffectiveUpdBy()); keep only the
effective-crBy/updBy assignments (and the existing setLangCode) so the method
uses sourceEntity.getEffectiveCrBy()/getEffectiveUpdBy() and avoid the duplicate
setCrBy/setUpdBy calls.

In
`@pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/AppointmentServiceImplTest.java`:
- Around line 598-606: The NullAuthority helper class should be made private
static final to limit its scope to this test class; locate the static inner
class named NullAuthority (implements GrantedAuthority) and change its
declaration from package-private static to private static final so it remains
test-scoped and immutable.

In
`@pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/DocumentServiceTest.java`:
- Around line 161-177: Extract the repeated DocumentEntity setup in
DocumentServiceTest into a test helper/builder to reduce noise: create a factory
method (e.g., buildTestDocumentEntity or DocumentEntityBuilder) that
encapsulates constructing DocumentEntity and setting fields used here
(demographicEntity, documentId, docName, docCatCode, docTypeCode, docFileFormat,
statusCode, langCode, crBy, crDtime, updBy, updDtime using
DateUtils.parseDateToLocalDateTime(new Date()), encryptedDateTime, docId,
docHash using HashUtill.hashUtill(cephBytes), refNumber) and replace the inline
block in tests with a call to that helper; keep the helper in test sources so
other tests can reuse it and update instantiations in DocumentServiceTest to use
the new builder API.

In
`@pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/test/service/util/DocumentServiceUtilTest.java`:
- Around line 88-90: The test suite needs explicit unit tests verifying the new
canonical mapping path in dtoToEntity: add two tests in DocumentServiceUtilTest
that exercise dtoToEntity using the mocked UserDetailsService—(1) a success case
where UserDetailsService returns a UserDetails with the canonical identifier and
assert the resulting entity fields crBy and updBy are set to that canonical
value, and (2) a failure/fallback case where UserDetailsService returns null or
throws and assert dtoToEntity falls back to the previous behavior (e.g., uses
the original dto values or a default) for crBy/updBy; locate the call to
dtoToEntity and the UserDetailsService mock in the test class and update
assertions accordingly to validate both canonical mapping and fallback.

In
`@pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/entity/AvailibityEntity.java`:
- Around line 90-96: The AvailibityEntity exposes getEffectiveCrBy() and
getEffectiveUpdBy() which are inconsistent with InterfaceDataSyncEntity's
getEffectiveCreatedBy() and getEffectiveUpdatedBy(); rename AvailibityEntity's
methods to getEffectiveCreatedBy() and getEffectiveUpdatedBy(), update any
internal field mappings (crBy -> createdBy/upBy -> updatedBy or keep fields but
ensure getters map correctly), and search/replace all usages of
getEffectiveCrBy() and getEffectiveUpdBy() to the new method names to maintain
API consistency across entities.

In
`@pre-registration/pre-registration-core/src/test/java/io/mosip/preregistration/core/common/service/UserDetailsServiceTest.java`:
- Around line 39-84: Add two edge-case unit tests in UserDetailsServiceTest: one
that verifies findOrCreateByIdentifier and getDecryptedIdentifier handle
non-text/binary ciphertext bytes by mocking cryptoUtil.encrypt to return
non-UTF8 bytes (e.g., arbitrary byte array) and asserting decryption and
save/lookup still behave correctly; and another that verifies
findOrCreateByIdentifier rejects blank/whitespace identifiers by calling
userDetailsService.findOrCreateByIdentifier with blank inputs and asserting no
save on userDetailsRepository and that an appropriate empty/exceptional result
is returned. Use existing mocks (userDetailsRepository, cryptoUtil) and methods
(findOrCreateByIdentifier, getDecryptedIdentifier, findById) to set up
repository returns and verify interactions.

Comment thread db_scripts/mosip_prereg/ddl/user_details.sql Outdated
Comment thread db_scripts/mosip_prereg/ddl/user_details.sql Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
db_scripts/mosip_prereg/ddl/user_details.sql (1)

17-19: ⚠️ Potential issue | 🔴 Critical

NOT NULL constraints conflict with current save-path behavior.

Lines 17 and 19 require encrypted fields always present, but UserDetailsService can persist identifier_encrypted = null (see pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/service/UserDetailsService.java, snippets around Lines 60-74 and 137-147). This will cause insert failures in fallback/encryption-failure paths.

💡 Suggested DDL fix
 CREATE TABLE IF NOT EXISTS prereg.user_details (
     user_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
     identifier_hash VARCHAR(128) NOT NULL UNIQUE,
-    identifier_encrypted TEXT NOT NULL,
+    identifier_encrypted TEXT,
     cr_dtimes TIMESTAMP NOT NULL,
-    encrypted_dtimes TIMESTAMP NOT NULL
+    encrypted_dtimes TIMESTAMP,
+    CONSTRAINT chk_user_details_encryption_pair CHECK (
+      (identifier_encrypted IS NULL AND encrypted_dtimes IS NULL) OR
+      (identifier_encrypted IS NOT NULL AND encrypted_dtimes IS NOT NULL)
+    )
 );
#!/bin/bash
set -euo pipefail

echo "== DDL nullability =="
rg -n "identifier_encrypted|encrypted_dtimes" db_scripts/mosip_prereg/ddl/user_details.sql

echo ""
echo "== Service behavior allowing null encrypted value =="
rg -n "encryptIdentifierIfConfigured|setIdentifierEncrypted|setEncryptedDtimes|return null" \
  pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/service/UserDetailsService.java
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@db_scripts/mosip_prereg/ddl/user_details.sql` around lines 17 - 19, The DDL
currently enforces NOT NULL on identifier_encrypted and encrypted_dtimes which
conflicts with UserDetailsService behavior that can persist null encrypted
values (see methods encryptIdentifierIfConfigured, setIdentifierEncrypted,
setEncryptedDtimes); update the user_details.sql column definitions for
identifier_encrypted and encrypted_dtimes to allow NULL (remove the NOT NULL
constraint) so inserts succeed in fallback/encryption-failure paths and preserve
existing application behavior.
🧹 Nitpick comments (3)
pre-registration/pre-registration-application-service/pom.xml (2)

98-98: Use ${kernel.core.version} property instead of hardcoded version.

The parent POM defines kernel.core.version=1.3.0. Using the property reference ensures version consistency and simplifies future upgrades.

♻️ Suggested refactor
 		<dependency>
 			<groupId>io.mosip.kernel</groupId>
 			<artifactId>kernel-core</artifactId>
-			<version>1.3.0</version>
+			<version>${kernel.core.version}</version>
 		</dependency>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pre-registration/pre-registration-application-service/pom.xml` at line 98,
Replace the hardcoded version element in the pre-registration module POM by
referencing the parent property: update the <version> element currently set to
"1.3.0" to use the property ${kernel.core.version} so the module uses the
kernel.core.version defined in the parent POM; locate the <version> tag in
pre-registration-application-service/pom.xml and change its value to the
property reference to maintain consistency with the parent.

248-248: Consider using a property instead of hardcoded version.

Similar to the kernel-core dependency, the kernel-auth-adapter version is hardcoded. For consistency with the rest of the project's version management approach, consider using ${kernel.bom.version} or defining a dedicated property.

♻️ Suggested refactor
 				<dependency>
 					<groupId>io.mosip.kernel</groupId>
 					<artifactId>kernel-auth-adapter</artifactId>
-					<version>1.3.0</version>
+					<version>${kernel.bom.version}</version>
 				</dependency>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pre-registration/pre-registration-application-service/pom.xml` at line 248,
The dependency declaration for kernel-auth-adapter currently hardcodes
<version>1.3.0</version>; change it to use the existing BOM/property strategy by
replacing the literal with the project property (e.g., ${kernel.bom.version} or
a new property like ${kernel.auth.adapter.version}) so it matches how
kernel-core is versioned; update the pom's dependency element that references
kernel-auth-adapter to reference the chosen property and, if you introduce a new
property, add it to the <properties> block (symbol references: the
kernel-auth-adapter dependency and the <version> element).
pre-registration/pre-registration-datasync-service/pom.xml (1)

169-169: Consider using a property instead of hardcoded version.

The kernel-auth-adapter version is hardcoded as 1.3.0 while other dependencies in this file use property references. For easier maintenance during future upgrades, consider using ${kernel.bom.version} or defining a dedicated property.

♻️ Suggested refactor
 				<dependency>
 					<groupId>io.mosip.kernel</groupId>
 					<artifactId>kernel-auth-adapter</artifactId>
-					<version>1.3.0</version>
+					<version>${kernel.bom.version}</version>
 				</dependency>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pre-registration/pre-registration-datasync-service/pom.xml` at line 169,
Replace the hardcoded <version>1.3.0</version> for the kernel-auth-adapter
dependency with a Maven property reference to centralize version management
(e.g., use ${kernel.bom.version} or a new <kernel.auth.version> property);
update the dependency entry that references kernel-auth-adapter and add or reuse
the property in the <properties> section so future upgrades can be done by
changing the single property value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@db_scripts/mosip_prereg/ddl/user_details.sql`:
- Around line 25-29: Remove the redundant full index creation for
idx_prereg_user_details_hash on prereg.user_details because
UNIQUE(identifier_hash) already creates the same index; delete the CREATE INDEX
IF NOT EXISTS idx_prereg_user_details_hash ... ON
prereg.user_details(identifier_hash) statement, but keep the partial index
statement CREATE INDEX IF NOT EXISTS idx_prereg_user_details_active ... ON
prereg.user_details(user_id) WHERE identifier_encrypted IS NOT NULL as-is.

---

Duplicate comments:
In `@db_scripts/mosip_prereg/ddl/user_details.sql`:
- Around line 17-19: The DDL currently enforces NOT NULL on identifier_encrypted
and encrypted_dtimes which conflicts with UserDetailsService behavior that can
persist null encrypted values (see methods encryptIdentifierIfConfigured,
setIdentifierEncrypted, setEncryptedDtimes); update the user_details.sql column
definitions for identifier_encrypted and encrypted_dtimes to allow NULL (remove
the NOT NULL constraint) so inserts succeed in fallback/encryption-failure paths
and preserve existing application behavior.

---

Nitpick comments:
In `@pre-registration/pre-registration-application-service/pom.xml`:
- Line 98: Replace the hardcoded version element in the pre-registration module
POM by referencing the parent property: update the <version> element currently
set to "1.3.0" to use the property ${kernel.core.version} so the module uses the
kernel.core.version defined in the parent POM; locate the <version> tag in
pre-registration-application-service/pom.xml and change its value to the
property reference to maintain consistency with the parent.
- Line 248: The dependency declaration for kernel-auth-adapter currently
hardcodes <version>1.3.0</version>; change it to use the existing BOM/property
strategy by replacing the literal with the project property (e.g.,
${kernel.bom.version} or a new property like ${kernel.auth.adapter.version}) so
it matches how kernel-core is versioned; update the pom's dependency element
that references kernel-auth-adapter to reference the chosen property and, if you
introduce a new property, add it to the <properties> block (symbol references:
the kernel-auth-adapter dependency and the <version> element).

In `@pre-registration/pre-registration-datasync-service/pom.xml`:
- Line 169: Replace the hardcoded <version>1.3.0</version> for the
kernel-auth-adapter dependency with a Maven property reference to centralize
version management (e.g., use ${kernel.bom.version} or a new
<kernel.auth.version> property); update the dependency entry that references
kernel-auth-adapter and add or reuse the property in the <properties> section so
future upgrades can be done by changing the single property value.

ℹ️ Review info
Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 03b0fad5-b433-4b84-ad60-9ce4a5f18d8b

📥 Commits

Reviewing files that changed from the base of the PR and between bdc2d9c and 9997b77.

📒 Files selected for processing (6)
  • db_scripts/mosip_prereg/ddl.sql
  • db_scripts/mosip_prereg/ddl/user_details.sql
  • pre-registration/pom.xml
  • pre-registration/pre-registration-application-service/pom.xml
  • pre-registration/pre-registration-core/pom.xml
  • pre-registration/pre-registration-datasync-service/pom.xml

Comment thread db_scripts/mosip_prereg/ddl/user_details.sql Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/impl/ApplicationConsumedStatusUpdater.java`:
- Line 151: In ApplicationConsumedStatusUpdater, avoid calling
resolveCanonicalUserId(auditUserId) for each row/document; resolve the canonical
user ID once at the start of the batch execution and reuse it when setting
updatedBy (e.g., replace repeated calls in places where
demographicEntityConsumed.setUpdatedBy(resolveCanonicalUserId(auditUserId)) and
similar lines are used); compute a local final variable like resolvedAuditUser =
resolveCanonicalUserId(auditUserId) in the batch-level method that drives the
per-record updates and use resolvedAuditUser for updatedBy assignments to
eliminate redundant work.
- Around line 226-239: The resolveCanonicalUserId method currently throws
IllegalStateException when userDetails mapping fails; change it to perform a
backward-compatible fallback like CommonServiceUtil: call
userDetailsService.findOrCreateByIdentifier(trimmedUserId), and if the returned
UserDetails or userId is null, log a warning/INFO and return the original
trimmedUserId instead of throwing; keep the UUID-short-circuit (isUuid) and
ensure you reference resolveCanonicalUserId,
userDetailsService.findOrCreateByIdentifier, and UserDetails.getUserId() while
implementing the fallback.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 66860152-5e43-488f-a5b9-d6ec8c471799

📥 Commits

Reviewing files that changed from the base of the PR and between 9997b77 and 9bc82f5.

📒 Files selected for processing (1)
  • pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/impl/ApplicationConsumedStatusUpdater.java

@mayuradesh mayuradesh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Move common code like adding record to UserDetails table, creating a list of userIds based on compatibility mode etc to common Utils.
  2. From all places use this common code.
  3. There are too many needless IF conditions which can cause silent failures in code, review all and include only MUST conditions.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (8)
pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/service/UserDetailsService.java (1)

76-90: Encryption/decryption round-trip appears correct but relies on CryptoUtil implementation details.

Based on the CryptoUtil context snippets, the encrypt() method returns bytes of a Base64-encoded string (from body.getResponse().getData().getBytes()), and decrypt() expects UTF-8 string bytes that it converts back to a String for the DTO. The current implementation should work correctly for this specific CryptoUtil behavior.

The past review comment about UTF-8 corruption assumed raw binary ciphertext, but the crypto service returns Base64-encoded responses. However, this coupling is implicit and could break if CryptoUtil's response format changes.

Consider adding a comment documenting this dependency:

// Note: CryptoUtil returns Base64-encoded strings from the crypto service,
// so UTF-8 round-trip is safe here. If CryptoUtil changes to return raw
// binary, this must be updated to use explicit Base64 encoding.

Also applies to: 100-114

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/service/UserDetailsService.java`
around lines 76 - 90, The encryptIdentifierIfConfigured method relies on
CryptoUtil.encrypt returning a Base64-encoded string as UTF-8 bytes, which makes
the current new String(byte[], UTF_8) safe but fragile; update the method
(encryptIdentifierIfConfigured) to add a clear inline comment above the
try-block documenting this dependency: note that CryptoUtil returns
Base64-encoded strings (UTF-8 bytes) and that if CryptoUtil changes to return
raw binary ciphertext this code must switch to explicit Base64
encoding/decoding, and apply the same comment to the corresponding
decrypt/round-trip code paths referenced in this class.
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/ApplicationService.java (1)

432-437: Duplicated validation logic; consider reusing userValidation method.

Lines 432-437 implement ownership validation inline, duplicating logic from the userValidation method at lines 547-559. Consider extracting a common helper or invoking the existing method to reduce duplication.

♻️ Consider extracting shared validation

The inline validation:

String authUserId = authUserDetails().getUserId();
String effectiveCrBy = applicationEntity.getEffectiveCrBy() == null ? "" : applicationEntity.getEffectiveCrBy().trim();
if (!userDetailsService.matchesUser(authUserId, effectiveCrBy, piiBackwardCompatibility)) {
    throw new PreIdInvalidForUserIdException(...);
}

Could potentially be consolidated with the userValidation method to avoid code duplication. Currently there's a commented-out //userValidation(applicationEntity); at line 431 suggesting this was considered.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/ApplicationService.java`
around lines 432 - 437, Replace the duplicated inline ownership check with a
single reusable validation call: remove the authUserId/effectiveCrBy/matchesUser
block and invoke the existing userValidation(applicationEntity) helper (or
extract a new private helper that calls userDetailsService.matchesUser and
throws PreIdInvalidForUserIdException) so the ownership check is centralized;
ensure the helper uses authUserDetails().getUserId(),
applicationEntity.getEffectiveCrBy() with the same trim/null handling, and the
same piiBackwardCompatibility flag to preserve behavior.
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/AppointmentServiceImpl.java (1)

477-488: Redundant updBy assignment at line 477.

Line 477 sets applicationEntity.setUpdBy(authUserDetails().getUserId()) with the raw user ID, then lines 480-481 immediately overwrite it with the canonical user ID. The initial assignment is unnecessary.

♻️ Remove redundant assignment
-    applicationEntity.setUpdBy(authUserDetails().getUserId());
     applicationEntity.setCrDtime(LocalDateTime.now(ZoneId.of("UTC")));
     try {
         applicationEntity.setUpdBy(
             userDetailsService.resolveCanonicalUserIdOrIdentifier(authUserDetails().getUserId()));
         return applicationRepostiory.save(applicationEntity);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/AppointmentServiceImpl.java`
around lines 477 - 488, Remove the redundant pre-overwrite of updBy: in
AppointmentServiceImpl remove the initial call
applicationEntity.setUpdBy(authUserDetails().getUserId()) and keep only the
canonical assignment using
userDetailsService.resolveCanonicalUserIdOrIdentifier(authUserDetails().getUserId())
before saving via applicationRepostiory.save; ensure no other logic depends on
the raw userId being set earlier and that exception handling around
applicationRepostiory.save remains unchanged.
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/CommonServiceUtil.java (1)

408-415: Side-effect mutation of input list in compareUploadedDocListAndValidMandatoryDocList.

The forEach with remove mutates the validMandatoryDocForApplicant list passed as a parameter. While functionally correct, this side effect can be surprising to callers and may cause issues if the caller expects the list to remain unchanged.

♻️ Consider using a non-mutating approach
 private boolean compareUploadedDocListAndValidMandatoryDocList(List<String> uploadedDocs,
     List<String> validMandatoryDocForApplicant) {
   if (validMandatoryDocForApplicant.isEmpty()) {
     return true;
   }
-  uploadedDocs.forEach(validMandatoryDocForApplicant::remove);
-  return validMandatoryDocForApplicant.isEmpty();
+  return uploadedDocs.containsAll(validMandatoryDocForApplicant);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/CommonServiceUtil.java`
around lines 408 - 415, The method
compareUploadedDocListAndValidMandatoryDocList mutates the input
validMandatoryDocForApplicant by calling remove in uploadedDocs.forEach;
instead, avoid side-effects by performing the check on a copy or using a
non-mutating containment check (e.g., create a new List/Set from
validMandatoryDocForApplicant or convert uploadedDocs to a Set and call
containsAll) so the original validMandatoryDocForApplicant is not modified;
update the implementation in compareUploadedDocListAndValidMandatoryDocList to
operate on that copy/Set and return the emptiness/containment result.
pre-registration/pre-registration-core/src/test/java/io/mosip/preregistration/core/common/service/UserDetailsServiceTest.java (1)

94-96: Prefer assertEquals over assertTrue for clearer failure messages.

Using assertEquals provides a more descriptive failure message showing expected vs actual values.

♻️ Use assertEquals for clearer assertion
     Optional<String> decrypted = userDetailsService.getDecryptedIdentifier(userId);
     assertTrue(decrypted.isPresent());
-    assertTrue("TestUser123".equals(decrypted.get()));
+    assertEquals("TestUser123", decrypted.get());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-core/src/test/java/io/mosip/preregistration/core/common/service/UserDetailsServiceTest.java`
around lines 94 - 96, Replace the boolean equality check that uses
assertTrue("TestUser123".equals(decrypted.get())) with an assertEquals call so
failures show expected vs actual; keep the presence check
assertTrue(decrypted.isPresent()) and change the comparison to
assertEquals("TestUser123", decrypted.get()) in UserDetailsServiceTest
(reference: variable decrypted).
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DemographicServiceUtil.java (2)

304-323: Redundant initial assignments before canonical resolution.

Lines 304-305 and 313 set crAppuserId, createdBy, and updatedBy to userId, which are then immediately overwritten at lines 321-323 with the effective (canonical) user ID. Consider removing the initial assignments or consolidating the logic.

♻️ Remove redundant assignments
-    demographicEntity.setCrAppuserId(userId);
-    demographicEntity.setCreatedBy(userId);
     demographicEntity.setCreateDateTime(LocalDateTime.now(ZoneId.of("UTC")));
     demographicEntity.setStatusCode(statuscode);
     log.info(LOGGER_SESSIONID, LOGGER_IDTYPE, LOGGER_ID,
             "Hashing start time : " + DateUtils.getUTCCurrentDateTimeString());
     demographicEntity.setDemogDetailHash(HashUtill.hashUtill(demographicEntity.getApplicantDetailJson()));
     log.info(LOGGER_SESSIONID, LOGGER_IDTYPE, LOGGER_ID,
             "Hashing end time : " + DateUtils.getUTCCurrentDateTimeString());
-    demographicEntity.setUpdatedBy(userId);
     demographicEntity.setUpdateDateTime(LocalDateTime.now(ZoneId.of("UTC")));
     demographicEntity.setEncryptedDateTime(encryptionDateTime);
     
     String effectiveUserId = userDetailsService.resolveCanonicalUserIdOrIdentifier(userId);
     // ... logging ...
     demographicEntity.setCrAppuserId(effectiveUserId);
     demographicEntity.setCreatedBy(effectiveUserId);
     demographicEntity.setUpdatedBy(effectiveUserId);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DemographicServiceUtil.java`
around lines 304 - 323, In DemographicServiceUtil, remove the redundant early
assignments of crAppuserId/createdBy/updatedBy to userId (the calls
demographicEntity.setCrAppuserId(userId);
demographicEntity.setCreatedBy(userId); demographicEntity.setUpdatedBy(userId))
since they are overwritten after resolving the canonical user via
userDetailsService.resolveCanonicalUserIdOrIdentifier; keep the other
initialization (timestamps, status, hashing) as-is and ensure that only the
resolved effectiveUserId is used in
demographicEntity.setCrAppuserId(effectiveUserId),
setCreatedBy(effectiveUserId), and setUpdatedBy(effectiveUserId) to avoid
unnecessary temporary values.

712-722: Redundant initial assignments in saveAndUpdateApplicationEntity.

Similar to prepareDemographicEntityForCreate, lines 712-715 set crBy, updBy to userId, then lines 720-722 overwrite with the effective user ID.

♻️ Consolidate user ID assignments
     applicationEntity.setApplicationStatusCode(applicationStatusCode);
     applicationEntity.setBookingType(bookingTypeCode);
     applicationEntity.setBookingStatusCode(bookingStatusCode);
-    applicationEntity.setCrBy(userId);
     applicationEntity.setCrDtime(LocalDateTime.now(ZoneId.of("UTC")));
-    applicationEntity.setUpdBy(userId);
     applicationEntity.setUpdDtime(LocalDateTime.now(ZoneId.of("UTC")));
     String effectiveUserId = userDetailsService.resolveCanonicalUserIdOrIdentifier(userId);
     log.info(...);
     applicationEntity.setCrBy(effectiveUserId);
     applicationEntity.setUpdBy(effectiveUserId);
     applicationEntity.setContactInfo(effectiveUserId);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DemographicServiceUtil.java`
around lines 712 - 722, In saveAndUpdateApplicationEntity remove the redundant
initial assignments of applicationEntity.setCrBy(userId) and
applicationEntity.setUpdBy(userId) so you only set the timestamps
(applicationEntity.setCrDtime / setUpdDtime) before resolving the canonical
user; then call userDetailsService.resolveCanonicalUserIdOrIdentifier(userId)
and assign the resulting effectiveUserId once to applicationEntity.setCrBy,
applicationEntity.setUpdBy and applicationEntity.setContactInfo, keeping the
existing log that uses maskIdentifier and isCanonicalApplied; this mirrors the
consolidated pattern used in prepareDemographicEntityForCreate and avoids
overwriting values.
pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/ApplicationServiceTest.java (1)

105-134: Unit test mocks both findOrCreateByIdentifier and matchesUser, missing validation of actual production behavior.

The test helper getCanonicalUserIdString uses UUID.nameUUIDFromBytes (deterministic), while production UserDetailsService.findOrCreateByIdentifier creates new users with UUID.randomUUID(). Both methods are mocked in this test, so the internally consistent mocking validates the contract between methods but not the actual implementation. Although production behavior is deterministic for the same identifier (due to hash-based lookup), adding an integration test would validate the real UUID generation and matching flow.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/ApplicationServiceTest.java`
around lines 105 - 134, The test currently stubs both
userDetailsService.findOrCreateByIdentifier and userDetailsService.matchesUser
with a deterministic UUID (getCanonicalUserIdString), which hides real
production behavior; update the setup so findOrCreateByIdentifier returns a
UserDetails whose userId is generated with UUID.randomUUID() (to mirror
production) and stop stubbing matchesUser (or replace the stub by using a spy on
UserDetailsService) so the real matchesUser logic is exercised; adjust
ApplicationServiceTest.setUp() to reflect these changes (modify the
Mockito.when(...) for findOrCreateByIdentifier and remove or convert the
Mockito.when(...) for matchesUser), or alternatively add an integration test
that does not mock UserDetailsService to validate the end-to-end UUID generation
and matching flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/OTPManager.java`:
- Around line 140-145: The code incorrectly canonicalizes a system client ID:
replace the call that obtains and canonicalizes the system property
(environment.getProperty(PreRegLoginConstant.MOSIP_PRE_REG_CLIENTID) +
userDetailsService.resolveCanonicalUserIdOrIdentifier(...)) and stop using the
resolved canonical value for audit fields like otpTxn.setUpdBy; instead set
otpTxn.setUpdBy to the original client id (the raw
environment.getProperty(PreRegLoginConstant.MOSIP_PRE_REG_CLIENTID)) or, if the
intent was to audit the actual end user, use requestDTO.getRequest().getUserId()
and canonicalize that via
userDetailsService.resolveCanonicalUserIdOrIdentifier(...) before
assigning—apply the same change for the similar block around lines 156-161.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DemographicServiceUtil.java`:
- Around line 31-33: Remove the duplicate import of
org.springframework.beans.factory.annotation.Value in the DemographicServiceUtil
class: keep a single import for Value alongside the existing imports (e.g.,
org.springframework.beans.factory.annotation.Qualifier) and delete the redundant
line to avoid duplicate imports.

---

Nitpick comments:
In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/ApplicationService.java`:
- Around line 432-437: Replace the duplicated inline ownership check with a
single reusable validation call: remove the authUserId/effectiveCrBy/matchesUser
block and invoke the existing userValidation(applicationEntity) helper (or
extract a new private helper that calls userDetailsService.matchesUser and
throws PreIdInvalidForUserIdException) so the ownership check is centralized;
ensure the helper uses authUserDetails().getUserId(),
applicationEntity.getEffectiveCrBy() with the same trim/null handling, and the
same piiBackwardCompatibility flag to preserve behavior.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/AppointmentServiceImpl.java`:
- Around line 477-488: Remove the redundant pre-overwrite of updBy: in
AppointmentServiceImpl remove the initial call
applicationEntity.setUpdBy(authUserDetails().getUserId()) and keep only the
canonical assignment using
userDetailsService.resolveCanonicalUserIdOrIdentifier(authUserDetails().getUserId())
before saving via applicationRepostiory.save; ensure no other logic depends on
the raw userId being set earlier and that exception handling around
applicationRepostiory.save remains unchanged.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/CommonServiceUtil.java`:
- Around line 408-415: The method compareUploadedDocListAndValidMandatoryDocList
mutates the input validMandatoryDocForApplicant by calling remove in
uploadedDocs.forEach; instead, avoid side-effects by performing the check on a
copy or using a non-mutating containment check (e.g., create a new List/Set from
validMandatoryDocForApplicant or convert uploadedDocs to a Set and call
containsAll) so the original validMandatoryDocForApplicant is not modified;
update the implementation in compareUploadedDocListAndValidMandatoryDocList to
operate on that copy/Set and return the emptiness/containment result.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DemographicServiceUtil.java`:
- Around line 304-323: In DemographicServiceUtil, remove the redundant early
assignments of crAppuserId/createdBy/updatedBy to userId (the calls
demographicEntity.setCrAppuserId(userId);
demographicEntity.setCreatedBy(userId); demographicEntity.setUpdatedBy(userId))
since they are overwritten after resolving the canonical user via
userDetailsService.resolveCanonicalUserIdOrIdentifier; keep the other
initialization (timestamps, status, hashing) as-is and ensure that only the
resolved effectiveUserId is used in
demographicEntity.setCrAppuserId(effectiveUserId),
setCreatedBy(effectiveUserId), and setUpdatedBy(effectiveUserId) to avoid
unnecessary temporary values.
- Around line 712-722: In saveAndUpdateApplicationEntity remove the redundant
initial assignments of applicationEntity.setCrBy(userId) and
applicationEntity.setUpdBy(userId) so you only set the timestamps
(applicationEntity.setCrDtime / setUpdDtime) before resolving the canonical
user; then call userDetailsService.resolveCanonicalUserIdOrIdentifier(userId)
and assign the resulting effectiveUserId once to applicationEntity.setCrBy,
applicationEntity.setUpdBy and applicationEntity.setContactInfo, keeping the
existing log that uses maskIdentifier and isCanonicalApplied; this mirrors the
consolidated pattern used in prepareDemographicEntityForCreate and avoids
overwriting values.

In
`@pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/ApplicationServiceTest.java`:
- Around line 105-134: The test currently stubs both
userDetailsService.findOrCreateByIdentifier and userDetailsService.matchesUser
with a deterministic UUID (getCanonicalUserIdString), which hides real
production behavior; update the setup so findOrCreateByIdentifier returns a
UserDetails whose userId is generated with UUID.randomUUID() (to mirror
production) and stop stubbing matchesUser (or replace the stub by using a spy on
UserDetailsService) so the real matchesUser logic is exercised; adjust
ApplicationServiceTest.setUp() to reflect these changes (modify the
Mockito.when(...) for findOrCreateByIdentifier and remove or convert the
Mockito.when(...) for matchesUser), or alternatively add an integration test
that does not mock UserDetailsService to validate the end-to-end UUID generation
and matching flow.

In
`@pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/service/UserDetailsService.java`:
- Around line 76-90: The encryptIdentifierIfConfigured method relies on
CryptoUtil.encrypt returning a Base64-encoded string as UTF-8 bytes, which makes
the current new String(byte[], UTF_8) safe but fragile; update the method
(encryptIdentifierIfConfigured) to add a clear inline comment above the
try-block documenting this dependency: note that CryptoUtil returns
Base64-encoded strings (UTF-8 bytes) and that if CryptoUtil changes to return
raw binary ciphertext this code must switch to explicit Base64
encoding/decoding, and apply the same comment to the corresponding
decrypt/round-trip code paths referenced in this class.

In
`@pre-registration/pre-registration-core/src/test/java/io/mosip/preregistration/core/common/service/UserDetailsServiceTest.java`:
- Around line 94-96: Replace the boolean equality check that uses
assertTrue("TestUser123".equals(decrypted.get())) with an assertEquals call so
failures show expected vs actual; keep the presence check
assertTrue(decrypted.isPresent()) and change the comparison to
assertEquals("TestUser123", decrypted.get()) in UserDetailsServiceTest
(reference: variable decrypted).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b9a8e103-a77f-42c8-adb8-1353609083df

📥 Commits

Reviewing files that changed from the base of the PR and between 9bc82f5 and b336166.

📒 Files selected for processing (20)
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/controller/LoginController.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/ApplicationService.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/AppointmentServiceImpl.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/DemographicService.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/LoginService.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/OTPManager.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/CommonServiceUtil.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DemographicServiceUtil.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DocumentServiceUtil.java
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/ApplicationServiceTest.java
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/AppointmentServiceImplTest.java
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/DemographicServiceTest.java
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/LoginServiceTest.java
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/OTPManagerTest.java
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/test/controller/LoginControllerTest.java
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/test/service/util/DocumentServiceUtilTest.java
  • pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/impl/ApplicationConsumedStatusUpdater.java
  • pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/service/UserDetailsService.java
  • pre-registration/pre-registration-core/src/test/java/io/mosip/preregistration/core/common/service/UserDetailsServiceTest.java
  • pre-registration/pre-registration-datasync-service/src/main/java/io/mosip/preregistration/datasync/service/util/DataSyncServiceUtil.java
🚧 Files skipped from review as they are similar to previous changes (5)
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/controller/LoginController.java
  • pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/impl/ApplicationConsumedStatusUpdater.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DocumentServiceUtil.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/DemographicService.java
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/DemographicServiceTest.java

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
pre-registration/pre-registration-core/src/test/java/io/mosip/preregistration/core/common/service/UserDetailsServiceTest.java (3)

124-136: Add verification that save() is not called to match test intent.

The test name indicates it should verify no repair occurs, but there's no assertion confirming that save() is never invoked on the repository.

♻️ Suggested improvement
+import static org.mockito.Mockito.never;

     `@Test`
     public void testResolveCanonicalUserIdReturnsExistingUuidWithoutRepairingRecord() {
         UserDetails mapped = new UserDetails();
         mapped.setUserId(UUID.randomUUID());
         mapped.setCrDtimes(LocalDateTime.now());
         mapped.setIdentifierEncrypted("");
         when(userDetailsRepository.findByIdentifierHash(any())).thenReturn(Optional.of(mapped));

         Optional<String> resolved = userDetailsService.resolveCanonicalUserId("TestUser");

         assertTrue(resolved.isPresent());
         assertEquals(mapped.getUserId().toString(), resolved.get());
+        verify(userDetailsRepository, never()).save(any());
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-core/src/test/java/io/mosip/preregistration/core/common/service/UserDetailsServiceTest.java`
around lines 124 - 136, The test
testResolveCanonicalUserIdReturnsExistingUuidWithoutRepairingRecord should
verify that no repair/save occurs: after stubbing
userDetailsRepository.findByIdentifierHash(...) to return the mapped UserDetails
and calling userDetailsService.resolveCanonicalUserId("TestUser"), add a Mockito
verification that userDetailsRepository.save(...) is never called
(verify(userDetailsRepository, never()).save(any())). Reference the test method
testResolveCanonicalUserIdReturnsExistingUuidWithoutRepairingRecord, the
userDetailsRepository.save method, and resolveCanonicalUserId to locate where to
insert the verification.

27-40: Consider using @ExtendWith(MockitoExtension.class) for proper resource management.

MockitoAnnotations.openMocks(this) returns an AutoCloseable that should be closed to avoid resource leaks. The JUnit 5 idiomatic approach is to use the Mockito extension instead.

♻️ Suggested refactor
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;

+@ExtendWith(MockitoExtension.class)
 public class UserDetailsServiceTest {

     `@Mock`
     private UserDetailsRepository userDetailsRepository;

     `@Mock`
     private CryptoUtil cryptoUtil;

     `@InjectMocks`
     private UserDetailsService userDetailsService;

-    public UserDetailsServiceTest() {
-        MockitoAnnotations.openMocks(this);
-    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-core/src/test/java/io/mosip/preregistration/core/common/service/UserDetailsServiceTest.java`
around lines 27 - 40, The test class UserDetailsServiceTest currently calls
MockitoAnnotations.openMocks(this) in its constructor which returns an
AutoCloseable that must be closed; replace this manual lifecycle management by
annotating the test class with `@ExtendWith`(MockitoExtension.class) and remove
the constructor/openMocks call so Mockito manages mocks automatically for fields
userDetailsRepository, cryptoUtil and the `@InjectMocks` userDetailsService.

95-95: Prefer assertEquals for better failure messages.

Using assertTrue(expected.equals(actual)) provides unhelpful failure messages. assertEquals shows both expected and actual values.

♻️ Suggested fix
-        assertTrue("TestUser123".equals(decrypted.get()));
+        assertEquals("TestUser123", decrypted.get());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-core/src/test/java/io/mosip/preregistration/core/common/service/UserDetailsServiceTest.java`
at line 95, Replace the brittle assertion in UserDetailsServiceTest that uses
assertTrue("TestUser123".equals(decrypted.get())) with an assertEquals call to
provide clearer failure messages; locate the assertion referencing the decrypted
variable in the test method and change it to assertEquals with "TestUser123" as
the expected value and decrypted.get() as the actual value (using the same test
class/method and import style already used in the file).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@pre-registration/pre-registration-core/src/test/java/io/mosip/preregistration/core/common/service/UserDetailsServiceTest.java`:
- Around line 54-60: The test testFindByIdentifierDelegatesToRepo currently
ignores the returned Optional res; update it to assert that res.isPresent() is
true and that res.get() equals the mocked UserDetails instance you set up.
Locate the test method testFindByIdentifierDelegatesToRepo and add assertions
after calling userDetailsService.findByIdentifier("TestUser") to check
Optional<UserDetails> res is present and that the contained object equals the
mock (the same instance created as UserDetails mock and returned by
userDetailsRepository.findByIdentifierHash(any())); keep the existing
verify(userDetailsRepository).findByIdentifierHash(any()) assertion.

---

Nitpick comments:
In
`@pre-registration/pre-registration-core/src/test/java/io/mosip/preregistration/core/common/service/UserDetailsServiceTest.java`:
- Around line 124-136: The test
testResolveCanonicalUserIdReturnsExistingUuidWithoutRepairingRecord should
verify that no repair/save occurs: after stubbing
userDetailsRepository.findByIdentifierHash(...) to return the mapped UserDetails
and calling userDetailsService.resolveCanonicalUserId("TestUser"), add a Mockito
verification that userDetailsRepository.save(...) is never called
(verify(userDetailsRepository, never()).save(any())). Reference the test method
testResolveCanonicalUserIdReturnsExistingUuidWithoutRepairingRecord, the
userDetailsRepository.save method, and resolveCanonicalUserId to locate where to
insert the verification.
- Around line 27-40: The test class UserDetailsServiceTest currently calls
MockitoAnnotations.openMocks(this) in its constructor which returns an
AutoCloseable that must be closed; replace this manual lifecycle management by
annotating the test class with `@ExtendWith`(MockitoExtension.class) and remove
the constructor/openMocks call so Mockito manages mocks automatically for fields
userDetailsRepository, cryptoUtil and the `@InjectMocks` userDetailsService.
- Line 95: Replace the brittle assertion in UserDetailsServiceTest that uses
assertTrue("TestUser123".equals(decrypted.get())) with an assertEquals call to
provide clearer failure messages; locate the assertion referencing the decrypted
variable in the test method and change it to assertEquals with "TestUser123" as
the expected value and decrypted.get() as the actual value (using the same test
class/method and import style already used in the file).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 06ba67bf-6a07-4ee8-b9e7-628730030712

📥 Commits

Reviewing files that changed from the base of the PR and between b336166 and 8f82a29.

📒 Files selected for processing (2)
  • pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/service/UserDetailsService.java
  • pre-registration/pre-registration-core/src/test/java/io/mosip/preregistration/core/common/service/UserDetailsServiceTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/service/UserDetailsService.java

@GOKULRAJ136

Copy link
Copy Markdown
Contributor Author
  1. Move common code like adding record to UserDetails table, creating a list of userIds based on compatibility mode etc to common Utils.
  2. From all places use this common code.
  3. There are too many needless IF conditions which can cause silent failures in code, review all and include only MUST conditions.
  • Resolved by centralizing the user handling logic into a common core utility/service, including user creation and compatibility-based userId resolution.

  • All relevant pre-registration and booking flows have been updated to reuse this shared implementation.

  • Additionally, redundant and unnecessary conditional checks have been removed, retaining only essential guard conditions with proper failure handling.

mohanachandran-s and others added 21 commits April 16, 2026 16:20
Signed-off-by: Mohanachandran S <165888272+mohanachandran-s@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: rajapandi1234 <138785181+rajapandi1234@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: rajapandi1234 <138785181+rajapandi1234@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: Mohanachandran S <165888272+mohanachandran-s@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: Sohan Kumar Dey <72375959+Sohandey@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: Sohan Kumar Dey <72375959+Sohandey@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: Sohan Kumar Dey <72375959+Sohandey@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: Sohan Kumar Dey <72375959+Sohandey@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: VSIVAKALYAN <103260988+VSIVAKALYAN@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: VSIVAKALYAN <103260988+VSIVAKALYAN@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: Nandhukumar <nandhukumare@gmail.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: Nandhukumar <nandhukumare@gmail.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: Nandhukumar <nandhukumare@gmail.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: Nandhukumar <nandhukumare@gmail.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
* MOSIP-39585 - Updated the apitest commons version

Signed-off-by: Mohanachandran S <165888272+mohanachandran-s@users.noreply.github.com>

* Update push-trigger.yml

Signed-off-by: Mohanachandran S <165888272+mohanachandran-s@users.noreply.github.com>

---------

Signed-off-by: Mohanachandran S <165888272+mohanachandran-s@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: dhanendra06 <dhanendra.tech@gmail.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: kameshsr <kameshsr1338@gmail.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: Rakshith B <79500257+Rakshithb1@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: Nandhukumar <nandhukumare@gmail.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
* MOSIP-36404 - Added admin role to GetPRIDByDate api

Signed-off-by: Mohanachandran S <mohanachandran.s@technoforte.co.in>

* MOSIP-39993 - Added the value mapping property file

Signed-off-by: Mohanachandran S <mohanachandran.s@technoforte.co.in>

---------

Signed-off-by: Mohanachandran S <mohanachandran.s@technoforte.co.in>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
* MOSIP-39719

Signed-off-by: Nandhukumar <nandhukumare@gmail.com>

* MOSIP-39047

Signed-off-by: Nandhukumar <nandhukumare@gmail.com>

* MOSIP-39047

Signed-off-by: Nandhukumar <nandhukumare@gmail.com>

* MOSIP-39047

Signed-off-by: Nandhukumar <nandhukumare@gmail.com>

* MOSIP-39047

Signed-off-by: Nandhukumar <nandhukumare@gmail.com>

* MOSIP-39047

Signed-off-by: Nandhukumar <nandhukumare@gmail.com>

---------

Signed-off-by: Nandhukumar <nandhukumare@gmail.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: Gokulraj C <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
@GOKULRAJ136

Copy link
Copy Markdown
Contributor Author

@coderabbitai review the new changes

@coderabbitai

coderabbitai Bot commented Apr 24, 2026

Copy link
Copy Markdown

@GOKULRAJ136: Sure! I'll review the new changes now.

✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
pre-registration/pre-registration-application-service/pom.xml (1)

19-24: ⚠️ Potential issue | 🟡 Minor

Sibling kernel/commons artifacts remain on 1.2.1-SNAPSHOT.

kernel-khazana, kernel-idobjectvalidator, kernel-templatemanager-velocity, kernel-qrcodegenerator-zxing, kernel-transliteration-icu4j, and kernel-pdfgenerator (line 167) are still 1.2.1-SNAPSHOT while the BOM is now 1.3.0. Please verify these artifacts are still published and binary-compatible with the 1.3.0 kernel's transitive dependency levels (Spring Boot 3.2.5, Jackson, Hibernate, etc.) to avoid runtime classpath issues. Root cause/verification script noted on pre-registration-core/pom.xml.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pre-registration/pre-registration-application-service/pom.xml` around lines
19 - 24, The Maven properties for sibling kernel artifacts
(kernel.khazana.version, kernel.idobjectvalidator.version,
kernel.templatemanager.velocity.version, kernel.qrcodegenerator.zxing.version,
kernel.transliteration.icu4j.version and the kernel-pdfgenerator property
referenced at line ~167) are still 1.2.1-SNAPSHOT while kernel.bom.version is
1.3.0; update these properties to the aligned 1.3.0 release (or to the correct
published version that matches kernel.bom.version) and/or verify those artifacts
are published and binary-compatible with the 1.3.0 BOM transitive deps (use the
verification script referenced in pre-registration-core/pom.xml) so the module
uses consistent kernel dependency versions.
pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/entity/RegistrationBookingEntityConsumed.java (1)

82-84: ⚠️ Potential issue | 🟡 Minor

Stale Javadoc on upBy.

The comment on line 82 still says /** Created by. */, but this field is the updated-by column (upd_by). While you updated the crBy Javadoc (line 74) to document the "legacy plaintext or user identifier" semantics, the corresponding upBy comment was missed and is also factually wrong.

📝 Proposed fix
-	/** Created by. */
+	/** Updated by (legacy plaintext or user identifier) */
 	`@Column`(name = "upd_by")
 	private String upBy;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/entity/RegistrationBookingEntityConsumed.java`
around lines 82 - 84, The Javadoc for the field upBy is stale and incorrectly
says "Created by."; update the comment above the field upBy (mapped to column
"upd_by") to describe it as the "updated-by" value and, like crBy, note it
stores the legacy plaintext or user identifier semantics; ensure the new Javadoc
mirrors the style and level of detail used for crBy so readers understand upBy
represents who last updated the record.
pre-registration/pre-registration-datasync-service/src/main/java/io/mosip/preregistration/datasync/service/DataSyncService.java (1)

182-194: ⚠️ Potential issue | 🟠 Major

Same auth-context NPE risk still present in all finally-block audit calls.

The PR correctly guards authUserDetails().getUserId() at Lines 438–449, but the finally blocks in retrieveAllPreRegIds (Lines 186/191), retrieveAllAppointmentsSyncV2 (Lines 252/257), getPreRegistrationData (Lines 343/347), fetchPreRegistrationData (Lines 405/409), and storeConsumedPreRegistrations (Lines 467/471) still dereference authUserDetails().getUserId()/getUsername() directly. If the security principal is absent (same scenario that motivated the fix), these calls will NPE from inside finally and mask the original exception captured in the catch, defeating DataSyncExceptionCatcher.handle(...).

Consider extracting a small helper and reusing it everywhere auth info is needed for audit:

🔧 Suggested helper + application
+	private String safeUserId() {
+		try {
+			AuthUserDetails u = authUserDetails();
+			return (u != null && u.getUserId() != null) ? u.getUserId() : "user";
+		} catch (Exception ex) {
+			return "user";
+		}
+	}
+
+	private String safeUserName() {
+		try {
+			AuthUserDetails u = authUserDetails();
+			return (u != null && u.getUsername() != null) ? u.getUsername() : "user";
+		} catch (Exception ex) {
+			return "user";
+		}
+	}

Then replace every authUserDetails().getUserId() / authUserDetails().getUsername() in the five finally blocks with safeUserId() / safeUserName(). This also removes the duplicated try/catch added in storeConsumedPreRegistrations (Lines 438–449) and centralizes the fallback behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-datasync-service/src/main/java/io/mosip/preregistration/datasync/service/DataSyncService.java`
around lines 182 - 194, Introduce a small safe accessor in DataSyncService
(e.g., safeUserId() and safeUserName()) that checks authUserDetails() for null
and returns a safe fallback (empty string or AuditLogVariables.NO_ID.toString())
instead of calling authUserDetails().getUserId()/getUsername() directly; then
replace all direct dereferences in the finally blocks of retrieveAllPreRegIds,
retrieveAllAppointmentsSyncV2, getPreRegistrationData, fetchPreRegistrationData,
and storeConsumedPreRegistrations with these helpers and remove the duplicated
try/catch added in storeConsumedPreRegistrations so finally-block audits cannot
NPE and the original exception from DataSyncExceptionCatcher.handle(...) is
preserved.
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DocumentServiceUtil.java (1)

184-194: ⚠️ Potential issue | 🟡 Minor

Redundant raw-userId assignment to crBy/updBy before it gets overwritten.

Lines 185-186 set crBy/updBy to the raw userId, then lines 193-194 overwrite them with effectiveUserId. The dead assignments are harmless today but:

  • They obscure intent and make it easy to accidentally reintroduce raw-PII persistence during a future refactor.
  • If resolveEffectiveUserId is ever short-circuited/early-returned, the raw value survives.
🔧 Proposed fix
 		documentEntity.setCrDtime(LocalDateTime.now(ZoneId.of("UTC")));
-		documentEntity.setCrBy(userId);
-		documentEntity.setUpdBy(userId);
 		documentEntity.setUpdDtime(LocalDateTime.now(ZoneId.of("UTC")));
 		String effectiveUserId = resolveEffectiveUserId(userId);
 		log.info(LOGGER_SESSIONID, LOGGER_IDTYPE, LOGGER_ID,
 				"Resolved effective user id for document write. preRegistrationId=" + preRegistrationId
 						+ ", maskedUserId=" + maskIdentifier(userId) + ", canonicalApplied="
 						+ isCanonicalApplied(userId, effectiveUserId));
 		documentEntity.setCrBy(effectiveUserId);
 		documentEntity.setUpdBy(effectiveUserId);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DocumentServiceUtil.java`
around lines 184 - 194, Remove the redundant assignments of raw userId to
documentEntity.crBy and documentEntity.updBy: call
resolveEffectiveUserId(userId) first, then set
documentEntity.setCrBy(effectiveUserId) and
documentEntity.setUpdBy(effectiveUserId); keep setting
documentEntity.setCrDtime(...) and setUpdDtime(...) as-is, and move the info log
that references maskIdentifier(userId) and isCanonicalApplied(userId,
effectiveUserId) to after resolveEffectiveUserId so the log and the final
crBy/updBy reflect the effectiveUserId; relevant symbols: DocumentServiceUtil,
resolveEffectiveUserId, documentEntity.setCrBy, documentEntity.setUpdBy,
maskIdentifier, isCanonicalApplied, LOGGER_SESSIONID/LOGGER_IDTYPE/LOGGER_ID.
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/CommonServiceUtil.java (1)

124-158: ⚠️ Potential issue | 🟡 Minor

Fix indentation and clarify access control semantics in the getDemographicData method.

The null-check block (lines 130–132) has misaligned indentation that obscures the code structure—it should align with the subsequent List<String> list = ... statement at line 133. Additionally, the else block at lines 152–155 throws UNABLE_TO_FETCH_THE_PRE_REGISTRATION when the caller lacks ROLE_INDIVIDUAL, but this error message is misleading since the record was successfully found and the failure is actually due to insufficient permissions. Compare this to other methods in the same file (e.g., line ~210) which perform the role check without an explicit else branch—consider whether this method should throw a role-based error (e.g., UNAUTHORIZED or INVALID_ROLE) instead of a retrieval error, or align its pattern with the rest of the codebase.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/CommonServiceUtil.java`
around lines 124 - 158, In getDemographicData, fix the misaligned null-check
block for demographicEntity so its if (demographicEntity == null) { ... } is
aligned with the subsequent List<String> list = listAuth(...) line, and replace
the misleading else branch that throws RecordNotFoundException when the caller
lacks ROLE_INDIVIDUAL with proper access-control handling: either throw an
authorization-specific exception (e.g., UNAUTHORIZED/INVALID_ROLE) or remove the
explicit else and follow the existing file pattern (perform role check via
listAuth(...) and let userValidation(...) enforce access), ensuring you
reference the demographicEntity null-check,
listAuth(authUserDetails().getAuthorities()), ROLE_INDIVIDUAL, and the current
RecordNotFoundException/HashingException code paths when making the change.
pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/ApplicationServiceTest.java (1)

403-413: ⚠️ Potential issue | 🟡 Minor

Stub the authenticated user in this negative-path test.

deleteLostOrUpdateApplication(...) now calls userDetailsService.matchesUser(authUserDetails().getUserId(), ...). In this test, applicationUser.getUserId() is never stubbed, so the mocked matchesUser(...) lambda can hit a null authUserId before the service throws PreIdInvalidForUserIdException.

💡 Minimal fix
 		SecurityContext securityContext = Mockito.mock(SecurityContext.class);
 		Mockito.when(securityContext.getAuthentication()).thenReturn(authentication);
 		SecurityContextHolder.setContext(securityContext);
 		Mockito.when(SecurityContextHolder.getContext().getAuthentication().getPrincipal()).thenReturn(applicationUser);
+		Mockito.when(applicationUser.getUserId()).thenReturn("1234");
 		Mockito.when(serviceUtil.findApplicationById(Mockito.any())).thenReturn(applicationEntity);
 		Mockito.when(validationUtil.requstParamValidator(Mockito.any())).thenReturn(true);
 		assertNotNull(applicationService.deleteLostOrUpdateApplication(applicationId,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/ApplicationServiceTest.java`
around lines 403 - 413, The test fails because deleteLostOrUpdateApplication
calls userDetailsService.matchesUser(authUserDetails().getUserId(), ...) but the
mocked AuthUserDetails applicationUser has no getUserId() stub and can be null;
to fix, stub applicationUser.getUserId() to return a non-null id (e.g., the same
user id on applicationEntity) before invoking
applicationService.deleteLostOrUpdateApplication so
userDetailsService.matchesUser receives a valid authUserId; keep the existing
SecurityContextHolder principal setup and ensure the stub is applied to the
mocked AuthUserDetails instance used in the test.
🧹 Nitpick comments (18)
pre-registration/pre-registration-application-service/pom.xml (2)

95-99: Drop the explicit kernel-core version or use the property.

Since kernel-bom is imported in dependencyManagement, kernel-core's version should come from the BOM automatically. Hardcoding 1.3.0 here duplicates the BOM version and will silently drift the next time the BOM is bumped. The parent POM already defines ${kernel.core.version} if you prefer an explicit reference.

♻️ Proposed change
 		<dependency>
 			<groupId>io.mosip.kernel</groupId>
 			<artifactId>kernel-core</artifactId>
-			<version>1.3.0</version>
 		</dependency>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pre-registration/pre-registration-application-service/pom.xml` around lines
95 - 99, The POM currently hardcodes the kernel-core version in the dependency
block (groupId io.mosip.kernel, artifactId kernel-core,
<version>1.3.0</version>); remove the explicit <version> element so the version
is resolved from the imported kernel-bom in dependencyManagement, or replace the
hardcoded value with the parent property ${kernel.core.version} to keep it
synced with the BOM.

245-249: Same hardcoded-version concern for kernel-auth-adapter.

Prefer ${kernel.bom.version} (or a dedicated property) over a literal 1.3.0, to keep the kernel version set managed from a single place.

♻️ Proposed change
 				<dependency>
 					<groupId>io.mosip.kernel</groupId>
 					<artifactId>kernel-auth-adapter</artifactId>
-					<version>1.3.0</version>
+					<version>${kernel.bom.version}</version>
 				</dependency>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pre-registration/pre-registration-application-service/pom.xml` around lines
245 - 249, The dependency for artifactId "kernel-auth-adapter" uses a hardcoded
version "1.3.0"; change it to use the shared property (e.g. replace the
<version>1.3.0</version> with <version>${kernel.bom.version}</version> or a
dedicated property) so the kernel version is managed centrally; update any
existing properties block if needed to define kernel.bom.version and ensure the
dependency block in the POM references that property instead of the literal.
pre-registration/pre-registration-datasync-service/pom.xml (1)

169-169: Avoid hardcoding 1.3.0; reuse a property.

Hardcoding the kernel release version here is prone to drift when the BOM is bumped. Prefer ${kernel.bom.version} (or a dedicated kernel.auth.adapter.version property) so all kernel versions stay aligned from a single source.

♻️ Proposed change
 				<dependency>
 					<groupId>io.mosip.kernel</groupId>
 					<artifactId>kernel-auth-adapter</artifactId>
-					<version>1.3.0</version>
+					<version>${kernel.bom.version}</version>
 				</dependency>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pre-registration/pre-registration-datasync-service/pom.xml` at line 169,
Replace the hardcoded <version>1.3.0</version> in the pom entry with a property
reference so the kernel adapter version follows the BOM; update the <version>
element to use either ${kernel.bom.version} or a new property
${kernel.auth.adapter.version} (and add that property to the project properties
if creating a new one) so the adapter version stays aligned with the BOM and can
be centrally managed; locate the <version> element inside the dependency or
plugin declaration that currently contains "1.3.0" to make the change.
pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/entity/DocumentEntity.java (1)

120-127: Method placement splits the field declaration block.

getEffectiveCrBy()/getEffectiveUpdBy() are injected between the updDtime field (line 118) and the encryptedDateTime field (line 132), which breaks the field-grouping convention in this entity. Move them to the end of the class, after all field declarations, for readability.

Also, because @Data already generates getCrBy()/getUpdBy(), these methods are pure aliases. That's fine as a semantic marker for the canonical-user migration, but a one-line comment noting "pass-through; canonical resolution handled in service layer" would help future maintainers avoid duplicating or "enhancing" this getter with DB lookups.

♻️ Suggested relocation
 	`@Column`(name = "upd_dtimes")
 	private LocalDateTime updDtime;
 
-	public String getEffectiveCrBy() {
-		return this.crBy;
-	}
-
-	public String getEffectiveUpdBy() {
-		return this.updBy;
-	}
-
 	/**
 	 * Encrypted Date Time
 	 */
@@
 	`@Column`(name = "doc_ref_id")
 	private String refNumber;
+
+	public String getEffectiveCrBy() {
+		return this.crBy;
+	}
+
+	public String getEffectiveUpdBy() {
+		return this.updBy;
+	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/entity/DocumentEntity.java`
around lines 120 - 127, The two methods getEffectiveCrBy() and
getEffectiveUpdBy() are interrupting the field declaration block; relocate both
methods to the end of the class after all field declarations (move them below
encryptedDateTime and any remaining fields) so fields remain grouped; keep them
as simple pass-through aliases to the Lombok-generated getCrBy()/getUpdBy(), and
add a one-line comment above each method like "pass-through; canonical
resolution handled in service layer" to clarify they are intentionally thin
wrappers.
pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/entity/AvailibityEntity.java (1)

90-96: Move new methods below field declarations; consider documenting the pass-through semantics.

Placing getEffectiveCrBy()/getEffectiveUpdBy() between the upBy/updDate fields and the isDeleted/delTime fields splits the field block and hurts readability. Please relocate them to the bottom of the class (after all @Column-annotated fields).

Also note: since Lombok @Getter already generates getCrBy()/getUpBy(), these new methods are currently pure aliases. If the intent is ever to resolve a canonical UUID through a resolver (as the Javadoc "returns canonical user id if present else legacy value" wording used in ApplicationEntity suggests), that logic belongs in the user/service layer rather than the JPA entity. Keeping the stub here is fine for backward-compat, but a brief inline comment clarifying "pass-through; canonicalization handled in UserDetailsService" would help future maintainers.

♻️ Suggested relocation
 	/** Updated date time. */
 	`@Column`(name = "upd_dtimes")
 	private LocalDateTime updDate;
 
-	public String getEffectiveCrBy() {
-		return this.crBy;
-	}
-
-	public String getEffectiveUpdBy() {
-		return this.upBy;
-	} 
-
 	/** Is deleted. */
 	`@Column`(name = "is_deleted")
 	private boolean isDeleted;
 
 	/** Delete date time. */
 	`@Column`(name = "del_dtimes")
 	private LocalDateTime delTime;
 
+	public String getEffectiveCrBy() {
+		return this.crBy;
+	}
+
+	public String getEffectiveUpdBy() {
+		return this.upBy;
+	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/entity/AvailibityEntity.java`
around lines 90 - 96, The two methods getEffectiveCrBy() and getEffectiveUpdBy()
are interrupting the field block (between upBy/updDate and isDeleted/delTime)
and are mere pass-through aliases to Lombok-generated getCrBy()/getUpBy(); move
these methods to the bottom of the class after all `@Column-annotated` fields to
keep field declarations contiguous, and add a brief inline comment above each
method stating that they are pass-through stubs (e.g., "pass-through;
canonicalization handled in UserDetailsService") so future maintainers know
canonical ID resolution happens in the user/service layer rather than the JPA
entity.
pre-registration/pre-registration-application-service/src/main/resources/application.properties (1)

216-216: Consider relocating the property for better grouping.

mosip.prereg.pii.backward.compatibility is currently inserted in the middle of the mosip.preregistration.booking.* block. Moving it next to other mosip.prereg.* / PII-related settings (or into a dedicated section) would improve readability. Purely cosmetic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/resources/application.properties`
at line 216, The property mosip.prereg.pii.backward.compatibility is placed
inside the mosip.preregistration.booking.* block; move this property out of that
booking block and relocate it next to other mosip.prereg.* or PII-related
settings (or create a dedicated PII section) so it is grouped logically with
related PII configuration keys for improved readability and maintenance.
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/controller/LoginController.java (1)

187-188: Minor optimization: use log.isDebugEnabled() to avoid unnecessary masking work.

Good use of maskIdentifier() to protect the user ID in debug logs. One optimization worth considering:

The masking computation runs unconditionally, even when debug logging is disabled. Wrap the log statement with log.isDebugEnabled() to skip the work on hot paths where debug level is not active.

(Note: maskIdentifier() is null-safe and already handles null/blank values gracefully, and the @Validated annotation on the method parameter ensures the request DTO structure is validated before method entry, so no additional null-checks are needed.)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/controller/LoginController.java`
around lines 187 - 188, The debug log in LoginController currently always calls
userDetailsService.maskIdentifier(userIdOtpRequest.getRequest().getUserId()),
causing unnecessary work when debug logging is off; wrap that log invocation in
a log.isDebugEnabled() check so the masking is only performed when debug is
enabled, referencing the existing log, maskIdentifier, userDetailsService, and
userIdOtpRequest symbols.
pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/entity/RegistrationBookingEntity.java (1)

91-97: getEffectiveCrBy/getEffectiveUpdBy are currently indistinguishable from the Lombok-generated getters.

Because the class is annotated with @Data, getCrBy()/getUpBy() already exist and return the exact same value. If the intent is to later branch on the PII backward-compatibility flag (e.g., resolve a UUID to a masked/plaintext identifier depending on mode), that logic is missing here — the methods are currently pure pass-throughs and add no behavior over getCrBy()/getUpBy(). The same pattern is repeated in DemographicEntity and InterfaceDataSyncEntity.

Please confirm whether these accessors are intentional intent-documenting facades (in which case a brief Javadoc explaining why callers should prefer them would help), or whether the resolution/masking logic was meant to live here and was inadvertently omitted.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/entity/RegistrationBookingEntity.java`
around lines 91 - 97, The getEffectiveCrBy and getEffectiveUpdBy methods in
RegistrationBookingEntity are no-ops duplicating Lombok getters; either document
them as intentional facades or implement the missing PII-resolution logic.
Update RegistrationBookingEntity (and mirror changes in DemographicEntity and
InterfaceDataSyncEntity) to either 1) add a brief Javadoc to
getEffectiveCrBy/getEffectiveUpdBy stating callers should use these facades (so
they can change behavior later), or 2) implement the intended logic: check the
PII backward-compatibility flag or service (e.g., the existing
configuration/method used elsewhere like isPiiBackwardCompatibilityEnabled() or
a PiiResolver service) and return either the raw creator/updater id or the
masked/resolved identifier accordingly; ensure you reference and reuse the same
flag/service used elsewhere in the codebase so behavior is consistent.
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DocumentServiceUtil.java (1)

109-110: Consider a default for piiBackwardCompatibility.

@Value("${mosip.prereg.pii.backward.compatibility}") without a default will fail context startup if the property is missing from any environment. Adding a safe default (e.g., :false) makes new deployments opt-in to backward compatibility explicitly.

-	`@Value`("${mosip.prereg.pii.backward.compatibility}")
+	`@Value`("${mosip.prereg.pii.backward.compatibility:false}")
 	private boolean piiBackwardCompatibility;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DocumentServiceUtil.java`
around lines 109 - 110, The field piiBackwardCompatibility in
DocumentServiceUtil uses `@Value`("${mosip.prereg.pii.backward.compatibility}")
with no default which will break context startup if the property is absent;
update the annotation to supply a safe default (for example change to
`@Value`("${mosip.prereg.pii.backward.compatibility:false}") or equivalent) so the
boolean defaults to false when the property is missing and deployments only opt
into backward compatibility explicitly.
pre-registration/pre-registration-datasync-service/src/main/java/io/mosip/preregistration/datasync/entity/ProcessedPreRegEntity.java (1)

89-108: Move the getEffective* methods below the field declarations.

isDeleted and delTime fields (lines 101-108) are declared after the new getEffectiveCrBy/getEffectiveUpdBy methods, splitting field declarations with method bodies. Relocate the two methods to follow all field declarations for cleaner structure. This also matches how Lombok typically expects class layout when used with @Getter/@Setter.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-datasync-service/src/main/java/io/mosip/preregistration/datasync/entity/ProcessedPreRegEntity.java`
around lines 89 - 108, In ProcessedPreRegEntity move the two accessor methods
getEffectiveCrBy and getEffectiveUpdBy so they appear after all field
declarations (including isDeleted and delTime) instead of between fields; locate
the methods named getEffectiveCrBy and getEffectiveUpdBy and cut/paste them to
below the field declarations (e.g., after the delTime field) to keep fields
grouped together and match the expected class layout.
pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/DocumentServiceTest.java (1)

164-180: Inconsistent indentation in setUp.

Line 164 starts at column 0 while lines 165-180 use tab indentation. Not functionally wrong, but inconsistent with the rest of the method and will cause diff noise. Consider reindenting this block to match the surrounding code.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/DocumentServiceTest.java`
around lines 164 - 180, The DocumentEntity setup block is mis-indented (line
starting with documentEntity = ... is at column 0) causing inconsistent
formatting; open the setUp method and reindent the entire block that initializes
documentEntity (all calls to documentEntity.set...) to match the surrounding
method indentation style (use the same tabs/spaces as other lines in setUp) so
the formatter/linters and diffs remain clean; ensure you update only whitespace
so no logic changes in the DocumentEntity initialization occur.
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/repository/RegAppointmentRepository.java (1)

13-14: Inconsistent parameter binding: positional ?1 mixed with @Param("preRegId").

The JPQL uses positional binding ?1 while the method argument is annotated with @Param("preRegId"). With positional binding, @Param is effectively unused. Pick one style:

🔧 Option A — named binding (recommended with `@Param`)
-	`@Query`("SELECT e FROM RegistrationBookingEntity e WHERE e.preregistrationId = ?1")
-	RegistrationBookingEntity getRegistrationAppointmentByPreRegistrationId(`@Param`("preRegId") String preRegId);
+	`@Query`("SELECT e FROM RegistrationBookingEntity e WHERE e.preregistrationId = :preRegId")
+	RegistrationBookingEntity getRegistrationAppointmentByPreRegistrationId(`@Param`("preRegId") String preRegId);
🔧 Option B — positional binding (drop `@Param`)
-	RegistrationBookingEntity getRegistrationAppointmentByPreRegistrationId(`@Param`("preRegId") String preRegId);
+	RegistrationBookingEntity getRegistrationAppointmentByPreRegistrationId(String preRegId);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/repository/RegAppointmentRepository.java`
around lines 13 - 14, The `@Query` on
RegistrationBookingEntity/getRegistrationAppointmentByPreRegistrationId mixes
positional binding (?1) with a named `@Param`("preRegId"); fix by using a single
style—recommended: change the JPQL to use a named parameter (:preRegId) and keep
the `@Param`("preRegId") on the method parameter (or alternatively remove `@Param`
and the `@Query` should remain positional); update the `@Query` string to "SELECT e
FROM RegistrationBookingEntity e WHERE e.preregistrationId = :preRegId" ensuring
the method parameter name and `@Param` match.
pre-registration/pre-registration-datasync-service/src/main/java/io/mosip/preregistration/datasync/service/util/DataSyncServiceUtil.java (1)

927-927: Redundant Optional != null check.

By contract, Optional itself should never be null. If UserDetailsService.resolveUserUuid returns null, that is a stronger API smell to address at the source. Either trust the Optional contract (drop the null check) or treat null explicitly like empty — currently the mix is misleading.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-datasync-service/src/main/java/io/mosip/preregistration/datasync/service/util/DataSyncServiceUtil.java`
at line 927, The if condition mixes a redundant null check with an Optional;
remove the null check and rely on the Optional contract: change the branch that
currently checks "userUuid != null && userUuid.isPresent()" to only check
"userUuid.isPresent()" (or better use userUuid.ifPresent(...) or
userUuid.orElse(...) as appropriate) where userUuid is the result from
UserDetailsService.resolveUserUuid in DataSyncServiceUtil; this eliminates the
misleading null handling and keeps Optional semantics consistent.
pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/AppointmentServiceImplTest.java (1)

237-239: Avoid raw (Collection) cast — produces unchecked-conversion warnings across all affected tests.

The cast (Collection) authorities (repeated at lines 284, 334, 378, 422) drops the type parameter on AuthUserDetails.getAuthorities(). Use doReturn or an explicit parameterized type to avoid unchecked warnings:

🔧 Option — use `doReturn` to sidestep the wildcard variance issue
-		List<GrantedAuthority> authorities = new ArrayList<>();
-		authorities.add(new SimpleGrantedAuthority("ROLE_INDIVIDUAL"));
-		when(applicationUser.getAuthorities()).thenReturn((Collection) authorities);
+		List<GrantedAuthority> authorities = new ArrayList<>();
+		authorities.add(new SimpleGrantedAuthority("ROLE_INDIVIDUAL"));
+		Mockito.doReturn(authorities).when(applicationUser).getAuthorities();

Applies to lines 239, 284, 334, 378, 422.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/AppointmentServiceImplTest.java`
around lines 237 - 239, The tests use an unchecked raw cast when stubbing
applicationUser.getAuthorities() (e.g.,
when(applicationUser.getAuthorities()).thenReturn((Collection) authorities))
which triggers warnings; replace those stubs by using
doReturn(authorities).when(applicationUser).getAuthorities() (or return a
properly parameterized Collection<GrantedAuthority>) for each occurrence (the
stubs around applicationUser.getAuthorities() in AppointmentServiceImplTest) to
eliminate the unchecked-conversion warnings.
pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/ApplicationIdentityMigrationServiceTest.java (2)

14-14: Redundant self-package import.

ApplicationIdentityMigrationService is in the same package (io.mosip.preregistration.application.service) as this test class, so the explicit import on Line 14 is unnecessary and can be removed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/ApplicationIdentityMigrationServiceTest.java`
at line 14, Remove the redundant self-package import of
ApplicationIdentityMigrationService from the test; since
ApplicationIdentityMigrationService is declared in the same package
io.mosip.preregistration.application.service as
ApplicationIdentityMigrationServiceTest, delete the import statement referencing
ApplicationIdentityMigrationService to avoid an unnecessary import.

118-128: Add coverage for the backward-compatibility branch of resolveEffectiveUserId.

resolveEffectiveUserIdUsesCanonicalUuidInStrictMode only exercises piiBackwardCompatibility = false. Please add a companion test that sets the flag to true and verifies that resolveEffectiveUserId delegates to userDetailsService.resolveUserUuidOrIdentifier(...) (and does not hard-fail when resolveUserUuid returns Optional.empty()), so both configured modes are regression-protected.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/ApplicationIdentityMigrationServiceTest.java`
around lines 118 - 128, Add a new test in
ApplicationIdentityMigrationServiceTest that enables the backward-compatibility
mode on the ApplicationIdentityMigrationService (set piiBackwardCompatibility =
true via the existing setter/constructor/config used in tests), mock
userDetailsService.resolveUserUuid(userId) to return Optional.empty(), mock
userDetailsService.resolveUserUuidOrIdentifier(userId) to return a fallback
identifier, call
applicationIdentityMigrationService.resolveEffectiveUserId(userId), assert the
returned value equals the fallback identifier, and verify that
userDetailsService.resolveUserUuidOrIdentifier(...) was invoked (and the flow
did not hard-fail when resolveUserUuid returned Optional.empty()).
pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/AppointmentServiceImpl.java (1)

480-487: Initial raw-id assignment on Line 480 is dead code; drop it to avoid confusion.

applicationEntity.setUpdBy(authUserDetails().getUserId()) at Line 480 is unconditionally overwritten by applicationEntity.setUpdBy(resolveEffectiveUserId(...)) at Line 483 before the entity is saved. Keeping the raw assignment is misleading (it implies a fallback that never applies) and was presumably left over from a prior iteration. Also, similar to DemographicService, a failure inside migrateRawUserToEffectiveUser at Line 485–486 will now throw AppointmentExecption even though the ApplicationEntity has already been persisted — consider whether migration errors should invalidate the whole appointment update, or be isolated/logged.

♻️ Proposed cleanup
-		applicationEntity.setUpdBy(authUserDetails().getUserId());
 		applicationEntity.setCrDtime(LocalDateTime.now(ZoneId.of("UTC")));
 		try {
 			applicationEntity.setUpdBy(resolveEffectiveUserId(authUserDetails().getUserId()));
 			ApplicationEntity savedApplicationEntity = applicationRepostiory.save(applicationEntity);
 			applicationIdentityMigrationService.migrateRawUserToEffectiveUser(preRegistrationId,
 					savedApplicationEntity.getEffectiveUpdBy());
 			return savedApplicationEntity;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/AppointmentServiceImpl.java`
around lines 480 - 487, Remove the dead initial assignment
applicationEntity.setUpdBy(authUserDetails().getUserId()) since it is
immediately overwritten by
applicationEntity.setUpdBy(resolveEffectiveUserId(...)) before save; instead
keep only the resolveEffectiveUserId call prior to applicationRepostiory.save.
Additionally, wrap the call to
applicationIdentityMigrationService.migrateRawUserToEffectiveUser(preRegistrationId,
savedApplicationEntity.getEffectiveUpdBy()) in a try/catch so that migration
failures do not abort a successfully persisted ApplicationEntity—log the
exception (using the existing logger) and decide whether to retry or surface a
non-fatal alert rather than throwing AppointmentExecption, mirroring the
non-fatal handling used in DemographicService.
pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/impl/ApplicationConsumedStatusUpdater.java (1)

142-214: Consider caching per-item owner UUID resolutions to reduce repository round-trips.

resolveUserUuid is invoked per record/document for getCrAppuserId(), getEffectiveCreatedBy(), and getEffectiveCrBy() on each demoEntity/applicantDoc/regAppointmentObj. In practice, these are typically the same end-user identifier for a given pre-reg item, so you may be performing 2-4× redundant lookups per item. Since auditUserId is already resolved once, you can similarly memoize the per-item creator within updateConsumedStatus (e.g., resolve demoEntity.getCrAppuserId() / getEffectiveCreatedBy() once before calling the helpers and pass the resolved value, falling back to per-doc resolution only when the raw creator differs). Not a blocker.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/impl/ApplicationConsumedStatusUpdater.java`
around lines 142 - 214, The code repeatedly calls resolveUserUuid per-item
inside addApplicantDemographicConsumed, addApplicantDocumentConsumed, and
addRegistrationAppointmentConsumed causing redundant repository lookups; modify
updateConsumedStatus to resolve the item's creator once (resolve
demoEntity.getCrAppuserId(), demoEntity.getEffectiveCreatedBy(), and
regAppointmentObj.getEffectiveCrBy()/applicantDoc.getEffectiveCrBy() as
applicable) store them in a local map or variables, and pass the resolved
creator UUIDs into the helper methods (addApplicantDemographicConsumed,
addApplicantDocumentConsumed, addRegistrationAppointmentConsumed) as additional
parameters; inside those methods use the passed resolved values (falling back to
calling resolveUserUuid only if the passed value is null or different) and
remove the per-field resolveUserUuid calls to eliminate duplicate lookups.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7c186525-90ee-437c-965c-49fcd2c0c201

📥 Commits

Reviewing files that changed from the base of the PR and between 98573c3 and dd99715.

📒 Files selected for processing (53)
  • db_scripts/mosip_prereg/ddl.sql
  • db_scripts/mosip_prereg/ddl/user_details.sql
  • pre-registration/pom.xml
  • pre-registration/pre-registration-application-service/pom.xml
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/controller/LoginController.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/repository/ApplicationRepostiory.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/repository/DemographicRepository.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/repository/RegAppointmentRepository.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/ApplicationIdentityMigrationService.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/ApplicationService.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/AppointmentServiceImpl.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/DemographicService.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/DocumentService.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/LoginService.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/OTPManager.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/CommonServiceUtil.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DemographicServiceUtil.java
  • pre-registration/pre-registration-application-service/src/main/java/io/mosip/preregistration/application/service/util/DocumentServiceUtil.java
  • pre-registration/pre-registration-application-service/src/main/resources/application.properties
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/ApplicationIdentityMigrationServiceTest.java
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/ApplicationServiceTest.java
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/AppointmentServiceImplTest.java
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/DemographicServiceTest.java
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/DocumentServiceTest.java
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/LoginServiceTest.java
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/service/OTPManagerTest.java
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/test/controller/LoginControllerTest.java
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/test/service/AppointmentServiceTest.java
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/test/service/util/DemographicServiceUtilTest.java
  • pre-registration/pre-registration-application-service/src/test/java/io/mosip/preregistration/application/test/service/util/DocumentServiceUtilTest.java
  • pre-registration/pre-registration-application-service/src/test/resources/application.properties
  • pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/entity/AvailibityEntity.java
  • pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/entity/RegistrationBookingEntityConsumed.java
  • pre-registration/pre-registration-batchjob/src/main/java/io/mosip/preregistration/batchjob/impl/ApplicationConsumedStatusUpdater.java
  • pre-registration/pre-registration-core/pom.xml
  • pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/entity/ApplicationEntity.java
  • pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/entity/DemographicEntity.java
  • pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/entity/DocumentEntity.java
  • pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/entity/RegistrationBookingEntity.java
  • pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/entity/UserDetails.java
  • pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/repository/UserDetailsRepository.java
  • pre-registration/pre-registration-core/src/main/java/io/mosip/preregistration/core/common/service/UserDetailsService.java
  • pre-registration/pre-registration-core/src/test/java/io/mosip/preregistration/core/common/service/UserDetailsServiceTest.java
  • pre-registration/pre-registration-datasync-service/pom.xml
  • pre-registration/pre-registration-datasync-service/src/main/java/io/mosip/preregistration/datasync/entity/InterfaceDataSyncEntity.java
  • pre-registration/pre-registration-datasync-service/src/main/java/io/mosip/preregistration/datasync/entity/ProcessedPreRegEntity.java
  • pre-registration/pre-registration-datasync-service/src/main/java/io/mosip/preregistration/datasync/service/DataSyncService.java
  • pre-registration/pre-registration-datasync-service/src/main/java/io/mosip/preregistration/datasync/service/util/DataSyncServiceUtil.java
  • pre-registration/pre-registration-datasync-service/src/main/resources/application.properties
  • pre-registration/pre-registration-datasync-service/src/test/java/io/mosip/preregistration/datasync/test/config/TestSecurityConfig.java
  • pre-registration/pre-registration-datasync-service/src/test/java/io/mosip/preregistration/datasync/test/service/DataSyncServiceTest.java
  • pre-registration/pre-registration-datasync-service/src/test/java/io/mosip/preregistration/datasync/test/service/util/DataSyncServiceUtilTest.java
  • pre-registration/pre-registration-datasync-service/src/test/resources/application.properties

Comment thread db_scripts/mosip_prereg/ddl.sql
Comment thread db_scripts/mosip_prereg/ddl/user_details.sql Outdated
Comment thread pre-registration/pre-registration-datasync-service/pom.xml Outdated
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
…harden UserLookupException handling across services

Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
@GOKULRAJ136 GOKULRAJ136 marked this pull request as ready for review June 1, 2026 08:01
@GOKULRAJ136 GOKULRAJ136 marked this pull request as draft June 5, 2026 05:44
Signed-off-by: Gokulraj C <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: Gokulraj C <110164849+GOKULRAJ136@users.noreply.github.com>
Signed-off-by: GOKULRAJ136 <110164849+GOKULRAJ136@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.