import React, {Component} from 'react'; import { observer, inject } from 'mobx-react'; import jss from 'jss'; import preset from 'jss-preset-default'; import { Menu, Segment, Dropdown, Table, Button, Loader, Icon, Header, Input, Checkbox } from 'semantic-ui-react'; import alertify from 'alertify.js'; import ReactHtmlParser from 'react-html-parser'; /* * Functions import */ import { decrypt } from '../../stores/functions/encryption'; /* * Component imports */ import New from '../../components/PasswordManager/New'; import Edit from '../../components/PasswordManager/Edit'; import Headline from '../../components/Headline'; jss.setup(preset()); //Firebase var firebase = require('../../stores/fb').fb; // Initialize Cloud Firestore through Firebase var db = firebase.firestore(); /* ############################### Components -- START ############################### */ /* ############################### Components -- END ############################### */ const PasswordManager = inject("rootStore") ( observer( class PasswordManager extends Component { constructor(props){ super(props); //Password Manager page /* * Expected props * - / */ //Stored information this.stores = this.props.rootStore.stores; this.toggleNewWindow = this.toggleNewWindow.bind(this); this.toggleEditWindow = this.toggleEditWindow.bind(this); this.onChangeInput = this.onChangeInput.bind(this); this.addDoc = this.addDoc.bind(this); this.updateDoc = this.updateDoc.bind(this); this.deleteDoc = this.deleteDoc.bind(this); this.togglePasswordDisplay = this.togglePasswordDisplay.bind(this); this.state = { newWindowOpen: false, editWindowOpen: false, editIndex: null, key: '', search: '', data: [], loading: true, togglePasswordDisplay: false } //Styles this.styles = this.getStyles(); this.sheet = jss.createStyleSheet(this.styles); const {classes} = this.sheet.attach(); this.classes = classes; //Styles } componentDidMount() { this.fetchData(); } componentWillUnmount() { this.sheet.detach() } addDoc(data) { //Add a new doc locally so no new data transfer from firestore //is needed var newArr = []; newArr[0] = data; newArr = newArr.concat(this.state.data); this.setState({ data: newArr }); } updateDoc(data, editIndex) { //Update a doc locally with the new values var newArr = this.state.data; newArr[editIndex] = data; this.setState({ data: newArr }); } deleteDoc(editIndex) { //Delete doc locally var newArr = []; this.state.data.forEach(function(element, index) { if(index !== editIndex) { newArr.push(element); } }); this.setState({ data: newArr }); } onChangeInput(e) { //Encryption key and search input this.setState({ [e.target.name]: e.target.value }); } toggleNewWindow() { //Open window to add a new password doc if(this.state.key !== '') { this.setState({ newWindowOpen: !this.state.newWindowOpen }); } else { alertify.error("Please set an encryption key!"); } } togglePasswordDisplay() { const oldState = this.state.togglePasswordDisplay; this.setState({ togglePasswordDisplay: !oldState }); } toggleEditWindow(index) { //Open window to add a new password doc if(this.state.key !== '') { this.setState({ editWindowOpen: !this.state.editWindowOpen, editIndex: index }); } else { alertify.error("Please set an encryption key!"); } } fetchData() { //Gets password list from firestore and //Writes in this.state const uid = this.stores.authStore.userData.uid; db.collection("passwords/"+uid+"/passwords").orderBy("time", "desc").get().then((querySnapshot) => { var arr = []; querySnapshot.forEach((doc) => { const data = doc.data(); arr.push(Object.assign(data, {id:doc.id})); }); this.setState({ data: arr, loading: false }); }).catch(function() { alertify.error("Data could not be loaded. Please check your internet connection"); }); } displayData(dataList) { //Returns array with all table row components var components = []; function copyToClipboard(string) { //Copy string to clipboard var textArea = document.createElement("textarea"); textArea.style.position = 'fixed'; textArea.style.top = 0; textArea.style.left = 0; textArea.style.width = '2em'; textArea.style.height = '2em'; textArea.style.padding = 0; textArea.style.border = 'none'; textArea.style.outline = 'none'; textArea.style.boxShadow = 'none'; textArea.style.background = 'transparent'; textArea.value = string; document.body.appendChild(textArea); textArea.focus(); textArea.select(); try { var successful = document.execCommand('copy'); if(successful) { alertify.success("Copied to clipboard!"); } else { alertify.error("Unable to copy to clipboard"); } } catch (err) { alertify.error("Unable to copy to clipboard"); } document.body.removeChild(textArea); } function transformUrl(text) { //Transforms url into clickable link var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%=~_|])/ig; var text1 = text.replace(exp, '$1'); var exp2 = /(^|[^/])(www\.[\S]+(\b|$))/gim; return ReactHtmlParser(text1.replace(exp2, '$1$2')); } dataList.forEach((element, index) => { const passwordDecrypted = decrypt(element.password, this.state.key) if(element.url.toLowerCase().includes(this.state.search.toLowerCase())) { const copyLogin = () => { copyToClipboard(decrypt(element.login, this.state.key)); } const copyPassword = () => { copyToClipboard(passwordDecrypted); } const toggleEditWindow = () => { this.toggleEditWindow(index); } const url = transformUrl(element.url); //If password display toggled off: Display dots //Otherwise display decrypted password var password =
•••••••••
; if(this.state.togglePasswordDisplay) { password = passwordDecrypted; } const login = decrypt(element.login, this.state.key); components.push( {url} {login} {password} ); } }); return components; } render() { const tableRows = this.displayData(this.state.data); return(
New
Application / URL Username / Email adress Password
{tableRows}

Rijndael 128 bit client-side encrypted!

); } getStyles() { return { } } } )); export default PasswordManager;