From 8d93af65520cacb6ed0eb302b3c0d5c71da94932 Mon Sep 17 00:00:00 2001 From: Alexander Bell Date: Wed, 19 Sep 2018 20:29:48 +0200 Subject: [PATCH] adding and fetching docs --- public/favicon.ico | Bin 3870 -> 1150 bytes public/index.html | 3 + src/App.js | 2 +- src/components/Headline.js | 17 + src/components/Menu/Menu.js | 54 ++-- src/files/images/ninja.svg | 1 + src/pages/Main.js | 1 + src/pages/home/Home.js | 15 +- src/pages/passwordManager/New.js | 256 +++++++++------ src/pages/passwordManager/PasswordManager.js | 324 +++++++++++-------- src/stores/functions/encryption.js | 13 + 11 files changed, 416 insertions(+), 270 deletions(-) create mode 100644 src/components/Headline.js create mode 100644 src/files/images/ninja.svg create mode 100644 src/stores/functions/encryption.js diff --git a/public/favicon.ico b/public/favicon.ico index a11777cc471a4344702741ab1c8a588998b1311a..c4b01980f456c71b84172a261b3804c77bd2fb0c 100644 GIT binary patch literal 1150 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 baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ diff --git a/public/index.html b/public/index.html index 2ed77f6..58e4df9 100644 --- a/public/index.html +++ b/public/index.html @@ -10,9 +10,12 @@ Ninja + +
+ diff --git a/src/App.js b/src/App.js index d0f6f89..672130e 100644 --- a/src/App.js +++ b/src/App.js @@ -42,7 +42,7 @@ const App = inject("rootStore") ( observer( const body = document.getElementsByTagName('body')[0]; body.style.margin = '0'; body.style.padding = '0'; - body.style.overflow = 'hidden'; + body.style.overflow = 'auto'; //Load app this.stores.rootStore.loadApp(); diff --git a/src/components/Headline.js b/src/components/Headline.js new file mode 100644 index 0000000..2921b5a --- /dev/null +++ b/src/components/Headline.js @@ -0,0 +1,17 @@ +import React from 'react'; + +export default function(props) { + /* + * Expected props + * - black: String + * the text in black + * - red: String + * the text in red + */ + + return( +

+ {props.black}{props.red} +

+ ) +} \ No newline at end of file diff --git a/src/components/Menu/Menu.js b/src/components/Menu/Menu.js index 30228d8..7f64059 100644 --- a/src/components/Menu/Menu.js +++ b/src/components/Menu/Menu.js @@ -113,9 +113,7 @@ const Main = inject("rootStore") ( observer( -
- {this.props.children} -
+ {this.props.children} ); } else { @@ -123,35 +121,33 @@ const Main = inject("rootStore") ( observer( //Mobile sidebar return( -
- - - - - + + + + + - - Home - + + Home + - - Password manager - - + + Password manager + + - - - {this.props.children} - - -
+ + + {this.props.children} + + ) } diff --git a/src/files/images/ninja.svg b/src/files/images/ninja.svg new file mode 100644 index 0000000..09b6cc6 --- /dev/null +++ b/src/files/images/ninja.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/pages/Main.js b/src/pages/Main.js index 36d4058..76a4efb 100644 --- a/src/pages/Main.js +++ b/src/pages/Main.js @@ -78,6 +78,7 @@ class Main extends Component { + diff --git a/src/pages/home/Home.js b/src/pages/home/Home.js index 51ca168..1bab6e3 100644 --- a/src/pages/home/Home.js +++ b/src/pages/home/Home.js @@ -1,6 +1,7 @@ import React, {Component} from 'react'; import jss from 'jss'; import preset from 'jss-preset-default'; +import { Container } from 'semantic-ui-react'; /* * Functions import @@ -9,6 +10,7 @@ import preset from 'jss-preset-default'; /* * Component imports */ +import Headline from '../../components/Headline'; jss.setup(preset()); @@ -53,9 +55,16 @@ class Home extends Component { render() { return( -
- Home -
+ + + +

+ You are signed in on alexbell.ninja. What can you do here?
+ Well, I like developing software in my freetime, mainly applications + for my personal use. They might also be of advantage for you, so feel free + to browse around a little and try some of what you can find here :) +

+
); } diff --git a/src/pages/passwordManager/New.js b/src/pages/passwordManager/New.js index 39e49f2..2dd6896 100644 --- a/src/pages/passwordManager/New.js +++ b/src/pages/passwordManager/New.js @@ -1,11 +1,14 @@ import React, {Component} from 'react'; +import { observer, inject } from 'mobx-react'; import jss from 'jss'; import preset from 'jss-preset-default'; import { Segment, Container, Header, Form, Input, Button, Icon } from 'semantic-ui-react'; +import alertify from 'alertify.js'; /* * Functions import */ +import { encrypt } from '../../stores/functions/encryption'; /* * Component imports @@ -13,6 +16,11 @@ import { Segment, Container, Header, Form, Input, Button, Icon } from 'semantic- jss.setup(preset()); +//Firebase +var firebase = require('../../stores/fb').fb; +// Initialize Cloud Firestore through Firebase +var db = firebase.firestore(); + /* ############################### @@ -28,54 +36,61 @@ Components -- END -class New extends Component { - constructor(props){ - super(props); - //Add a new password window +const New = inject("rootStore") ( observer( + class New extends Component { + constructor(props){ + super(props); + //Add a new password window - /* - * Expected props - * - open: bool - * display component or not - * - toggleNewWindow: function - * toggles window: open and close - * - encryptionkey: String - * encryption key of the password - */ + /* + * Expected props + * - open: bool + * display component or not + * - toggleNewWindow: function + * toggles window: open and close + * - encryptionkey: String + * encryption key of the password + */ - this.state = { - url: '', - login: '', - password: this.generatePassword(), - loading: false - } + //Stored information + this.stores = this.props.rootStore.stores; - //Styles - this.styles = this.getStyles(); - this.sheet = jss.createStyleSheet(this.styles); - const {classes} = this.sheet.attach(); - this.classes = classes; - //Styles - } + this.handleChange = this.handleChange.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); - - componentWillReceiveProps(nextProps) { - if(nextProps.open !== this.props.open) { - this.setState({ + this.state = { url: '', login: '', - password: this.generatePassword() - }); + password: this.generatePassword(), + loading: false + } + + //Styles + this.styles = this.getStyles(); + this.sheet = jss.createStyleSheet(this.styles); + const {classes} = this.sheet.attach(); + this.classes = classes; + //Styles + } + + + componentWillReceiveProps(nextProps) { + if(nextProps.open !== this.props.open) { + this.setState({ + url: '', + login: '', + password: this.generatePassword() + }); + } } - } - componentWillUnmount() { - this.sheet.detach() - } + componentWillUnmount() { + this.sheet.detach() + } - generatePassword() { + generatePassword() { //Creates a unique id for each chat bubble so that the animation can be triggered var random = ""; @@ -89,78 +104,111 @@ class New extends Component { return random; } } - } - - - handleChange(e, {name}) { - this.setState({ - [name]: e.target.value - }); - } - - - render() { - if(this.props.open) { - return( -
- - -
-
- Add a password -
- - - - - - - - - - - - - - - - - - - - - - -

The encryption key for this entry is '{this.props.encryptionkey}'

- - - - -
-
-
-
- ); - } else { - return null; } - } - getStyles() { - return { - box: { - width: '100vw', - height: '100vh', - position: 'fixed', - zIndex: 1000, - top: 0, - left: 0, - backgroundColor: 'rgba(255,255,255,0.9)', - paddingTop: '5%' + handleChange(e) { + this.setState({ + [e.target.name]: e.target.value + }); + } + + + handleSubmit() { + const url = this.state.url; + const login = encrypt(this.state.login, this.props.encryptionkey); + const password = encrypt(this.state.password, this.props.encryptionkey); + const uid = this.stores.authStore.userData.uid; + + db.collection("passwords").add({ + uid: uid, + url: url, + login: login, + password: password, + time: new Date() + }) + .then((docRef) => { + console.log("Document written with ID: ", docRef.id); + alertify.success("Document successfully added!"); + this.props.toggleNewWindow(); + }) + .catch(function(error) { + console.error("Error adding document: ", error); + alertify.error("Something went wrong! Please check your internet connection"); + this.setState({ + loading: false + }); + }); + + this.setState({ + loading: true + }); + } + + + render() { + if(this.props.open) { + return( +
+ + +
+
+ Add a password +
+ + + + + + + + + + + + + + + + + + + + + + +

The encryption key for this entry is '{this.props.encryptionkey}'

+ + + + +
+
+
+
+ ); + } else { + return null; + } + } + + + getStyles() { + return { + box: { + width: '100vw', + height: '100vh', + position: 'fixed', + zIndex: 1000, + top: 0, + left: 0, + backgroundColor: 'rgba(255,255,255,0.9)', + paddingTop: '5%' + } } } } -} +)); export default New; diff --git a/src/pages/passwordManager/PasswordManager.js b/src/pages/passwordManager/PasswordManager.js index c233adc..64623c7 100644 --- a/src/pages/passwordManager/PasswordManager.js +++ b/src/pages/passwordManager/PasswordManager.js @@ -1,20 +1,28 @@ 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, Icon, Header } from 'semantic-ui-react'; +import { Menu, Segment, Dropdown, Table, Button, Loader, Icon, Header, Input } from 'semantic-ui-react'; import alertify from 'alertify.js'; /* * Functions import */ +import { decrypt } from '../../stores/functions/encryption'; /* * Component imports */ import New from './New'; +import Headline from '../../components/Headline'; jss.setup(preset()); +//Firebase +var firebase = require('../../stores/fb').fb; +// Initialize Cloud Firestore through Firebase +var db = firebase.firestore(); + /* ############################### @@ -30,151 +38,201 @@ Components -- END -class PasswordManager extends Component { - constructor(props){ - super(props); - //Password Manager page +const PasswordManager = inject("rootStore") ( observer( + class PasswordManager extends Component { + constructor(props){ + super(props); + //Password Manager page - /* - * Expected props - * - / - */ + /* + * Expected props + * - / + */ - this.toggleNewWindow = this.toggleNewWindow.bind(this); - this.onChangeInput = this.onChangeInput.bind(this); + //Stored information + this.stores = this.props.rootStore.stores; - this.state = { - newWindowOpen: false, - key: '', - search: '' + this.toggleNewWindow = this.toggleNewWindow.bind(this); + this.onChangeInput = this.onChangeInput.bind(this); + + this.state = { + newWindowOpen: false, + key: '', + search: '', + data: [], + loading: true + } + + //Styles + this.styles = this.getStyles(); + this.sheet = jss.createStyleSheet(this.styles); + const {classes} = this.sheet.attach(); + this.classes = classes; + //Styles } - //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() - } + componentWillUnmount() { + this.sheet.detach() + } - onChangeInput(e) { - //Encryption key and search input + onChangeInput(e) { + //Encryption key and search input - this.setState({ - [e.target.name]: e.target.value - }); - } - - - toggleNewWindow() { - //Add new password window - - if(this.state.key !== '') - { - alertify.log("Encryption key: '" + this.state.key + "'"); this.setState({ - newWindowOpen: !this.state.newWindowOpen + [e.target.name]: e.target.value }); - } else { - alertify.error("Please set an encryption key!"); + } + + + 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!"); + } + } + + + fetchData() { + //Gets password list from firestore and + //Writes in this.state + + const uid = this.stores.authStore.userData.uid; + + db.collection("passwords").where("uid", "==", uid).orderBy("time", "desc").get().then((querySnapshot) => { + var arr = []; + querySnapshot.forEach((doc) => { + const data = doc.data(); + arr.push(data); + }); + this.setState({ + data: arr, + loading: false + }); + }); + } + + + displayData(dataList) { + //Returns array with all table row components + var components = []; + + dataList.forEach((element, index) => { + components.push( + + {element.url} + {decrypt(element.login, this.state.key)} + + + + {decrypt(element.password, this.state.key)} + + + + + ); + }); + + return components; + } + + + render() { + const tableRows = this.displayData(this.state.data) + + return( +
+ + + + + + + + + + + + + + New + + + + + +
+
+ + +
+
+
+
+ + + + + + + Application / URL + Username / Email adress + Copy + Password + Copy + + + + + {tableRows} + +
+ +
+ +
+ How does it work? Is it safe? +
+

