Skip to content

Commit

Permalink
Adding change password feature
Browse files Browse the repository at this point in the history
  • Loading branch information
ashaban committed Nov 21, 2022
1 parent bd24165 commit d197527
Show file tree
Hide file tree
Showing 11 changed files with 312 additions and 8 deletions.
2 changes: 1 addition & 1 deletion server/gui/index.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/crux/favicon.ico"><title>crux</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css"><link href="/crux/css/chunk-vendors.a3068dc3.css" rel="preload" as="style"><link href="/crux/js/app.80a0f5f9.js" rel="preload" as="script"><link href="/crux/js/chunk-vendors.ce988c77.js" rel="preload" as="script"><link href="/crux/css/chunk-vendors.a3068dc3.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but crux doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/crux/js/chunk-vendors.ce988c77.js"></script><script src="/crux/js/app.80a0f5f9.js"></script></body></html>
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/crux/favicon.ico"><title>crux</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css"><link href="/crux/css/chunk-vendors.a3068dc3.css" rel="preload" as="style"><link href="/crux/js/app.d5613f03.js" rel="preload" as="script"><link href="/crux/js/chunk-vendors.ce988c77.js" rel="preload" as="script"><link href="/crux/css/chunk-vendors.a3068dc3.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but crux doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/crux/js/chunk-vendors.ce988c77.js"></script><script src="/crux/js/app.d5613f03.js"></script></body></html>
2 changes: 0 additions & 2 deletions server/gui/js/app.80a0f5f9.js

This file was deleted.

1 change: 0 additions & 1 deletion server/gui/js/app.80a0f5f9.js.map

This file was deleted.

2 changes: 2 additions & 0 deletions server/gui/js/app.d5613f03.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions server/gui/js/app.d5613f03.js.map

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion server/lib/prerequisites.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const URI = require('urijs');
const async = require('async');
const config = require('./config');
const logger = require('./winston');
const mixin = require('./mixins/generalMixin');
const fs = require('fs');
const Fhir = require('fhir').Fhir;

Expand Down Expand Up @@ -105,6 +106,10 @@ const modifyRelationship = () => {

const loadResources = async (callback) => {
await modifyRelationship();
const installed = config.get('app:installed');
if (installed) {
return callback(false);
}
let processingError = false;
const folders = [
`${__dirname}/../../resources/StructureDefinition`,
Expand Down Expand Up @@ -354,7 +359,10 @@ const init = (callback) => {
});
}
}, () => {
return callback(errFound);
mixin.updateConfigFile(['app', 'installed'], true, () => {
logger.info('Done loading Default data');
return callback(errFound);
});
});
};
module.exports = {
Expand Down
105 changes: 105 additions & 0 deletions server/lib/routes/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,111 @@ router.post("/addUser", function (req, res, next) {
});
});
});

