From ca1d86acf11c60825e453642fc71ab6bfa6630fc Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Thu, 31 Oct 2019 13:56:40 -0400 Subject: [PATCH 1/4] Users can be verified and have a check mark displayed on their profiles --- functions/handlers/users.js | 3 ++- twistter-frontend/src/pages/user.js | 11 +++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/functions/handlers/users.js b/functions/handlers/users.js index 203aa58..1ff9298 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -76,7 +76,8 @@ exports.signup = (req, res) => { handle: newUser.handle, createdAt: newUser.createdAt, userId, - followedTopics: [] + followedTopics: [], + verified: false }; return db.doc(`/users/${newUser.handle}`).set(userCred); }) diff --git a/twistter-frontend/src/pages/user.js b/twistter-frontend/src/pages/user.js index 27a0c94..ef764b3 100644 --- a/twistter-frontend/src/pages/user.js +++ b/twistter-frontend/src/pages/user.js @@ -10,6 +10,8 @@ import Chip from '@material-ui/core/Chip'; import Typography from "@material-ui/core/Typography"; import AddCircle from '@material-ui/icons/AddCircle'; import TextField from '@material-ui/core/TextField'; +import VerifiedIcon from '@material-ui/icons/CheckSharp'; + // component import Userline from '../Userline'; @@ -56,7 +58,8 @@ class user extends Component { .then(res => { this.setState({ profile: res.data.credentials.handle, - imageUrl: res.data.credentials.imageUrl + imageUrl: res.data.credentials.imageUrl, + verified: res.data.credentials.verified ? res.data.credentials.verified : false }); }) .catch(err => console.log(err)); @@ -72,9 +75,9 @@ class user extends Component { render() { const classes = this.props; let profileMarkup = this.state.profile ? ( -