+ I ask you to give an encryption key. Once you set that key you can create a new password which is then encrypted with that key. +

+

+ The password is then saved in the encrypted form on the database in the internet, which means that nobody (not even me) can read your password unless that person knows the key. In other words: The password is completely safe and solely accessible by you and only you! +

+

+ To decrypt your passwords simply type in the encryption key and all your passwords in your list are displayed correctly and readable in the form you had set them. +

+
+

+ I advise you to choose one encryption key and use it for all your passwords in your list. Why? Using the same key makes decrypting easier for you because all entries are being decrypted correctly at the same time by using one single key. +

+
+ ); + } + + + getStyles() { + return { + + } } } - - - render() { - return( -
- - - - - - New - - - - - -
-
- - -
-
- -
-
- - -
-
-
-
- - - - - - Application / URL - Username / Email adress - Copy - Password - Copy - - - - - - cell - cell - - - - cell - - - - - -
-
- -
- How does it work? -
-

- I ask you to give an encryption key. Once you set that key you can create a new password which is then encrypted with that key. -

-

- The password is then saved in the encrypted form on the database in the internet, which means that nobody (not even me) can read your password unless that person knows the key. In other words: The password is completely safe and solely accessible by you and only you! -

-

- To decrypt your passwords simply type in the encryption key and all your passwords in your list are displayed correctly and readable in the form you had set them. -

-
-

- I advise you to choose one encryption key and use it for all your passwords in your list. Why? Using the same key makes decrypting easier for you because all entries are being decrypted correctly at the same time by using one single key. -

-
- ); - } - - - getStyles() { - return { - - } - } -} +)); export default PasswordManager; diff --git a/src/stores/functions/encryption.js b/src/stores/functions/encryption.js new file mode 100644 index 0000000..bc605eb --- /dev/null +++ b/src/stores/functions/encryption.js @@ -0,0 +1,13 @@ +function norm_to_ascii(string){return unescape(encodeURIComponent(string))}; +function norm_to_unicode(string){return decodeURIComponent(escape(string))}; +function crypt_sym(string,k){return String.fromCharCode.apply(undefined,string.split("").map(function(c){return c.charCodeAt(0)^(k||13)}))}; + +export function encrypt(string, key) { + //Encrypt a string + return btoa(crypt_sym(norm_to_ascii(string), key)); +} + +export function decrypt(string, key) { + //Decrypt a string + return crypt_sym(norm_to_unicode(atob(string)), key); +} \ No newline at end of file