From e9ee8cfcd639d2b4ab3ba5b43f60c10f4fba3d99 Mon Sep 17 00:00:00 2001 From: Vivek Singh Date: Fri, 6 Oct 2023 15:32:08 +0530 Subject: [PATCH 1/5] avniproject/avni-server#621 - model version change for onMigration instead of migration function. --- packages/openchs-android/package-lock.json | 14 +++++++------- packages/openchs-android/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/openchs-android/package-lock.json b/packages/openchs-android/package-lock.json index 4ce54f1f3..7a43ab641 100644 --- a/packages/openchs-android/package-lock.json +++ b/packages/openchs-android/package-lock.json @@ -33,7 +33,7 @@ "lodash": "4.17.21", "moment": "2.29.4", "native-base": "3.4.9", - "openchs-models": "1.30.71", + "openchs-models": "1.30.72", "prop-types": "15.8.1", "react": "18.2.0", "react-native": "0.72.3", @@ -16431,9 +16431,9 @@ } }, "node_modules/openchs-models": { - "version": "1.30.71", - "resolved": "https://registry.npmjs.org/openchs-models/-/openchs-models-1.30.71.tgz", - "integrity": "sha512-ggakdscwcuB6RBGuJUTBqOxcRlAq6F3VbXFFCUqTU/JAmsCi5Usv65qlVfZPjUiaDvHox8BUsoBB2VYJGkiUPQ==", + "version": "1.30.72", + "resolved": "https://registry.npmjs.org/openchs-models/-/openchs-models-1.30.72.tgz", + "integrity": "sha512-1j0HY+LOMJvTH+fLvpXJ9u4OrxKhTCxwzUM7yt/2gw9ZdPM52Ez9bYUYfol44F8PgiF2grnTkd7mfrvB0V/C6w==", "peerDependencies": { "lodash": "*", "moment": "*" @@ -35131,9 +35131,9 @@ } }, "openchs-models": { - "version": "1.30.71", - "resolved": "https://registry.npmjs.org/openchs-models/-/openchs-models-1.30.71.tgz", - "integrity": "sha512-ggakdscwcuB6RBGuJUTBqOxcRlAq6F3VbXFFCUqTU/JAmsCi5Usv65qlVfZPjUiaDvHox8BUsoBB2VYJGkiUPQ==" + "version": "1.30.72", + "resolved": "https://registry.npmjs.org/openchs-models/-/openchs-models-1.30.72.tgz", + "integrity": "sha512-1j0HY+LOMJvTH+fLvpXJ9u4OrxKhTCxwzUM7yt/2gw9ZdPM52Ez9bYUYfol44F8PgiF2grnTkd7mfrvB0V/C6w==" }, "opencollective-postinstall": { "version": "2.0.3", diff --git a/packages/openchs-android/package.json b/packages/openchs-android/package.json index 3574de3e3..dcd8d3f23 100644 --- a/packages/openchs-android/package.json +++ b/packages/openchs-android/package.json @@ -57,7 +57,7 @@ "lodash": "4.17.21", "moment": "2.29.4", "native-base": "3.4.9", - "openchs-models": "1.30.71", + "openchs-models": "1.30.72", "prop-types": "15.8.1", "react": "18.2.0", "react-native": "0.72.3", From 5d31c0356016e3c2cd72427cab9f3011f7619b36 Mon Sep 17 00:00:00 2001 From: himeshr Date: Fri, 6 Oct 2023 19:26:30 +0530 Subject: [PATCH 2/5] #1125 | restore userInfo after fast sync db restore --- .../openchs-android/src/service/BackupRestoreRealm.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/openchs-android/src/service/BackupRestoreRealm.js b/packages/openchs-android/src/service/BackupRestoreRealm.js index 57fccec17..461f9ecd4 100644 --- a/packages/openchs-android/src/service/BackupRestoreRealm.js +++ b/packages/openchs-android/src/service/BackupRestoreRealm.js @@ -16,6 +16,7 @@ import SubjectTypeService from "./SubjectTypeService"; import IndividualService from "./IndividualService"; import SubjectMigrationService from "./SubjectMigrationService"; import FormMappingService from "./FormMappingService"; +import UserInfoService from './UserInfoService'; const REALM_FILE_NAME = "default.realm"; const REALM_FILE_FULL_PATH = `${fs.DocumentDirectoryPath}/${REALM_FILE_NAME}`; @@ -85,6 +86,7 @@ export default class BackupRestoreRealmService extends BaseService { let downloadedFile = `${fs.DocumentDirectoryPath}/${General.randomUUID()}.zip`; let downloadedUncompressedDir = `${fs.DocumentDirectoryPath}/${General.randomUUID()}`; const prevSettings = settingsService.getSettings().clone(); + const prevUserInfo = UserInfo.fromResource({username: prevSettings.userId, organisationName: 'dummy', name: prevSettings.userId}); General.logInfo("BackupRestoreRealm", `To be downloaded file: ${downloadedFile}, Unzipped directory: ${downloadedUncompressedDir}, Realm file: ${REALM_FILE_FULL_PATH}`); @@ -143,6 +145,11 @@ export default class BackupRestoreRealmService extends BaseService { this._deleteUserInfoAndIdAssignment(); General.logDebug("BackupRestoreRealmService", "Deleted user info and id assignment"); }) + .then(() => { + this._restoreUserInfo(prevUserInfo); + General.logDebug("BackupRestoreRealmService", "Restoring prev userInfo to ensure we have user details" + + " immediately after fast sync restore"); + }) .then(() => { this._deleteUserGroups(); General.logDebug("BackupRestoreRealmService", "Deleted user groups"); @@ -217,6 +224,10 @@ export default class BackupRestoreRealmService extends BaseService { this.getService(SettingsService).saveOrUpdate(prevSettings); } + _restoreUserInfo(prevUserInfo) { + this.getService(UserInfoService).saveOrUpdate(prevUserInfo); + } + resetSyncForSubjectType(subjectType, db) { const formMappingsForSubjectType = this.getService(FormMappingService).getFormMappingsForSubjectType(subjectType).map(_.identity); _.forEach(formMappingsForSubjectType, (formMapping) => { From a0c2423b6385ccb913933f53ee2dafb98429a631 Mon Sep 17 00:00:00 2001 From: himeshr Date: Mon, 9 Oct 2023 11:32:07 +0530 Subject: [PATCH 3/5] Revert "#1125 | restore userInfo after fast sync db restore" This reverts commit 5d31c0356016e3c2cd72427cab9f3011f7619b36. --- .../openchs-android/src/service/BackupRestoreRealm.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/openchs-android/src/service/BackupRestoreRealm.js b/packages/openchs-android/src/service/BackupRestoreRealm.js index 461f9ecd4..57fccec17 100644 --- a/packages/openchs-android/src/service/BackupRestoreRealm.js +++ b/packages/openchs-android/src/service/BackupRestoreRealm.js @@ -16,7 +16,6 @@ import SubjectTypeService from "./SubjectTypeService"; import IndividualService from "./IndividualService"; import SubjectMigrationService from "./SubjectMigrationService"; import FormMappingService from "./FormMappingService"; -import UserInfoService from './UserInfoService'; const REALM_FILE_NAME = "default.realm"; const REALM_FILE_FULL_PATH = `${fs.DocumentDirectoryPath}/${REALM_FILE_NAME}`; @@ -86,7 +85,6 @@ export default class BackupRestoreRealmService extends BaseService { let downloadedFile = `${fs.DocumentDirectoryPath}/${General.randomUUID()}.zip`; let downloadedUncompressedDir = `${fs.DocumentDirectoryPath}/${General.randomUUID()}`; const prevSettings = settingsService.getSettings().clone(); - const prevUserInfo = UserInfo.fromResource({username: prevSettings.userId, organisationName: 'dummy', name: prevSettings.userId}); General.logInfo("BackupRestoreRealm", `To be downloaded file: ${downloadedFile}, Unzipped directory: ${downloadedUncompressedDir}, Realm file: ${REALM_FILE_FULL_PATH}`); @@ -145,11 +143,6 @@ export default class BackupRestoreRealmService extends BaseService { this._deleteUserInfoAndIdAssignment(); General.logDebug("BackupRestoreRealmService", "Deleted user info and id assignment"); }) - .then(() => { - this._restoreUserInfo(prevUserInfo); - General.logDebug("BackupRestoreRealmService", "Restoring prev userInfo to ensure we have user details" + - " immediately after fast sync restore"); - }) .then(() => { this._deleteUserGroups(); General.logDebug("BackupRestoreRealmService", "Deleted user groups"); @@ -224,10 +217,6 @@ export default class BackupRestoreRealmService extends BaseService { this.getService(SettingsService).saveOrUpdate(prevSettings); } - _restoreUserInfo(prevUserInfo) { - this.getService(UserInfoService).saveOrUpdate(prevUserInfo); - } - resetSyncForSubjectType(subjectType, db) { const formMappingsForSubjectType = this.getService(FormMappingService).getFormMappingsForSubjectType(subjectType).map(_.identity); _.forEach(formMappingsForSubjectType, (formMapping) => { From e223a2cbc5100e5f53c512aca2c085f6ba2eeeed Mon Sep 17 00:00:00 2001 From: Joy A Date: Tue, 10 Oct 2023 09:57:39 +0530 Subject: [PATCH 4/5] avniproject/avni-security#28 | Restrict text selection on password fields (to prevent copy) Create reusable component for TextInputs requiring restrictions on copy/paste, context menu and text selection --- .../src/views/ChangePasswordView.js | 33 ++++++------------ .../openchs-android/src/views/LoginView.js | 8 +---- .../src/views/ResetForgottenPasswordView.js | 21 ++---------- .../src/views/SetPasswordView.js | 13 ++----- .../src/views/common/SecureTextInput.js | 25 ++++++++++++++ .../views/form/formElement/TextFormElement.js | 34 ++++++++++++++----- 6 files changed, 68 insertions(+), 66 deletions(-) create mode 100644 packages/openchs-android/src/views/common/SecureTextInput.js diff --git a/packages/openchs-android/src/views/ChangePasswordView.js b/packages/openchs-android/src/views/ChangePasswordView.js index 48d42a481..b62bca8a6 100644 --- a/packages/openchs-android/src/views/ChangePasswordView.js +++ b/packages/openchs-android/src/views/ChangePasswordView.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React from "react"; import AbstractComponent from "../framework/view/AbstractComponent"; import Path from "../framework/routing/Path"; -import {Text, View, TextInput, TouchableNativeFeedback} from "react-native"; +import {Text, View, TouchableNativeFeedback} from "react-native"; import Styles from "./primitives/Styles"; import {Checkbox as CheckBox, Spinner} from "native-base"; import CHSContainer from "./common/CHSContainer"; @@ -11,6 +11,7 @@ import AuthService from "../service/AuthService"; import CHSNavigator from "../utility/CHSNavigator"; import Colors from "./primitives/Colors"; import General from "../utility/General"; +import {SecureTextInput} from "./common/SecureTextInput"; @Path('/changePasswordView') class ChangePasswordView extends AbstractComponent { @@ -120,27 +121,15 @@ class ChangePasswordView extends AbstractComponent { justifyContent: 'center' }}>{this.errorMessage()} - this.setState({password})} - contextMenuHidden={true} - onFocus={() => { - General.clearClipboard(); - }} - onBlur={() => { - General.clearClipboard(); - }} - secureTextEntry={!this.state.showPassword}/> - - this.setState({newPassword})} - contextMenuHidden={true} - onFocus={() => { - General.clearClipboard(); - }} - onBlur={() => { - General.clearClipboard(); - }} - secureTextEntry={!this.state.showPassword}/> + this.setState({password})} + secureTextEntry={!this.state.showPassword} + /> + + this.setState({newPassword})} + secureTextEntry={!this.state.showPassword} + /> { - General.clearClipboard(); - }} - onBlur={() => { - General.clearClipboard(); - }} + isSecureInput={true} /> this.setState({verificationCode})}/> - this.setState({password})} - contextMenuHidden={true} - onFocus={() => { - General.clearClipboard(); - }} - onBlur={() => { - General.clearClipboard(); - }} secureTextEntry={!this.state.showPassword} /> - this.setState({ConfirmnewPassword})} - contextMenuHidden={true} - onFocus={() => { - General.clearClipboard(); - }} - onBlur={() => { - General.clearClipboard(); - }} secureTextEntry={!this.state.showPassword} /> diff --git a/packages/openchs-android/src/views/SetPasswordView.js b/packages/openchs-android/src/views/SetPasswordView.js index 30a8e0673..27260f47e 100644 --- a/packages/openchs-android/src/views/SetPasswordView.js +++ b/packages/openchs-android/src/views/SetPasswordView.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React from "react"; import AbstractComponent from "../framework/view/AbstractComponent"; import Path from "../framework/routing/Path"; -import {Text, TextInput, TouchableNativeFeedback, View} from "react-native"; +import {Text, TouchableNativeFeedback, View} from "react-native"; import Styles from "./primitives/Styles"; import {Checkbox as CheckBox, Spinner} from "native-base"; import CHSContainer from "./common/CHSContainer"; @@ -10,7 +10,7 @@ import CHSContent from "./common/CHSContent"; import AuthService from "../service/AuthService"; import Colors from "./primitives/Colors"; import DBRestoreProgress from "./DBRestoreProgress"; -import General from "../utility/General"; +import {SecureTextInput} from "./common/SecureTextInput"; @Path('/setPasswordView') class SetPasswordView extends AbstractComponent { @@ -100,16 +100,9 @@ class SetPasswordView extends AbstractComponent { justifyContent: 'center' }}>{this.errorMessage()} - this.setState({password})} secureTextEntry={!this.state.showPassword} - contextMenuHidden={true} - onFocus={() => { - General.clearClipboard(); - }} - onBlur={() => { - General.clearClipboard(); - }} /> this.setState((oldState) => { return {showPassword: !oldState.showPassword} diff --git a/packages/openchs-android/src/views/common/SecureTextInput.js b/packages/openchs-android/src/views/common/SecureTextInput.js new file mode 100644 index 000000000..24b467d1f --- /dev/null +++ b/packages/openchs-android/src/views/common/SecureTextInput.js @@ -0,0 +1,25 @@ +import React, {useState} from "react"; +import {TextInput} from "react-native"; +import General from "../../utility/General"; + +export const SecureTextInput = (props) => { + const [inputTextSelection, setInputTextSelection] = useState({ start: 0, end: 0 }); + const onSelectionChange = ({ nativeEvent: { selection } }) => { + setInputTextSelection({ start: selection.end, end: selection.end } ); + }; + + return ( + { + General.clearClipboard(); + }} + onBlur={() => { + General.clearClipboard(); + }} + onSelectionChange={onSelectionChange} + selection={inputTextSelection} + /> + ) +} \ No newline at end of file diff --git a/packages/openchs-android/src/views/form/formElement/TextFormElement.js b/packages/openchs-android/src/views/form/formElement/TextFormElement.js index a37b064d9..ea8acc1f2 100644 --- a/packages/openchs-android/src/views/form/formElement/TextFormElement.js +++ b/packages/openchs-android/src/views/form/formElement/TextFormElement.js @@ -10,6 +10,7 @@ import Colors from "../../primitives/Colors"; import ValueSelectFormElement from "./ValueSelectFormElement"; import {HelpText} from "../../common/HelpText"; import FormElementLabelWithDocumentation from "../../common/FormElementLabelWithDocumentation"; +import {SecureTextInput} from "../../common/SecureTextInput"; class TextFormElement extends AbstractFormElement { static propTypes = { @@ -25,10 +26,12 @@ class TextFormElement extends AbstractFormElement { inputStyle: PropTypes.object, allowedValues: PropTypes.array, helpText: PropTypes.string, - isTableView: PropTypes.bool + isTableView: PropTypes.bool, + isSecureInput: PropTypes.bool }; static defaultProps = { - style: {} + style: {}, + isSecureInput: false }; constructor(props, context) { @@ -60,13 +63,26 @@ class TextFormElement extends AbstractFormElement { - this.onInputChange(text)} multiline={this.props.multiline} - numberOfLines={this.props.multiline ? 4 : 1} - keyboardType={this.props.keyboardType || 'default'}/> - + {this.props.isSecureInput === true ? + this.onInputChange(text)} multiline={this.props.multiline} + numberOfLines={this.props.multiline ? 4 : 1} + keyboardType={this.props.keyboardType || 'default'} + /> + : + this.onInputChange(text)} + multiline={this.props.multiline} + numberOfLines={this.props.multiline ? 4 : 1} + keyboardType={this.props.keyboardType || 'default'} + /> + } + ); } From 660a9e442628b2c97b1e70e731b5f6db18dd532f Mon Sep 17 00:00:00 2001 From: Vivek Singh Date: Tue, 10 Oct 2023 14:00:15 +0530 Subject: [PATCH 5/5] #000 - minor refactoring --- packages/openchs-android/.gitignore | 2 ++ .../openchs-android/src/framework/view/AbstractComponent.js | 2 +- packages/openchs-android/src/service/BackupRestoreRealm.js | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/openchs-android/.gitignore b/packages/openchs-android/.gitignore index 8587b3b72..57a2c77ec 100644 --- a/packages/openchs-android/.gitignore +++ b/packages/openchs-android/.gitignore @@ -20,3 +20,5 @@ src/framework/Config.js config/env/dev.json android/fastlane/metadata/android/en-GB/changelogs /android/app/src/main/assets/index.android.bundle + +.watchman* diff --git a/packages/openchs-android/src/framework/view/AbstractComponent.js b/packages/openchs-android/src/framework/view/AbstractComponent.js index 6796ad89c..d2d3831bc 100644 --- a/packages/openchs-android/src/framework/view/AbstractComponent.js +++ b/packages/openchs-android/src/framework/view/AbstractComponent.js @@ -60,7 +60,7 @@ class AbstractComponent extends Component { dispatchAction(action, params) { const type = action instanceof Function ? action.Id : action; if (General.canLog(General.LogLevel.Debug)) { - General.logDebugTemp(`${this.constructor.name}::AC`, `Dispatching action: ${JSON.stringify(type)}`); + General.logDebug(`${this.constructor.name}::AC`, `Dispatching action: ${JSON.stringify(type)}`); } const dispatchResult = this.context.getStore().dispatch({type, ...params}); if (General.canLog(General.LogLevel.Debug)) { diff --git a/packages/openchs-android/src/service/BackupRestoreRealm.js b/packages/openchs-android/src/service/BackupRestoreRealm.js index 57fccec17..35e09390d 100644 --- a/packages/openchs-android/src/service/BackupRestoreRealm.js +++ b/packages/openchs-android/src/service/BackupRestoreRealm.js @@ -26,8 +26,8 @@ export default class BackupRestoreRealmService extends BaseService { super(db, context); } - subscribeOnRestore(notify) { - this.notify = notify; + subscribeOnRestore(dumpFileRestoreCompleted) { + this.dumpFileRestoreCompleted = dumpFileRestoreCompleted; } isDatabaseEverSynced() { @@ -122,7 +122,7 @@ export default class BackupRestoreRealmService extends BaseService { General.logDebug("BackupRestoreRealmService", "Refreshing application context"); cb(92, "restoringDb"); }) - .then(() => this.notify()) + .then(() => this.dumpFileRestoreCompleted()) .then(() => { General.logDebug("BackupRestoreRealmService", "Removing downloaded files"); cb(94, "restoringDb");