- {this.state.profile} -

) : (

loading username...

); +
+ {this.state.profile} {this.state.verified ? (): (null)} +
) : (

loading username...

); let topicsMarkup = this.state.topics ? ( From 325d37f0de4bf8f8824fc57f0669e3952fab00e5 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Thu, 31 Oct 2019 15:12:22 -0400 Subject: [PATCH 2/4] Back-end for verifying users --- functions/handlers/users.js | 52 +++++++++++++++++++++++++++++++++++++ functions/index.js | 12 ++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/functions/handlers/users.js b/functions/handlers/users.js index 1ff9298..e1f58fe 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -308,4 +308,56 @@ exports.getAuthenticatedUser = (req, res) => { }); }; +// Verifies the user sent to the request +// Must be run by the Admin user +exports.verifyUser = (req, res) => { + if (req.userData.handle !== "Admin") { + return res.status(403).json({error: "This must be done as Admin"}); + } + db.doc(`/users/${req.body.user}`) + .get() + .then((doc) => { + if (doc.exists) { + let verifiedUser = doc.data(); + verifiedUser.verified = true; + return db.doc(`/users/${req.body.user}`).set(verifiedUser, {merge: true}); + } else { + return res.status(400).json({error: `User ${req.body.user} was not found`}); + } + }) + .then(() => { + return res.status(201).json({message: `${req.body.user} is now verified`}); + }) + .catch((err) => { + console.error(err); + return res.status(500).json({error: err.code}); + }); +} + +// Unverifies the user sent to the request +// Must be run by admin +exports.unverifyUser = (req, res) => { + if (req.userData.handle !== "Admin") { + return res.status(403).json({error: "This must be done as Admin"}); + } + + db.doc(`/users/${req.body.user}`) + .get() + .then((doc) => { + if (doc.exists) { + let unverifiedUser = doc.data(); + unverifiedUser.verified = false; + return db.doc(`/users/${req.body.user}`).set(unverifiedUser, {merge: true}); + } else { + return res.status(400).json({error: `User ${req.body.user} was not found`}); + } + }) + .then(() => { + return res.status(201).json({message: `${req.body.user} is no longer verified`}); + }) + .catch((err) => { + console.error(err); + return res.status(500).json({error: err.code}); + }); +} \ No newline at end of file diff --git a/functions/index.js b/functions/index.js index 33ce166..59bc8db 100644 --- a/functions/index.js +++ b/functions/index.js @@ -16,7 +16,9 @@ const { login, signup, deleteUser, - updateProfileInfo + updateProfileInfo, + verifyUser, + unverifyUser } = require("./handlers/users"); // Adds a user to the database and registers them in firebase with @@ -41,6 +43,14 @@ app.post("/updateProfileInfo", fbAuth, updateProfileInfo); app.get("/user", fbAuth, getAuthenticatedUser); +// Verifies the user sent to the request +// Must be run by the Admin user +app.post("/verifyUser", fbAuth, verifyUser); + +// Unverifies the user sent to the request +// Must be run by admin +app.post("/unverifyUser", fbAuth, unverifyUser); + /*------------------------------------------------------------------* * handlers/post.js * *------------------------------------------------------------------*/ From 112988c8fb1dbc556b18dc5219ef7334a57724fb Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Thu, 31 Oct 2019 15:46:33 -0400 Subject: [PATCH 3/4] Add '@' to profile handle --- twistter-frontend/src/pages/user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/twistter-frontend/src/pages/user.js b/twistter-frontend/src/pages/user.js index ef764b3..824412e 100644 --- a/twistter-frontend/src/pages/user.js +++ b/twistter-frontend/src/pages/user.js @@ -76,7 +76,7 @@ class user extends Component { const classes = this.props; let profileMarkup = this.state.profile ? (
- {this.state.profile} {this.state.verified ? (): (null)} + @{this.state.profile} {this.state.verified ? (): (null)}
) : (

loading username...

); From 97bc9c4fb1912169b0cf524177b0ead5355018dc Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Fri, 1 Nov 2019 16:11:02 -0400 Subject: [PATCH 4/4] Verify users front-end --- functions/handlers/users.js | 3 +- twistter-frontend/src/App.js | 2 + twistter-frontend/src/pages/user.js | 32 +++++- twistter-frontend/src/pages/verify.js | 153 ++++++++++++++++++++++++++ 4 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 twistter-frontend/src/pages/verify.js diff --git a/functions/handlers/users.js b/functions/handlers/users.js index 1e9ecf0..a3cea0d 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -238,7 +238,8 @@ exports.deleteUser = (req, res) => { .then((query) => { query.forEach((snap) => { snap.ref.delete(); - }) + }); + return; }) let promises = [ diff --git a/twistter-frontend/src/App.js b/twistter-frontend/src/App.js index 71c16a2..aeec8ee 100644 --- a/twistter-frontend/src/App.js +++ b/twistter-frontend/src/App.js @@ -31,6 +31,7 @@ import Delete from "./pages/Delete"; import writeMicroblog from "./Writing_Microblogs.js"; import editProfile from "./pages/editProfile"; import userLine from "./Userline.js"; +import verify from "./pages/verify"; import Search from "./pages/Search.js"; const theme = createMuiTheme(themeObject); @@ -76,6 +77,7 @@ class App extends Component { + diff --git a/twistter-frontend/src/pages/user.js b/twistter-frontend/src/pages/user.js index 63a418a..3340b47 100644 --- a/twistter-frontend/src/pages/user.js +++ b/twistter-frontend/src/pages/user.js @@ -102,6 +102,7 @@ class user extends Component { render() { let authenticated = this.props.user.authenticated; let classes = this.props; + let profileMarkup = this.state.profile ? (
@{this.state.profile} {this.state.verified ? (): (null)} @@ -169,7 +170,36 @@ class user extends Component { onClick={this.handleAddCircle} />
- {authenticated && } + + + { + authenticated && + } + + + { + authenticated && + this.state.profile === 'Admin' && + } + + {postMarkup} diff --git a/twistter-frontend/src/pages/verify.js b/twistter-frontend/src/pages/verify.js new file mode 100644 index 0000000..b0c6583 --- /dev/null +++ b/twistter-frontend/src/pages/verify.js @@ -0,0 +1,153 @@ +import React, { Component } from "react"; +import axios from "axios"; +import PropTypes from "prop-types"; +// TODO: Add a read-only '@' in the left side of the handle input +// TODO: Add a cancel button, that takes the user back to their profile page + +// Material-UI stuff +import Button from "@material-ui/core/Button"; +import { Link } from 'react-router-dom'; +import CircularProgress from "@material-ui/core/CircularProgress"; +import Grid from "@material-ui/core/Grid"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; +import withStyles from "@material-ui/core/styles/withStyles"; + +const styles = { + form: { + textAlign: "center" + }, + textField: { + marginBottom: 30 + }, + pageTitle: { + // marginTop: 20, + marginBottom: 40 + }, + button: { + positon: "relative", + marginBottom: 10 + }, + progress: { + position: "absolute" + } +}; + +export class verify extends Component { + + // Constructor for the state + constructor() { + super(); + this.state = { + handle: "", + loading: false, + errors: {} + }; + } + +// // Runs whenever the submit button is clicked. + handleSubmit = (event) => { + event.preventDefault(); + this.setState({ + loading: true + }); + const verifyHandle = { + user: this.state.handle + }; + + axios + .post("/verifyUser", verifyHandle) + .then((res) => { + console.log(res); + this.setState({ + loading: false + }); + // this.props.history.push('/'); + // TODO: Need to redirect user to their profile page + }) + .catch((err) => { + console.log(err); + this.setState({ + errors: err.response.data, + loading: false + }); + }); + }; + + // Updates the state whenever one of the textboxes changes. + // The key is the name of the textbox and the value is the + // value in the text box. + // Also sets errors to null of textboxes that have been edited + handleChange = (event) => { + this.setState({ + [event.target.name]: event.target.value, + errors: { + [event.target.name]: null + } + }); + }; + + render() { + const { classes } = this.props; + const { errors, loading } = this.state; + + return ( + + + + + Verify Users + +
+ + + + + + + + + + +
+ + + ); + } +} + +verify.propTypes = { + classes: PropTypes.object.isRequired +}; + +export default withStyles(styles)(verify);