281 | ```
282 |
283 | * Create a nodejs application with mongodb. The ```-s``` option is for "Scaling"
284 |
285 | ```
286 | rhc app-create mysnowflake nodejs-0.10 mongodb-2.4 -s
287 |
288 | ```
289 |
290 | * Note that if you get an error during this step, most likely it has to do with copying the Openshift GIT repository to your local system. What you can do is to your OpenShift account and use the link they provided for the **Source Code**. Just ```git clone xxx``` where xxx is the link you copied from Openshift.
291 |
292 |
293 | * This next command will load the Redis cartridge
294 |
295 | ```
296 | rhc add-cartridge \
297 | http://cartreflect-claytondev.rhcloud.com/reflect?github=transformatordesign/openshift-redis-cart \
298 | -a mysnowflake
299 | ```
300 |
301 |
302 | * These next few steps copy the SnowFlake server to your local Git repository from Openshift.
303 |
304 | ```
305 | cd mysnowflake
306 | git remote add upstream -m master git://github.com/bartonhammond/snowflake-hapi-openshift.git
307 | git pull -s recursive -X theirs upstream master
308 | ```
309 |
310 | * Copy config.sample.js to config.js and provide values
311 |
312 | ```
313 | cp src/config.sample.js src/config.js
314 | ```
315 |
316 | * Then push the repo to OpenShift
317 |
318 | ```
319 | git push origin master
320 | ```
321 |
322 | * **That's it!!!**
323 |
324 | * You can now checkout your application at:[ http://mysnowflake-$yournamespace.rhcloud.com](http://mysnowflake-$yournamespace.rhcloud.com/)
325 |
326 |
327 | * If you want to have this project also in your personal GitHub account also, follow these steps but be **WARNED**: unless you use a "Private" repository, your
328 | ```config.js``` will be visible.
329 |
330 | * https://forums.openshift.com/how-to-keep-a-github-repository-and-an-openshift-repository-in-sync
331 |
--------------------------------------------------------------------------------
/src/docs/home.md:
--------------------------------------------------------------------------------
1 | # Home
2 |
3 | The quick brown paragraph jumps over the lazy lists.
4 |
5 | > "Hooray for block quotes!"
6 | > — Peter
7 |
8 | **Bold and a bulleted list:**
9 |
10 | - One fish
11 | - Two fish
12 |
13 | _Italics and an ordered list:_
14 |
15 | 1. Red fish
16 | 2. Blue fish
17 |
18 | ---
19 |
20 | ## Indented code block:
21 |
22 | var name = 'Princess Bubblegum';
23 |
24 | ## Generic code block (fenced):
25 |
26 | ```
27 | var fs = require('fs');
28 | fs.readFileSync('home.md', 'utf8');
29 | ```
30 |
31 | ## JavaScript code block (fenced):
32 |
33 | ```js
34 | var path = require('path');
35 | path.join(__dirname, 'home.md');
36 | ```
37 |
38 | ---
39 |
40 | ## Tables?
41 |
42 | | Name | Value |
43 | |----------|-------|
44 | | Jake | true |
45 | | Finn | true |
46 | | Ice King | false |
47 |
48 |
--------------------------------------------------------------------------------
/src/lib/Crypto.js:
--------------------------------------------------------------------------------
1 | /**
2 | * # ErrorAlert.js
3 | *
4 | * This class uses a component which displays the appropriate alert
5 | * depending on the platform
6 | *
7 | * The main purpose here is to determine if there is an error and then
8 | * plucking off the message depending on the shape of the error object.
9 | */
10 | 'use strict';
11 | /**
12 | * ## Imports
13 | *
14 | */
15 | // our configurations
16 | var Config = require('../config'),
17 | //the crypto library
18 | crypto = require('crypto'),
19 | //algorithm for encryption
20 | algorithm = 'aes-256-ctr',
21 | privateKey = Config.crypto.privateKey;
22 |
23 |
24 | /**
25 | * ### public decrypt
26 | *
27 | */
28 | exports.decrypt = function(password) {
29 | return decrypt(password);
30 | };
31 | /**
32 | * ### public encrypt
33 | *
34 | */
35 | exports.encrypt = function(password) {
36 | return encrypt(password);
37 | };
38 |
39 | /**
40 | * ## method to decrypt data(password)
41 | *
42 | */
43 | function decrypt(password) {
44 | var decipher = crypto.createDecipher(algorithm, privateKey);
45 | var dec = decipher.update(password, 'hex', 'utf8');
46 | dec += decipher.final('utf8');
47 | return dec;
48 | }
49 | /**
50 | * ## method to encrypt data(password)
51 | *
52 | */
53 | function encrypt(password) {
54 | var cipher = crypto.createCipher(algorithm, privateKey);
55 | var crypted = cipher.update(password, 'utf8', 'hex');
56 | crypted += cipher.final('hex');
57 | return crypted;
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/src/lib/Mailer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * # Mailer.js
3 | *
4 | * Create email and send content
5 | *
6 | *
7 | */
8 | 'use strict';
9 | /**
10 | * ## Imports
11 | *
12 | */
13 | var CONFIG = require('../config'),
14 | // kind of like underscore, but specific to Hapi
15 | Hoek = require('hoek'),
16 | // the email library
17 | nodemailer = require("nodemailer");
18 |
19 | /**
20 | * ## transporter
21 | * configure the email provider
22 | */
23 | var transporter = nodemailer.createTransport({
24 | service: "Gmail",
25 | auth: {
26 | user: CONFIG.email.username,
27 | pass: CONFIG.email.password
28 | }
29 | });
30 | /**
31 | * ## getUrl
32 | * Local or OpenShift
33 | *
34 | */
35 | function getUrl( ) {
36 | var url = '';
37 | if (process.env.OPENSHIFT_APP_DNS) {
38 | url = 'https://' + process.env.OPENSHIFT_APP_DNS;
39 | } else {
40 | url = 'http://127.0.0.1:8080';
41 | }
42 | return url;
43 | }
44 | /**
45 | * ## sendMailVerificationLink
46 | *
47 | * send email for verification of the account's email address
48 | *
49 | */
50 | exports.sendMailVerificationLink = function(user,token) {
51 | var url = getUrl();
52 | var from = CONFIG.email.accountName;
53 | var mailbody = "Thanks for Registering on"
54 | + " "
55 | + CONFIG.email.accountName
56 | +"
Please verify your email by clicking on the"
57 | + " verification link below.
Verification Link
";
62 | mail(from, user.email , "Account Verification", mailbody);
63 | };
64 | /**
65 | * ## sendMailResetPassword
66 | *
67 | * Set email to user so they can reset their password
68 | *
69 | */
70 | exports.sendMailResetPassword = function(user, token) {
71 |
72 | var url = getUrl();
73 | var from = CONFIG.email.accountName;
74 | var mailbody = "A reset password action has been requested from"
75 | + " "
76 | + CONFIG.email.accountName
77 | +"
Please click on the "
78 | + " reset password link below.
"
79 | + " The link is only available for 15 minutes.
"
80 | + "Reset Password Link
";
85 | mail(from, user.email , "Reset Password", mailbody);
86 | };
87 | /**
88 | * ## mail
89 | *
90 | * The main function, sends the email
91 | *
92 | * @param from who email is from
93 | * @param email who email is sent to
94 | * @param subject the subject of the email
95 | * @param mailbody the body of the email
96 | */
97 | function mail(from, email, subject, mailbody){
98 | var mailOptions = {
99 | from: from, // sender address
100 | to: email, // list of receivers
101 | subject: subject, // Subject line
102 | html: mailbody // html body
103 | };
104 | //Send email
105 | if (!CONFIG.email.test) {
106 | transporter.sendMail(mailOptions, function(error) {
107 | Hoek.assert(!error,error);
108 | });
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/routes/account/endpoints.js:
--------------------------------------------------------------------------------
1 | /**
2 | * # ErrorAlert.js
3 | *
4 | * This class uses a component which displays the appropriate alert
5 | * depending on the platform
6 | *
7 | * The main purpose here is to determine if there is an error and then
8 | * plucking off the message depending on the shape of the error object.
9 | */
10 | 'use strict';
11 | /**
12 | * ## Imports
13 | *
14 | */
15 | //Handle the endpoints
16 | var AccountHandlers = require('./handlers'),
17 | //The static configurations
18 | CONFIG = require('../../config'),
19 | //Joi is Hapi's validation library
20 | Joi = require('joi');
21 |
22 | var internals = {};
23 | /**
24 | * ## Set the method, path, and handler
25 | *
26 | * Note the account/logout requires authentication
27 | *
28 | * Note the validation of the account/register parameters
29 | *
30 | * Note account/register has same Regex expression as Snowflake client
31 | */
32 | internals.endpoints = [
33 | {
34 | method: 'POST',
35 | path: '/account/register',
36 | handler: AccountHandlers.registerUser,
37 | config: {
38 | // Include this API in swagger documentation
39 | tags: ['api'],
40 | description: 'Register user',
41 | notes: 'The user registration generates an email for verification',
42 | validate: {
43 | payload: {
44 | //username required with same regex as client
45 | username: Joi.string().regex(CONFIG.validation.username).required(),
46 | //password required with same regex as client
47 | password: Joi.string().regex(CONFIG.validation.password).required(),
48 | //email required
49 | email: Joi.string().email().required()
50 | }
51 | }
52 | }
53 | },
54 | {
55 | method: 'POST',
56 | path: '/account/login',
57 | handler: AccountHandlers.loginUser,
58 | config: {
59 | // Include this API in swagger documentation
60 | tags: ['api'],
61 | description: 'A user can login',
62 | notes: 'The user login will return a sessionToken',
63 | validate: {
64 | payload: {
65 | //username required with same regex as client
66 | username: Joi.string().regex(CONFIG.validation.username).required(),
67 | //password required with same regex as client
68 | password: Joi.string().regex(CONFIG.validation.password).required()
69 | }
70 | }
71 | }
72 | },
73 | {
74 | method: 'POST',
75 | path: '/account/logout',
76 | handler: AccountHandlers.logoutUser,
77 | config: {
78 | // Include this API in swagger documentation
79 | tags: ['api'],
80 | description: 'A user can logout',
81 | notes: 'A user may be already be logged in',
82 | //authorization optional
83 | auth: 'token',
84 | validate: {
85 | headers: Joi.object({
86 | 'Authorization': Joi.string()
87 | }).unknown()
88 | }
89 | }
90 | },
91 | {
92 | method: 'GET',
93 | path: '/account/verifyEmail/{token}',
94 | handler: AccountHandlers.verifyEmail,
95 | config: {
96 | // Include this API in swagger documentation
97 | tags: ['api'],
98 | description: 'Users email is verified',
99 | notes: 'User clicks link in email sent during registration',
100 | validate: {
101 | params: {
102 | token: Joi.string().required()
103 | }
104 | }
105 | }
106 | },
107 | {
108 | method: 'POST',
109 | path: '/account/resetPasswordRequest',
110 | handler: AccountHandlers.resetPasswordRequest,
111 | config: {
112 | // Include this API in swagger documentation
113 | tags: ['api'],
114 | description: 'User requests to reset password',
115 | notes: 'Email is sent to email address provided',
116 | validate: {
117 | payload: {
118 | //email required
119 | email: Joi.string().email().required()
120 | }
121 | }
122 |
123 | }
124 | },
125 | {
126 | //send html w/ password reset form
127 | method: 'GET',
128 | path: '/account/resetPassword/{token}',
129 | handler: AccountHandlers.displayResetPassword
130 | },
131 | {
132 | method: 'POST',
133 | path: '/account/resetPassword',
134 | handler: AccountHandlers.resetPassword,
135 | config: {
136 | // Include this API in swagger documentation
137 | tags: ['api'],
138 | description: 'User posts new password',
139 | notes: 'Password form posts new password',
140 | validate: {
141 | payload: {
142 | //password required with same regex as client
143 | password: Joi.string().regex(CONFIG.validation.password).required(),
144 | //email required
145 | token: Joi.string().required()
146 | }
147 | }
148 |
149 | }
150 | },
151 | {
152 | method: 'GET',
153 | path: '/account/profile/me',
154 | handler: AccountHandlers.getMyProfile,
155 | config: {
156 | auth: 'token',
157 | tags: ['api'],
158 | description: 'Get the current users profile',
159 | notes: 'The user has username, email and emailVerified',
160 | validate: {
161 | headers: Joi.object({
162 | 'Authorization': Joi.string()
163 | }).unknown()
164 | }
165 | }
166 | },
167 | {
168 | method: 'POST',
169 | path: '/account/profile/{_id}',
170 | handler: AccountHandlers.updateProfile,
171 | config: {
172 | auth: 'token',
173 | tags: ['api'],
174 | description: 'Update user profile',
175 | notes: 'User is able to change their username and email',
176 | validate: {
177 | payload: {
178 | //email required
179 | email: Joi.string().email().required(),
180 | //username required
181 | username: Joi.string().regex(CONFIG.validation.username).required()
182 | },
183 | params: {
184 | _id: Joi.string().required()
185 | },
186 | headers: Joi.object({
187 | 'Authorization': Joi.string()
188 | }).unknown()
189 | }
190 | }
191 | },
192 |
193 | ];
194 |
195 |
196 | module.exports = internals;
197 |
--------------------------------------------------------------------------------
/src/routes/account/handlers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * # account/handlers.js
3 | *
4 | * This handles all the account actions
5 | *
6 | *
7 | */
8 | 'use strict';
9 | /**
10 | * ## Imports
11 | *
12 | */
13 | //Boom is an abstraction over http error codes
14 | var Boom = require('boom'),
15 | // our configuration
16 | CONFIG = require('../../config'),
17 | // our encrpyt and decrypt
18 | Crypto = require('../../lib/Crypto'),
19 | // support for token signing and verification
20 | JasonWebToken = require('jsonwebtoken'),
21 | // the Hapi strategy for jwt
22 | JwtAuth = require('../../auth/jwt-strategy'),
23 | // how we email
24 | Mailer = require('../../lib/Mailer'),
25 | // time/date functions
26 | Moment = require('moment'),
27 | // the client for redis
28 | redisClient = require('../../database/redis'),
29 | // helper library
30 | _ = require('underscore'),
31 | // our user in mongodb
32 | User = require('../../database/models/User');
33 |
34 | var internals = {};
35 | /**
36 | * ## registerUser
37 | *
38 | * Encrypt the password and store the user
39 | */
40 | internals.registerUser = function (req, reply) {
41 | console.log('registerUser',req.payload);
42 | req.payload.password = Crypto.encrypt(req.payload.password);
43 | req.payload.emailVerified = false;
44 | var user = new User(req.payload);
45 |
46 | //save the user w/ the encrypted password
47 | user.save(function (err, user) {
48 | if (err) {
49 | reply(Boom.conflict(err));
50 | } else {
51 | var tokenData = {
52 | username: user.username,
53 | id: user._id
54 | };
55 | // send an email verification with a JWT token
56 | Mailer.sendMailVerificationLink(user,
57 | JasonWebToken.sign(tokenData,
58 | CONFIG.crypto.privateKey));
59 |
60 | /** user:
61 | register { _id: 56844c798d4dce65e2b45b6e,
62 | emailVerified: false,
63 | password: 'd5be02df44dafbbcfb',
64 | email: 'barton@acclivyx.com',
65 | username: 'barton',
66 | __v: 0 }
67 | */
68 | //Let the user know they are registered
69 | //Note that the token is created only with the user._id since
70 | //the user can change their username & email
71 | //If the token embeds either of those fields, it becomes
72 | //an invalid token once the user changes those fields
73 | reply({
74 | statusCode: 201,
75 | objectId: user._id,
76 | sessionToken: JwtAuth.createToken({ id: user._id})
77 | });
78 | }
79 | });
80 | };
81 | /**
82 | * ## loginUser
83 | *
84 | * Find the user by username, verify the password matches and return
85 | * the user
86 | *
87 | */
88 | internals.loginUser = function (req, reply) {
89 | User.findOne({ username: req.payload.username }, function (err, user) {
90 |
91 | if (err) {
92 | reply(Boom.unauthorized('Authentication failed'));
93 | }
94 |
95 | if (_.isNull(user)) {
96 | reply(Boom.unauthorized('Authentication failed'));
97 | } else {
98 | if (Crypto.decrypt(user.password) !=
99 | req.payload.password) {
100 | reply(Boom.unauthorized('Authentication failed'));
101 | } else {
102 | reply({
103 | email: user.email,
104 | objectId: user._id,
105 | username: user.username,
106 | sessionToken: JwtAuth.createToken({
107 | id: user._id
108 | })
109 | });//reply
110 | }
111 | }
112 |
113 | });
114 | };
115 | /**
116 | * ## logoutUser
117 | *
118 | * Create a token blacklist with Redis
119 | * see: https://auth0.com/blog/2015/03/10/blacklist-json-web-token-api-keys/
120 | *
121 | */
122 | internals.logoutUser = function(req, reply) {
123 | var headers = req.headers.authorization.split(' ');
124 | redisClient.set(headers[1], new Date());
125 | reply({});
126 | };
127 | /**
128 | * ## verifyEmail
129 | *
130 | * If the token is verified, find the user using the decoded info
131 | * from the token.
132 | *
133 | * Set the emailVeried to true if user is found
134 | *
135 | */
136 | internals.verifyEmail = function (req, reply) {
137 | JasonWebToken.verify(req.params.token, CONFIG.crypto.privateKey, function(err, decoded) {
138 | if(decoded === undefined) {
139 | return reply(Boom.forbidden("invalid verification link"));
140 | }
141 |
142 | User.findUserByIdAndUserName(decoded.id, decoded.username, function(err, user){
143 | //oops, something wrong
144 | if (err) {
145 | return reply(Boom.badImplementation(err));
146 | }
147 |
148 | //No user found for this link
149 | if (user === null) {
150 | return reply(Boom.forbidden("invalid verification link"));
151 | }
152 |
153 | //not sure if this is really an error...
154 | if (user.isVerified === true) {
155 | return reply(Boom.forbidden("account is already verified"));
156 | }
157 |
158 | //Everything is fine, update the users flag for emailVerified
159 | user.emailVerified = true;
160 | user.save(function(err){
161 | if (err) {
162 | return reply(Boom.badImplementation(err));
163 | }
164 | return reply("account sucessfully verified");
165 | });
166 | })
167 |
168 | });
169 | };
170 | /**
171 | * ## resetPasswordRequest
172 | *
173 | */
174 | internals.resetPasswordRequest = function (req, reply) {
175 | User.findUserByEmail(req.payload.email, function(err, user) {
176 | if (err) {
177 | return reply(Boom.badImplementation(err));
178 | }
179 |
180 |
181 | //Provide no indication if user exists
182 | if (user) {
183 | var tokenData = {
184 | username: user.username,
185 | id: user._id
186 | };
187 |
188 | //The token will have id & username encrypted
189 | Mailer.sendMailResetPassword(
190 | user,
191 | JasonWebToken.sign(tokenData,
192 | CONFIG.crypto.privateKey));
193 |
194 | reply({});
195 | }
196 | });
197 | };
198 |
199 |
200 | /**
201 | * Display the resetPassword form
202 | */
203 | /**
204 | * ## Imports
205 | *
206 | */
207 | internals.displayResetPassword = function (req, reply) {
208 | reply.view('resetpassword.html', {token: req.params.token});
209 | };
210 |
211 | /**
212 | * Update password of user
213 | */
214 | /**
215 | * ## Imports
216 | *
217 | */
218 | internals.resetPassword = function (req, reply) {
219 |
220 | JasonWebToken.verify(req.payload.token, CONFIG.crypto.privateKey, function(err, decoded) {
221 |
222 | if(decoded === undefined) {
223 | return reply(Boom.forbidden("invalid resetPassword link"));
224 | }
225 |
226 | //Must fit w/in time allocated
227 | var diff = Moment().diff(Moment(decoded.iat * 1000));
228 | if (diff > CONFIG.crypto.tokenExpires) {
229 | return reply(Boom.unauthorized('reset password allowed time has past'));
230 | }
231 |
232 | User.findUserByIdAndUserName(decoded.id, decoded.username, function(err, user){
233 | if (err) {
234 | return reply(Boom.badImplementation(err));
235 | }
236 | if (user === null) {
237 | return reply(Boom.forbidden("invalid resetPassword link"));
238 | }
239 |
240 | user.password = Crypto.encrypt(req.payload.password);
241 | user.save(function(err){
242 | if (err) {
243 | return reply(Boom.badImplementation(err));
244 | }
245 |
246 | return reply("account password updated");
247 |
248 | });
249 | })
250 | });
251 |
252 | };
253 | /**
254 | * ## getMyProfile
255 | *
256 | * We only get here through authentication
257 | *
258 | * note: the user is available from the credentials!
259 | */
260 | internals.getMyProfile = function (req, reply) {
261 | reply({
262 | objectId: req.auth.credentials._id,
263 | username: req.auth.credentials.username,
264 | email: req.auth.credentials.email,
265 | emailVerified: req.auth.credentials.emailVerified,
266 | sessionToken: req.headers.authorization.split(' ')[1]
267 | });
268 | };
269 | /**
270 | * ## getMyProfile
271 | *
272 | * We only get here through authentication
273 | *
274 | * note: the user is available from the credentials!
275 | */
276 | internals.updateProfile = function (req, reply) {
277 | User.findById(req.params._id, function(err, user) {
278 | if (err) {
279 | return reply(Boom.badImplementation(err));
280 | }
281 |
282 |
283 | //Provide no indication if user exists
284 | if (user) {
285 | user.username = req.payload.username;
286 |
287 | //If user changed email, it needs to be verified
288 | if (user.email !== req.payload.email) {
289 | user.emailVerified = false;
290 | }
291 | user.email = req.payload.email;
292 |
293 |
294 | user.save(function(err, updatedUser) {
295 | if (err) {
296 | return reply(Boom.conflict("User couldn't be saved."));
297 | }
298 |
299 | //Send verification email if needed
300 | if (!updatedUser.emailVerified) {
301 | var tokenData = {
302 | username: user.username,
303 | id: user._id
304 | };
305 | // send an email verification with a JWT token
306 | Mailer.sendMailVerificationLink(user,
307 | JasonWebToken.sign(tokenData,
308 | CONFIG.crypto.privateKey));
309 | }
310 | reply({});
311 | });
312 |
313 | }
314 | });
315 | };
316 |
317 | module.exports = internals;
318 |
--------------------------------------------------------------------------------
/src/routes/general/endpoints.js:
--------------------------------------------------------------------------------
1 | /**
2 | * # general/endpoints.js
3 | *
4 | * This supports a status and env request
5 | *
6 | */
7 | 'use strict';
8 | /**
9 | * ## Imports
10 | *
11 | */
12 | var GeneralHandlers = require('./handlers'),
13 | internals = {};
14 | /**
15 | * ## endpoints
16 | *
17 | * both are simple gets
18 | */
19 | internals.endpoints = [
20 | {
21 | method: 'GET',
22 | path: '/',
23 | handler: GeneralHandlers.index,
24 | config: {
25 | description: 'Get the default/home template.',
26 | notes: 'Renders the /docs/home.md file as HTML.',
27 | tags: ['api']
28 | }
29 | },
30 | {
31 | method: 'GET',
32 | path: '/status',
33 | handler: GeneralHandlers.status,
34 | config: {
35 | description: 'Show the status.',
36 | notes: 'renders json if server is running',
37 | tags: ['api']
38 | }
39 | },
40 | {
41 | method: 'GET',
42 | path: '/env',
43 | handler: GeneralHandlers.env,
44 | config: {
45 | description: 'Show the environment variables.',
46 | notes: 'Renders the variables known to the server',
47 | tags: ['api']
48 | }
49 | }
50 |
51 | ];
52 |
53 | module.exports = internals;
54 |
--------------------------------------------------------------------------------
/src/routes/general/handlers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * # general/handlers.js
3 | *
4 | * Simple display of status and the environment we're running in
5 | *
6 | */
7 | 'use strict';
8 |
9 | /**
10 | * ## Declaration
11 | *
12 | */
13 | var internals = {};
14 |
15 | /**
16 | * ## status - are we alive?
17 | *
18 | */
19 | internals.index = function (req, reply) {
20 | //src/docs/home.md is a markdown file
21 | reply.view('README.md');
22 | };
23 | /**
24 | * ## status - are we alive?
25 | *
26 | */
27 | internals.status = function (req, reply) {
28 | reply( {"status": "ok"} );
29 | };
30 |
31 | /**
32 | * ## env - display the environment variables available
33 | *
34 | */
35 | internals.env = function (req, reply) {
36 | var content = 'Node Version: ' + process.version + '\n
\n' +
37 | 'Env: {
\n';
38 | // Add env entries.
39 | for (var k in process.env) {
40 | content += ' ' + k + ': ' + process.env[k] + '\n';
41 | }
42 | content += '}\n
\n';
43 | reply('\n' +
44 | ' Node.js Process Env\n' +
45 | ' \n
\n' + content + '\n');
46 |
47 | }
48 |
49 | module.exports = internals;
50 |
--------------------------------------------------------------------------------
/src/routes/restricted/endpoints.js:
--------------------------------------------------------------------------------
1 | /**
2 | * # This is our one restricted endpoint (beside logout)
3 | *
4 | */
5 | 'use strict';
6 |
7 | /**
8 | * ## Imports
9 | *
10 | */
11 | var RestrictedHandlers = require('./handlers');
12 |
13 | var internals = {};
14 | /**
15 | * ## endpoints
16 | *
17 | * note the config which means authentication is required to access
18 | * this end point
19 | *
20 | */
21 | internals.endpoints = [
22 | {
23 | method: 'GET',
24 | path: '/restricted',
25 | handler: RestrictedHandlers.restrictedArea,
26 | config: {
27 | auth: 'token'
28 | }
29 | }
30 | ];
31 |
32 | module.exports = internals;
33 |
--------------------------------------------------------------------------------
/src/routes/restricted/handlers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * # restricted/handlers.js
3 | *
4 | * Display simple message if user has access
5 | *
6 | */
7 | 'use strict';
8 |
9 | var internals = {};
10 | /**
11 | * ## restrictedArea - you can only reach here if you've passed
12 | * authentication
13 | *
14 | * note: the user name is available from the credentials!
15 | */
16 | internals.restrictedArea = function (req, reply) {
17 | reply({ message: req.auth.credentials.username + ', welcome to restricted area!' });
18 | };
19 |
20 | module.exports = internals;
21 |
--------------------------------------------------------------------------------
/src/views/resetpassword.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Snowflake - Reset Password
5 |
6 |
7 |
8 | Snowflake - Reset Password
9 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------