Skip to content

Commit 1f21501

Browse files
committed
sends emails now
1 parent 1fd6546 commit 1f21501

File tree

7 files changed

+319
-2
lines changed

7 files changed

+319
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
node_modules
22
.sublime-workspace
33
.sublime-project
4+
.env

api/api.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
var account = require("./account");
22
var auth = require("./auth");
3+
var forgot = require("./forgot");
34
var scoreboard = require("./scoreboard");
45

56
module.exports = function(app) {
@@ -39,4 +40,11 @@ module.exports = function(app) {
3940
app.get("/api/scoreboard/graph", function(req, res) {
4041
scoreboard.get_graph(req, res);
4142
});
43+
44+
// *******************
45+
// FORGOT PASSWORD
46+
// *******************
47+
app.post("/api/forgot", function(req, res) {
48+
forgot.send_reset_email(req, res);
49+
});
4250
};

api/forgot.js

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
var common = require("./common");
2+
var moment = require("moment");
3+
require("dotenv").load();
4+
5+
// THIS INFO GOES INTO A .env FOLDER OR YOU CAN PROVIDE IT AS ENVIRONMENTAL VARIABLES ON YOUR HOST
6+
var sendgrid = require("sendgrid")(process.env.SENDGRID_USERNAME, process.env.SENDGRID_PASSWORD);
7+
8+
// *************
9+
// CONSTANTS
10+
// *************
11+
// change this stuff
12+
var FROM_EMAIL = "[email protected]";
13+
var FROM_NAME = "Your CTF";
14+
var DOMAIN = "yourctf.com";
15+
16+
exports.send_reset_email = function(req, res) {
17+
var email = req.param("email");
18+
19+
if (!email) {
20+
res.send({
21+
success: 0,
22+
message: "You have to enter an email!",
23+
});
24+
return;
25+
}
26+
27+
common.db.collection("accounts").find({
28+
email: email
29+
}).toArray(function(err, data) {
30+
if (err) {
31+
res.send({
32+
success: 0,
33+
message: "An internal error occurred."
34+
});
35+
return;
36+
}
37+
38+
if (data.length == 0) {
39+
res.send({
40+
success: 0,
41+
message: "Couldn't find your email. Try signing up?"
42+
});
43+
return;
44+
}
45+
46+
if (data.length > 1) {
47+
res.send({
48+
success: 0,
49+
message: ""
50+
});
51+
return;
52+
}
53+
54+
var checkTeam = data[0];
55+
var salt = common.generateSalt();
56+
var code = common.hash(email+salt, "sha256") + salt;
57+
var expire = moment().add(48, "hours");
58+
59+
// deactivate existing codes for this email
60+
common.db.collection("reset_password").update({
61+
email: email,
62+
active: true
63+
}, {
64+
$set: {
65+
active: false
66+
}
67+
}, function(err2, data2) {
68+
// insert a request
69+
common.db.collection("reset_password").insert({
70+
code: code,
71+
email: email,
72+
salt: salt,
73+
expire: expire.format(),
74+
active: true
75+
}, { w: 1 }, function(err3, data3) {
76+
if (err3) {
77+
res.send({
78+
success: 0,
79+
message: "Couldn't make a password reset request."
80+
});
81+
return;
82+
}
83+
84+
sendgrid.send({
85+
to: email,
86+
from: FROM_EMAIL,
87+
fromname: FROM_NAME,
88+
replyto: FROM_EMAIL,
89+
subject: "Password Reset Requested.",
90+
html: '<p>Hey there</p><p>Someone (hopefully you) requested to change the password for the account with the email ' + req.param('email') + '. Obviously, we\'ll be asking for verification of identity, so if you did in fact request this, follow this link: http://' + DOMAIN + '/forgot/' + code + "</p><p>" + FROM_NAME + "</p>"
91+
}, function(err4, json) {
92+
if (err4) {
93+
console.dir(err4);
94+
res.send({
95+
success: 0,
96+
message: "Failed to send email"
97+
});
98+
return;
99+
} else {
100+
res.send({
101+
success: 1,
102+
message: "Email successfully sent!"
103+
});
104+
return;
105+
}
106+
});
107+
});
108+
});
109+
});
110+
};
111+
112+
exports.verify_code = function(req, res) {
113+
var code = req.param("code");
114+
var pass = req.param("p1");
115+
var confirm = req.param("p2");
116+
117+
if (!(code && pass && confirm)) {
118+
res.send({
119+
success: 0,
120+
message: "You must fill out all of the fields!"
121+
});
122+
return;
123+
}
124+
125+
if (pass !== confirm) {
126+
res.send({
127+
success: 0,
128+
message: "Passwords don't match."
129+
});
130+
return;
131+
}
132+
133+
common.db.collection("reset_password").find({
134+
code: code,
135+
active: true
136+
}).toArray(function(err, data) {
137+
if (err) {
138+
res.send({
139+
success: 0,
140+
message: "Internal error.",
141+
});
142+
return;
143+
}
144+
if (data.length == 0) {
145+
res.send({
146+
success: 0,
147+
message: "Couldn't find an active ticket for this code. Try requesting another"
148+
});
149+
return;
150+
}
151+
152+
var obj = data[0];
153+
var now = moment();
154+
var exp = moment(obj.expire);
155+
if (now.isBefore(exp) && obj.active) {
156+
var salted = common.encryptPass(pass); //yum
157+
common.db.collection("reset_password").update({
158+
code: code
159+
}, {
160+
$set: {
161+
active: false
162+
}
163+
}, function(err2, data2) {
164+
if (err2) {
165+
res.send({
166+
success: 0,
167+
message: "Internal error.",
168+
});
169+
return;
170+
}
171+
common.db.collection("accounts").update({
172+
email: obj.email
173+
}, {
174+
$set: {
175+
pass: salted
176+
}
177+
}, function(err3, data3) {
178+
if (err3) {
179+
res.send({
180+
success: 0,
181+
message: "Internal error.",
182+
});
183+
return;
184+
} else {
185+
res.send({
186+
success: 1,
187+
message: "Your password has been changed!"
188+
});
189+
return;
190+
}
191+
});
192+
});
193+
} else {
194+
res.send({
195+
success: 0,
196+
message: "Maybe your code expired?"
197+
});
198+
return;
199+
}
200+
});
201+
}

app/pages/forgot.html

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
6+
7+
<title>Forgot Password - CTF Platform</title>
8+
9+
<link rel="stylesheet" href="css/main.css" media="screen" />
10+
<link rel="icon" href="images/logo.ico" media="screen" />
11+
</head>
12+
13+
<body>
14+
<nav role="navigation" class="navbar navbar-default">
15+
<div class="container-fluid">
16+
<div class="navbar-header">
17+
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-content">
18+
<span class="sr-only">Toggle Navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span>
19+
</button><a href="/" class="navbar-brand">CTF-Platform</a>
20+
</div>
21+
<div id="navbar-content" class="collapse navbar-collapse">
22+
<ul class="nav navbar-nav" id="nav-left">
23+
</ul>
24+
<ul class="nav navbar-nav navbar-right" id="nav-right">
25+
</ul>
26+
</div>
27+
</div>
28+
</nav>
29+
30+
<div class="container">
31+
<div class="container">
32+
<div class="page-header">
33+
<h1>Forgot Password</h1>
34+
</div>
35+
</div>
36+
37+
<div class="container">
38+
<div id="forgot_msg"></div>
39+
<div class="well col-lg-7">
40+
<div>
41+
<form action="javascript:dispatch();" class="form-horizontal">
42+
<fieldset>
43+
<legend>Enter the email you used to sign up.</legend>
44+
<div class="form-group">
45+
<label for="forgot-email" class="col-lg-3 control-label">Email</label>
46+
<div class="col-lg-8">
47+
<input type="email" name="forgot-email" id="forgot-email" placeholder="Email" autofocus="on" class="form-control">
48+
</div>
49+
</div>
50+
<div class="form-group">
51+
<label class="col-lg-3 control-label"></label>
52+
<div class="col-lg-7">
53+
<button id="forgot-btn" onClick="javascript:dispatch(); return false;" class="btn btn-primary">Send Password Reset Email</button>
54+
</div>
55+
</div>
56+
</fieldset>
57+
</form>
58+
</div>
59+
</div>
60+
</div>
61+
62+
<div id="footer"></div>
63+
</div>
64+
65+
<script type="text/javascript" src="js/jquery.js"></script>
66+
<script type="text/javascript" src="js/bootstrap.min.js"></script>
67+
<script type="text/javascript" src="js/dependencies.js"></script>
68+
<script type="text/javascript" src="js/forgot.js"></script>
69+
<script type="text/javascript">
70+
$(function() {
71+
display_navbar();
72+
load_footer();
73+
});
74+
</script>
75+
</body>
76+
</html>

app/router.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ var pages = [
1010
"register",
1111
"about",
1212
"updates",
13-
"scoreboard"
13+
"scoreboard",
14+
"forgot"
1415
];
1516

1617
var auth_pages = [

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
"private": true,
55
"dependencies": {
66
"async": "^0.9.0",
7+
"dotenv": "^0.4.0",
78
"express": "3.x",
9+
"html-entities": "^1.1.1",
810
"moment": "^2.8.4",
911
"mongodb": "^1.4.23",
10-
"html-entities": "^1.1.1"
12+
"sendgrid": "^1.3.0"
1113
},
1214
"engines": {
1315
"node": "0.10.x"

web/js/forgot.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
(function() {
2+
window.dispatch = function() {
3+
$("[id^=forgot-]").attr("disabled", "disabled");
4+
$.ajax({
5+
url: "/api/forgot",
6+
dataType: "json",
7+
data: {
8+
email: $("#forgot-email").val()
9+
},
10+
type: "POST",
11+
success: function(data) {
12+
var alert_class = "";
13+
if (data.success == 1) {
14+
alert_class = "success";
15+
} else {
16+
alert_class = "danger";
17+
$("[id^=forgot-]").removeAttr("disabled");
18+
}
19+
$("#forgot_msg").hide().html("<div class=\"alert alert-" + alert_class + "\"> " + data['message'] + " </div>").slideDown("normal");
20+
setTimeout(function() {
21+
$("#forgot_msg").slideUp("normal", function() {
22+
return $("#forgot_msg").html("").show();
23+
});
24+
}, 2000);
25+
},
26+
});
27+
};
28+
})();

0 commit comments

Comments
 (0)