router.post("/changepassword", (req, res) => {
const form = new formidable.IncomingForm();
form.parse(req, (err, fields, files) => {
let url = URI(config.get('fhirServer:baseURL')).segment("Person");
url.addQuery('username:exact', fields.username);
url = url.toString();

const options = {
url,
withCredentials: true,
auth: {
username: config.get('fhirServer:username'),
password: config.get('fhirServer:password'),
},
headers: {
'Cache-Control': 'no-cache',
}
};
request.get(options, (err, response, body) => {
if (!isJSON(body)) {
logger.error(body);
logger.error('Non JSON has been returned while getting user information for user ' + req.query.username);
return res.status(500).json({
info: "Internal Error Occured"
});
}
body = JSON.parse(body);
const numMatches = body.total;
if (numMatches == 0) {
return res.status(400).send("Cant find user " + fields.username);
} else {
const user = body.entry[0].resource;
const extensions = user.extension;

for (var i in extensions) {
if (extensions[i].url.includes("OCRUserDetails")) {
const userDetails = extensions[i].extension;
let password = null;
let salt = null;

for (var j in userDetails) {
if (userDetails[j].url == "password") {
password = userDetails[j].valueString;
}

if (userDetails[j].url == "salt") {
salt = userDetails[j].valueString;
}
}

const hash = crypto.pbkdf2Sync(
fields.password,
salt,
1000,
64,
"sha512"
).toString("hex");

// matching password
if (hash === password) {
const salt = crypto.randomBytes(16).toString('hex');
const password = crypto.pbkdf2Sync(
fields.newpassword,
salt,
1000,
64,
"sha512"
).toString("hex");
for (const j in userDetails) {
if (userDetails[j].url == "password") {
userDetails[j].valueString = password;
}
if (userDetails[j].url == "salt") {
userDetails[j].valueString = salt;
}
}
const url = URI(config.get('fhirServer:baseURL')).segment("Person").segment(user.id).toString();
const options = {
url,
headers: {
'Content-Type': 'application/json',
},
withCredentials: true,
auth: {
username: config.get('fhirServer:username'),
password: config.get('fhirServer:password'),
},
json: user
};
request.put(options, (err, resp, body) => {
if (err) {
return res.status(400).json(err);
}
return res.status(200).send("Password Changed");
});
} else {
return res.status(400).json("Password mismatch");
}
}
}
}
});
});
});
/**
* Check login credentials
*/
Expand Down
6 changes: 3 additions & 3 deletions tests/sampleSinglePatient.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
},
"identifier": [{
"system": "http://clientregistry.org/openmrs",
"value": "sourceid18"
"value": "sourceid27"
}, {
"system": "http://clientregistry.org/nationalid",
"value": "228374844"
"value": ""
}, {
"system": "http://system1.org",
"value": "12347",
"value": "12351",
"period": {
"start": "2001-05-07"
},
Expand Down
7 changes: 7 additions & 0 deletions ui/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@
>
<v-icon>mdi-account-plus</v-icon> Add User
</v-btn>
<v-btn
color="primary"
to="/changePassword"
v-if='!$store.state.denyAccess'
>
<v-icon>mdi-account-plus</v-icon> Change Password
</v-btn>
<v-btn
color="primary"
to="/logout"
Expand Down
6 changes: 6 additions & 0 deletions ui/src/router/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Review from "../views/Review.vue";
import Resolve from "../views/Resolve.vue";
import CSVReport from "../views/CSVReport.vue";
import AddUser from "../views/AddUser.vue";
import ChangePassword from "../views/ChangePassword.vue"
import Login from '@/views/Login.vue'
import Logout from '@/components/Logout.vue'
import VueCookies from 'vue-cookies'
Expand Down Expand Up @@ -45,6 +46,11 @@ const routes = [{
name: 'AddUser',
component: AddUser
},
{
path: '/changePassword',
name: 'ChangePassword',
component: ChangePassword
},
{
path: '/login',
name: 'Login',
Expand Down
178 changes: 178 additions & 0 deletions ui/src/views/ChangePassword.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<template>
<v-container>
<v-layout
row
wrap
>
<v-spacer />
<v-flex xs6>
<v-card
class="mx-auto"
style="max-width: 500px;"
>
<v-system-bar
color="primary"
dark
/>
<v-toolbar
color="secondary"
cards
dark
flat
>
<v-card-title class="title font-weight-regular">Add New User</v-card-title>
</v-toolbar>
<v-form
ref="form"
class="pa-3 pt-4"
>
<v-text-field
required
@blur="$v.password.$touch()"
@change="$v.password.$touch()"
:error-messages="passwordErrors"
v-model="password"
type="password"
filled
color="deep-purple"
label="Current Password*"
/>
<v-text-field
required
@blur="$v.newpassword.$touch()"
@change="$v.newpassword.$touch()"
:error-messages="newpasswordErrors"
v-model="newpassword"
type="password"
filled
color="deep-purple"
label="New Password*"
/>
<v-text-field
v-model="retype_newpassword"
label="Re-type New Password*"
required
type="password"
filled
color="deep-purple"
:error-messages="retype_newpasswordErrors"
@blur="$v.retype_newpassword.$touch()"
@change="$v.retype_newpassword.$touch()"
/>
</v-form>
<v-divider />
<v-card-actions>
<v-btn
text
@click="$refs.form.reset()"
>
<v-icon>mdi-clear</v-icon>Clear
</v-btn>
<v-spacer />
<v-btn
depressed
:disabled="$v.$invalid"
class="white--text"
color="deep-purple accent-4"
@click="changePassword()"
>
<v-icon left>
mdi-language
</v-icon>Change
</v-btn>
</v-card-actions>
</v-card>
</v-flex>
<v-spacer />
</v-layout>
</v-container>
</template>
<script>
import axios from "axios";
import { required } from "vuelidate/lib/validators";
export default {
validations: {
newpassword: { required },
retype_newpassword: { required },
password: { required }
},
data() {
return {
password: "",
newpassword: "",
retype_newpassword: ""
};
},
computed: {
newpasswordErrors() {
const errors = [];
if (!this.$v.newpassword.$dirty) return errors;
!this.$v.newpassword.required && errors.push("New password is required");
return errors;
},
retype_newpasswordErrors() {
const errors = [];
if (!this.$v.retype_newpassword.$dirty) return errors;
!this.$v.retype_newpassword.required && errors.push("Must re-type New Password");
return errors;
},
passwordErrors() {
const errors = [];
if (!this.$v.password.$dirty) return errors;
!this.$v.password.required && errors.push("Password is required");
return errors;
}
},
methods: {
changePassword() {
if (this.newpassword !== this.retype_newpassword) {
this.$store.state.alert.show = true;
this.$store.state.alert.width = "500px";
this.$store.state.alert.msg = "New password mismatch";
this.$store.state.alert.type = "error";
return;
}
this.$store.state.progress.enable = true;
this.$store.state.progress.width = "300px";
this.$store.state.progress.title = "Changing password"
let formData = new FormData();
formData.append("password", this.password);
formData.append("username", this.$store.state.auth.username);
formData.append("newpassword", this.newpassword);
axios
.post("/ocrux/user/changepassword/", formData, {
headers: {
"Content-Type": "multipart/form-data"
}
})
.then(() => {
this.$store.state.progress.enable = false;
this.$store.state.alert.show = true;
this.$store.state.alert.width = "500px";
this.$store.state.alert.msg = "Password changed";
this.$store.state.alert.type = "success";
this.$refs.form.reset();
})
.catch((error) => {
let msg = ""
if (error.response) {
msg = error.response.data
} else if (error.request) {
msg = error.request
} else if(error.message) {
msg = error.message
} else {
msg = "An error occured"
}
this.$store.state.progress.enable = false;
this.$store.state.alert.show = true;
this.$store.state.alert.width = "500px";
this.$store.state.alert.msg = msg
this.$store.state.alert.type = "error";
});
}
}
};
</script>

0 comments on commit d197527

Please sign in to comment.