{"record":{"id":"8a4f92ad293da0a3","repo":"OtterMind/Chat2DB","slug":"common-needloggedin","errorCode":"common.needLoggedIn","errorMessage":"common.needLoggedIn","messagePattern":"common\\.needLoggedIn","errorType":"exception","errorClass":"NeedLoggedInBusinessException","httpStatus":401,"severity":"error","filePath":"chat2db-community-server/chat2db-community-domain/chat2db-community-domain-core/src/main/java/ai/chat2db/community/domain/core/impl/db/DbWorkspaceDataSourceServiceImpl.java","lineNumber":207,"sourceCode":"        } catch (BeanInstantiationException exception) {\n            log.debug(\"Cannot preserve workspace datasource subtype {}.\", dataSource.getClass().getName(), exception);\n            copy = new WorkspaceDataSource();\n        }\n        BeanUtils.copyProperties(dataSource, copy);\n        return copy;\n    }\n\n    private void decryptSensitiveFields(WorkspaceDataSource dataSource) {\n        if (dataSource == null) {\n            return;\n        }\n        if (\"LOCAL\".equalsIgnoreCase(dataSource.getStorageType()) || ConfigUtils.isLocalPersistence()) {\n            dataSource.setPassword(decryptString(dataSource.getPassword()));\n            return;\n        }\n        Context context = ContextUtils.queryContext();\n        if (context == null || context.getOrganizationToken() == null) {\n            throw new NeedLoggedInBusinessException();\n        }\n        PrivateKey privateKey = stringToPrivateKey(context.getOrganizationToken());\n        if (StringUtils.isNotBlank(dataSource.getPassword())) {\n            dataSource.setPassword(decryptToken(dataSource.getPassword(), privateKey));\n        }\n        if (StringUtils.isNotBlank(dataSource.getHost())) {\n            dataSource.setHost(decryptToken(dataSource.getHost(), privateKey));\n        }\n        if (StringUtils.isNotBlank(dataSource.getUrl())) {\n            dataSource.setUrl(decryptToken(dataSource.getUrl(), privateKey));\n        }\n        if (StringUtils.isNotBlank(dataSource.getUser())) {\n            dataSource.setUser(decryptToken(dataSource.getUser(), privateKey));\n        }\n    }\n\n    private PrivateKey stringToPrivateKey(String privateKeyString) {\n        try {","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/OtterMind/Chat2DB/blob/5ee1e990e73fbcae1969dc554be254fedb3ab888/chat2db-community-server/chat2db-community-domain/chat2db-community-domain-core/src/main/java/ai/chat2db/community/domain/core/impl/db/DbWorkspaceDataSourceServiceImpl.java#L189-L225","documentation":"NeedLoggedInBusinessException ('common.needLoggedIn') is raised in decryptSensitiveFields when a non-local datasource needs RSA decryption but the request Context is missing or has no organizationToken. Without the org token the private key needed to decrypt host/url/user/password cannot be derived.","triggerScenarios":"Reading/decoding a CLOUD-stored datasource (storageType != 'LOCAL' and not ConfigUtils.isLocalPersistence()) on a request whose ContextUtils.queryContext() returns null or context.getOrganizationToken() is null — typically an unauthenticated or session-expired API call.","commonSituations":"Session expired mid-use; API call missing the auth header that populates the context; misconfigured environment where local-persistence flag is off but no org-token provider is wired; exportDisplayDataSources called without a logged-in user.","solutions":["Ensure the client re-authenticates so a valid organization token populates the request context before listing/exporting datasources.","Verify the auth filter/interceptor that sets Context.organizationToken is registered and runs before this service.","If this deployment stores datasources locally, confirm storageType is 'LOCAL' or ConfigUtils.isLocalPersistence() is true so the RSA path is skipped."],"exampleFix":"// before: caller does not ensure auth\nList<WorkspaceDataSource> out = svc.exportDisplayDataSources(ids);\n// after: caller guarantees a logged-in context first\nif (ContextUtils.queryContext() == null || ContextUtils.queryContext().getOrganizationToken() == null) {\n    throw new NeedLoggedInBusinessException();\n}\nList<WorkspaceDataSource> out = svc.exportDisplayDataSources(ids);\n","handlingStrategy":"validation","validationCode":"Context ctx = ContextUtils.queryContext();\nboolean needsOrgToken = !(\"LOCAL\".equalsIgnoreCase(ds.getStorageType()) || ConfigUtils.isLocalPersistence());\nif (needsOrgToken && (ctx == null || ctx.getOrganizationToken() == null)) {\n    throw new NeedLoggedInBusinessException();\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure the auth interceptor populates Context.organizationToken before any datasource decrypt path.","Mark locally-persisted datasources with storageType LOCAL to avoid the RSA path.","Re-authenticate on session expiry before export/list calls."],"tags":["auth","session","encryption","datasource"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}